Merge remote-tracking branch 'origin/master' into buoyzone_drawing

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
This commit is contained in:
fmittag
2013-02-18 17:50:32 +01:00
46 changed files with 3260 additions and 581 deletions
+5 -1
View File
@@ -90,6 +90,10 @@ MAVEN_SETTINGS=$ACDIR/maven-settings.xml
if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
# yield build so that we get updated product
# preserve old build log
cp $ACDIR/build.log $ACDIR/build.log.old
cd $PROJECT_HOME/java
if [ $gwtcompile -eq 1 ]; then
echo "INFO: Compiling GWT (rm -rf com.sap.sailing.gwt.ui/com.sap.sailing.*)"
@@ -116,7 +120,7 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
extra="$extra -P no-debug.without-proxy"
fi
echo "Using following command: mvn $extra -P no-debug.without-proxy -fae -s $MAVEN_SETTINGS $clean install"
echo "Using following command: mvn $extra -fae -s $MAVEN_SETTINGS $clean install"
mvn $extra -fae -s $MAVEN_SETTINGS $clean install 2>&1 | tee $ACDIR/build.log
echo "Build complete. Do not forget to install product..."
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
public interface PolarSheetGenerationTriggerResponse extends Serializable{
public String getId();
public String getBoatClassName();
}
@@ -0,0 +1,15 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
public interface PolarSheetsData extends Serializable {
Number[][] getAveragedPolarDataByWindSpeed();
int getDataCount();
boolean isComplete();
Integer[] getDataCountPerAngleForWindspeed(int beaufort);
}
@@ -0,0 +1,16 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
public interface PolarSheetsHistogramData extends Serializable{
public int getAngle();
public int getDataCount();
public Number[] getyValues();
public Number[] getxValues();
}
@@ -0,0 +1,30 @@
package com.sap.sailing.domain.common.impl;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
public class PolarSheetGenerationTriggerResponseImpl implements PolarSheetGenerationTriggerResponse {
private static final long serialVersionUID = -2160795576114448218L;
private String id;
private String boatClassName;
// For GWT serialization
PolarSheetGenerationTriggerResponseImpl() {
};
public PolarSheetGenerationTriggerResponseImpl(String id, String boatClassName) {
this.id = id;
this.boatClassName = boatClassName;
}
@Override
public String getId() {
return id;
}
@Override
public String getBoatClassName() {
return boatClassName;
}
}
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.common.impl;
import java.util.Map;
import com.sap.sailing.domain.common.PolarSheetsData;
public class PolarSheetsDataImpl implements PolarSheetsData {
private static final long serialVersionUID = -4649254807341866894L;
private Number[][] averagedPolarDataByWindSpeed;
private boolean complete;
private int dataCount;
private Map<Integer,Integer[]> dataCountPerAngleForWindspeed;
//For GWT Serialization
PolarSheetsDataImpl() {};
public PolarSheetsDataImpl(Number[][] averagedPolarDataByWindSpeed, boolean complete, int dataCount, Map<Integer,Integer[]> dataCountPerAngleForWindspeed) {
this.averagedPolarDataByWindSpeed = averagedPolarDataByWindSpeed;
this.complete = complete;
this.dataCount = dataCount;
this.dataCountPerAngleForWindspeed = dataCountPerAngleForWindspeed;
}
@Override
public Number[][] getAveragedPolarDataByWindSpeed() {
return averagedPolarDataByWindSpeed;
}
@Override
public boolean isComplete() {
return complete;
}
@Override
public int getDataCount() {
return dataCount;
}
@Override
public Integer[] getDataCountPerAngleForWindspeed(int beaufort) {
return dataCountPerAngleForWindspeed.get(beaufort);
}
}
@@ -0,0 +1,48 @@
package com.sap.sailing.domain.common.impl;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
public class PolarSheetsHistogramDataImpl implements PolarSheetsHistogramData {
private static final long serialVersionUID = 7492650285773447757L;
private Number[] yValues;
private Number[] xValues;
private int angle;
private int dataCount;
//For GWT serialization
PolarSheetsHistogramDataImpl() {}
public PolarSheetsHistogramDataImpl(int angle, Number[] xValues, Number[] yValues, int dataCount) {
super();
this.angle = angle;
this.yValues = yValues;
this.xValues = xValues;
this.dataCount = dataCount;
}
@Override
public Number[] getyValues() {
return yValues;
}
@Override
public Number[] getxValues() {
return xValues;
}
@Override
public int getAngle() {
return angle;
}
@Override
public int getDataCount() {
return dataCount;
}
}
@@ -84,4 +84,6 @@ public interface MongoObjectFactory {
void storeRegattaForRaceID(String id, Regatta regatta);
void removeRegattaForRaceID(String raceIDAsString, Regatta regatta);
}
@@ -467,4 +467,11 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
entry.put(FieldNames.REGATTA_NAME.name(), regatta.getName());
regattaForRaceIDCollection.update(query, entry, /* upsrt */ true, /* multi */ false);
}
@Override
public void removeRegattaForRaceID(String raceIDAsString, Regatta regatta) {
DBCollection regattaForRaceIDCollection = database.getCollection(CollectionNames.REGATTA_FOR_RACE_ID.name());
DBObject query = new BasicDBObject(FieldNames.RACE_ID_AS_STRING.name(), raceIDAsString);
regattaForRaceIDCollection.remove(query);
}
}
@@ -0,0 +1,137 @@
package com.sap.sailing.domain.test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.NavigableSet;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.polarsheets.PerRaceAndCompetitorPolarSheetGenerationWorker;
import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker;
import com.sap.sailing.domain.test.mock.MockedTrackedRace;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
public class PolarSheetGenerationTest {
@Test
public void testPolarSheetRawDataGeneration() throws InterruptedException {
Executor executor = new ThreadPoolExecutor(/* corePoolSize */ 0,
/* maximumPoolSize */ Runtime.getRuntime().availableProcessors(),
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>());
MockTrackedRaceForPolarSheetGeneration race = new MockTrackedRaceForPolarSheetGeneration();
TimePoint startTime = new MillisecondsTimePoint(1);
TimePoint endTime = new MillisecondsTimePoint(4);
//Only used for storing and exporting results in this test case:
PolarSheetGenerationWorker resultContainer = new PolarSheetGenerationWorker(new HashSet<TrackedRace>(), executor);
BoatClass forelle = new BoatClassImpl("Forelle", true);
Competitor competitor = new CompetitorImpl(UUID.randomUUID(), "Hans Frantz", new TeamImpl("SAP", null, null), new BoatImpl("Schnelle Forelle", forelle, "GER000"));
PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker(race, resultContainer, startTime, endTime, competitor);
executor.execute(task);
double timeUntilTimeout = 1000;
while (!task.isDone() && timeUntilTimeout > 0) {
Thread.sleep(100);
timeUntilTimeout = timeUntilTimeout - 0.1;
}
Assert.assertTrue(task.isDone());
PolarSheetsData data = resultContainer.getPolarData();
Assert.assertEquals(4, data.getDataCount());
Assert.assertEquals(4.0, data.getAveragedPolarDataByWindSpeed()[1][45]);
Assert.assertEquals(2.0, data.getAveragedPolarDataByWindSpeed()[1][55]);
Assert.assertEquals(6.0, data.getAveragedPolarDataByWindSpeed()[1][30]);
}
@SuppressWarnings("serial")
private class MockTrackedRaceForPolarSheetGeneration extends MockedTrackedRace {
@Override
public DynamicGPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new MockDynamicGPSFixMovinTrackForPolarSheetGeneration<Competitor>(competitor, 0);
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.0, 1.0), new MillisecondsTimePoint(1), new KnotSpeedWithBearingImpl(3.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.001, 1.001), new MillisecondsTimePoint(2), new KnotSpeedWithBearingImpl(5.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.002, 1.002), new MillisecondsTimePoint(3), new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(-55.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.003, 1.003), new MillisecondsTimePoint(4), new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(-30.0))));
return track;
}
@Override
public Wind getWind(Position p, TimePoint at) {
Wind wind = new WindImpl(p, at, new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0)));
return wind;
}
@Override
public RaceDefinition getRace() {
BoatClass forelle = new BoatClassImpl("Forelle", true);
RaceDefinition race = new RaceDefinitionImpl("Forelle1", new CourseImpl("ForelleCourse", new ArrayList<Waypoint>()), forelle, new ArrayList<Competitor>());
return race;
}
}
@SuppressWarnings("serial")
private class MockGPSFixMovingForPolarSheetGeneration extends GPSFixMovingImpl {
public MockGPSFixMovingForPolarSheetGeneration(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(position, timePoint, speed);
}
@Override
public boolean isValid() {
return true;
}
}
@SuppressWarnings("serial")
private class MockDynamicGPSFixMovinTrackForPolarSheetGeneration<ItemType> extends DynamicGPSFixMovingTrackImpl<ItemType> {
public MockDynamicGPSFixMovinTrackForPolarSheetGeneration(ItemType trackedItem,
long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
}
@Override
protected boolean isValid(NavigableSet<GPSFixMoving> rawFixes, GPSFixMoving e) {
return true;
}
}
}
@@ -13,6 +13,7 @@ Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.leaderboard,
com.sap.sailing.domain.leaderboard.impl,
com.sap.sailing.domain.leaderboard.meta,
com.sap.sailing.domain.polarsheets,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl,
com.sap.sailing.util,
@@ -29,12 +29,6 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
private static final long serialVersionUID = 2370461218294770084L;
private final Regatta regatta;
/**
* If this member is <code>null</code>, {@link #getName()} will use the regatta name as the default name for this
* leaderboard. Otherwise, the {@link #displayName} is used.
*/
private String displayName;
public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection,
ThresholdBasedResultDiscardingRule resultDiscardingRule) {
super(scoreCorrection, resultDiscardingRule);
@@ -47,7 +41,7 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
*/
@Override
public void setName(String newName) {
displayName = newName;
setDisplayName(newName);
}
@Override
@@ -58,8 +52,8 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
@Override
public String getName() {
String result;
if (displayName != null) {
result = displayName;
if (getDisplayName() != null) {
result = getDisplayName();
} else {
result = getRegatta().getName();
}
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.polarsheets;
import com.sap.sailing.domain.common.Speed;
public interface BoatAndWindSpeed {
public Speed getBoatSpeed();
public Speed getWindSpeed();
}
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.polarsheets;
import com.sap.sailing.domain.common.Speed;
public class BoatAndWindSpeedImpl implements BoatAndWindSpeed{
private Speed boatSpeed;
private Speed windSpeed;
public BoatAndWindSpeedImpl(Speed boatSpeed, Speed windSpeed) {
this.boatSpeed = boatSpeed;
this.windSpeed = windSpeed;
}
@Override
public Speed getBoatSpeed() {
return boatSpeed;
}
@Override
public Speed getWindSpeed() {
return windSpeed;
}
}
@@ -0,0 +1,109 @@
package com.sap.sailing.domain.polarsheets;
import java.util.Iterator;
import java.util.NavigableSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
/**
* Iterates through the fixes of one competitor in one tracked race and fills the {@link PolarSheetGenerationWorker}
* with datapoints that are found for the speed of the boat and its angle to the wind
*
* @author D054528 Frederik Petersen
*
*/
public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable{
private final TrackedRace race;
private final PolarSheetGenerationWorker polarSheetGenerationWorker;
private TimePoint startTime;
private TimePoint endTime;
private final Competitor competitor;
private boolean done = false;
public PerRaceAndCompetitorPolarSheetGenerationWorker(TrackedRace race,
PolarSheetGenerationWorker polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime,
Competitor competitor) {
super();
this.race = race;
this.polarSheetGenerationWorker = polarSheetGenerationWorker;
this.startTime = startTime;
this.endTime = endTime;
this.competitor = competitor;
optimizeStartTime();
optimizeEndTime();
}
private void optimizeEndTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
MarkPassing passedFinish = markPassings.last();
TimePoint passedFinishTimePoint = passedFinish.getTimePoint();
if (passedFinishTimePoint.before(endTime)) {
endTime = passedFinishTimePoint;
}
}
private void optimizeStartTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
MarkPassing passedStart = markPassings.first();
TimePoint passedStartTimePoint = passedStart.getTimePoint();
if (passedStartTimePoint.after(startTime)) {
startTime = passedStartTimePoint;
}
}
@Override
public void run() {
GPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(competitor);
track.lockForRead();
Iterator<GPSFixMoving> fixesIterator = track.getFixesIterator(startTime, true);
while (fixesIterator.hasNext()) {
GPSFixMoving fix = fixesIterator.next();
if (fix.getTimePoint().after(endTime)) {
break;
}
if (track.hasDirectionChange(fix.getTimePoint(), race.getRace().getBoatClass()
.getManeuverDegreeAngleThreshold())) {
continue;
}
SpeedWithBearing speedWithBearing = fix.getSpeed();
Bearing bearing = speedWithBearing.getBearing();
Position position = fix.getPosition();
Wind wind = race.getWind(position, fix.getTimePoint());
Bearing windBearing = wind.getFrom();
double angleToWind = bearing.getDifferenceTo(windBearing).getDegrees();
polarSheetGenerationWorker.addPolarData(Math.round(angleToWind), speedWithBearing, wind);
}
track.unlockAfterRead();
done = true;
}
public boolean isDone() {
return done;
}
}
@@ -0,0 +1,174 @@
package com.sap.sailing.domain.polarsheets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl;
import com.sap.sailing.domain.tracking.TrackedRace;
/**
* Allows extracting data about average speed for each angle creating the foundation of polar-sheet generation. Tasks
* are assigned to the executor per race & per competitor and the results are filled on the go to allow progress
* indication in the polar-sheet itself.
*
* @author D054528 Frederik Petersen
*
*/
public class PolarSheetGenerationWorker {
private final Set<PerRaceAndCompetitorPolarSheetGenerationWorker> workers;
private final List<List<BoatAndWindSpeed>> polarData;
private final Executor executor;
/**
* Will prepare the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s per race & per competitor. This includes
* determining start and end time.
*
* @param trackedRaces
* from which the data is to be collected
* @param executor
* executes the tasks upon {@link #startPolarSheetGeneration()}
*/
public PolarSheetGenerationWorker(Set<TrackedRace> trackedRaces, Executor executor) {
polarData = initializePolarDataContainer();
this.executor = executor;
workers = new HashSet<PerRaceAndCompetitorPolarSheetGenerationWorker>();
for (TrackedRace race : trackedRaces) {
TimePoint startTime = race.getStartOfRace();
TimePoint endTime = race.getEndOfRace();
if (endTime == null) {
// TODO Figure out if there is an alternative:
endTime = race.getTimePointOfNewestEvent();
}
RaceDefinition raceDefinition = race.getRace();
Iterable<Competitor> competitors = raceDefinition.getCompetitors();
for (Competitor competitor : competitors) {
PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker(race, this, startTime, endTime, competitor);
workers.add(task);
}
}
}
private List<List<BoatAndWindSpeed>> initializePolarDataContainer() {
List<List<BoatAndWindSpeed>> container = new ArrayList<List<BoatAndWindSpeed>>();
for (int i = 0; i < 360; i++) {
container.add(new ArrayList<BoatAndWindSpeed>());
}
return container;
}
/**
* Starts the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s
*/
public void startPolarSheetGeneration() {
for (Runnable task : workers) {
executor.execute(task);
}
}
/**
* To be called from {@link PerRaceAndCompetitorPolarSheetGenerationWorker} for adding a datapoint to the list of
* results
*
* @param roundedAngle
* boat's angle to the wind
* @param boatSpeed
* boat's speed in knots
* @param windSpeed
* wind's speed in knots
*/
protected void addPolarData(long roundedAngle, Speed boatSpeed, Speed windSpeed) {
int angle = (int) roundedAngle;
if (angle < 0) {
angle = (360 + angle);
}
BoatAndWindSpeed speeds = new BoatAndWindSpeedImpl(boatSpeed, windSpeed);
polarData.get(angle).add(speeds);
}
/**
*
* @return results already averaged per angle. Also datacount overall and per angle. And a completion flag to show
* if the complete polar sheet has been generated or if the current results are only an intermediate
* product.
*/
public PolarSheetsData getPolarData() {
Number[][] averagedPolarDataByWindSpeed = new Number[13][360];
Integer[] dataCountPerAngle = new Integer[360];
int dataCount = 0;
Map<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<Integer, Integer[]>();
for (int i = 0; i < 13; i++) {
dataCountPerAngleForWindspeed.put(i, new Integer[360]);
}
for (int i = 0; i < 360; i++) {
// Avoid Concurrent modification, lock would slow things down
BoatAndWindSpeed[] values = polarData.get(i).toArray(new BoatAndWindSpeed[polarData.get(i).size()]);
dataCount = dataCount + values.length;
dataCountPerAngle[i] = values.length;
double[] sumsPerWindSpeed = new double[13];
int[] dataCountPerWindSpeed = new int[13];
for (BoatAndWindSpeed singleDataPoint : values) {
if (singleDataPoint != null) {
int windSpeed = (int) singleDataPoint.getWindSpeed().getBeaufort();
//Somehow beaufort sometimes gets bigger than twelve. TODO: Investigation
if (windSpeed > 12) {
windSpeed = 12;
}
//TODO enable different kinds of metrics for boats speed
sumsPerWindSpeed[windSpeed] = sumsPerWindSpeed[windSpeed] + singleDataPoint.getBoatSpeed().getKnots();
dataCountPerWindSpeed[windSpeed]++;
}
}
for (int j = 0; j < 13; j++) {
Double average = sumsPerWindSpeed[j] / dataCountPerWindSpeed[j];
if (average.isNaN()) {
average = new Double(0);
}
averagedPolarDataByWindSpeed[j][i] = average;
dataCountPerAngleForWindspeed.get(j)[i] = dataCountPerWindSpeed[j];
}
}
boolean complete = true;
for (PerRaceAndCompetitorPolarSheetGenerationWorker task : workers) {
if (!task.isDone()) {
complete = false;
break;
}
}
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, complete, dataCount,
dataCountPerAngleForWindspeed);
return data;
}
/**
* Can be used to present more detailed data.
*
* @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races
* and competitors.
*/
public List<List<BoatAndWindSpeed>> getCompleteData() {
return polarData;
}
}
@@ -5,10 +5,10 @@ import java.util.List;
import java.util.NavigableSet;
import java.util.SortedSet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
@@ -41,41 +41,42 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
*/
public interface TrackedRace extends Serializable {
final long MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS = 30000;
final long DEFAULT_LIVE_DELAY_IN_MILLISECONDS = 5000;
RaceDefinition getRace();
RegattaAndRaceIdentifier getRaceIdentifier();
/**
* Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time point
* which is expected to be before the race start time). When there are no {@link MarkPassing}s for the first mark, <code>null</code>
* is returned. If there are mark passings for the first mark and the start time is less than
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark passing for the
* Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time
* point which is expected to be before the race start time). When there are no {@link MarkPassing}s for the first
* mark, <code>null</code> is returned. If there are mark passings for the first mark and the start time is less
* than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark passing for the
* first mark. Otherwise, the first mark passing for the first mark minus
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.<p>
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.
* <p>
*
* If no start time can be determined this way, <code>null</code> is returned.
*/
TimePoint getStartOfRace();
/**
* Determine the race end time is tricky. Boats may sink, stop, not finish, although they started the race. We therefore
* cannot wait for all boats to reach the finish line.
* The following rules are used to calculate the endOfRace:
* Determine the race end time is tricky. Boats may sink, stop, not finish, although they started the race. We
* therefore cannot wait for all boats to reach the finish line. The following rules are used to calculate the
* endOfRace:
* <ol>
* <li>Returns <code>null</code> if no boat passed the finish line</li>
* <li>Returns time of the last mark passing recorded for the finish line</li>
* <li>TODO: Returns the time of the first passing of the finish line + the target window (defined in the competition rules)
* if a target window has been defined for the race</li>
* </ol>
* <li>Returns <code>null</code> if no boat passed the finish line</li>
* <li>Returns time of the last mark passing recorded for the finish line</li>
* <li>TODO: Returns the time of the first passing of the finish line + the target window (defined in the
* competition rules) if a target window has been defined for the race</li>
* </ol>
*/
TimePoint getEndOfRace();
/**
* Returns a list of the first and last mark passing times of all course waypoints. Callers wanting to iterate over the
* result must <code>synchronize</code> on the result.
* Returns a list of the first and last mark passing times of all course waypoints. Callers wanting to iterate over
* the result must <code>synchronize</code> on the result.
*/
Iterable<Pair<Waypoint, Pair<TimePoint, TimePoint>>> getMarkPassingsTimes();
@@ -83,89 +84,84 @@ public interface TrackedRace extends Serializable {
* Shorthand for <code>{@link #getStart()}.{@link TimePoint#compareTo(TimePoint) compareTo(at)} &lt;= 0</code>
*/
boolean hasStarted(TimePoint at);
/**
* Clients can safely iterate over the iterable returned because it's a non-live copy of the tracked legs of this
* tracked race. This implies that should an update to the underlying list of waypoints in this race's {@link Course}
* take place after this method has returned, then this won't be reflected in the result returned. Callers should
* obtain the {@link Course#lockForRead() course's read lock} while using the result of this call if they want to
* ensure that no course update is applied concurrently.
* tracked race. This implies that should an update to the underlying list of waypoints in this race's
* {@link Course} take place after this method has returned, then this won't be reflected in the result returned.
* Callers should obtain the {@link Course#lockForRead() course's read lock} while using the result of this call if
* they want to ensure that no course update is applied concurrently.
*/
Iterable<TrackedLeg> getTrackedLegs();
TrackedLeg getTrackedLeg(Leg leg);
/**
* Tracking information about the leg <code>competitor</code> is on at <code>timePoint</code>, or
* <code>null</code> if the competitor hasn't started any leg yet at <code>timePoint</code> or has
* already finished the race.
* Tracking information about the leg <code>competitor</code> is on at <code>timePoint</code>, or <code>null</code>
* if the competitor hasn't started any leg yet at <code>timePoint</code> or has already finished the race.
*/
TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint);
/**
* Tells which leg the leader at <code>timePoint</code> is on
*/
TrackedLeg getCurrentLeg(TimePoint timePoint);
/**
* Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}.
*/
TrackedLeg getTrackedLegFinishingAt(Waypoint endOfLeg);
/**
* Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}.
*/
TrackedLeg getTrackedLegStartingAt(Waypoint startOfLeg);
/**
* The raw, updating feed of a single competitor participating in this race
*/
GPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor);
/**
* Tells the leg on which the <code>competitor</code> was at time <code>at</code>.
* If the competitor hasn't passed the start waypoint yet, <code>null</code> is
* returned because the competitor was not yet on any leg at that point in time. If
* the time point happens to be after the last fix received from that competitor,
* the last known leg for that competitor is returned. If the time point is after the
* competitor's mark passing for the finish line, <code>null</code> is returned.
* For all legs except the last, if the time point equals a mark passing time point
* of the leg's starting waypoint, that leg is returned. For the time point of
* the mark passing for the finish line, the last leg is returned.
* Tells the leg on which the <code>competitor</code> was at time <code>at</code>. If the competitor hasn't passed
* the start waypoint yet, <code>null</code> is returned because the competitor was not yet on any leg at that point
* in time. If the time point happens to be after the last fix received from that competitor, the last known leg for
* that competitor is returned. If the time point is after the competitor's mark passing for the finish line,
* <code>null</code> is returned. For all legs except the last, if the time point equals a mark passing time point
* of the leg's starting waypoint, that leg is returned. For the time point of the mark passing for the finish line,
* the last leg is returned.
*/
TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, TimePoint at);
TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, Leg leg);
/**
* @return a sequential number counting the updates that occurred to this tracked race. Callers may use this to ask
* for updates newer than such a sequence number.
*/
long getUpdateCount();
int getRankDifference(Competitor competitor, Leg leg, TimePoint timePoint);
/**
* Computes the rank of the competitor in this race for the current time.
* Computes the rank of the competitor in this race for the current time.
*/
int getRank(Competitor competitor) throws NoWindException;
/**
* Computes the rank of <code>competitor</code> in this race. A competitor is ahead of all
* competitors that are one or more legs behind. Within the same leg, the rank is determined
* by the windward distance to go and therefore depends on the assumptions of the wind direction
* for the given <code>timePoint</code>. If the race hasn't {@link #hasStarted(TimePoint) started}
* yet, the result is undefined.
* Computes the rank of <code>competitor</code> in this race. A competitor is ahead of all competitors that are one
* or more legs behind. Within the same leg, the rank is determined by the windward distance to go and therefore
* depends on the assumptions of the wind direction for the given <code>timePoint</code>. If the race hasn't
* {@link #hasStarted(TimePoint) started} yet, the result is undefined.
*
* @return <code>0</code> in case the competitor hasn't participated in the race; a rank starting
* with <code>1</code> where rank <code>1</code> identifies the leader otherwise
* @return <code>0</code> in case the competitor hasn't participated in the race; a rank starting with
* <code>1</code> where rank <code>1</code> identifies the leader otherwise
*/
int getRank(Competitor competitor, TimePoint timePoint) throws NoWindException;
/**
* For a competitor, computes the distance (TODO not yet clear whether over ground or
* projected onto wind direction) into the race <code>secondsIntoTheRace</code> after
* the race {@link TrackedRace#getStart() started}.
* For a competitor, computes the distance (TODO not yet clear whether over ground or projected onto wind direction)
* into the race <code>secondsIntoTheRace</code> after the race {@link TrackedRace#getStart() started}.
*/
Distance getStartAdvantage(Competitor competitor, double secondsIntoTheRace);
@@ -183,14 +179,14 @@ public interface TrackedRace extends Serializable {
Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint);
/**
* Obtains the {@link MarkPassing} for <code>competitor</code> passing <code>waypoint</code>. If no such
* mark passing has been reported (yet), <code>null</code> is returned.
* Obtains the {@link MarkPassing} for <code>competitor</code> passing <code>waypoint</code>. If no such mark
* passing has been reported (yet), <code>null</code> is returned.
*/
MarkPassing getMarkPassing(Competitor competitor, Waypoint waypoint);
/**
* Yields the track describing <code>mark</code>'s movement over time; never <code>null</code> because a
* new track will be created in case no track was present for <code>mark</code> so far.
* Yields the track describing <code>mark</code>'s movement over time; never <code>null</code> because a new track
* will be created in case no track was present for <code>mark</code> so far.
*/
GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark);
@@ -204,7 +200,7 @@ public interface TrackedRace extends Serializable {
* is returned. Otherwise, the center of gravity between the mark positions is computed and returned.
*/
Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint);
/**
* Same as {@link #getWind(Position, TimePoint, Iterable) getWind(p, at, Collections.emptyList())}
*/
@@ -212,8 +208,8 @@ public interface TrackedRace extends Serializable {
/**
* Obtains estimated interpolated wind information for a given position and time point. The information is taken
* from all wind sources available except for those listed in <code>windSourcesToExclude</code>, using the confidences
* of the wind values provided by the various sources during averaging.
* from all wind sources available except for those listed in <code>windSourcesToExclude</code>, using the
* confidences of the wind values provided by the various sources during averaging.
*/
Wind getWind(Position p, TimePoint at, Iterable<WindSource> windSourcesToExclude);
@@ -231,10 +227,11 @@ public interface TrackedRace extends Serializable {
Iterable<WindSource> getWindSources();
/**
* Same as {@link #getOrCreateWindTrack(WindSource, long) getOrCreateWindTrack(windSource, getMillisecondsOverWhichToAverageWind())}.
* Same as {@link #getOrCreateWindTrack(WindSource, long) getOrCreateWindTrack(windSource,
* getMillisecondsOverWhichToAverageWind())}.
*/
WindTrack getOrCreateWindTrack(WindSource windSource);
WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation);
/**
@@ -243,26 +240,26 @@ public interface TrackedRace extends Serializable {
void waitForNextUpdate(int sinceUpdate) throws InterruptedException;
/**
* Time stamp of the start of the actual tracking.
* The value can be null (e.g. if we have not received any signal from the tracking infrastructure)
* Time stamp of the start of the actual tracking. The value can be null (e.g. if we have not received any signal
* from the tracking infrastructure)
*/
TimePoint getStartOfTracking();
/**
* Time stamp of the end of the actual tracking.
* The value can be null (e.g. if we have not received any signal from the tracking infrastructure)
* Time stamp of the end of the actual tracking. The value can be null (e.g. if we have not received any signal from
* the tracking infrastructure)
*/
TimePoint getEndOfTracking();
/**
* Regardless of the order in which events were received, this method returns the latest time point contained by any of
* the events received and processed.
* Regardless of the order in which events were received, this method returns the latest time point contained by any
* of the events received and processed.
*/
TimePoint getTimePointOfNewestEvent();
/**
* Regardless of the order in which events were received, this method returns the oldest time point contained by any of
* the events received and processed.
* Regardless of the order in which events were received, this method returns the oldest time point contained by any
* of the events received and processed.
*/
TimePoint getTimePointOfOldestEvent();
@@ -274,17 +271,18 @@ public interface TrackedRace extends Serializable {
* some tracking providers such as TracTrac. If the caller wants to iterate on the resulting collection or
* construct a {@link SortedSet#headSet(Object)} or {@link SortedSet#tailSet(Object)} and then iterate over
* that, the caller needs to invoke {@link #lockForRead(Iterable)} with the collection returned as parameter
* because insertions into the competitor's mark passing collection will obtain the corresponding write lock.
* because insertions into the competitor's mark passing collection will obtain the corresponding write
* lock.
*/
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor);
void lockForRead(Iterable<MarkPassing> markPassings);
void unlockAfterRead(Iterable<MarkPassing> markPassings);
/**
* Time stamp that the event received last from the underlying push service carried on it.
* Note that these times may not increase monotonically.
* Time stamp that the event received last from the underlying push service carried on it. Note that these times may
* not increase monotonically.
*/
TimePoint getTimePointOfLastEvent();
@@ -296,7 +294,7 @@ public interface TrackedRace extends Serializable {
* Gets the current delay of incoming events to the real time of the events in milliseconds
*/
long getDelayToLiveInMillis();
/**
* Estimates the wind direction based on the observed boat courses at the time given for the position provided. The
* estimate is based on the assumption that the boats which are on an upwind or a downwind leg sail with very
@@ -321,13 +319,12 @@ public interface TrackedRace extends Serializable {
* we may be able to also infer the wind speed from the boat tracks.
*/
Wind getEstimatedWindDirection(Position position, TimePoint timePoint);
/**
* Determines whether the <code>competitor</code> is sailing on port or starboard tack at the
* <code>timePoint</code> requested. Note that this will have to retrieve information about the wind.
* This, in turn, can lead to the current thread obtaining the monitor of the various wind tracks,
* and, if the {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the
* competitors' GPS tracks.
* Determines whether the <code>competitor</code> is sailing on port or starboard tack at the <code>timePoint</code>
* requested. Note that this will have to retrieve information about the wind. This, in turn, can lead to the
* current thread obtaining the monitor of the various wind tracks, and, if the
* {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the competitors' GPS tracks.
*/
Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException;
@@ -345,30 +342,34 @@ public interface TrackedRace extends Serializable {
* mark positions are not known (yet)
*/
Wind getDirectionFromStartToNextMark(TimePoint at);
/**
* Uses a {@link DouglasPeucker Douglas-Peucker} algorithm to approximate this track's fixes starting at
* time <code>from</code> until time point <code>to</code> such that the maximum distance between the
* track's fixes and the approximation is at most <code>maxDistance</code>.
* Uses a {@link DouglasPeucker Douglas-Peucker} algorithm to approximate this track's fixes starting at time
* <code>from</code> until time point <code>to</code> such that the maximum distance between the track's fixes and
* the approximation is at most <code>maxDistance</code>.
*/
List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to);
/**
* @return a non-<code>null</code> but perhaps empty list of the maneuvers that <code>competitor</code> performed in
* this race between <code>from</code> and <code>to</code>.
* this race between <code>from</code> and <code>to</code>. Depending on <code>waitForLatest</code> the
* result is taken from the cache straight away (<code>waitForLatest==false</code>) or, if a re-calculation
* for the <code>key</code> is still ongoing, the result of that ongoing re-calculation is returned.
*/
List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest) throws NoWindException;
List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest)
throws NoWindException;
/**
* @return <code>true</code> if this race is known to start with an {@link LegType#UPWIND upwind} leg.
* If this is the case, the wind estimation may default to using the first leg's direction at race start
* time as the direction the wind comes from.
* @return <code>true</code> if this race is known to start with an {@link LegType#UPWIND upwind} leg. If this is
* the case, the wind estimation may default to using the first leg's direction at race start time as the
* direction the wind comes from.
*/
boolean raceIsKnownToStartUpwind();
/**
* Many calculations require valid wind data. In order to prevent NoWindException's to be handled by those calculation
* this method can be used to check whether the tracked race has sufficient wind information available.
* Many calculations require valid wind data. In order to prevent NoWindException's to be handled by those
* calculation this method can be used to check whether the tracked race has sufficient wind information available.
*
* @return <code>true</code> if {@link #getWind(Position, TimePoint)} delivers a (not null) wind fix.
*/
boolean hasWindData();
@@ -380,23 +381,23 @@ public interface TrackedRace extends Serializable {
boolean hasGPSData();
/**
* Adds a race change listener to the set of listeners that will be notified about changes to this race.
* The listener won't be serialized together with this object.
* Adds a race change listener to the set of listeners that will be notified about changes to this race. The
* listener won't be serialized together with this object.
*/
void addListener(RaceChangeListener listener);
void removeListener(RaceChangeListener listener);
Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint);
Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint) throws NoWindException;
/**
* Calls {@link #getWindWithConfidence(Position, TimePoint, Iterable)} and excludes those wind sources listed in
* {@link #getWindSourcesToExclude}.
*/
WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at);
/**
* Lists those wind sources which by default are not considered in {@link #getWind(Position, TimePoint)} and
* {@link #getWindWithConfidence(Position, TimePoint)}.
@@ -406,8 +407,8 @@ public interface TrackedRace extends Serializable {
/**
* Loops over this tracked race's wind sources and from each asks its averaged wind for the position <code>p</code>
* and time point <code>at</code>, using the particular wind source's averaging interval. The confidences delivered
* by each wind source are used during computing the averaged result across the wind sources. The result has the averaged
* confidence attached.
* by each wind source are used during computing the averaged result across the wind sources. The result has the
* averaged confidence attached.
*/
WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Iterable<WindSource> windSourcesToExclude);
@@ -419,7 +420,8 @@ public interface TrackedRace extends Serializable {
WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(Position position, TimePoint timePoint);
/**
* After the call returns, {@link #getWindSourcesToExclude()} returns an iterable that equals <code>windSourcesToExclude</code>
* After the call returns, {@link #getWindSourcesToExclude()} returns an iterable that equals
* <code>windSourcesToExclude</code>
*/
void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude);
@@ -430,25 +432,27 @@ public interface TrackedRace extends Serializable {
* if <code>true</code> and any cache update is currently going on, wait for the update to complete and
* then fetch the updated value; otherwise, serve this requests from whatever is currently in the cache
*/
Distance getAverageCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
throws NoWindException;
WindStore getWindStore();
Competitor getOverallLeader(TimePoint timePoint) throws NoWindException;
/**
* Returns the competitors of this tracked race, according to their ranking. Competitors whose {@link #getRank(Competitor)} is 0 will
* be sorted "worst".
* Returns the competitors of this tracked race, according to their ranking. Competitors whose
* {@link #getRank(Competitor)} is 0 will be sorted "worst".
*/
List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
boolean waitForLatestAnalyses) throws NoWindException;
/**
* When provided with a {@link WindStore} during construction, the tracked race will asynchronously load the wind
* data for this tracked race from the wind store in a background thread and update this tracked race with the results.
* Clients that want to wait for the wind loading process to complete can do so by calling this method which will block
* until the wind loading has completed.
* data for this tracked race from the wind store in a background thread and update this tracked race with the
* results. Clients that want to wait for the wind loading process to complete can do so by calling this method
* which will block until the wind loading has completed.
*/
void waitUntilWindLoadingComplete() throws InterruptedException;
@@ -7,7 +7,8 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Fragment-Host: com.sap.sailing.gwt.ui
Require-Bundle: com.sap.sailing.server,
org.hamcrest.core;bundle-version="1.1.0"
org.hamcrest.core;bundle-version="1.1.0",
com.sap.sailing.expeditionconnector
Bundle-ClassPath: .
Import-Package: javax.jms;version="1.1.0",
javax.servlet;version="2.5.0",
@@ -0,0 +1,727 @@
package com.sap.sailing.gwt.ui.test;
import java.io.Serializable;
import java.util.List;
import java.util.NavigableSet;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RegattaListener;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
public class MockedTrackedRace implements DynamicTrackedRace {
private static final long serialVersionUID = 5827912985564121181L;
private final WindTrack windTrack = new WindTrackImpl(/* millisecondsOverWhichToAverage */ 30000, /* useSpeed */ true, "TestWindTrack");
public WindTrack getWindTrack() {
return windTrack;
}
@Override
public RaceDefinition getRace() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getStartOfRace() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<TrackedLeg> getTrackedLegs() {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLeg(Leg leg) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getCurrentLeg(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLegFinishingAt(Waypoint endOfLeg) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLegStartingAt(Waypoint startOfLeg) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, Leg leg) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getUpdateCount() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getRankDifference(Competitor competitor, Leg leg, TimePoint timePoint) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getRank(Competitor competitor) throws NoWindException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getRank(Competitor competitor, TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Distance getStartAdvantage(Competitor competitor, double secondsIntoTheRace) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public MarkPassing getMarkPassing(Competitor competitor, Waypoint waypoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public DynamicGPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitForNextUpdate(int sinceUpdate) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfNewestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public void recordFix(Competitor competitor, GPSFixMoving fix) {
// TODO Auto-generated method stub
}
@Override
public void recordWind(Wind wind, WindSource windSource) {
if (windSource.getType() == WindSourceType.EXPEDITION) {
windTrack.add(wind);
}
}
@Override
public void addListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public void updateMarkPassings(Competitor competitor, Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public void setStartTimeReceived(TimePoint start) {
// TODO Auto-generated method stub
}
@Override
public DynamicGPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public void removeWind(Wind wind, WindSource windSource) {
// TODO Auto-generated method stub
}
@Override
public TimePoint getTimePointOfLastEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setMillisecondsOverWhichToAverageSpeed(long millisecondsOverWhichToAverageSpeed) {
// TODO Auto-generated method stub
}
@Override
public void setMillisecondsOverWhichToAverageWind(long millisecondsOverWhichToAverageWind) {
// TODO Auto-generated method stub
}
@Override
public long getMillisecondsOverWhichToAverageSpeed() {
// TODO Auto-generated method stub
return 0;
}
@Override
public long getMillisecondsOverWhichToAverageWind() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Wind getEstimatedWindDirection(Position position, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean hasStarted(TimePoint at) {
// TODO Auto-generated method stub
return false;
}
@Override
public DynamicTrackedRegatta getTrackedRegatta() {
return new DynamicTrackedRegatta() {
private static final long serialVersionUID = 2651590861333064588L;
@Override
public Regatta getRegatta() {
return new Regatta() {
private static final long serialVersionUID = -4908774269425170811L;
@Override
public String getName() {
return "A Mocked Test Regatta";
}
@Override
public Serializable getId() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<RaceDefinition> getAllRaces() {
// TODO Auto-generated method stub
return null;
}
@Override
public BoatClass getBoatClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<Competitor> getCompetitors() {
// TODO Auto-generated method stub
return null;
}
@Override
public void addRace(RaceDefinition race) {
// TODO Auto-generated method stub
}
@Override
public void removeRace(RaceDefinition raceDefinition) {
// TODO Auto-generated method stub
}
@Override
public RaceDefinition getRaceByName(String raceName) {
// TODO Auto-generated method stub
return null;
}
@Override
public void addRegattaListener(RegattaListener listener) {
// TODO Auto-generated method stub
}
@Override
public void removeRegattaListener(RegattaListener listener) {
// TODO Auto-generated method stub
}
@Override
public RegattaIdentifier getRegattaIdentifier() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getBaseName() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<? extends Series> getSeries() {
// TODO Auto-generated method stub
return null;
}
@Override
public Series getSeriesByName(String seriesName) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isPersistent() {
// TODO Auto-generated method stub
return false;
}
@Override
public void addRaceColumnListener(RaceColumnListener listener) {
// TODO Auto-generated method stub
}
@Override
public void removeRaceColumnListener(RaceColumnListener listener) {
// TODO Auto-generated method stub
}
@Override
public ScoringScheme getScoringScheme() {
// TODO Auto-generated method stub
return null;
}
};
}
@Override
public Iterable<TrackedRace> getTrackedRaces() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass) {
// TODO Auto-generated method stub
return null;
}
@Override
public void addTrackedRace(TrackedRace trackedRace) {
// TODO Auto-generated method stub
}
@Override
public void removeTrackedRace(TrackedRace trackedRace) {
// TODO Auto-generated method stub
}
@Override
public void addRaceListener(RaceListener listener) {
// TODO Auto-generated method stub
}
@Override
public int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return 0;
}
@Override
public DynamicTrackedRace getTrackedRace(RaceDefinition race) {
// TODO Auto-generated method stub
return null;
}
@Override
public DynamicTrackedRace getExistingTrackedRace(RaceDefinition race) {
// TODO Auto-generated method stub
return null;
}
@Override
public void removeTrackedRace(RaceDefinition raceDefinition) {
// TODO Auto-generated method stub
}
@Override
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate) {
// TODO Auto-generated method stub
return null;
}
};
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Tack getTack(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Wind getDirectionFromStartToNextMark(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean raceIsKnownToStartUpwind() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setRaceIsKnownToStartUpwind(boolean raceIsKnownToStartUpwind) {
// TODO Auto-generated method stub
}
@Override
public RegattaAndRaceIdentifier getRaceIdentifier() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getEndOfRace() {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Wind getWind(Position p, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public Wind getWind(Position p, TimePoint at, Iterable<WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<WindSource> getWindSources(WindSourceType type) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<WindSource> getWindSources() {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Iterable<WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(Position position, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<WindSource> getWindSourcesToExclude() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getEndOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfOldestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setStartOfTrackingReceived(TimePoint startOfTrackingReceived) {
// TODO Auto-generated method stub
}
@Override
public void setEndOfTrackingReceived(TimePoint endOfTrackingReceived) {
// TODO Auto-generated method stub
}
@Override
public Iterable<Pair<Waypoint, Pair<TimePoint, TimePoint>>> getMarkPassingsTimes() {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public WindTrack getOrCreateWindTrack(WindSource windSource) {
// TODO Auto-generated method stub
return null;
}
@Override
public void recordFix(Mark mark, GPSFix fix) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public WindStore getWindStore() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
}
@Override
public Competitor getOverallLeader(TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public long getDelayToLiveInMillis() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setDelayToLiveInMillis(long delayToLiveInMillis) {
// TODO Auto-generated method stub
}
@Override
public void setAndFixDelayToLiveInMillis(long delayToLiveInMillis) {
// TODO Auto-generated method stub
}
@Override
public List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitUntilWindLoadingComplete() throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public Iterable<Mark> getMarks() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean hasWindData() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasGPSData() {
// TODO Auto-generated method stub
return false;
}
@Override
public void lockForRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public void unlockAfterRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public TrackedRaceStatus getStatus() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setStatus(TrackedRaceStatus newStatus) {
// TODO Auto-generated method stub
}
@Override
public void waitUntilNotLoading() {
// TODO Auto-generated method stub
}
}
@@ -0,0 +1,203 @@
package com.sap.sailing.gwt.ui.test;
import java.util.ArrayList;
import java.util.List;
import java.util.NavigableSet;
import java.util.UUID;
import junit.framework.Assert;
import org.junit.Test;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.gwt.ui.server.SailingServiceImpl;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.server.replication.ReplicationService;
public class PolarSheetGenerationServiceTest {
private static final String BOAT_CLASS = "Forelle";
@Test
public void testPolarSheetGenerationService() throws InterruptedException {
SailingService service = new MockSailingServiceForPolarSheetGeneration();
List<RegattaAndRaceIdentifier> idList = new ArrayList<RegattaAndRaceIdentifier>();
idList.add(new RegattaNameAndRaceName("IrgendeineRegatta", "IrgendeinRennen"));
PolarSheetGenerationTriggerResponse triggerData = service.generatePolarSheetForRaces(idList);
Assert.assertNotNull(triggerData);
Assert.assertEquals(BOAT_CLASS, triggerData.getBoatClassName());
Assert.assertNotNull(triggerData.getId());
boolean complete = false;
PolarSheetsData results = null;
double timeOut = 10;
while (!complete && timeOut > 0) {
Thread.sleep(200);
timeOut = timeOut - 0.2;
results = service.getPolarSheetsGenerationResults(triggerData.getId());
complete = results.isComplete();
}
Assert.assertTrue(complete);
Assert.assertNotNull(results);
Assert.assertEquals(4, results.getDataCount());
Assert.assertEquals(4.0, results.getAveragedPolarDataByWindSpeed()[1][45]);
Assert.assertEquals(2.0, results.getAveragedPolarDataByWindSpeed()[1][55]);
Assert.assertEquals(6.0, results.getAveragedPolarDataByWindSpeed()[1][30]);
}
@SuppressWarnings("serial")
private class MockSailingServiceForPolarSheetGeneration extends SailingServiceImpl {
@Override
protected RacingEventService getService() {
RacingEventService service = new MockRacingEventServiceForPolarSheetGeneration();
return service;
}
@Override
protected ServiceTracker<RacingEventService, RacingEventService> createAndOpenRacingEventServiceTracker(
BundleContext context) {
return null;
}
@Override
protected ServiceTracker<ReplicationService, ReplicationService> createAndOpenReplicationServiceTracker(
BundleContext context) {
return null;
}
@Override
protected ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> createAndOpenScoreCorrectionProviderServiceTracker(
BundleContext bundleContext) {;
return null;
}
}
private class MockRacingEventServiceForPolarSheetGeneration extends RacingEventServiceImpl {
@Override
public TrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier) {
MockTrackedRaceForPolarSheetGeneration trackedRace = new MockTrackedRaceForPolarSheetGeneration();
return trackedRace;
}
}
@SuppressWarnings("serial")
private class MockTrackedRaceForPolarSheetGeneration extends MockedTrackedRace {
@Override
public DynamicGPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new MockDynamicGPSFixMovinTrackForPolarSheetGeneration<Competitor>(competitor, 0);
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.0, 1.0), new MillisecondsTimePoint(1), new KnotSpeedWithBearingImpl(3.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.001, 1.001), new MillisecondsTimePoint(2), new KnotSpeedWithBearingImpl(5.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.002, 1.002), new MillisecondsTimePoint(3), new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(-55.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.003, 1.003), new MillisecondsTimePoint(4), new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(-30.0))));
//Should not be taken into consideration because it happens after the race has ended
track.addGPSFix(new GPSFixMovingImpl(new DegreePosition(2.0, 3.0), new MillisecondsTimePoint(7), new KnotSpeedWithBearingImpl(5.0, new DegreeBearingImpl(-45.0))));
return track;
}
@Override
public Wind getWind(Position p, TimePoint at) {
Wind wind = new WindImpl(p, at, new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0)));
return wind;
}
@Override
public RaceDefinition getRace() {
BoatClass forelle = new BoatClassImpl(BOAT_CLASS, true);
Competitor competitor = new CompetitorImpl(UUID.randomUUID(), "Hans Frantz", new TeamImpl("SAP", null, null), new BoatImpl("Schnelle Forelle", forelle, "GER000"));
ArrayList<Competitor> competitors = new ArrayList<Competitor>();
competitors.add(competitor);
RaceDefinition race = new RaceDefinitionImpl("Forelle1", new CourseImpl("ForelleCourse", new ArrayList<Waypoint>()), forelle, competitors);
return race;
}
@Override
public TimePoint getStartOfRace() {
return new MillisecondsTimePoint(1);
}
@Override
public TimePoint getEndOfRace() {
return new MillisecondsTimePoint(5);
}
}
@SuppressWarnings("serial")
private class MockGPSFixMovingForPolarSheetGeneration extends GPSFixMovingImpl {
public MockGPSFixMovingForPolarSheetGeneration(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(position, timePoint, speed);
}
@Override
public boolean isValid() {
return true;
}
}
@SuppressWarnings("serial")
private class MockDynamicGPSFixMovinTrackForPolarSheetGeneration<ItemType> extends DynamicGPSFixMovingTrackImpl<ItemType> {
public MockDynamicGPSFixMovinTrackForPolarSheetGeneration(ItemType trackedItem,
long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
}
@Override
protected boolean isValid(NavigableSet<GPSFixMoving> rawFixes, GPSFixMoving e) {
return true;
}
}
}
+1
View File
@@ -2,4 +2,5 @@
/.gwt
/.generated
/gwt-unitCache
/extras
/com.sap.sailing.gwt.ui.*
@@ -1,4 +1,4 @@
eclipse.preferences.version=1
entryPointModules=
filesCopiedToWebInfLib=gwt-servlet.jar
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDEwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5TcGVjdGF0b3I8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVXNlck1hbmFnZW1lbnQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVHZWaWV3PC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlZpZGVvUG9wdXA8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuWW91dHViZVBvcHVwPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
eclipse.preferences.version=1
entryPointModules=
filesCopiedToWebInfLib=gwt-servlet.jar
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDEwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5TcGVjdGF0b3I8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVXNlck1hbmFnZW1lbnQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuVHZWaWV3PC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlZpZGVvUG9wdXA8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuWW91dHViZVBvcHVwPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlBvbGFyU2hlZXRzPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
+133
View File
@@ -0,0 +1,133 @@
@import url(fontface.css);
@import url(CommonControls.css);
/** Add css rules here for your application. */
/*
body #calendar:after
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
*/
.clear {
clear: both;
font-size: 1px;
height: 0px;
line-height: 0px;
margin: -1px 0 0;
width: 100%; }
.cover {
left: -1000px;
overflow: hidden;
position: absolute;
top: -1000px; }
body {
background: #fff;
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
font-weight: 400;
padding: 85px 0 120px 0;
margin: 0;
}
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-weight: bold;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
table {
border-collapse: separate; }
.leaderboardMargin {
margin: 5px;
}
.leaderboardContent {
}
.leaderboardContent td, .leaderboardContent th {
vertical-align: bottom;
line-height: 1.3;
padding-top: 2px;
}
.leaderboardContent-toolbar {
}
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
margin: 5px;
}
.refreshAndSettings {
background: #f2f2f2;
border-bottom: 2px solid #fff;
border-top: 2px solid #fff;
height: 30px; }
.refreshAndSettings table td {
padding: 0 10px 0 0;}
.refreshAndSettings table table td{
padding: 0 0 0 0;}
input.magnifier{
display: block;
margin: 3px auto 0 auto !important;
}
body, table td, select, button {
font-family: 'UbuntuLight', Arial, Verdana, sans-serif;
font-size: 14px;
}
.busyIndicator-simpleBusyIndicator {
margin-top: 7px;
}
.errorLabel {
color: #FF0000;
}
.abstractChartPanel-importantMessageOfChart {
font-family: 'UbuntuBold',Arial,Verdana,sans-serif;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 80px;
width: 300px;
color: #7c7c7c;
background: url(images/important-message.png) left center no-repeat;
}
.LeaderboardHeader {
position: relative;
margin: 0 auto;
width: 400px;
text-align: center;
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif;
font-size: 18px;
color: #fff;
margin-top: 22px;
}
.leaderboardHeading {
font-size: 16px;
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif;
font-weight: bold;
}
.gwt-DialogBox {
z-index: 20;
}
@@ -0,0 +1,34 @@
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>SAP Sailing Analytics Polar Sheets</title>
<meta content='width = device-width, initial-scale = 1.0, user-scalable = yes' name='viewport'>
<meta content='yes' name='apple-mobile-web-app-capable'>
<meta content='black' name='apple-mobile-web-app-status-bar-style'>
<!-- <link href='iphone-splash-screen.png' rel='apple-touch-startup-image'> -->
<link href='images/sap-sailing-app-icon.png' rel='apple-touch-icon'>
<link rel="shortcut icon" type="image/x-icon" href="images/sap.ico" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="highcharts/js/highcharts.js"></script>
<script type="text/javascript" src="highcharts/js/highcharts-more.js"></script>
<!-- Optionally, add a highcharts theme file -->
<script type="text/javascript" src="highcharts/js/themes/grid.js"></script>
<script type="text/javascript" language="javascript" src="com.sap.sailing.gwt.ui.PolarSheets/com.sap.sailing.gwt.ui.PolarSheets.nocache.js"></script>
</head>
<body>
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
</body>
</html>
@@ -4,7 +4,7 @@
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/AdminConsole.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
@@ -48,7 +48,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sapjvm_7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024m -Xdebug -Xrunjdwp:transport=dt_socket,address=7999,server=y"/>
</launchConfiguration>
@@ -4,7 +4,7 @@
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.TvView"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/AdminConsole.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
@@ -47,7 +47,7 @@
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m"/>
</launchConfiguration>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1,32 @@
package com.sap.sailing.gwt.ui.actions;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
public class GetPolarSheetDataByAngleAction extends DefaultAsyncAction<PolarSheetsHistogramData> {
private final SailingServiceAsync sailingService;
private final String polarSheetId;
private final int angle;
private final int windSpeed;
public GetPolarSheetDataByAngleAction(SailingServiceAsync sailingService,
String polarSheetId, int angle, int windSpeed, AsyncCallback<PolarSheetsHistogramData> callback) {
super(callback);
this.sailingService = sailingService;
this.polarSheetId = polarSheetId;
this.angle = angle;
this.windSpeed = windSpeed;
}
@Override
public void execute(AsyncActionsExecutor asyncActionsExecutor) {
sailingService.getPolarSheetData(polarSheetId, angle, windSpeed, (AsyncCallback<PolarSheetsHistogramData>) getWrapperCallback(asyncActionsExecutor));
}
}
@@ -14,6 +14,9 @@ import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
@@ -283,4 +286,10 @@ public interface SailingService extends RemoteService {
List<SwissTimingArchiveConfigurationDTO> getPreviousSwissTimingArchiveConfigurations();
void storeSwissTimingArchiveConfiguration(String swissTimingUrl);
PolarSheetGenerationTriggerResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces);
PolarSheetsData getPolarSheetsGenerationResults(String id);
PolarSheetsHistogramData getPolarSheetData(String polarSheetId, int angle, int windSpeed);
}
@@ -1,438 +1,447 @@
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingReplayRaceDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
/**
* The async counterpart of {@link SailingService}
*/
public interface SailingServiceAsync {
void getRegattas(AsyncCallback<List<RegattaDTO>> callback);
/**
* The string returned in the callback's pair is the common event name
*/
void listTracTracRacesInEvent(String eventJsonURL, AsyncCallback<Pair<String, List<TracTracRaceRecordDTO>>> callback);
/**
* @param regattaToAddTo
* if <code>null</code>, an existing regatta by the name of the TracTrac event with the boat class name
* appended in parentheses will be looked up; if not found, a default regatta with that name will be
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
* @param liveURI
* may be <code>null</code> or the empty string in which case the server will use the
* {@link TracTracRaceRecordDTO#liveURI} from the <code>rr</code> race record.
* @param simulateWithStartTimeNow
* if <code>true</code>, the connector will adjust the time stamps of all events received such that the
* first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all
* events received such that they seem to be sent in "real-time." So, more or less the time points attached
* to the events sent to the receivers will again approximate the wall time.
* @param storedURImay
* be <code>null</code> or the empty string in which case the server will use the
* {@link TracTracRaceRecordDTO#storedURI} from the <code>rr</code> race record.
*/
void trackWithTracTrac(RegattaIdentifier regattaToAddTo,
Iterable<TracTracRaceRecordDTO> rrs, String liveURI, String storedURI, boolean trackWind, boolean correctWindByDeclination,
boolean simulateWithStartTimeNow, AsyncCallback<Void> callback);
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs,
String hostname, int port, boolean canSendRequests, boolean trackWind, boolean correctWindByDeclination,
AsyncCallback<Void> asyncCallback);
void replaySwissTimingRace(RegattaIdentifier regattaIdentifier, Iterable<SwissTimingReplayRaceDTO> replayRaces,
boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow,
AsyncCallback<Void> asyncCallback);
void getPreviousTracTracConfigurations(AsyncCallback<List<TracTracConfigurationDTO>> callback);
void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI,
AsyncCallback<Void> callback);
void stopTrackingEvent(RegattaIdentifier eventIdentifier, AsyncCallback<Void> callback);
void stopTrackingRaces(Iterable<RegattaAndRaceIdentifier> racesToStopTracking, AsyncCallback<Void> asyncCallback);
/**
* Untracks the race and removes it from the regatta. It will also be removed in all leaderboards
* @param regattaNamesAndRaceNames The identifier for the regatta name, and the race name to remove
*/
void removeAndUntrackRaces(Iterable<RegattaNameAndRaceName> regattaNamesAndRaceNames, AsyncCallback<Void> callback);
void getRawWindFixes(RegattaAndRaceIdentifier raceIdentifier, Collection<WindSource> windSources, AsyncCallback<WindInfoForRaceDTO> callback);
/**
* @param from if <code>null</code>, the tracked race's start of tracking is used
* @param to if <code>null</code>, the tracked race's time point of newest event is used
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, Date to, long resolutionInMilliseconds,
Collection<String> windSourceTypeNames, AsyncCallback<WindInfoForRaceDTO> callback);
/**
* @param windSourceTypeNames
* if <code>null</code>, data from all available wind sources will be returned, otherwise only from those
* whose {@link WindSource} name is contained in the <code>windSources</code> collection.
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes,
double latDeg, double lngDeg, Collection<String> windSourceTypeNames,
AsyncCallback<WindInfoForRaceDTO> callback);
/**
* Same as {@link #getWindInfo(RegattaAndRaceIdentifier, Date, long, int, double, double, Collection, AsyncCallback)}, only
* that the wind is not requested for a specific position, but instead the wind sources associated with the tracked
* race identified by <code>raceIdentifier</code> are requested to deliver their original position. This will in
* particular preserve the positions of actual measurements and will deliver the averaged positions for averaged /
* combined wind read-outs.
*
* @param from
* must not be <code>null</code>
* @param numberOfFixes
* no matter how great this value is chosen, never returns data beyond the newest event recorded in the
* race
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes,
Collection<String> windSourceTypeNames, AsyncCallback<WindInfoForRaceDTO> callback);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind, AsyncCallback<Void> callback);
void removeWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO windDTO, AsyncCallback<Void> callback);
/**
* @param from
* for the list of competitors provided as keys of this map, requests the GPS fixes starting with the
* date provided as value
* @param to
* for the list of competitors provided as keys (expected to be equal to the set of competitors used as
* keys in the <code>from</code> parameter, requests the GPS fixes up to but excluding the date provided
* as value
* @param extrapolate
* if <code>true</code> and no position is known for <code>date</code>, the last entry returned in the
* list of GPS fixes will be obtained by extrapolating from the competitors last known position before
* <code>date</code> and the estimated speed.
* @return a map where for each competitor participating in the race the list of GPS fixes in increasing
* chronological order is provided. The last one is the last position at or before <code>date</code>.
*/
void getBoatPositions(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
boolean extrapolate, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>> callback);
void getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<RaceTimesInfoDTO> callback);
void getRaceTimesInfos(Collection<RegattaAndRaceIdentifier> raceIdentifiers, AsyncCallback<List<RaceTimesInfoDTO>> callback);
void getCoursePositions(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<CourseDTO> asyncCallback);
void getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<List<QuickRankDTO>> callback);
/**
* Returns a {@link LeaderboardDTO} will information about all races, their points and competitor display names
* filled in. The column details are filled for the races whose named are provided in
* <code>namesOfRacesForWhichToLoadLegDetails</code>.
*
* @param date
* the time point for the leaderboard data to retrieve, or <code>null</code> for "live mode" which means
* that the server will produce whatever it has ready for the currently active live delay set on the
* server; in live mode, data served may be taken from caches that lag up to a few seconds.
* @param namesOfRaceColumnsForWhichToLoadLegDetails
* if <code>null</code>, no {@link LeaderboardEntryDTO#legDetails leg details} will be present in the
* result ({@link LeaderboardEntryDTO#legDetails} will be <code>null</code> for all
* {@link LeaderboardEntryDTO} objects contained). Otherwise, the {@link LeaderboardEntryDTO#legDetails}
* list will contain one entry per leg of the race {@link com.sap.sailing.domain.base.Course} for those race columns whose
* {@link com.sap.sailing.domain.base.RaceColumn#getType() name} is contained in <code>namesOfRacesForWhichToLoadLegDetails</code>.
* For all other columns, {@link LeaderboardEntryDTO#legDetails} is <code>null</code>.
*/
void getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
AsyncCallback<LeaderboardDTO> callback);
void getLeaderboardNames(AsyncCallback<List<String>> callback);
void getLeaderboards(AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void getLeaderboardsByEvent(RegattaDTO regatta, AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void getLeaderboardsByRace(RaceDTO race, AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName,
int[] newDiscardingThreasholds, AsyncCallback<Void> callback);
void createFlexibleLeaderboard(String leaderboardName, int[] discardThresholds, ScoringSchemeType scoringSchemeType,
AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
void createRegattaLeaderboard(RegattaIdentifier regattaIdentifier, int[] discardThresholds,
AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
void removeLeaderboard(String leaderboardName, AsyncCallback<Void> asyncCallback);
void renameLeaderboard(String leaderboardName, String newLeaderboardName, AsyncCallback<Void> asyncCallback);
void addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace,
AsyncCallback<Void> callback);
void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName, AsyncCallback<Void> callback);
void removeLeaderboardColumn(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void connectTrackedRaceToLeaderboardColumn(String leaderboardName, String raceColumnName, String fleetName,
RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<Boolean> asyncCallback);
/**
* The key set of the map returned contains all fleets of the race column identified by the combination of
* <code>leaderboardName</code> and <code>raceColumnName</code>. If a value is <code>null</code>, there is no
* tracked race currently linked to the fleet in the race column; otherwise, the value is the {@link RaceIdentifier}
* of the tracked race currently connected for the fleet whose name is the key. The map returned is never <code>null</code>.
*/
void getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(String leaderboardName, String raceColumnName,
AsyncCallback<Map<String, RegattaAndRaceIdentifier>> callback);
void disconnectLeaderboardColumnFromTrackedRace(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints, AsyncCallback<Void> callback);
void updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString, String raceColumnName,
MaxPointsReason maxPointsReason, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString, String columnName,
Double correctedScore, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment, AsyncCallback<Void> callback);
void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates,
AsyncCallback<Void> callback);
void updateCompetitorDisplayNameInLeaderboard(String leaderboardName, String competitorID, String displayName,
AsyncCallback<Void> callback);
void moveLeaderboardColumnUp(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void moveLeaderboardColumnDown(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void updateIsMedalRace(String leaderboardName, String columnName, boolean isMedalRace, AsyncCallback<Void> callback);
void updateRaceDelayToLive(RegattaAndRaceIdentifier regattaAndRaceIdentifier, long delayToLiveInMs, AsyncCallback<Void> callback);
void updateRacesDelayToLive(List<RegattaAndRaceIdentifier> regattaAndRaceIdentifiers, long delayToLiveInMs, AsyncCallback<Void> callback);
void getPreviousSwissTimingConfigurations(AsyncCallback<List<SwissTimingConfigurationDTO>> asyncCallback);
void listSwissTimingRaces(String hostname, int port, boolean canSendRequests,
AsyncCallback<List<SwissTimingRaceRecordDTO>> asyncCallback);
void storeSwissTimingConfiguration(String configName, String hostname, int port, boolean canSendRequests, AsyncCallback<Void> asyncCallback);
void sendSwissTimingDummyRace(String racMessage, String stlMesssage, String ccgMessage, AsyncCallback<Void> callback);
void getCountryCodes(AsyncCallback<String[]> callback);
void getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
double meters, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>> callback);
void getManeuvers(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
AsyncCallback<Map<CompetitorDTO, List<ManeuverDTO>>> callback);
void getLeaderboardGroups(boolean withGeoLocationData, AsyncCallback<List<LeaderboardGroupDTO>> callback);
void getLeaderboardGroupByName(String groupName, boolean withGeoLocationData,
AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Renames the group with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*/
void renameLeaderboardGroup(String oldName, String newName, AsyncCallback<Void> callback);
/**
* Removes the leaderboard group with the name <code>groupName</code> from the service and the persistant store.
*/
void removeLeaderboardGroup(String groupName, AsyncCallback<Void> callback);
/**
* Creates a new group with the name <code>groupname</code>, the description <code>description</code> and an empty list of leaderboards.<br/>
* @param displayGroupsInReverseOrder TODO
*/
void createLeaderboardGroup(String groupName, String description,
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType, 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 leaderboardNames The list of names of the new leaderboards of the group
*/
void updateLeaderboardGroup(String oldName, String newName, String description,
List<String> leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType, AsyncCallback<Void> callback);
void setRaceIsKnownToStartUpwind(RegattaAndRaceIdentifier raceIdentifier, boolean raceIsKnownToStartUpwind,
AsyncCallback<Void> callback);
void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable<WindSource> windSourcesToExclude,
AsyncCallback<Void> callback);
void getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<CompetitorDTO, Date> from,
Map<CompetitorDTO, Date> to, boolean extrapolate, AsyncCallback<RaceMapDataDTO> callback);
void getReplicaInfo(AsyncCallback<ReplicationStateDTO> callback);
void startReplicatingFromMaster(String masterName, String exchangeName, int servletPort, int messagingPort,
AsyncCallback<Void> callback);
void getEvents(AsyncCallback<List<EventDTO>> callback);
/**
* Creates a {@link EventDTO} for the {@link com.sap.sailing.domain.base.Event} with the name <code>eventName</code>, which contains the
* name, the description and a list with {@link RegattaDTO RegattaDTOs} contained in the event.<br />
* If no event with the name <code>eventName</code> is known, an {@link IllegalArgumentException} is thrown.
*/
void getEventByName(String eventName, AsyncCallback<EventDTO> callback);
/**
* Renames the event with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no event with the name <code>oldName</code> or there's already a event with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*/
void renameEvent(String oldName, String newName, AsyncCallback<Void> callback);
/**
* Removes the event with the name <code>eventName</code> from the service and the persistence store.
*/
void removeEvent(String eventName, AsyncCallback<Void> callback);
void createEvent(String eventName, String description, String publicationUrl, boolean isPublic, AsyncCallback<EventDTO> callback);
void updateEvent(String eventName, Serializable id, VenueDTO venue, String publicationUrl, boolean isPublic,
List<String> regattaNames, AsyncCallback<Void> callback);
void removeRegatta(RegattaIdentifier regattaIdentifier, AsyncCallback<Void> callback);
void addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<RaceColumnInSeriesDTO> callback);
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, AsyncCallback<RegattaDTO> callback);
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
void getScoreCorrectionProviderDTOs(AsyncCallback<Iterable<ScoreCorrectionProviderDTO>> callback);
void getScoreCorrections(String scoreCorrectionProviderName, String eventName, String boatClassName,
Date timePointWhenResultPublished, AsyncCallback<RegattaScoreCorrectionDTO> asyncCallback);
void getWindSourcesInfo(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<WindInfoForRaceDTO> callback);
void getRaceCourse(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<List<ControlPointDTO>> callback);
void updateRaceCourse(RegattaAndRaceIdentifier raceIdentifier, List<ControlPointDTO> controlPoints, AsyncCallback<Void> callback);
void getFregResultUrls(AsyncCallback<List<String>> asyncCallback);
void removeFregURLs(Set<String> toRemove, AsyncCallback<Void> asyncCallback);
void addFragUrl(String result, AsyncCallback<Void> asyncCallback);
void getRaceCourseMarks(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<RaceCourseMarksDTO> callback);
void addColumnsToLeaderboard(String leaderboardName, List<Pair<String, Boolean>> columnsToAdd,
AsyncCallback<Void> callback);
void removeLeaderboardColumns(String leaderboardName, List<String> columnsToRemove, AsyncCallback<Void> callback);
void getLeaderboard(String leaderboardName, AsyncCallback<StrippedLeaderboardDTO> callback);
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed, AsyncCallback<Void> asyncCallback);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor,
AsyncCallback<Void> callback);
void listSwissTiminigReplayRaces(String swissTimingUrl, AsyncCallback<List<SwissTimingReplayRaceDTO>> asyncCallback);
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSize, DetailType detailType, String leaderboarGroupName, String leaderboardName, AsyncCallback<CompetitorsRaceDataDTO> callback);
/**
* Finds out the names of all {@link com.sap.sailing.domain.leaderboard.MetaLeaderboard}s managed by this server that
* {@link com.sap.sailing.domain.leaderboard.MetaLeaderboard#getLeaderboards() contain} the leaderboard identified by <code>leaderboardName</code>. The
* names of those meta-leaderboards are returned. The list returned is never <code>null</code> but may be empty if no such
* leaderboard is found.
*/
void getOverallLeaderboardNamesContaining(String leaderboardName, AsyncCallback<List<String>> asyncCallback);
void getPreviousSwissTimingArchiveConfigurations(
AsyncCallback<List<SwissTimingArchiveConfigurationDTO>> asyncCallback);
void storeSwissTimingArchiveConfiguration(String swissTimingUrl, AsyncCallback<Void> asyncCallback);
}
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO;
import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingReplayRaceDTO;
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
/**
* The async counterpart of {@link SailingService}
*/
public interface SailingServiceAsync {
void getRegattas(AsyncCallback<List<RegattaDTO>> callback);
/**
* The string returned in the callback's pair is the common event name
*/
void listTracTracRacesInEvent(String eventJsonURL, AsyncCallback<Pair<String, List<TracTracRaceRecordDTO>>> callback);
/**
* @param regattaToAddTo
* if <code>null</code>, an existing regatta by the name of the TracTrac event with the boat class name
* appended in parentheses will be looked up; if not found, a default regatta with that name will be
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
* @param liveURI
* may be <code>null</code> or the empty string in which case the server will use the
* {@link TracTracRaceRecordDTO#liveURI} from the <code>rr</code> race record.
* @param simulateWithStartTimeNow
* if <code>true</code>, the connector will adjust the time stamps of all events received such that the
* first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all
* events received such that they seem to be sent in "real-time." So, more or less the time points attached
* to the events sent to the receivers will again approximate the wall time.
* @param storedURImay
* be <code>null</code> or the empty string in which case the server will use the
* {@link TracTracRaceRecordDTO#storedURI} from the <code>rr</code> race record.
*/
void trackWithTracTrac(RegattaIdentifier regattaToAddTo,
Iterable<TracTracRaceRecordDTO> rrs, String liveURI, String storedURI, boolean trackWind, boolean correctWindByDeclination,
boolean simulateWithStartTimeNow, AsyncCallback<Void> callback);
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs,
String hostname, int port, boolean canSendRequests, boolean trackWind, boolean correctWindByDeclination,
AsyncCallback<Void> asyncCallback);
void replaySwissTimingRace(RegattaIdentifier regattaIdentifier, Iterable<SwissTimingReplayRaceDTO> replayRaces,
boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow,
AsyncCallback<Void> asyncCallback);
void getPreviousTracTracConfigurations(AsyncCallback<List<TracTracConfigurationDTO>> callback);
void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI,
AsyncCallback<Void> callback);
void stopTrackingEvent(RegattaIdentifier eventIdentifier, AsyncCallback<Void> callback);
void stopTrackingRaces(Iterable<RegattaAndRaceIdentifier> racesToStopTracking, AsyncCallback<Void> asyncCallback);
/**
* Untracks the race and removes it from the regatta. It will also be removed in all leaderboards
* @param regattaNamesAndRaceNames The identifier for the regatta name, and the race name to remove
*/
void removeAndUntrackRaces(Iterable<RegattaNameAndRaceName> regattaNamesAndRaceNames, AsyncCallback<Void> callback);
void getRawWindFixes(RegattaAndRaceIdentifier raceIdentifier, Collection<WindSource> windSources, AsyncCallback<WindInfoForRaceDTO> callback);
/**
* @param from if <code>null</code>, the tracked race's start of tracking is used
* @param to if <code>null</code>, the tracked race's time point of newest event is used
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, Date to, long resolutionInMilliseconds,
Collection<String> windSourceTypeNames, AsyncCallback<WindInfoForRaceDTO> callback);
/**
* @param windSourceTypeNames
* if <code>null</code>, data from all available wind sources will be returned, otherwise only from those
* whose {@link WindSource} name is contained in the <code>windSources</code> collection.
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes,
double latDeg, double lngDeg, Collection<String> windSourceTypeNames,
AsyncCallback<WindInfoForRaceDTO> callback);
/**
* Same as {@link #getWindInfo(RegattaAndRaceIdentifier, Date, long, int, double, double, Collection, AsyncCallback)}, only
* that the wind is not requested for a specific position, but instead the wind sources associated with the tracked
* race identified by <code>raceIdentifier</code> are requested to deliver their original position. This will in
* particular preserve the positions of actual measurements and will deliver the averaged positions for averaged /
* combined wind read-outs.
*
* @param from
* must not be <code>null</code>
* @param numberOfFixes
* no matter how great this value is chosen, never returns data beyond the newest event recorded in the
* race
*/
void getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes,
Collection<String> windSourceTypeNames, AsyncCallback<WindInfoForRaceDTO> callback);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind, AsyncCallback<Void> callback);
void removeWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO windDTO, AsyncCallback<Void> callback);
/**
* @param from
* for the list of competitors provided as keys of this map, requests the GPS fixes starting with the
* date provided as value
* @param to
* for the list of competitors provided as keys (expected to be equal to the set of competitors used as
* keys in the <code>from</code> parameter, requests the GPS fixes up to but excluding the date provided
* as value
* @param extrapolate
* if <code>true</code> and no position is known for <code>date</code>, the last entry returned in the
* list of GPS fixes will be obtained by extrapolating from the competitors last known position before
* <code>date</code> and the estimated speed.
* @return a map where for each competitor participating in the race the list of GPS fixes in increasing
* chronological order is provided. The last one is the last position at or before <code>date</code>.
*/
void getBoatPositions(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
boolean extrapolate, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>> callback);
void getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<RaceTimesInfoDTO> callback);
void getRaceTimesInfos(Collection<RegattaAndRaceIdentifier> raceIdentifiers, AsyncCallback<List<RaceTimesInfoDTO>> callback);
void getCoursePositions(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<CourseDTO> asyncCallback);
void getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<List<QuickRankDTO>> callback);
/**
* Returns a {@link LeaderboardDTO} will information about all races, their points and competitor display names
* filled in. The column details are filled for the races whose named are provided in
* <code>namesOfRacesForWhichToLoadLegDetails</code>.
*
* @param date
* the time point for the leaderboard data to retrieve, or <code>null</code> for "live mode" which means
* that the server will produce whatever it has ready for the currently active live delay set on the
* server; in live mode, data served may be taken from caches that lag up to a few seconds.
* @param namesOfRaceColumnsForWhichToLoadLegDetails
* if <code>null</code>, no {@link LeaderboardEntryDTO#legDetails leg details} will be present in the
* result ({@link LeaderboardEntryDTO#legDetails} will be <code>null</code> for all
* {@link LeaderboardEntryDTO} objects contained). Otherwise, the {@link LeaderboardEntryDTO#legDetails}
* list will contain one entry per leg of the race {@link com.sap.sailing.domain.base.Course} for those race columns whose
* {@link com.sap.sailing.domain.base.RaceColumn#getType() name} is contained in <code>namesOfRacesForWhichToLoadLegDetails</code>.
* For all other columns, {@link LeaderboardEntryDTO#legDetails} is <code>null</code>.
*/
void getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
AsyncCallback<LeaderboardDTO> callback);
void getLeaderboardNames(AsyncCallback<List<String>> callback);
void getLeaderboards(AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void getLeaderboardsByEvent(RegattaDTO regatta, AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void getLeaderboardsByRace(RaceDTO race, AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName,
int[] newDiscardingThreasholds, AsyncCallback<Void> callback);
void createFlexibleLeaderboard(String leaderboardName, int[] discardThresholds, ScoringSchemeType scoringSchemeType,
AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
void createRegattaLeaderboard(RegattaIdentifier regattaIdentifier, int[] discardThresholds,
AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
void removeLeaderboard(String leaderboardName, AsyncCallback<Void> asyncCallback);
void renameLeaderboard(String leaderboardName, String newLeaderboardName, AsyncCallback<Void> asyncCallback);
void addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace,
AsyncCallback<Void> callback);
void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName, AsyncCallback<Void> callback);
void removeLeaderboardColumn(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void connectTrackedRaceToLeaderboardColumn(String leaderboardName, String raceColumnName, String fleetName,
RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<Boolean> asyncCallback);
/**
* The key set of the map returned contains all fleets of the race column identified by the combination of
* <code>leaderboardName</code> and <code>raceColumnName</code>. If a value is <code>null</code>, there is no
* tracked race currently linked to the fleet in the race column; otherwise, the value is the {@link RaceIdentifier}
* of the tracked race currently connected for the fleet whose name is the key. The map returned is never <code>null</code>.
*/
void getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(String leaderboardName, String raceColumnName,
AsyncCallback<Map<String, RegattaAndRaceIdentifier>> callback);
void disconnectLeaderboardColumnFromTrackedRace(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints, AsyncCallback<Void> callback);
void updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString, String raceColumnName,
MaxPointsReason maxPointsReason, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString, String columnName,
Double correctedScore, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment, AsyncCallback<Void> callback);
void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates,
AsyncCallback<Void> callback);
void updateCompetitorDisplayNameInLeaderboard(String leaderboardName, String competitorID, String displayName,
AsyncCallback<Void> callback);
void moveLeaderboardColumnUp(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void moveLeaderboardColumnDown(String leaderboardName, String columnName, AsyncCallback<Void> callback);
void updateIsMedalRace(String leaderboardName, String columnName, boolean isMedalRace, AsyncCallback<Void> callback);
void updateRaceDelayToLive(RegattaAndRaceIdentifier regattaAndRaceIdentifier, long delayToLiveInMs, AsyncCallback<Void> callback);
void updateRacesDelayToLive(List<RegattaAndRaceIdentifier> regattaAndRaceIdentifiers, long delayToLiveInMs, AsyncCallback<Void> callback);
void getPreviousSwissTimingConfigurations(AsyncCallback<List<SwissTimingConfigurationDTO>> asyncCallback);
void listSwissTimingRaces(String hostname, int port, boolean canSendRequests,
AsyncCallback<List<SwissTimingRaceRecordDTO>> asyncCallback);
void storeSwissTimingConfiguration(String configName, String hostname, int port, boolean canSendRequests, AsyncCallback<Void> asyncCallback);
void sendSwissTimingDummyRace(String racMessage, String stlMesssage, String ccgMessage, AsyncCallback<Void> callback);
void getCountryCodes(AsyncCallback<String[]> callback);
void getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
double meters, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>> callback);
void getManeuvers(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
AsyncCallback<Map<CompetitorDTO, List<ManeuverDTO>>> callback);
void getLeaderboardGroups(boolean withGeoLocationData, AsyncCallback<List<LeaderboardGroupDTO>> callback);
void getLeaderboardGroupByName(String groupName, boolean withGeoLocationData,
AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Renames the group with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*/
void renameLeaderboardGroup(String oldName, String newName, AsyncCallback<Void> callback);
/**
* Removes the leaderboard group with the name <code>groupName</code> from the service and the persistant store.
*/
void removeLeaderboardGroup(String groupName, AsyncCallback<Void> callback);
/**
* Creates a new group with the name <code>groupname</code>, the description <code>description</code> and an empty list of leaderboards.<br/>
* @param displayGroupsInReverseOrder TODO
*/
void createLeaderboardGroup(String groupName, String description,
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType, 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 leaderboardNames The list of names of the new leaderboards of the group
*/
void updateLeaderboardGroup(String oldName, String newName, String description,
List<String> leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType, AsyncCallback<Void> callback);
void setRaceIsKnownToStartUpwind(RegattaAndRaceIdentifier raceIdentifier, boolean raceIsKnownToStartUpwind,
AsyncCallback<Void> callback);
void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable<WindSource> windSourcesToExclude,
AsyncCallback<Void> callback);
void getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<CompetitorDTO, Date> from,
Map<CompetitorDTO, Date> to, boolean extrapolate, AsyncCallback<RaceMapDataDTO> callback);
void getReplicaInfo(AsyncCallback<ReplicationStateDTO> callback);
void startReplicatingFromMaster(String masterName, String exchangeName, int servletPort, int messagingPort,
AsyncCallback<Void> callback);
void getEvents(AsyncCallback<List<EventDTO>> callback);
/**
* Creates a {@link EventDTO} for the {@link com.sap.sailing.domain.base.Event} with the name <code>eventName</code>, which contains the
* name, the description and a list with {@link RegattaDTO RegattaDTOs} contained in the event.<br />
* If no event with the name <code>eventName</code> is known, an {@link IllegalArgumentException} is thrown.
*/
void getEventByName(String eventName, AsyncCallback<EventDTO> callback);
/**
* Renames the event with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no event with the name <code>oldName</code> or there's already a event with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*/
void renameEvent(String oldName, String newName, AsyncCallback<Void> callback);
/**
* Removes the event with the name <code>eventName</code> from the service and the persistence store.
*/
void removeEvent(String eventName, AsyncCallback<Void> callback);
void createEvent(String eventName, String description, String publicationUrl, boolean isPublic, AsyncCallback<EventDTO> callback);
void updateEvent(String eventName, Serializable id, VenueDTO venue, String publicationUrl, boolean isPublic,
List<String> regattaNames, AsyncCallback<Void> callback);
void removeRegatta(RegattaIdentifier regattaIdentifier, AsyncCallback<Void> callback);
void addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<RaceColumnInSeriesDTO> callback);
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName,
AsyncCallback<Void> callback);
void createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, AsyncCallback<RegattaDTO> callback);
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
void getScoreCorrectionProviderDTOs(AsyncCallback<Iterable<ScoreCorrectionProviderDTO>> callback);
void getScoreCorrections(String scoreCorrectionProviderName, String eventName, String boatClassName,
Date timePointWhenResultPublished, AsyncCallback<RegattaScoreCorrectionDTO> asyncCallback);
void getWindSourcesInfo(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<WindInfoForRaceDTO> callback);
void getRaceCourse(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<List<ControlPointDTO>> callback);
void updateRaceCourse(RegattaAndRaceIdentifier raceIdentifier, List<ControlPointDTO> controlPoints, AsyncCallback<Void> callback);
void getFregResultUrls(AsyncCallback<List<String>> asyncCallback);
void removeFregURLs(Set<String> toRemove, AsyncCallback<Void> asyncCallback);
void addFragUrl(String result, AsyncCallback<Void> asyncCallback);
void getRaceCourseMarks(RegattaAndRaceIdentifier raceIdentifier, Date date, AsyncCallback<RaceCourseMarksDTO> callback);
void addColumnsToLeaderboard(String leaderboardName, List<Pair<String, Boolean>> columnsToAdd,
AsyncCallback<Void> callback);
void removeLeaderboardColumns(String leaderboardName, List<String> columnsToRemove, AsyncCallback<Void> callback);
void getLeaderboard(String leaderboardName, AsyncCallback<StrippedLeaderboardDTO> callback);
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed, AsyncCallback<Void> asyncCallback);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor,
AsyncCallback<Void> callback);
void listSwissTiminigReplayRaces(String swissTimingUrl, AsyncCallback<List<SwissTimingReplayRaceDTO>> asyncCallback);
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSize, DetailType detailType, String leaderboarGroupName, String leaderboardName, AsyncCallback<CompetitorsRaceDataDTO> callback);
/**
* Finds out the names of all {@link com.sap.sailing.domain.leaderboard.MetaLeaderboard}s managed by this server that
* {@link com.sap.sailing.domain.leaderboard.MetaLeaderboard#getLeaderboards() contain} the leaderboard identified by <code>leaderboardName</code>. The
* names of those meta-leaderboards are returned. The list returned is never <code>null</code> but may be empty if no such
* leaderboard is found.
*/
void getOverallLeaderboardNamesContaining(String leaderboardName, AsyncCallback<List<String>> asyncCallback);
void getPreviousSwissTimingArchiveConfigurations(
AsyncCallback<List<SwissTimingArchiveConfigurationDTO>> asyncCallback);
void storeSwissTimingArchiveConfiguration(String swissTimingUrl, AsyncCallback<Void> asyncCallback);
void generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces, AsyncCallback<PolarSheetGenerationTriggerResponse> asyncCallback);
void getPolarSheetsGenerationResults(String id, AsyncCallback<PolarSheetsData> asyncCallback);
void getPolarSheetData(String polarSheetId, int angle, int windSpeed, AsyncCallback<PolarSheetsHistogramData> wrapperCallback);
}
@@ -482,6 +482,8 @@ public interface StringMessages extends Messages {
String gapChangeSinceLegStartInSecondsTooltip();
String velocityMadeGoodInKnotsTooltip();
String sideToWhichMarkAtLegStartWasRoundedTooltip();
String generatePolarSheet();
String polarSheetChart();
String numberOfManeuversInRaceTooltip();
String competitorColumnTooltip();
String sailIdColumnTooltip();
@@ -497,6 +499,7 @@ public interface StringMessages extends Messages {
String overallRankTooltip();
String noDataFound();
String displayName();
String buoyZone();
String radiusInMeters();
String histogram();
String numberOfDataPoints();
String angleAndTotalNumberOfDataPoints(int angle, int numberOfDataPoints);
}
@@ -486,8 +486,11 @@ sideToWhichMarkAtLegStartWasRounded=Mark rounded to
sideToWhichMarkAtLegStartWasRoundedTooltip=Side to which the mark at leg start was rounded.
raceIsLive=Race {0} is live
racesAreLive=Races {0} are live
knotsUnit=kts
scoringSchemeHighPointFirstGetsTen=High Point, winner gets 10 points
knotsUnit=kts
generatePolarSheet=Generate Polar Sheet
polarSheetChart=Polar Sheet
competitorColumnTooltip=Names of the members of this competitor.
sailIdColumnTooltip=Shows the flag of the country and the sail number of the competitor.
rankColumnTooltip=The overall rank of this competitor in the regatta.
@@ -498,5 +501,6 @@ gpsData=GPS data
status=Status
noDataFound=No data found
displayName=Display name
buoyZone=Buoy zone
radiusInMeters=Radius (m)
histogram=Histogram
numberOfDataPoints=Number of data points
angleAndTotalNumberOfDataPoints=Angle: {0}; Total number of data-points: {1}
@@ -489,6 +489,8 @@ raceIsLive=Rennen {0} ist live
racesAreLive=Rennen {0} sind live
scoringSchemeHighPointFirstGetsTen=High Point System, Gewinner erhält 10 Punkte
knotsUnit=kn
generatePolarSheet=Polardiagramm erstellen
polarSheetChart=Polardiagramm
competitorColumnTooltip=Namen der Segler dieses Teilnehmers
sailIdColumnTooltip=Zeigt die Länderflagge und die Segelnummer des Teilnehmers
rankColumnTooltip=Der Gesamtplatz des Teilnehmers in der Regatta
@@ -499,5 +501,6 @@ gpsData=GPS-Daten
status=Status
noDataFound=Keine Daten gefunden
displayName=Anzeigename
buoyZone=Bojenzone
radiusInMeters=Radius (m)
histogram=Histogramm
numberOfDataPoints=Anzahl Datenpunkte
angleAndTotalNumberOfDataPoints=Winkel: {0}; Anzahl Datenpunkte: {1}
@@ -0,0 +1,31 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.sap.sailing.gwt.ui.adminconsole.AbstractTrackedRacesListComposite;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
public abstract class AbstractFilteredTrackedRacesList extends AbstractTrackedRacesListComposite {
private RaceFilter filter;
public AbstractFilteredTrackedRacesList(SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, RaceSelectionProvider raceSelectionProvider,
StringMessages stringMessages, boolean hasMultiSelection, RaceFilter filter) {
super(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, hasMultiSelection);
this.filter = filter;
}
@Override
protected boolean raceIsToBeAddedToList(RaceDTO race) {
if (filter!=null) {
return filter.compliesToFilter(race);
}
return true;
}
}
@@ -0,0 +1,171 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.events.PointMouseOverEventHandler;
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class PolarSheetsChartPanel extends SimplePanel implements RequiresResize {
private StringMessages stringMessages;
private Chart chart;
private Map<String, Series[]> seriesMap;
public PolarSheetsChartPanel(StringMessages stringMessages) {
this.stringMessages = stringMessages;
setPixelSize(800, 800);
chart = createPolarSheetChart();
seriesMap = new HashMap<String, Series[]>();
setWidget(chart);
}
private Chart createPolarSheetChart() {
Chart polarSheetChart = new Chart().setType(Series.Type.LINE)
.setLinePlotOptions(new LinePlotOptions().setLineWidth(1)).setZoomType(Chart.ZoomType.X_AND_Y)
.setPolar(true).setSize(800, 800);
polarSheetChart.setChartTitleText(stringMessages.polarSheetChart());
polarSheetChart.getYAxis().setMin(0);
return polarSheetChart;
}
private void newSeriesArray(String name) {
if (!seriesMap.containsKey(name)) {
Series[] seriesPerWindSpeed = new Series[13];
seriesMap.put(name, seriesPerWindSpeed);
} else {
// TODO exception handling
}
}
private void createSeriesForWindspeed(String name, int windSpeed) {
Series[] seriesPerWindSpeed = seriesMap.get(name);
Number[] forEachDeg = initializeDataForNewSeries();
seriesPerWindSpeed[windSpeed] = chart.createSeries().setPoints(forEachDeg);
seriesPerWindSpeed[windSpeed].setName(name + "-" + (windSpeed));
chart.addSeries(seriesPerWindSpeed[windSpeed]);
}
private Number[] initializeDataForNewSeries() {
Number[] forEachDeg = new Number[360];
return forEachDeg;
}
private void addValuesToSeries(String seriesId, PolarSheetsData result) {
if (seriesMap.containsKey(seriesId)) {
for (int i = 0; i < 13; i++) {
if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i), result.getDataCount())) {
if (seriesMap.get(seriesId)[i] == null) {
createSeriesForWindspeed(seriesId, i);
}
Series series = seriesMap.get(seriesId)[i];
series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false);
Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i);
if (points != null) {
series.setPoints(points);
}
}
}
}
}
private boolean hasSufficientDataForWindspeed(Integer[] dataCountPerAngleForWindspeed, int countOverall) {
int sum = 0;
for (int count : dataCountPerAngleForWindspeed) {
sum = sum + count;
}
//TODO make configurable
if (sum >= (0.05 * countOverall)) {
return true;
}
return false;
}
private Point[] createPointsWithMarkerAlphaAccordingToDataCount(PolarSheetsData result, int beaufort) {
Point[] points = new Point[360];
List<Integer> dataCountList = Arrays.asList(result.getDataCountPerAngleForWindspeed(beaufort));
Integer max = Collections.max(dataCountList);
if (max <= 0) {
return null;
}
for (int i = 0; i < 360; i++) {
points[i] = new Point(result.getAveragedPolarDataByWindSpeed()[beaufort][i]);
if (points[i] == null) {
points[i] = new Point(0);
}
double alpha = (double) dataCountList.get(i) / (double) max;
int blue;
int red;
int radius;
if (alpha > 0.2) {
red = (int) (alpha * 255);
blue = 0;
radius = 4;
} else {
blue = (int) ((1 - alpha) * 255);
red = 0;
radius = 2;
}
// Don't let the markers be invisible
alpha = 0.5 + 0.5 * alpha;
// TODO maybe set to series color. Not sure if this (highcharts-generated color) can be queried before
// rendering
points[i].setMarker(new Marker().setFillColor(new Color(red, 0, blue, alpha)).setRadius(radius));
}
return points;
}
public void removeSeries(String seriesId) {
if (seriesMap.containsKey(seriesId)) {
Series[] seriesPerWindSpeed = seriesMap.get(seriesId);
for (Series series : seriesPerWindSpeed) {
chart.removeSeries(series);
}
seriesMap.remove(seriesId);
}
}
public void removeAllSeries() {
chart.removeAllSeries();
seriesMap.clear();
}
public void setData(String id, PolarSheetsData result) {
if (id == null) {
// TODO Exception handling
return;
}
if (!seriesMap.containsKey(id)) {
newSeriesArray(id);
}
addValuesToSeries(id, result);
chart.redraw();
}
public void setSeriesPointMouseOverHandler(PointMouseOverEventHandler pointMouseOverHandler) {
chart.setSeriesPlotOptions(new SeriesPlotOptions().setPointMouseOverEventHandler(pointMouseOverHandler));
}
@Override
public void onResize() {
chart.setSizeToMatchContainer();
chart.redraw();
}
}
@@ -0,0 +1,63 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
public class PolarSheetsEntryPoint extends AbstractEntryPoint implements RegattaRefresher {
// private static final Logger logger =
// Logger.getLogger(PolarSheetsEntryPoint.class.getName());
private Set<RegattaDisplayer> regattaDisplayers;
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
regattaDisplayers = new HashSet<RegattaDisplayer>();
createUI();
fillRegattas();
}
private void createUI() {
RootPanel rootPanel = RootPanel.get();
DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
RootLayoutPanel.get().add(mainPanel);
ScrollPanel contentScrollPanel = new ScrollPanel();
PolarSheetsPanel polarSheetsPanel = new PolarSheetsPanel(sailingService, this, stringMessages, this);
polarSheetsPanel.addStyleName(PolarSheetsPanel.POLARSHEETS_STYLE);
regattaDisplayers.add(polarSheetsPanel);
contentScrollPanel.setWidget(polarSheetsPanel);
mainPanel.add(contentScrollPanel);
rootPanel.add(mainPanel);
}
@Override
public void fillRegattas() {
sailingService.getRegattas(new AsyncCallback<List<RegattaDTO>>() {
@Override
public void onSuccess(List<RegattaDTO> result) {
for (RegattaDisplayer regattaDisplayer : regattaDisplayers) {
regattaDisplayer.fillRegattas(result);
}
}
@Override
public void onFailure(Throwable caught) {
reportError("Remote Procedure Call getRegattas() - Failure");
}
});
}
}
@@ -0,0 +1,67 @@
package com.sap.sailing.gwt.ui.polarsheets;
import org.moxieapps.gwt.highcharts.client.AxisTitle;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartSubtitle;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Legend;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.Series.Type;
import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class PolarSheetsHistogramPanel extends SimplePanel implements RequiresResize{
private final Chart chart;
private final StringMessages stringMessages;
public PolarSheetsHistogramPanel(StringMessages stringMessages) {
this.stringMessages = stringMessages;
setSize("100%", "100%");
chart = createHistogramChart();
chart.getElement().setAttribute("align", "top");
setWidget(chart);
}
private Chart createHistogramChart() {
Chart histogramChart = new Chart().setType(Type.COLUMN).setZoomType(Chart.ZoomType.X).setWidth(800);
histogramChart.setChartTitleText(stringMessages.histogram());
histogramChart.getYAxis().setMin(0).setAxisTitle(new AxisTitle().setText(stringMessages.numberOfDataPoints()));
histogramChart.getXAxis().setLabels(new XAxisLabels().setRotation(-90f).setY(10)).setAxisTitle(new AxisTitle().setText(
stringMessages.speedInKnots()));
histogramChart.setLegend(new Legend().setEnabled(false));
return histogramChart;
}
public void setData(PolarSheetsHistogramData data) {
chart.removeAllSeries();
chart.setTitle(new ChartTitle().setText(stringMessages.histogram()),
new ChartSubtitle().setText(stringMessages.angleAndTotalNumberOfDataPoints(data.getAngle(), data.getDataCount())));
Point[] points = toPoints(data);
Series series = chart.createSeries();
series.setPoints(points);
chart.addSeries(series);
}
private Point[] toPoints(PolarSheetsHistogramData data) {
Number[] xValues = data.getxValues();
Number[] yValues = data.getyValues();
Point[] points = new Point[xValues.length];
for (int i = 0; i < xValues.length; i++) {
points[i] = new Point(xValues[i], yValues[i]);
}
return points;
}
@Override
public void onResize() {
chart.setSizeToMatchContainer();
chart.redraw();
}
}
@@ -0,0 +1,259 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.moxieapps.gwt.highcharts.client.events.PointMouseOverEvent;
import org.moxieapps.gwt.highcharts.client.events.PointMouseOverEventHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.actions.GetPolarSheetDataByAngleAction;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
public class PolarSheetsPanel extends FormPanel implements RaceSelectionChangeListener, RegattaDisplayer {
// TODO UI stuff
public static final String POLARSHEETS_STYLE = "polarSheets";
private FlowPanel mainPanel;
private SailingServiceAsync sailingService;
private PolarSheetsTrackedRacesList polarSheetsTrackedRacesList;
private ErrorReporter errorReporter;
private StringMessages stringMessages;
private RaceSelectionModel raceSelectionProvider;
private PolarSheetsEntryPoint polarSheetsEntryPoint;
private List<RegattaAndRaceIdentifier> selectedRaces;
private PolarSheetsChartPanel chartPanel;
private PolarSheetsHistogramPanel histogramPanel;
private AsyncActionsExecutor asyncActionsExecutor;
private Label polarSheetsGenerationLabel;
private Label dataCountLabel;
//Dual mapping. Another solution would be using commons BidiMap.
//But this would require to attach source code for gwt
private Map<String,String> idNameMapping;
private Map<String,String> nameIdMapping;
public PolarSheetsPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter,
StringMessages stringMessages, PolarSheetsEntryPoint polarSheetsEntryPoint) {
this.polarSheetsEntryPoint = polarSheetsEntryPoint;
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
idNameMapping = new HashMap<String, String>();
nameIdMapping = new HashMap<String, String>();
this.mainPanel = new FlowPanel();
setSize("100%", "100%");
mainPanel.setSize("100%", "100%");
setWidget(mainPanel);
HorizontalPanel splitPanel = createSplitPanel();
VerticalPanel leftPanel = addFilteredTrackedRacesList(splitPanel);
polarSheetsGenerationLabel = createPolarSheetGenerationStatusLabel();
leftPanel.add(polarSheetsGenerationLabel);
dataCountLabel = new Label();
leftPanel.add(dataCountLabel);
VerticalPanel rightPanel = addPolarSheetsChartPanel(splitPanel);
histogramPanel = new PolarSheetsHistogramPanel(stringMessages);
histogramPanel.getElement().setAttribute("align", "top");
rightPanel.add(histogramPanel);
mainPanel.add(splitPanel);
asyncActionsExecutor = new AsyncActionsExecutor();
setEventListenersForPolarSheetChart();
}
private void setEventListenersForPolarSheetChart() {
PointMouseOverEventHandler pointMouseOverHandler = new PointMouseOverEventHandler() {
@Override
public boolean onMouseOver(PointMouseOverEvent pointMouseOverEvent) {
int angle = (int) pointMouseOverEvent.getXAsLong();
String polarSheetNameWithWind = pointMouseOverEvent.getSeriesName();
String[] split = polarSheetNameWithWind.split("-");
String polarSheetName = split[0] + "-" + split[1];
String polarSheetId = nameIdMapping.get(polarSheetName);
int windSpeed = Integer.parseInt(split[2]);
GetPolarSheetDataByAngleAction action = new GetPolarSheetDataByAngleAction(sailingService,
polarSheetId, angle, windSpeed, new AsyncCallback<PolarSheetsHistogramData>() {
@Override
public void onSuccess(PolarSheetsHistogramData result) {
if (result != null) {
histogramPanel.setData(result);
}
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
});
asyncActionsExecutor.execute(action);
return true;
}
};
chartPanel.setSeriesPointMouseOverHandler(pointMouseOverHandler);
}
private Label createPolarSheetGenerationStatusLabel() {
Label polarSheetsGenerationStatusLabel = new Label();
return polarSheetsGenerationStatusLabel;
}
private VerticalPanel addPolarSheetsChartPanel(HorizontalPanel splitPanel) {
VerticalPanel verticalPanel = new VerticalPanel();
verticalPanel.setSize("100%", "100%");
chartPanel = new PolarSheetsChartPanel(stringMessages);
verticalPanel.add(chartPanel);
verticalPanel.setCellHeight(chartPanel, "800px");
splitPanel.add(verticalPanel);
splitPanel.setCellWidth(verticalPanel, "50%");
return verticalPanel;
}
private HorizontalPanel createSplitPanel() {
HorizontalPanel splitPanel = new HorizontalPanel();
splitPanel.setSize("100%", "100%");
return splitPanel;
}
private VerticalPanel addFilteredTrackedRacesList(HorizontalPanel splitPanel) {
VerticalPanel trackedRacesPanel = new VerticalPanel();
trackedRacesPanel.setWidth("100%");
createPolarSheetsTrackedRacesList();
trackedRacesPanel.add(polarSheetsTrackedRacesList);
splitPanel.add(trackedRacesPanel);
splitPanel.setCellWidth(trackedRacesPanel, "50%");
return trackedRacesPanel;
}
private void createPolarSheetsTrackedRacesList() {
raceSelectionProvider = new RaceSelectionModel();
ClickHandler polarSheetsGenerationButtonClickHandler = new ClickHandler() {
@Override
public void onClick(ClickEvent arg0) {
startPolarSheetGeneration();
}
};
polarSheetsTrackedRacesList = new PolarSheetsTrackedRacesList(sailingService, errorReporter,
polarSheetsEntryPoint, raceSelectionProvider, stringMessages, true, new RaceFilter(true, true),
polarSheetsGenerationButtonClickHandler);
raceSelectionProvider.addRaceSelectionChangeListener(this);
}
protected void startPolarSheetGeneration() {
// List conversion, to make List serializable
final List<RegattaAndRaceIdentifier> selectedRacesInArrayList = new ArrayList<RegattaAndRaceIdentifier>();
selectedRacesInArrayList.addAll(selectedRaces);
polarSheetsTrackedRacesList.changeGenerationButtonState(false);
sailingService.generatePolarSheetForRaces(selectedRacesInArrayList, new AsyncCallback<PolarSheetGenerationTriggerResponse>() {
@Override
public void onSuccess(PolarSheetGenerationTriggerResponse result) {
// TODO string messages
setCompletionLabel("Generating...");
startPullingResults(result.getId());
addNameForPolarSheet(result);
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
});
}
protected void addNameForPolarSheet(PolarSheetGenerationTriggerResponse result) {
String boatClassName = result.getBoatClassName();
int index = 0;
String name = "";
do {
index++;
name = boatClassName + "-" + index;
} while (nameIdMapping.containsKey(name));
idNameMapping.put(result.getId(), name);
nameIdMapping.put(name, result.getId());
}
protected void startPullingResults(final String id) {
sailingService.getPolarSheetsGenerationResults(id, new AsyncCallback<PolarSheetsData>() {
@Override
public void onSuccess(PolarSheetsData result) {
// TODO string messages
setCompletionLabel("Generating...");
dataCountLabel.setText("DataCount: " + result.getDataCount());
chartPanel.setData(idNameMapping.get(id), result);
if (!result.isComplete()) {
Timer timer = new Timer() {
@Override
public void run() {
startPullingResults(id);
}
};
timer.schedule(1500);
} else {
// TODO string messages
setCompletionLabel("Generation finished!");
polarSheetsTrackedRacesList.changeGenerationButtonState(true);
}
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
});
}
protected void setCompletionLabel(String string) {
polarSheetsGenerationLabel.setText(string);
}
@Override
public void onRaceSelectionChange(List<RegattaAndRaceIdentifier> selectedRaces) {
this.selectedRaces = selectedRaces;
}
@Override
public void fillRegattas(List<RegattaDTO> regattas) {
polarSheetsTrackedRacesList.fillRegattas(regattas);
}
}
@@ -0,0 +1,63 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.List;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
public class PolarSheetsTrackedRacesList extends AbstractFilteredTrackedRacesList {
private Button btnPolarSheetGeneration;
public PolarSheetsTrackedRacesList(SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, RaceSelectionProvider raceSelectionProvider,
StringMessages stringMessages, boolean hasMultiSelection, RaceFilter filter,
ClickHandler polarSheetsGenerationButtonClickedHandler) {
super(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages,
hasMultiSelection, filter);
btnPolarSheetGeneration.addClickHandler(polarSheetsGenerationButtonClickedHandler);
}
@Override
protected void addControlButtons(HorizontalPanel trackedRacesButtonPanel) {
btnPolarSheetGeneration = new Button(stringMessages.generatePolarSheet());
btnPolarSheetGeneration.ensureDebugId("PolarSheetGeneration");
trackedRacesButtonPanel.add(btnPolarSheetGeneration);
}
@Override
protected void makeControlsReactToSelectionChange(List<RaceDTO> selectedRaces) {
if (selectedRaces.isEmpty()) {
btnPolarSheetGeneration.setEnabled(false);
} else {
btnPolarSheetGeneration.setEnabled(true);
}
}
@Override
protected void makeControlsReactToFillRegattas(List<RegattaDTO> regattas) {
if (regattas.isEmpty()) {
btnPolarSheetGeneration.setVisible(false);
} else {
btnPolarSheetGeneration.setVisible(true);
btnPolarSheetGeneration.setEnabled(false);
}
}
/**
* Changes the state of the generation-start button
* @param enable
*/
public void changeGenerationButtonState(boolean enable) {
btnPolarSheetGeneration.setEnabled(enable);
}
}
@@ -0,0 +1,47 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.sap.sailing.gwt.ui.shared.RaceDTO;
public class RaceFilter {
private Boolean withGpsFixes;
private Boolean withWindData;
public RaceFilter(Boolean withGpsFixes, Boolean withWindData) {
this.withGpsFixes = withGpsFixes;
this.withWindData = withWindData;
}
public boolean compliesToFilter(RaceDTO race) {
if (!compliesToGpsFixFilter(race) || !compliesToWindDataFilter(race)) {
return false;
}
return true;
}
private boolean compliesToGpsFixFilter(RaceDTO race) {
if (withGpsFixes!=null) {
if (race.trackedRace.hasGPSData != withGpsFixes) {
return false;
}
}
return true;
}
private boolean compliesToWindDataFilter(RaceDTO race) {
if (withGpsFixes!=null) {
if (race.trackedRace.hasWindData != withWindData) {
return false;
}
}
return true;
}
}
@@ -38,6 +38,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
@@ -75,6 +77,9 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindError;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Placemark;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceFetcher;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
@@ -96,6 +101,8 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationTriggerResponseImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
@@ -109,6 +116,8 @@ import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
import com.sap.sailing.domain.polarsheets.BoatAndWindSpeed;
import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingArchiveConfiguration;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingConfiguration;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
@@ -304,6 +313,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private final DomainFactory tractracDomainFactory;
private final com.sap.sailing.domain.base.DomainFactory baseDomainFactory;
private final Map<String,PolarSheetGenerationWorker> polarSheetGenerationWorkers = new HashMap<String, PolarSheetGenerationWorker>();
public SailingServiceImpl() {
BundleContext context = Activator.getDefault();
@@ -353,7 +364,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
/**
* Asks the OSGi system for registered score correction provider services
*/
private ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> createAndOpenScoreCorrectionProviderServiceTracker(
protected ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> createAndOpenScoreCorrectionProviderServiceTracker(
BundleContext bundleContext) {
ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> tracker = new ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider>(bundleContext,
ScoreCorrectionProvider.class.getName(),
@@ -3095,4 +3106,113 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
swissTimingAdapterPersistence.storeSwissTimingArchiveConfiguration(swissTimingFactory.createSwissTimingArchiveConfiguration(
swissTimingJsonUrl));
}
@Override
public PolarSheetGenerationTriggerResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces) {
String id = UUID.randomUUID().toString();
RacingEventService service = getService();
Set<TrackedRace> trackedRaces = new HashSet<TrackedRace>();
for (RegattaAndRaceIdentifier race : selectedRaces) {
trackedRaces.add(service.getTrackedRace(race));
}
PolarSheetGenerationWorker genWorker = new PolarSheetGenerationWorker(trackedRaces, executor);
polarSheetGenerationWorkers.put(id, genWorker);
genWorker.startPolarSheetGeneration();
String name = getCommonBoatClass(trackedRaces);
return new PolarSheetGenerationTriggerResponseImpl(id, name);
}
private String getCommonBoatClass(Set<TrackedRace> trackedRaces) {
BoatClass boatClass = null;
for (TrackedRace race : trackedRaces) {
if (boatClass == null) {
boatClass = race.getRace().getBoatClass();
}
if (!boatClass.getName().matches(race.getRace().getBoatClass().getName())) {
return "Mixed";
}
}
return boatClass.getName();
}
@Override
public PolarSheetsData getPolarSheetsGenerationResults(String id) {
PolarSheetsData data = null;
if (polarSheetGenerationWorkers.containsKey(id)) {
PolarSheetGenerationWorker worker = polarSheetGenerationWorkers.get(id);
data = worker.getPolarData();
if (data.isComplete()) {
polarSheetGenerationWorkers.remove(id);
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
if (httpServletRequest != null) {
HttpSession session = httpServletRequest.getSession();
session.setAttribute(id, worker.getCompleteData());
}
}
} else {
//TODO Exception handling
}
return data;
}
@Override
public PolarSheetsHistogramData getPolarSheetData(String polarSheetId, int angle, int windSpeed) {
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
HttpSession session = httpServletRequest.getSession();
@SuppressWarnings("unchecked")
List<List<BoatAndWindSpeed>> data = (List<List<BoatAndWindSpeed>>) session.getAttribute(polarSheetId);
if (data == null) {
//TODO exception handling
return null;
}
List<BoatAndWindSpeed> dataForAngle = data.get(angle);
if (dataForAngle.size() < 1) {
//TODO exception handling
return null;
}
List<Double> dataForAngleAndWindSpeed = new ArrayList<Double>();
for (BoatAndWindSpeed dataPoint: dataForAngle) {
if (((int) dataPoint.getWindSpeed().getBeaufort()) == windSpeed) {
dataForAngleAndWindSpeed.add(dataPoint.getBoatSpeed().getKnots());
}
}
if (dataForAngleAndWindSpeed.size() < 1) {
//TODO exception handling
return null;
}
Double min = Collections.min(dataForAngleAndWindSpeed);
Double max = Collections.max(dataForAngleAndWindSpeed);
//TODO make number of columns dynamic to chart size
int numberOfColumns = 20;
double range = (max - min) / numberOfColumns;
Double[] xValues = new Double[numberOfColumns];
for (int i = 0; i < numberOfColumns; i++) {
xValues[i] = min + i * range + ( 0.5 * range);
}
Integer[] yValues = new Integer[numberOfColumns];
for (Double dataPoint : dataForAngleAndWindSpeed) {
int i = (int) (((dataPoint - min) / range));
if (i == numberOfColumns) {
//For max value
i = 19;
}
if (yValues[i] == null) {
yValues[i] = 0;
}
yValues[i]++;
}
PolarSheetsHistogramData histogramData = new PolarSheetsHistogramDataImpl(angle, xValues, yValues, dataForAngleAndWindSpeed.size());
return histogramData;
}
}
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.5.0/distro-source/core/src/gwt-module.dtd">
<module>
<stylesheet src="../PolarSheets.css" />
<inherits name="com.google.gwt.user.User" />
<!-- Specify the paths for translatable code -->
<extend-property name="locale" values="de" />
<source path="client" />
<source path="polarsheets" />
<source path='leaderboard' />
<source path='raceboard' />
<source path='adminconsole' />
<source path='usermanagement' />
<source path='shared' />
<source path='common' />
<source path='actions' />
<entry-point class="com.sap.sailing.gwt.ui.polarsheets.PolarSheetsEntryPoint">
</entry-point>
<!-- Google Maps API -->
<inherits name='com.google.gwt.maps.GoogleMaps' />
<!-- Highcharts API -->
<inherits name="org.moxieapps.gwt.highcharts.Highcharts" />
<!-- Other module inherits -->
<inherits name="com.sap.sailing.domain.SailingDomain" />
</module>
@@ -21,20 +21,26 @@ import org.junit.Test;
import com.mongodb.MongoException;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseAreaImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.EventImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.RaceColumnInSeriesImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.VenueImpl;
@@ -386,4 +392,25 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
}
}
@Test
public void testRegattaRaceAssociationStore() throws Exception {
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("112er", /* typicallyStartsUpwind */ true);
Regatta regatta = createRegatta("Cologne Masters", boatClass, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
List<Competitor> competitors = new ArrayList<Competitor>();
competitors.add(new CompetitorImpl("Axel", "Axel Uhl", null, null));
Iterable<Waypoint> waypoints = Collections.emptyList();
Course course = new CourseImpl("Course", waypoints);
RaceDefinition racedef = new RaceDefinitionImpl("M1", course, boatClass, competitors);
regatta.addRace(racedef);
RacingEventServiceImpl evs = new RacingEventServiceImpl(getMongoService());
assertNull(evs.getRememberedRegattaForRace(racedef.getId()));
evs.raceAdded(regatta, racedef);
assertNotNull(evs.getRememberedRegattaForRace(racedef.getId()));
evs.removeRegatta(regatta);
assertNull(evs.getRememberedRegattaForRace(racedef.getId()));
}
}
@@ -1080,6 +1080,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
public void removeRegatta(Regatta regatta) throws MalformedURLException, IOException, InterruptedException {
for (RaceDefinition race : regatta.getAllRaces()) {
removeRace(regatta, race);
mongoObjectFactory.removeRegattaForRaceID(race.getName(), regatta);
persistentRegattasForRaceIDs.remove(race.getId().toString());
}
if (regatta.isPersistent()) {
mongoObjectFactory.removeRegatta(regatta);
-1
View File
@@ -1 +0,0 @@
This is a test