Merge branch 'master' into ubilabs--tracking_app--develop

This commit is contained in:
Bennet Brunsen committed 2015-04-28 13:47:56 +02:00
commit ff1b0c6f87
136 files changed
+5699 -1564

No files matched your search

@@ -1,7 +1,7 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
public interface HasLeaderboardContext {
@@ -1,7 +1,7 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
public interface HasLeaderboardGroupContext {
@@ -5,7 +5,7 @@ import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import com.sap.sailing.datamining.impl.data.LeaderboardGroupWithContext;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
@@ -2,7 +2,7 @@ package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.data.HasLeaderboardGroupContext;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
public class LeaderboardGroupWithContext implements HasLeaderboardGroupContext {
@@ -2,7 +2,7 @@ package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.data.HasLeaderboardContext;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
public class LeaderboardWithContext implements HasLeaderboardContext {
@@ -20,11 +20,11 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeed;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.common;
public interface LegIdentifier extends RegattaAndRaceIdentifier {
RaceIdentifier getRaceIdentifier();
String getLegName();
int getLegNumber();
}
@@ -0,0 +1,62 @@
package com.sap.sailing.domain.common;
public class LegIdentifierImpl extends RegattaNameAndRaceName implements LegIdentifier {
private static final long serialVersionUID = 3599904513673776450L;
private RaceIdentifier raceIdentifier = null;
private String legName;
LegIdentifierImpl() {}
public LegIdentifierImpl(RegattaAndRaceIdentifier raceIdentifier, String legName) {
super(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
this.raceIdentifier = raceIdentifier;
this.legName = legName;
}
@Override
public RaceIdentifier getRaceIdentifier() {
return this.raceIdentifier;
}
@Override
public String getLegName() {
return legName;
}
@Override
public int getLegNumber() {
return Integer.parseInt(legName)-1;
}
@Override
public String toString() {
return getRegattaName() + "/" + getRaceName() + "/" + getLegName();
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((legName == null) ? 0 : legName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
LegIdentifierImpl other = (LegIdentifierImpl) obj;
if (legName == null) {
if (other.legName != null)
return false;
} else if (!legName.equals(other.legName))
return false;
return true;
}
}
@@ -149,7 +149,9 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
Wind combinedWindDirection = combinedTrack.getAveragedWind(/* position */ null, now);
// The course layout makes a wind estimation around 7deg. The confidence of the cluster based wind estimation is a lot
// higher that that of the course layout wind (currently at approximately 10E-13), we will end up quite near 180 deg.
assertEquals(180.0, combinedWindDirection.getBearing().getDegrees(), 0.2);
// Update 04/24/15: Since polar data service is not available here, the confidence will not be as good as it once was.
// So we have quite a big delta in the assert
assertEquals(180.0, combinedWindDirection.getBearing().getDegrees(), 0.9);
}
/**
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeListener;
@@ -904,4 +905,8 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setPolarDataService(PolarDataService polarDataService) {
}
}
@@ -29,6 +29,7 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
@@ -591,12 +592,12 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
return null;
}
@Override
public long getGateStartGolfDownTime() {
return 0;
}
@Override
public boolean isUsingMarkPassingCalculator() {
// TODO Auto-generated method stub
@@ -608,5 +609,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setPolarDataService(PolarDataService polarDataService) {
}
}
@@ -17,6 +17,7 @@ Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.markpassingcalculation.impl,
com.sap.sailing.domain.markpassingcalculation.splining,
com.sap.sailing.domain.masterdataimport,
com.sap.sailing.domain.polars,
com.sap.sailing.domain.racelog,
com.sap.sailing.domain.racelog.impl,
com.sap.sailing.domain.racelog.tracking,
@@ -38,4 +39,5 @@ Require-Bundle: com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sse.datamining.shared,
com.sap.sse.common,
com.sap.sse,
com.sap.sse.shared.android
com.sap.sse.shared.android,
org.apache.commons.math
@@ -1,4 +1,4 @@
package com.sap.sailing.polars;
package com.sap.sailing.domain.polars;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.common.Bearing;
@@ -1,4 +1,4 @@
package com.sap.sailing.polars.regression;
package com.sap.sailing.domain.polars;
public class NotEnoughDataHasBeenAddedException extends Exception {
@@ -1,4 +1,4 @@
package com.sap.sailing.polars;
package com.sap.sailing.domain.polars;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -17,10 +17,9 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.mining.MovingAverageProcessor;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.Util.Pair;
/**
@@ -206,7 +205,25 @@ public interface PolarDataService {
*/
PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) throws NotEnoughDataHasBeenAddedException;
/**
* For Upwind and Downwind returns the typical angle needed for a maneuver.
*
* @param boatClass
* @param legType needs to be Upwind or Downwind
* @param windSpeed Since the maneuver angle is windSpeed depended, supply it here
* @return angle with confidence as supplied by polars. Angle is always >= 0
* @throws NotEnoughDataHasBeenAddedException
*/
BearingWithConfidence<Void> getManeuverAngle(BoatClass boatClass, LegType legType, Speed windSpeed) throws NotEnoughDataHasBeenAddedException;
void raceFinishedLoading(TrackedRace race);
/**
* See {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack, boolean)}
* Always use regression
*/
SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException;
}
@@ -35,6 +35,7 @@ import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
@@ -770,4 +771,6 @@ public interface TrackedRace extends Serializable {
boolean isUsingMarkPassingCalculator();
void setPolarDataService(PolarDataService polarDataService);
}
@@ -101,6 +101,8 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
@@ -317,6 +319,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
private transient ShortTimeWindCache shortTimeWindCache;
private transient PolarDataService polarDataService;
public TrackedRaceImpl(final TrackedRegatta trackedRegatta, RaceDefinition race,
final Iterable<Sideline> sidelines, final WindStore windStore, final GPSFixStore gpsFixStore,
long delayToLiveInMillis, final long millisecondsOverWhichToAverageWind,
@@ -1860,40 +1864,66 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
BearingWithConfidence<TimePoint> resultBearing = null;
ScalablePosition scaledPosition = null;
int numberOfFixesConsideredForScaledPosition = 0;
Set<WindSource> estimationExcluded = new HashSet<>();
estimationExcluded.addAll(getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
estimationExcluded.addAll(getWindSources(WindSourceType.COURSE_BASED));
if (bearings != null) {
BearingWithConfidenceCluster<TimePoint>[] bearingClustersUpwind = bearings.get(LegType.UPWIND).getA().splitInTwo(
getMinimumAngleBetweenDifferentTacksUpwind(), timePoint);
if (!bearingClustersUpwind[0].isEmpty() && !bearingClustersUpwind[1].isEmpty()) {
BearingWithConfidence<TimePoint> average0 = bearingClustersUpwind[0].getAverage(timePoint);
BearingWithConfidence<TimePoint> average1 = bearingClustersUpwind[1].getAverage(timePoint);
upwindNumberOfRelevantBoats = Math
.min(bearingClustersUpwind[0].size(), bearingClustersUpwind[1].size());
confidence = Math.min(average0.getConfidence(), average1.getConfidence())
* getRace().getBoatClass().getUpwindWindEstimationConfidence(upwindNumberOfRelevantBoats);
reversedUpwindAverage = new BearingWithConfidenceImpl<TimePoint>(average0.getObject()
.middle(average1.getObject()).reverse(), confidence, timePoint);
scaledPosition = bearings.get(LegType.UPWIND).getB();
numberOfFixesConsideredForScaledPosition += bearings.get(LegType.UPWIND).getA().size();
int numberOfFixesUpwind = bearings.get(LegType.UPWIND).getA().size();
if (numberOfFixesUpwind > 0) {
ScalablePosition upwindPosition = bearings.get(LegType.UPWIND).getB();
Pair<Double, Double> minimumAngleBetweenDifferentTacksUpwindWithConfidence = getMinimumAngleBetweenDifferentTacksUpwind(getWind(
upwindPosition.divide(numberOfFixesUpwind), timePoint, estimationExcluded));
BearingWithConfidenceCluster<TimePoint>[] bearingClustersUpwind = bearings
.get(LegType.UPWIND)
.getA()
.splitInTwo(
minimumAngleBetweenDifferentTacksUpwindWithConfidence.getA(),
timePoint);
if (!bearingClustersUpwind[0].isEmpty() && !bearingClustersUpwind[1].isEmpty()) {
BearingWithConfidence<TimePoint> average0 = bearingClustersUpwind[0].getAverage(timePoint);
BearingWithConfidence<TimePoint> average1 = bearingClustersUpwind[1].getAverage(timePoint);
upwindNumberOfRelevantBoats = Math.min(bearingClustersUpwind[0].size(),
bearingClustersUpwind[1].size());
confidence = Math.min(average0.getConfidence(), average1.getConfidence())
* getRace().getBoatClass().getUpwindWindEstimationConfidence(upwindNumberOfRelevantBoats)
* minimumAngleBetweenDifferentTacksUpwindWithConfidence.getB();
reversedUpwindAverage = new BearingWithConfidenceImpl<TimePoint>(average0.getObject()
.middle(average1.getObject()).reverse(), confidence, timePoint);
scaledPosition = upwindPosition;
numberOfFixesConsideredForScaledPosition += bearings.get(LegType.UPWIND).getA().size();
}
}
BearingWithConfidenceImpl<TimePoint> downwindAverage = null;
int downwindNumberOfRelevantBoats = 0;
BearingWithConfidenceCluster<TimePoint>[] bearingClustersDownwind = bearings.get(LegType.DOWNWIND).getA()
.splitInTwo(getMinimumAngleBetweenDifferentTacksDownwind(), timePoint);
if (!bearingClustersDownwind[0].isEmpty() && !bearingClustersDownwind[1].isEmpty()) {
BearingWithConfidence<TimePoint> average0 = bearingClustersDownwind[0].getAverage(timePoint);
BearingWithConfidence<TimePoint> average1 = bearingClustersDownwind[1].getAverage(timePoint);
downwindNumberOfRelevantBoats = Math.min(bearingClustersDownwind[0].size(),
bearingClustersDownwind[1].size());
confidence = Math.min(average0.getConfidence(), average1.getConfidence())
* getRace().getBoatClass().getDownwindWindEstimationConfidence(downwindNumberOfRelevantBoats);
downwindAverage = new BearingWithConfidenceImpl<TimePoint>(average0.getObject().middle(
average1.getObject()), confidence, timePoint);
if (scaledPosition == null) {
scaledPosition = bearings.get(LegType.DOWNWIND).getB();
} else {
scaledPosition.add(bearings.get(LegType.DOWNWIND).getB());
int numberOfFixesDownwind = bearings.get(LegType.DOWNWIND).getA().size();
if (numberOfFixesDownwind > 0) {
ScalablePosition downwindPosition = bearings.get(LegType.DOWNWIND).getB();
Pair<Double, Double> minimumAngleBetweenDifferentTacksDownwindWithConfidence = getMinimumAngleBetweenDifferentTacksDownwind(getWind(
downwindPosition.divide(numberOfFixesDownwind), timePoint, estimationExcluded));
BearingWithConfidenceCluster<TimePoint>[] bearingClustersDownwind = bearings
.get(LegType.DOWNWIND)
.getA()
.splitInTwo(
minimumAngleBetweenDifferentTacksDownwindWithConfidence.getA(),
timePoint);
if (!bearingClustersDownwind[0].isEmpty() && !bearingClustersDownwind[1].isEmpty()) {
BearingWithConfidence<TimePoint> average0 = bearingClustersDownwind[0].getAverage(timePoint);
BearingWithConfidence<TimePoint> average1 = bearingClustersDownwind[1].getAverage(timePoint);
downwindNumberOfRelevantBoats = Math.min(bearingClustersDownwind[0].size(),
bearingClustersDownwind[1].size());
confidence = Math.min(average0.getConfidence(), average1.getConfidence())
* getRace().getBoatClass().getDownwindWindEstimationConfidence(
downwindNumberOfRelevantBoats)
* minimumAngleBetweenDifferentTacksDownwindWithConfidence.getB();
downwindAverage = new BearingWithConfidenceImpl<TimePoint>(average0.getObject().middle(
average1.getObject()), confidence, timePoint);
if (scaledPosition == null) {
scaledPosition = downwindPosition;
} else {
scaledPosition.add(downwindPosition);
}
numberOfFixesConsideredForScaledPosition += bearings.get(LegType.DOWNWIND).getA().size();
}
numberOfFixesConsideredForScaledPosition += bearings.get(LegType.DOWNWIND).getA().size();
}
BearingWithConfidenceCluster<TimePoint> resultCluster = new BearingWithConfidenceCluster<TimePoint>(weigher);
assert upwindNumberOfRelevantBoats == 0 || reversedUpwindAverage != null;
@@ -2715,12 +2745,55 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return getRace().getBoatClass().getManeuverDegreeAngleThreshold();
}
private double getMinimumAngleBetweenDifferentTacksDownwind() {
return getRace().getBoatClass().getMinimumAngleBetweenDifferentTacksDownwind();
private Pair<Double, Double> getMinimumAngleBetweenDifferentTacksDownwind(Wind wind) {
Pair<Double, Double> result;
double defaultAngle = getRace().getBoatClass().getMinimumAngleBetweenDifferentTacksDownwind();
double threshold = 20;
result = usePolarsIfPossible(wind, defaultAngle, LegType.DOWNWIND, threshold);
return result;
}
private double getMinimumAngleBetweenDifferentTacksUpwind() {
return getRace().getBoatClass().getMinimumAngleBetweenDifferentTacksUpwind();
private Pair<Double, Double> usePolarsIfPossible(Wind wind, double defaultAngle, LegType legType, double threshold) {
Pair<Double, Double> result;
if (polarDataService != null) {
try {
BearingWithConfidence<Void> average = polarDataService
.getManeuverAngle(getRace().getBoatClass(), legType, wind);
double averageAngleInDegMinusThreshold = average.getObject().getDegrees() - threshold;
if (averageAngleInDegMinusThreshold < defaultAngle) {
result = new Pair<Double, Double>(defaultAngle, 0.1);
} else {
result = new Pair<Double, Double>(averageAngleInDegMinusThreshold, average.getConfidence());
}
} catch (NotEnoughDataHasBeenAddedException e) {
result = new Pair<Double, Double>(defaultAngle, 0.1);
}
} else {
result = new Pair<Double, Double>(defaultAngle, 0.1);
}
return result;
}
private Pair<Double, Double> getMinimumAngleBetweenDifferentTacksUpwind(Wind wind) {
Pair<Double, Double> result;
double defaultAngle = getRace().getBoatClass().getMinimumAngleBetweenDifferentTacksUpwind();
if (polarDataService != null) {
try {
BearingWithConfidence<Void> average = polarDataService
.getManeuverAngle(getRace().getBoatClass(), LegType.UPWIND, wind);
double averageAngleInDegMinusThreshold = average.getObject().getDegrees() - 10;
if (averageAngleInDegMinusThreshold < defaultAngle) {
result = new Pair<Double, Double>(defaultAngle, 0.1);
} else {
result = new Pair<Double, Double>(averageAngleInDegMinusThreshold, average.getConfidence());
}
} catch (NotEnoughDataHasBeenAddedException e) {
result = new Pair<Double, Double>(defaultAngle, 0.1);
}
} else {
result = new Pair<Double, Double>(defaultAngle, 0.1);
}
return result;
}
private long getApproximateManeuverDurationInMilliseconds() {
@@ -3455,5 +3528,10 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
return result;
}
@Override
public void setPolarDataService(PolarDataService polarDataService) {
this.polarDataService = polarDataService;
}
}
+7
View File
@@ -444,4 +444,11 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.polars"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeListener;
@@ -902,4 +903,8 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setPolarDataService(PolarDataService polarDataService) {
}
}
@@ -49,10 +49,7 @@ Require-Bundle: com.sap.sailing.domain,
org.apache.shiro.core;bundle-version="1.2.2"
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.factory,
com.sap.sailing.polars.regression,
javax.jms;version="1.1.0",
Import-Package: javax.jms;version="1.1.0",
javax.servlet;version="3.1.0",
javax.servlet.descriptor;version="3.1.0",
javax.servlet.http;version="3.1.0",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

After

Width:  |  Height:  |  Size: 286 KiB

@@ -41,6 +41,7 @@ public interface TextMessages extends Messages {
String overview();
String sapSailing();
String search();
String startSimulator();
String countdownDays();
String countdownHours();
@@ -33,6 +33,7 @@ whatsNew=What''s new
overview=Overview
sapSailing=SAP Sailing
search=Search
startSimulator=Start Simulator
countdownDays=Days
countdownHours=Hrs
@@ -33,6 +33,7 @@ whatsNew=Neuigkeiten
overview=Übersicht
sapSailing=SAP Sailing
search=Suche
startSimulator=Simulator starten
countdownDays=Tage
countdownHours=Std.
@@ -1,5 +1,8 @@
package com.sap.sailing.gwt.home.client.app;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.place.shared.PlaceController;
import com.sap.sailing.gwt.home.client.place.aboutus.AboutUsPlace;
import com.sap.sailing.gwt.home.client.place.contact.ContactPlace;
@@ -17,6 +20,7 @@ import com.sap.sailing.gwt.home.client.place.sponsoring.SponsoringPlace;
import com.sap.sailing.gwt.home.client.place.start.StartPlace;
import com.sap.sailing.gwt.home.client.place.whatsnew.WhatsNewPlace;
import com.sap.sailing.gwt.home.client.place.whatsnew.WhatsNewPlace.WhatsNewNavigationTabs;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
public class HomePlacesNavigator extends AbstractPlaceNavigator {
@@ -40,6 +44,11 @@ public class HomePlacesNavigator extends AbstractPlaceNavigator {
return createLocalPlaceNavigation(new WhatsNewPlace(navigationTab), new WhatsNewPlace.Tokenizer());
}
public String getSimulatorURL() {
Map<String, String> parameters = new HashMap<String, String>();
return EntryPointLinkFactory.createSimulatorLink(parameters);
}
public PlaceNavigation<SponsoringPlace> getSponsoringNavigation() {
return createGlobalPlaceNavigation(new SponsoringPlace(), new SponsoringPlace.Tokenizer());
}
@@ -28,15 +28,15 @@ public class TabletAndDesktopWhatsNewView extends Composite implements WhatsNewV
private static final HyperlinkImpl HYPERLINK_IMPL = GWT.create(HyperlinkImpl.class);
@UiField HTML sailingAnalyticsNotes;
// @UiField HTML sailingSimulatorNotes;
@UiField HTML sailingSimulatorNotes;
@UiField HTML raceCommitteeAppNotes;
@UiField Anchor sailingAnalyticsNotesAnchor;
// @UiField Anchor sailingSimulatorNotesAnchor;
@UiField Anchor sailingSimulatorNotesAnchor;
@UiField Anchor raceCommitteeAppNotesAnchor;
private final PlaceNavigation<WhatsNewPlace> sailingAnalyticNotesNavigation;
// private final PlaceNavigation<WhatsNewPlace> sailingSimulatorNoteNavigation;
private final PlaceNavigation<WhatsNewPlace> sailingSimulatorNoteNavigation;
private final PlaceNavigation<WhatsNewPlace> raceCommitteeAppNotesNavigation;
private final List<Anchor> links;
@@ -49,19 +49,19 @@ public class TabletAndDesktopWhatsNewView extends Composite implements WhatsNewV
initWidget(uiBinder.createAndBindUi(this));
sailingAnalyticsNotes.setHTML(WhatsNewResources.INSTANCE.getSailingAnalyticsNotesHtml().getText());
// sailingSimulatorNotes.setHTML(WhatsNewResources.INSTANCE.getSailingSimulatorNotesHtml().getText());
sailingSimulatorNotes.setHTML(WhatsNewResources.INSTANCE.getSailingSimulatorNotesHtml().getText());
raceCommitteeAppNotes.setHTML(WhatsNewResources.INSTANCE.getRaceCommitteeAppNotesHtml().getText());
sailingAnalyticNotesNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingAnalytics);
// sailingSimulatorNoteNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingSimulator);
sailingSimulatorNoteNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingSimulator);
raceCommitteeAppNotesNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.RaceCommiteeApp);
sailingAnalyticsNotesAnchor.setHref(sailingAnalyticNotesNavigation.getTargetUrl());
// sailingSimulatorNotesAnchor.setHref(sailingSimulatorNoteNavigation.getTargetUrl());
sailingSimulatorNotesAnchor.setHref(sailingSimulatorNoteNavigation.getTargetUrl());
raceCommitteeAppNotesAnchor.setHref(raceCommitteeAppNotesNavigation.getTargetUrl());
links = Arrays.asList(new Anchor[] { sailingAnalyticsNotesAnchor, /*sailingSimulatorNotesAnchor,*/ raceCommitteeAppNotesAnchor });
contentWidgets = Arrays.asList(new HTML[] { sailingAnalyticsNotes, /*sailingSimulatorNotes,*/ raceCommitteeAppNotes });
links = Arrays.asList(new Anchor[] { sailingAnalyticsNotesAnchor, sailingSimulatorNotesAnchor, raceCommitteeAppNotesAnchor });
contentWidgets = Arrays.asList(new HTML[] { sailingAnalyticsNotes, sailingSimulatorNotes, raceCommitteeAppNotes });
switch(navigationTab) {
case PostRaceAnalytics:
@@ -73,7 +73,7 @@ public class TabletAndDesktopWhatsNewView extends Composite implements WhatsNewV
setActiveContent(sailingAnalyticsNotes, sailingAnalyticsNotesAnchor);
break;
case SailingSimulator:
// setActiveContent(sailingSimulatorNotes, sailingSimulatorNotesAnchor);
setActiveContent(sailingSimulatorNotes, sailingSimulatorNotesAnchor);
break;
case TrainingDiary:
break;
@@ -85,13 +85,13 @@ public class TabletAndDesktopWhatsNewView extends Composite implements WhatsNewV
setActiveContent(sailingAnalyticsNotes, sailingAnalyticsNotesAnchor);
handleClickEventWithLocalNavigation(event, sailingAnalyticNotesNavigation);
}
/*
@UiHandler("sailingSimulatorNotesAnchor")
void featuresClicked(ClickEvent event) {
setActiveContent(sailingSimulatorNotes, sailingSimulatorNotesAnchor);
handleClickEventWithLocalNavigation(event, sailingSimulatorNoteNavigation);
}
*/
@UiHandler("raceCommitteeAppNotesAnchor")
void releaseNotesClicked(ClickEvent event) {
setActiveContent(raceCommitteeAppNotes, raceCommitteeAppNotesAnchor);
@@ -10,9 +10,7 @@
<div class="{res.mainCss.grid}">
<div class="{res.mediaCss.small12} {res.mediaCss.columns}">
<g:Anchor ui:field="sailingAnalyticsNotesAnchor" href="javascript:;" styleName="{local_res.css.whatsnew_nav_link}"><ui:text from='{i18n.solutionsSAPHeadline}'/></g:Anchor>
<!--
<g:Anchor ui:field="sailingSimulatorNotesAnchor" href="javascript:;" styleName="{local_res.css.whatsnew_nav_link}"><ui:text from='{i18n.solutionsSimulatorHeadline}'/></g:Anchor>
-->
<g:Anchor ui:field="sailingSimulatorNotesAnchor" href="javascript:;" styleName="{local_res.css.whatsnew_nav_link}"><ui:text from='{i18n.solutionsSimulatorHeadline}'/></g:Anchor>
<g:Anchor ui:field="raceCommitteeAppNotesAnchor" href="javascript:;" styleName="{local_res.css.whatsnew_nav_link}"><ui:text from='{i18n.solutionsRaceHeadline}'/></g:Anchor>
</div>
</div>
@@ -20,9 +18,7 @@
<div class="{res.mainCss.grid}">
<div class="{res.mediaCss.small12} {res.mediaCss.columns}">
<g:HTML ui:field="sailingAnalyticsNotes" />
<!--
<g:HTML ui:field="sailingSimulatorNotes" />
-->
<g:HTML ui:field="raceCommitteeAppNotes" />
</div>
</div>
@@ -44,6 +44,8 @@ public class Solutions extends Composite {
@UiField Anchor sailingAnalyticsDetailsAnchor;
@UiField Anchor raceCommitteeAppDetailsAnchor;
@UiField Anchor simulatorStartAnchor;
@UiField Anchor simulatorAppDetailsAnchor;
private final PlaceNavigation<SolutionsPlace> sailingAnalyticsNavigation;
private final PlaceNavigation<SolutionsPlace> raceCommitteeAppNavigation;
@@ -55,6 +57,7 @@ public class Solutions extends Composite {
private final PlaceNavigation<WhatsNewPlace> sailingAnalyticsDetailsNavigation;
private final PlaceNavigation<WhatsNewPlace> raceCommitteeAppDetailsNavigation;
private final PlaceNavigation<WhatsNewPlace> simulatorAppDetailsNavigation;
private final HomePlacesNavigator placesNavigator;
public Solutions(SolutionsNavigationTabs navigationTab, HomePlacesNavigator placesNavigator) {
@@ -67,9 +70,12 @@ public class Solutions extends Composite {
sailingAnalyticsDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingAnalytics);
raceCommitteeAppDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.RaceCommiteeApp);
simulatorAppDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingSimulator);
sailingAnalyticsDetailsAnchor.setHref(sailingAnalyticsDetailsNavigation.getTargetUrl());
raceCommitteeAppDetailsAnchor.setHref(raceCommitteeAppDetailsNavigation.getTargetUrl());
simulatorAppDetailsAnchor.setHref(simulatorAppDetailsNavigation.getTargetUrl());
simulatorStartAnchor.setHref(placesNavigator.getSimulatorURL());
sailingAnalyticsNavigation = placesNavigator.getSolutionsNavigation(SolutionsNavigationTabs.SailingAnalytics);
raceCommitteeAppNavigation = placesNavigator.getSolutionsNavigation(SolutionsNavigationTabs.RaceCommiteeApp);
@@ -127,7 +133,12 @@ public class Solutions extends Composite {
public void sailingAnalyticsDetailsClicked(ClickEvent e) {
handleClickEventWithPlaceController(e, sailingAnalyticsDetailsNavigation);
}
@UiHandler("simulatorAppDetailsAnchor")
public void simulatorAppDetailsAnchorClicked(ClickEvent e) {
handleClickEventWithPlaceController(e, simulatorAppDetailsNavigation);
}
private void scrollToView(SolutionsNavigationTabs navigationTab) {
switch (navigationTab) {
case SailingAnalytics:
@@ -67,7 +67,9 @@
<div class="{res.mediaCss.small5} {res.mediaCss.smalloffset7} {res.mediaCss.columns}">
<h2><ui:text from='{i18n.solutionsSimulatorHeadline}'/></h2>
<p><ui:text from='{i18n.solutionsSimulatorText}'/></p>
<!-- <a href="#" class="{res.mainCss.button}">Learn More</a> -->
<g:Anchor ui:field="simulatorAppDetailsAnchor" styleName="{res.mainCss.button}"><ui:text from='{i18n.whatsNew}'/></g:Anchor>
<span>&nbsp;&nbsp;</span>
<g:Anchor ui:field="simulatorStartAnchor" target="_blank" styleName="{res.mainCss.button}"><ui:text from='{i18n.startSimulator}'/></g:Anchor>
</div>
</div>
</div>
@@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.Map;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -20,10 +21,11 @@ public class GetRaceMapDataAction implements AsyncAction<RaceMapDataDTO> {
private final Map<CompetitorDTO, Date> to;
private final boolean extrapolate;
private final Date date;
private final LegIdentifier simulationLegIdentifier;
public GetRaceMapDataAction(SailingServiceAsync sailingService, Iterable<CompetitorDTO> allCompetitors,
RegattaAndRaceIdentifier raceIdentifier, Date date, Map<CompetitorDTO, Date> from,
Map<CompetitorDTO, Date> to, boolean extrapolate) {
Map<CompetitorDTO, Date> to, boolean extrapolate, LegIdentifier simulationLegIdentifier) {
this.allCompetitors = allCompetitors;
this.sailingService = sailingService;
this.raceIdentifier = raceIdentifier;
@@ -31,6 +33,7 @@ public class GetRaceMapDataAction implements AsyncAction<RaceMapDataDTO> {
this.from = from;
this.to = to;
this.extrapolate = extrapolate;
this.simulationLegIdentifier = simulationLegIdentifier;
}
@Override
@@ -46,7 +49,7 @@ public class GetRaceMapDataAction implements AsyncAction<RaceMapDataDTO> {
}
sailingService.getRaceMapData(raceIdentifier, date, fromByCompetitorIdAsString, toByCompetitorIdAsString,
extrapolate, new AsyncCallback<CompactRaceMapDataDTO>() {
extrapolate, simulationLegIdentifier, new AsyncCallback<CompactRaceMapDataDTO>() {
@Override
public void onFailure(Throwable caught) {
callback.onFailure(caught);
@@ -1,12 +1,11 @@
package com.sap.sailing.gwt.ui.actions;
import java.util.Date;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap;
import com.sap.sailing.gwt.ui.shared.SimulatorResultsDTO;
import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay;
import com.sap.sse.gwt.client.async.AsyncAction;
/**
@@ -18,19 +17,15 @@ import com.sap.sse.gwt.client.async.AsyncAction;
*/
public class GetSimulationAction implements AsyncAction<SimulatorResultsDTO> {
private final SailingServiceAsync sailingService;
private final RegattaAndRaceIdentifier raceIdentifier;
private final Date from;
private final Date prevStartTime;
private final LegIdentifier legIdentifier;
public GetSimulationAction(SailingServiceAsync sailingService, RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime) {
public GetSimulationAction(SailingServiceAsync sailingService, LegIdentifier legIdentifier) {
this.sailingService = sailingService;
this.raceIdentifier = raceIdentifier;
this.from = from;
this.prevStartTime = prevStartTime;
this.legIdentifier = legIdentifier;
}
@Override
public void execute(AsyncCallback<SimulatorResultsDTO> callback) {
sailingService.getSimulatorResults(raceIdentifier, from, prevStartTime, callback);
sailingService.getSimulatorResults(legIdentifier, callback);
}
}
@@ -9,6 +9,10 @@ public class EntryPointLinkFactory extends AbstractEntryPointLinkFactory {
return createEntryPointLink("/gwt/RaceBoard.html", parameters);
}
public static String createSimulatorLink(Map<String, String> parameters) {
return createEntryPointLink("/gwt/Simulator.html", parameters);
}
public static String createLeaderboardLink(Map<String, String> parameters) {
return createEntryPointLink("/gwt/Leaderboard.html", parameters);
}
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction;
@@ -143,10 +144,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier);
SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime);
SimulatorResultsDTO getSimulatorResults(LegIdentifier legIdentifier);
CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException;
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate, LegIdentifier simulationLegIdentifier) throws NoWindException;
RaceTimesInfoDTO getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier);
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
@@ -190,7 +191,7 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
void getPolarResults(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<Boolean> callback);
void getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime, AsyncCallback<SimulatorResultsDTO> callback);
void getSimulatorResults(LegIdentifier legIdentifier, AsyncCallback<SimulatorResultsDTO> callback);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind, AsyncCallback<Void> callback);
@@ -335,7 +336,7 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
* identified by <code>raceIdentifier</code> will be used, considering that race's delay.
*/
void getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate, AsyncCallback<CompactRaceMapDataDTO> callback);
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate, LegIdentifier simulationLegIdentifier, AsyncCallback<CompactRaceMapDataDTO> callback);
void getReplicaInfo(AsyncCallback<ReplicationStateDTO> callback);
@@ -307,6 +307,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private boolean isMapInitialized;
private Date lastTimeChangeBeforeInitialization;
private int lastLegNumber;
/**
* The last quick ranks received from a call to {@link SailingServiceAsync#getQuickRanks(RaceIdentifier, Date, AsyncCallback)} upon
@@ -739,7 +741,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
// next, do the full thing; being the later call, if request throttling kicks in, the later call
// supersedes the earlier call which may get dropped then
GetRaceMapDataAction getRaceMapDataAction = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(), race,
useNullAsTimePoint() ? null : newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), /* extrapolate */ true);
useNullAsTimePoint() ? null : newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), /* extrapolate */ true, (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null));
asyncActionsExecutor.execute(getRaceMapDataAction, GET_RACE_MAP_DATA_CATEGORY,
getRaceMapDataCallback(oldTime, newTime, fromAndToAndOverlap.getC(), competitorsToShow, requestID));
@@ -820,7 +822,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
final GetRaceMapDataAction result;
if (!fromTimes.isEmpty()) {
result = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(),
race, useNullAsTimePoint() ? null : newTime, fromTimes, toTimes, /* extrapolate */true);
race, useNullAsTimePoint() ? null : newTime, fromTimes, toTimes, /* extrapolate */true, (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null));
} else {
result = null;
}
@@ -843,7 +845,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (map != null && raceMapDataDTO != null) {
quickRanks = raceMapDataDTO.quickRanks;
if (showViewSimulation && settings.isShowSimulationOverlay()) {
simulationOverlay.updateLeg(getCurrentLeg(), newTime, /* clearCanvas */ false);
lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber;
simulationOverlay.updateLeg(Math.max(lastLegNumber,1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion);
}
// process response only if not received out of order
if (startedProcessingRequestID < requestID) {
@@ -2228,7 +2231,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (newSettings.isShowSimulationOverlay() != settings.isShowSimulationOverlay()) {
settings.setShowSimulationOverlay(newSettings.isShowSimulationOverlay());
simulationOverlay.setVisible(newSettings.isShowSimulationOverlay());
simulationOverlay.updateLeg(getCurrentLeg(), timer.getTime(), true);
if (newSettings.isShowSimulationOverlay()) {
simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */);
}
}
if (newSettings.isWindUp() != settings.isWindUp()) {
settings.setWindUp(newSettings.isWindUp());
@@ -2437,16 +2442,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
return result;
}
public int getCurrentLeg() {
com.sap.sse.common.Util.Pair<Integer, CompetitorDTO> leaderWithLeg = this
.getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow());
if (leaderWithLeg == null) {
return 0;
} else {
return leaderWithLeg.getA();
}
}
private Image createSAPLogo() {
ImageResource sapLogoResource = resources.sapLogoOverlay();
Image sapLogo = new Image(sapLogoResource);
@@ -137,6 +137,7 @@ import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.MaxPointsReason;
@@ -221,6 +222,8 @@ import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.RaceStateOfSameDayHelper;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
@@ -340,8 +343,6 @@ import com.sap.sailing.manage2sail.EventResultDescriptor;
import com.sap.sailing.manage2sail.Manage2SailEventResultsParserImpl;
import com.sap.sailing.manage2sail.RaceResultDescriptor;
import com.sap.sailing.manage2sail.RegattaResultDescriptor;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.resultimport.ResultUrlProvider;
import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.server.RacingEventService;
@@ -395,24 +396,20 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCo
import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
import com.sap.sailing.server.operationaltransformation.UpdateSeries;
import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
import com.sap.sailing.server.simulation.SimulationService;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PathType;
import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SailingSimulator;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.SimulationResults;
import com.sap.sailing.simulator.TimedPositionWithSpeed;
import com.sap.sailing.simulator.impl.PolarDiagramGPS;
import com.sap.sailing.simulator.impl.SailingSimulatorImpl;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
import com.sap.sailing.simulator.windfield.WindFieldGenerator;
import com.sap.sailing.simulator.windfield.impl.WindFieldTrackedRaceImpl;
import com.sap.sailing.simulator.impl.SparsePolarDataException;
import com.sap.sailing.xrr.schema.RegattaResults;
import com.sap.sailing.xrr.structureimport.SeriesParameters;
import com.sap.sailing.xrr.structureimport.StructureImporter;
import com.sap.sailing.xrr.structureimport.buildstructure.SetRacenumberFromSeries;
import com.sap.sse.BuildVersion;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.Duration;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -421,7 +418,6 @@ import com.sap.sse.common.TypeBasedServiceFinderFactory;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.TimeRangeImpl;
import com.sap.sse.common.mail.MailException;
@@ -1435,107 +1431,55 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} else {
BoatClass boatClass = trackedRace.getRace().getBoatClass();
PolarDataService polarData = getService().getPolarDataService();
PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData);
PolarDiagram polarDiagram;
try {
polarDiagram = new PolarDiagramGPS(boatClass, polarData);
} catch (SparsePolarDataException e) {
polarDiagram = null;
// TODO: raise a UI message, to inform user about missing polar data resulting in unability to simulate
}
result = polarDiagram != null;
}
return result;
}
@Override
public SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime) {
TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
public SimulatorResultsDTO getSimulatorResults(LegIdentifier legIdentifier) {
// get simulation-results from smart-future-cached simulation-service
SimulatorResultsDTO result = null;
if (trackedRace != null) {
TimePoint fromTimePoint = from == null ? new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis() + 10000) : new MillisecondsTimePoint(from);
// get previous mark or start line as start-position
TrackedLeg trackedLeg = trackedRace.getCurrentLeg(fromTimePoint);
if (trackedLeg == null) {
return result;
}
Waypoint fromWaypoint = trackedLeg.getLeg().getFrom();
// get next mark as end-position
Waypoint toWaypoint = trackedLeg.getLeg().getTo();
TimePoint startTimePoint = null;
TimePoint endTimePoint = null;
Iterable<Util.Pair<Waypoint, Util.Pair<TimePoint, TimePoint>>> markPassings = trackedRace.getMarkPassingsTimes();
long legDuration = 0;
synchronized(markPassings) {
for(Pair<Waypoint, Pair<TimePoint, TimePoint>> markPassing : markPassings) {
if (markPassing.getA().equals(fromWaypoint)) {
startTimePoint = markPassing.getB().getA();
}
if (markPassing.getA().equals(toWaypoint)) {
endTimePoint = markPassing.getB().getA();
}
SimulationService simulationService = getService().getSimulationService();
if (simulationService == null)
return result;
SimulationResults simulationResults = simulationService.getSimulationResults(legIdentifier);
if (simulationResults == null)
return result;
// prepare simulator-results-dto
Map<PathType, Path> paths = simulationResults.getPaths();
if (paths != null) {
int noOfPaths = paths.size();
PathDTO[] pathDTOs = new PathDTO[noOfPaths];
int index = noOfPaths - 1;
for (Entry<PathType, Path> entry : paths.entrySet()) {
pathDTOs[index] = new PathDTO(entry.getKey().getTxtId());
// fill pathDTO with path points where speed is true wind speed
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
for (TimedPositionWithSpeed p : entry.getValue().getPathPoints()) {
wList.add(createSimulatorWindDTO(p));
}
pathDTOs[index].setPoints(wList);
pathDTOs[index].setAlgorithmTimedOut(entry.getValue().getAlgorithmTimedOut());
pathDTOs[index].setMixedLeg(entry.getValue().getMixedLeg());
index--;
}
legDuration = endTimePoint.asMillis() - startTimePoint.asMillis();
Position startPosition = trackedRace.getApproximatePosition(fromWaypoint, startTimePoint);
Position endPosition = trackedRace.getApproximatePosition(toWaypoint, endTimePoint);
if (startTimePoint.asDate().equals(prevStartTime)) {
return new SimulatorResultsDTO(startTimePoint.asDate(), 0, 0, null, null, null, null);
}
// determine legtype upwind/downwind/reaching
LegType legType = null;
try {
legType = trackedLeg.getLegType(fromTimePoint);
} catch (NoWindException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// get windfield
WindFieldGenerator windField = new WindFieldTrackedRaceImpl(trackedRace);
Duration timeStep = new MillisecondsDurationImpl(15 * 1000);
windField.generate(startTimePoint, null, timeStep);
// prepare simulation-parameters
List<Position> course = new ArrayList<>();
course.add(startPosition);
course.add(endPosition);
BoatClass boatClass = trackedRace.getRace().getBoatClass();
PolarDataService polarData = getService().getPolarDataService();
PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData);
SimulationParameters simulationPars = new SimulationParametersImpl(course, polarDiagram, windField, SailingSimulatorConstants.ModeEvent, true, true);
// for upwind/downwind, run simulation with start-time, start-position, end-position and windfield
SailingSimulator simulator = new SailingSimulatorImpl(simulationPars);
Map<String, Path> pathsAndNames = null;
if (legType != LegType.REACHING) {
pathsAndNames = simulator.getAllPathsEvenTimed(timeStep.asMillis(), null);
}
// prepare simulator-results-dto
if (pathsAndNames != null) {
int noOfPaths = pathsAndNames.size();
PathDTO[] pathDTOs = new PathDTO[noOfPaths];
int index = noOfPaths - 1;
for (Entry<String, Path> entry : pathsAndNames.entrySet()) {
pathDTOs[index] = new PathDTO(entry.getKey());
// fill pathDTO with path points where speed is true wind speed
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
for (TimedPositionWithSpeed p : entry.getValue().getPathPoints()) {
wList.add(createSimulatorWindDTO(p));
}
pathDTOs[index].setPoints(wList);
index--;
}
RaceMapDataDTO rcDTO;
rcDTO = new RaceMapDataDTO();
rcDTO.coursePositions = new CoursePositionsDTO();
rcDTO.coursePositions.waypointPositions = new ArrayList<>();
rcDTO.coursePositions.waypointPositions.add(startPosition);
rcDTO.coursePositions.waypointPositions.add(endPosition);
result = new SimulatorResultsDTO(startTimePoint.asDate(), timeStep.asMillis(), legDuration, rcDTO, pathDTOs, null, null);
}
RaceMapDataDTO rcDTO;
rcDTO = new RaceMapDataDTO();
rcDTO.coursePositions = new CoursePositionsDTO();
rcDTO.coursePositions.waypointPositions = new ArrayList<Position>();
rcDTO.coursePositions.waypointPositions.add(simulationResults.getStartPosition());
rcDTO.coursePositions.waypointPositions.add(simulationResults.getEndPosition());
result = new SimulatorResultsDTO(simulationResults.hashCode(), simulationResults.getStartTime(), simulationResults.getTimeStep(),
simulationResults.getLegDuration(), rcDTO, pathDTOs, null, null);
}
return result;
}
@@ -1611,13 +1555,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date,
Map<String, Date> fromPerCompetitorIdAsString, Map<String, Date> toPerCompetitorIdAsString,
boolean extrapolate) throws NoWindException {
boolean extrapolate, LegIdentifier simulationLegIdentifier) throws NoWindException {
final Map<CompetitorDTO, List<GPSFixDTO>> boatPositions = getBoatPositions(raceIdentifier,
fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate);
final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date);
final List<SidelineDTO> courseSidelines = getCourseSidelines(raceIdentifier, date);
final List<QuickRankDTO> quickRanks = getQuickRanks(raceIdentifier, date);
return new CompactRaceMapDataDTO(boatPositions, coursePositions, courseSidelines, quickRanks);
int simulationResultVersion = 0;
if (simulationLegIdentifier != null) {
SimulationService simulationService = getService().getSimulationService();
simulationResultVersion = simulationService.getSimulationResultsVersion(simulationLegIdentifier);
}
return new CompactRaceMapDataDTO(boatPositions, coursePositions, courseSidelines, quickRanks, simulationResultVersion);
}
/**
@@ -1896,6 +1845,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} else {
dateAsTimePoint = new MillisecondsTimePoint(date);
}
result.totalLegsCount = trackedRace.getRace().getCourse().getLegs().size();
result.currentLegNumber = trackedRace.getLastLegStarted(dateAsTimePoint);
result.marks = new HashSet<MarkDTO>();
result.waypointPositions = new ArrayList<>();
Set<Mark> marks = new HashSet<Mark>();
@@ -9,6 +9,11 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
@@ -21,6 +26,7 @@ import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.gwt.ui.client.SimulatorService;
import com.sap.sailing.gwt.ui.shared.BoatClassDTOsAndNotificationMessage;
@@ -50,11 +56,14 @@ import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplay;
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplayManager;
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternNotFoundException;
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternSetting;
import com.sap.sailing.server.simulation.SimulationService;
import com.sap.sailing.server.simulation.SimulationServiceFactory;
import com.sap.sailing.simulator.BoatClassProperties;
import com.sap.sailing.simulator.Grid;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PathType;
import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SailingSimulator;
import com.sap.sailing.simulator.Simulator;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.TimedPosition;
import com.sap.sailing.simulator.TimedPositionWithSpeed;
@@ -64,7 +73,7 @@ import com.sap.sailing.simulator.impl.PathGenerator1Turner;
import com.sap.sailing.simulator.impl.PathImpl;
import com.sap.sailing.simulator.impl.PolarDiagramCSV;
import com.sap.sailing.simulator.impl.ReadingConfigurationFileStatus;
import com.sap.sailing.simulator.impl.SailingSimulatorImpl;
import com.sap.sailing.simulator.impl.SimulatorImpl;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
import com.sap.sailing.simulator.impl.TimedPositionImpl;
import com.sap.sailing.simulator.impl.TimedPositionWithSpeedImpl;
@@ -77,6 +86,7 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class SimulatorServiceImpl extends RemoteServiceServlet implements SimulatorService {
private static final long serialVersionUID = 4445427185387524086L;
@@ -89,11 +99,23 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
// private static final double TOTAL_TIME_SCALE_FACTOR = 0.9;
private static final int DEFAULT_STEP_MAX = 800;
private static final long DEFAULT_TIMESTEP = 6666;
private final SimulationService simulationService;
private double stepSizeMeters = 0.0;
private WindControlParameters controlParameters = new WindControlParameters(0, 0);
private SpeedWithBearing averageWind = null;
public SimulatorServiceImpl() {
final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
Executor simulatorExecutor = new ThreadPoolExecutor(/* corePoolSize */THREAD_POOL_SIZE,
/* maximumPoolSize */THREAD_POOL_SIZE,
/* keepAliveTime */60, TimeUnit.SECONDS,
/* workQueue */new LinkedBlockingQueue<Runnable>());
// TODO: initialize smart-future-cache for simulation-results and add to simulation-service
simulationService = SimulationServiceFactory.INSTANCE.getService(simulatorExecutor, null);
}
@Override
public Position[] getRaceLocations() {
Position lakeGarda = new DegreePosition(45.57055337226086, 10.693345069885254);
@@ -185,7 +207,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
throw new WindPatternNotFoundException("Please select a valid wind pattern.");
}
Position[][] grid = bd.generatePositions(params.getxRes(), params.getyRes(), params.getBorderY(), params.getBorderX());
Position[][] grid = bd.generatePositions(params.getxRes(), params.getyRes(), params.getBorderY(),
params.getBorderX());
wf.setPositionGrid(grid);
TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime());
@@ -220,13 +243,13 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
e.printStackTrace();
}
return null;
}
@Override
public SimulatorResultsDTO getSimulatorResults(char mode, char rcDirection, WindFieldGenParamsDTO params, WindPatternDisplay pattern, boolean withWindField,
SimulatorUISelectionDTO selection) throws WindPatternNotFoundException,
ConfigurationException {
public SimulatorResultsDTO getSimulatorResults(char mode, char rcDirection, WindFieldGenParamsDTO params,
WindPatternDisplay pattern, boolean withWindField, SimulatorUISelectionDTO selection)
throws WindPatternNotFoundException, ConfigurationException {
WindFieldGenerator wf = null;
List<Position> course = null;
TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime());
@@ -235,7 +258,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
this.controlParameters.resetBlastRandomStream = params.isKeepState();
this.retreiveWindControlParameters(pattern);
if (rcDirection == SailingSimulatorConstants.LegTypeDownwind) {
this.controlParameters.baseWindBearing += 180.0;
this.controlParameters.baseWindBearing += 180.0;
}
wf = wfGenFactory.createWindFieldGenerator(pattern.getWindPatternName(), null, this.controlParameters);
@@ -259,13 +282,14 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
gridRes[0] = params.getxRes();
gridRes[1] = params.getyRes();
gridRes[2] = params.getBorderY();
gridRes[3] = params.getBorderX();
gridRes[3] = params.getBorderX();
wf.setGridResolution(gridRes);
wf.generate(startTime, null, timeStep);
Long longestPathTime = 0L;
SimulatedPathsEvenTimedResultDTO simulatedPaths = this.getSimulatedPathsEvenTimed(course, wf, mode, selection, params.showOmniscient, params.showOpportunist);
SimulatedPathsEvenTimedResultDTO simulatedPaths = this.getSimulatedPathsEvenTimed(course, wf, mode, selection,
params.showOmniscient, params.showOpportunist);
PathDTO[] pathDTOs = simulatedPaths.pathDTOs;
RaceMapDataDTO rcDTO = simulatedPaths.raceMapDataDTO;
@@ -283,10 +307,13 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
WindFieldDTO windFieldDTO = null;
if (pattern != null) {
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines());
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(),
// params.isShowLines(),
// params.getSeedLines());
}
return new SimulatorResultsDTO(null, timeStep.asMillis(), 0, rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage);
return new SimulatorResultsDTO(0, null, timeStep.asMillis(), 0, rcDTO, pathDTOs, windFieldDTO,
simulatedPaths.notificationMessage);
}
@Override
@@ -349,20 +376,23 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
@Override
public ResponseTotalTimeDTO getTotalTime(RequestTotalTimeDTO requestData) throws ConfigurationException {
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils.getAverage(requestData.allPoints)
: SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils
.getAverage(requestData.allPoints) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots()) * (requestData.stepDurationMilliseconds / 1000.);
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots())
* (requestData.stepDurationMilliseconds / 1000.);
// List<Position> points = SimulatorServiceUtils.getIntermediatePoints2(requestData.turnPoints,
// this.stepSizeMeters);
// int noOfPointsMinus1 = points.size() - 1;
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(requestData.selection.boatClassIndex);
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
.getPolarDiagram(requestData.selection.boatClassIndex);
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
String notificationMessage = polarDiagramAndNotificationMessage.getB();
SailingSimulator simulator = new SailingSimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null, SailingSimulatorConstants.ModeMeasured, true, true));
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null,
null, SailingSimulatorConstants.ModeMeasured, true, true));
Path gpsTrack = simulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection));
// Position startPoint = null;
@@ -405,14 +435,12 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
//
// double totalTimeSeconds = (timepointAsMillis - courseStartPoint.timepoint) / 1000;
//
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1).getTimePoint().asMillis() - gpsTrack
.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
//while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
//}
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1)
.getTimePoint().asMillis() - gpsTrack.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
// while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
// }
long startTimePoint = requestData.allPoints.get(0).timepoint;
long segmentTime = 0;
@@ -425,8 +453,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
totalTime += segmentTime;
segmentTime = getTimeMillisecondsBetween(requestData.turnPoints.get(index), requestData.turnPoints.get(index + 1), this.stepSizeMeters,
requestData.useRealAverageWindSpeed, gpsTrack, polarDiagram, startTimePoint + totalTime);
segmentTime = getTimeMillisecondsBetween(requestData.turnPoints.get(index),
requestData.turnPoints.get(index + 1), this.stepSizeMeters, requestData.useRealAverageWindSpeed,
gpsTrack, polarDiagram, startTimePoint + totalTime);
System.out.println("Total time of segment " + segmentIndex + " = " + segmentTime + " milliseconds");
}
@@ -437,25 +466,30 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
System.out.println("TotalTimeGPS: " + totalTimeGPSTrackSeconds + " TotalTimePoly: " + totalTimeSeconds);
return new ResponseTotalTimeDTO(totalTimeSeconds, totalTimeSeconds/totalTimeGPSTrackSeconds, notificationMessage);
return new ResponseTotalTimeDTO(totalTimeSeconds, totalTimeSeconds / totalTimeGPSTrackSeconds,
notificationMessage);
}
//@Override
// @Override
public ResponseTotalTimeDTO getTotalTimeOld(RequestTotalTimeDTO requestData) throws ConfigurationException {
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils.getAverage(requestData.allPoints)
: SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils
.getAverage(requestData.allPoints) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots()) * (requestData.stepDurationMilliseconds / 1000.);
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots())
* (requestData.stepDurationMilliseconds / 1000.);
List<Position> points = SimulatorServiceUtils.getIntermediatePoints2(requestData.turnPoints, this.stepSizeMeters);
List<Position> points = SimulatorServiceUtils.getIntermediatePoints2(requestData.turnPoints,
this.stepSizeMeters);
int noOfPointsMinus1 = points.size() - 1;
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(requestData.selection.boatClassIndex);
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
.getPolarDiagram(requestData.selection.boatClassIndex);
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
String notificationMessage = polarDiagramAndNotificationMessage.getB();
SailingSimulator simulator = new SailingSimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null, SailingSimulatorConstants.ModeMeasured, true, true));
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null,
null, SailingSimulatorConstants.ModeMeasured, true, true));
Path gpsTrack = simulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection));
Position startPoint = null;
@@ -476,17 +510,18 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
endPoint = points.get(index + 1);
distanceMeters = startPoint.getDistance(endPoint).getMeters();
windAtTimePoint = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(timepointAsMillis, gpsTrack)
: SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
windAtTimePoint = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(
timepointAsMillis, gpsTrack) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
boatBearingDeg = SimulatorServiceUtils.getInitialBearing(startPoint, endPoint);
polarDiagram.setWind(windAtTimePoint);
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg)).getMetersPerSecond();
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg))
.getMetersPerSecond();
stepTimeMilliseconds = (long) ((distanceMeters / boatSpeedMetersPerSecond) * 1000);
//problem right here
//boatSpeed might be 0 for very small distances
//this is a rough fix
if(boatSpeedMetersPerSecond == 0.0) {
// problem right here
// boatSpeed might be 0 for very small distances
// this is a rough fix
if (boatSpeedMetersPerSecond == 0.0) {
stepTimeMilliseconds = 1000;
}
@@ -496,23 +531,23 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
double totalTimeSeconds = (timepointAsMillis - requestData.allPoints.get(0).timepoint) / 1000;
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1).getTimePoint().asMillis() - gpsTrack
.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1)
.getTimePoint().asMillis() - gpsTrack.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
// while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
// }
System.out.println("TotalTimeGPS: " + totalTimeGPSTrackSeconds + " TotalTimePoly: " + totalTimeSeconds);
//while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
//}
System.out.println("TotalTimeGPS: "+totalTimeGPSTrackSeconds+" TotalTimePoly: "+totalTimeSeconds);
return new ResponseTotalTimeDTO((long) totalTimeSeconds, (double)totalTimeSeconds/(double)totalTimeGPSTrackSeconds, notificationMessage);
return new ResponseTotalTimeDTO((long) totalTimeSeconds, (double) totalTimeSeconds
/ (double) totalTimeGPSTrackSeconds, notificationMessage);
}
@Override
public Response1TurnerDTO get1Turner(Request1TurnerDTO requestData) throws ConfigurationException {
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(requestData.selection.boatClassIndex);
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
.getPolarDiagram(requestData.selection.boatClassIndex);
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
String notificationMessage = polarDiagramAndNotificationMessage.getB();
@@ -522,16 +557,19 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
Position oldMovedPosition = requestData.oldMovedPoint;
Position newMovedPosition = requestData.newMovedPoint;
Bearing oldMovedToNewMovedBearing = oldMovedPosition.getBearingGreatCircle(newMovedPosition);
boolean areTowardsSameDirection = SimulatorServiceUtils.areTowardsSameDirection(oldMovedToNewMovedBearing, new DegreeBearingImpl(
requestData.startToEndBearingDegrees));
boolean areTowardsSameDirection = SimulatorServiceUtils.areTowardsSameDirection(oldMovedToNewMovedBearing,
new DegreeBearingImpl(requestData.startToEndBearingDegrees));
System.out.println("oldMovedToNewMovedBearing = " + oldMovedToNewMovedBearing.getDegrees() + " degrees");
System.out.println("requestData.startToEndBearingDegrees = " + requestData.startToEndBearingDegrees + " degrees");
System.out.println("requestData.startToEndBearingDegrees = " + requestData.startToEndBearingDegrees
+ " degrees");
System.out.println("areTowardsSameDirection = " + areTowardsSameDirection);
SimulationParameters simulationParameters = new SimulationParametersImpl(null, polarDiagram, null, SailingSimulatorConstants.ModeMeasured, true, true);
SailingSimulator sailingSimulator = new SailingSimulatorImpl(simulationParameters);
Path gpsWind = sailingSimulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection));
SimulationParameters simulationParameters = new SimulationParametersImpl(null, polarDiagram, null, null,
SailingSimulatorConstants.ModeMeasured, true, true);
Simulator sailingSimulator = new SimulatorImpl(simulationParameters);
Path gpsWind = sailingSimulator.getLegGPSTrack(SimulatorServiceUtils
.toSimulatorUISelection(requestData.selection));
Grid grid = new CurvedGrid(oldMovedPosition, newMovedPosition);
this.controlParameters.baseWindBearing += grid.getSouth().getDegrees();
@@ -548,8 +586,10 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
Position realStart = areTowardsSameDirection ? oldMovedPosition : newMovedPosition;
Position realEnd = areTowardsSameDirection ? newMovedPosition : oldMovedPosition;
leftSide1Turner = pathGenerator.get1Turner(windFieldGenerator, polarDiagram, realStart, realEnd, startTime, true, DEFAULT_STEP_MAX, DEFAULT_TIMESTEP);
rightSide1Turner = pathGenerator.get1Turner(windFieldGenerator, polarDiagram, realStart, realEnd, startTime, false, DEFAULT_STEP_MAX, DEFAULT_TIMESTEP);
leftSide1Turner = pathGenerator.get1Turner(windFieldGenerator, polarDiagram, realStart, realEnd, startTime,
true, DEFAULT_STEP_MAX, DEFAULT_TIMESTEP);
rightSide1Turner = pathGenerator.get1Turner(windFieldGenerator, polarDiagram, realStart, realEnd, startTime,
false, DEFAULT_STEP_MAX, DEFAULT_TIMESTEP);
boolean isLeftSide1TurnerOnTheInside = SimulatorServiceUtils.isPointInsideTriangle(leftSide1Turner.getPosition(),
requestData.beforeMovedPoint,
@@ -579,8 +619,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
} else if (SimulatorServiceUtils.equals(rightSide1Turner.getPosition(), newMovedPosition, 0.0001)) {
System.out.println("voi folosi endPosition = rightSide1Turner!");
correct1Turner = rightSide1Turner;
}
else {
} else {
System.out.println("nu ar trebui sa ajunga aici NICIODATA!");
}
}
@@ -588,11 +627,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
long timeStepMilliseconds = 2000;
double minimumDistanceMeters = 4.0;
List<TimedPositionWithSpeed> path = pathGenerator.getIntersectionOptimalTowardWind(windFieldGenerator, polarDiagram, edgeStart, edgeEnd,
correct1Turner,
true,
timeStepMilliseconds, minimumDistanceMeters);
List<TimedPositionWithSpeed> path = pathGenerator.getIntersectionOptimalTowardWind(windFieldGenerator,
polarDiagram, edgeStart, edgeEnd, correct1Turner, true, timeStepMilliseconds, minimumDistanceMeters);
return new Response1TurnerDTO(SimulatorServiceUtils.toSimulatorWindDTOList(path), SimulatorServiceUtils.toSimulatorWindDTO(leftSide1Turner),
SimulatorServiceUtils.toSimulatorWindDTO(rightSide1Turner), oldMovedPosition,
@@ -607,7 +643,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
selectedRaceIndex = 0;
}
SailingSimulator simulator = new SailingSimulatorImpl(new SimulationParametersImpl(null, null, null, SailingSimulatorConstants.ModeMeasured, true, true));
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
SailingSimulatorConstants.ModeMeasured, true, true));
return simulator.getLegsNames(selectedRaceIndex);
}
@@ -615,7 +652,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
@Override
public List<String> getRacesNames() {
SailingSimulator simulator = new SailingSimulatorImpl(new SimulationParametersImpl(null, null, null, SailingSimulatorConstants.ModeMeasured, true, true));
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
SailingSimulatorConstants.ModeMeasured, true, true));
return simulator.getRacesNames();
}
@@ -627,9 +665,10 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
selectedRaceIndex = 0;
}
SailingSimulator simulator = new SailingSimulatorImpl(new SimulationParametersImpl(null, null, null, SailingSimulatorConstants.ModeMeasured,true, true));
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
SailingSimulatorConstants.ModeMeasured, true, true));
return simulator.getComeptitorsNames(selectedRaceIndex);
return simulator.getCompetitorsNames(selectedRaceIndex);
}
private void retreiveWindControlParameters(WindPatternDisplay pattern) {
@@ -687,11 +726,11 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
SimulatorWindDTO result = new SimulatorWindDTO();
if (speedWithBearing == null) {
result.trueWindBearingDeg = 0.0;
result.trueWindSpeedInKnots = 0.0;
result.trueWindBearingDeg = 0.0;
result.trueWindSpeedInKnots = 0.0;
} else {
result.trueWindBearingDeg = speedWithBearing.getBearing().getDegrees();
result.trueWindSpeedInKnots = speedWithBearing.getKnots();
result.trueWindBearingDeg = speedWithBearing.getBearing().getDegrees();
result.trueWindSpeedInKnots = speedWithBearing.getKnots();
}
if (position != null) {
@@ -779,9 +818,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
}
}
}
//TODO: throws null pointer exception for when reading serialized paths.
//TODO: should windlines also be serialized?
//logger.info("Added : " + windFieldDTO.getWindLinesDTO().getWindLinesMap().size() + " wind lines");
// TODO: throws null pointer exception for when reading serialized paths.
// TODO: should windlines also be serialized?
// logger.info("Added : " + windFieldDTO.getWindLinesDTO().getWindLinesMap().size() + " wind lines");
}
private void getWindLinesFromEndLine(WindFieldGenerator wf, WindFieldDTO windFieldDTO, TimePoint startTime,
@@ -850,22 +889,49 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
}
}
private SimulatedPathsEvenTimedResultDTO getSimulatedPathsEvenTimed(List<Position> course, WindFieldGenerator wf, char mode,
SimulatorUISelectionDTO selection, boolean showOmniscient, boolean showOpportunist) throws ConfigurationException {
private SimulatedPathsEvenTimedResultDTO getSimulatedPathsEvenTimed(List<Position> course, WindFieldGenerator wf,
char mode, SimulatorUISelectionDTO selection, boolean showOmniscient, boolean showOpportunist)
throws ConfigurationException {
LOGGER.info("Retrieving simulated paths");
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(selection.boatClassIndex);
PolarDiagram pd = polarDiagramAndNotificationMessage.getA();
SimulationParameters sp = new SimulationParametersImpl(course, pd, wf, mode, showOmniscient, showOpportunist);
SailingSimulator simulator = new SailingSimulatorImpl(sp);
Map<String, Path> pathsAndNames = null;
pathsAndNames = simulator.getAllPathsEvenTimed(wf.getTimeStep().asMillis(),
(mode == SailingSimulatorConstants.ModeMeasured) ? SimulatorServiceUtils.toSimulatorUISelection(selection) : null);
int[] gridRes = wf.getGridResolution();
Position[] gridArea = wf.getGridAreaGps();
LOGGER.info("showOmniscient : "+showOmniscient);
LOGGER.info("showOpportunist: "+showOpportunist);
if (gridArea != null) {
// initialize grid of supporting location for windfield
Grid bd = new CurvedGrid(gridArea[0], gridArea[1]);
// set base wind bearing
wf.getWindParameters().baseWindBearing += bd.getSouth().getDegrees();
LOGGER.info("base wind: " + pd.getWind().getKnots() + " kn, "
+ ((wf.getWindParameters().baseWindBearing) % 360.0) + "\u00B0");
// set water current
SpeedWithBearing current = new KnotSpeedWithBearingImpl(wf.getWindParameters().curSpeed, new DegreeBearingImpl(wf.getWindParameters().curBearing));
if (wf.getWindParameters().curSpeed > 0) {
pd.initializeSOGwithCurrent(); // polar-diagram is extended with data to support water currents
}
pd.setCurrent(current);
if (pd.getCurrent() != null) {
LOGGER.info("water current: " + pd.getCurrent().getKnots() + " kn, "
+ pd.getCurrent().getBearing().getDegrees() + "\u00B0");
}
wf.setBoundary(bd);
Position[][] positionGrid = bd.generatePositions(gridRes[0], gridRes[1], gridRes[2], gridRes[3]);
wf.setPositionGrid(positionGrid);
wf.generate(wf.getStartTime(), wf.getEndTime(), wf.getTimeStep());
}
SimulationParameters sp = new SimulationParametersImpl(course, pd, wf, null, mode, showOmniscient, showOpportunist);
Map<PathType, Path> pathsAndNames = null;
try {
pathsAndNames = simulationService.getAllPathsEvenTimed(sp, wf.getTimeStep().asMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int noOfPaths = pathsAndNames.size();
if (mode == SailingSimulatorConstants.ModeMeasured) {
noOfPaths++; // the last path is the polyline
@@ -878,20 +944,21 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
pathDTOs[0] = this.getPolylinePathDTO(pathsAndNames.get("6#GPS Poly"), pathsAndNames.get("7#GPS Track"));
}
for (Entry<String, Path> entry : pathsAndNames.entrySet()) {
LOGGER.info("Path " + entry.getKey());
for (Entry<PathType, Path> entry : pathsAndNames.entrySet()) {
LOGGER.info("Path " + entry.getKey().getTxtId());
// NOTE: pathName convention is: sort-digit + "#" + path-name
// pathsAndNames is TreeMap which ensures sorting
pathDTOs[index] = new PathDTO(entry.getKey());
// pathsAndNames is TreeMap which ensures sorting
pathDTOs[index] = new PathDTO(entry.getKey().getTxtId());
// fill pathDTO with path points where speed is true wind speed
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
for (TimedPositionWithSpeed p : entry.getValue().getPathPoints()) {
wList.add(createSimulatorWindDTO(p));
}
pathDTOs[index].setPoints(wList);
pathDTOs[index].setAlgorithmTimedOut(entry.getValue().getAlgorithmTimedOut());
pathDTOs[index].setMixedLeg(entry.getValue().getMixedLeg());
index--;
}
@@ -901,9 +968,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
rcDTO = new RaceMapDataDTO();
rcDTO.coursePositions = new CoursePositionsDTO();
rcDTO.coursePositions.waypointPositions = new ArrayList<>();
Path rc = simulator.getRaceCourse();
rcDTO.coursePositions.waypointPositions.add(rc.getPathPoints().get(0).getPosition());
rcDTO.coursePositions.waypointPositions.add(rc.getPathPoints().get(1).getPosition());
rcDTO.coursePositions.waypointPositions.add(course.get(0));
rcDTO.coursePositions.waypointPositions.add(course.get(1));
} else {
rcDTO = null;
}
@@ -963,7 +1029,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
List<TimedPositionWithSpeed> polylinePoints = gpsTrackPoints.subList(startPointIndex, endPointIndex + 1);
List<TimedPositionWithSpeed> turns = (new PathImpl(polylinePoints, null)).getTurns();
List<TimedPositionWithSpeed> turns = (new PathImpl(polylinePoints, null, false /* algorithmTimedOut */, false /* mixedLeg */)).getTurns();
// PathImpl.saveToGpxFile(new PathImpl(turns, null), "C:\\gps_path_turns_20deg_not_even_timed.gpx");
List<SimulatorWindDTO> points = new ArrayList<SimulatorWindDTO>();
@@ -977,8 +1043,10 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
isTurn = false;
for (TimedPositionWithSpeed turn : turns) {
if (turn.getPosition().getLatDeg() == point.getPosition().getLatDeg() && turn.getPosition().getLngDeg() == point.getPosition().getLngDeg()
&& turn.getTimePoint().asMillis() == point.getTimePoint().asMillis() && turn.getSpeed().getKnots() == point.getSpeed().getKnots()
if (turn.getPosition().getLatDeg() == point.getPosition().getLatDeg()
&& turn.getPosition().getLngDeg() == point.getPosition().getLngDeg()
&& turn.getTimePoint().asMillis() == point.getTimePoint().asMillis()
&& turn.getSpeed().getKnots() == point.getSpeed().getKnots()
&& turn.getSpeed().getBearing().getDegrees() == point.getSpeed().getBearing().getDegrees()) {
isTurn = true;
break;
@@ -989,12 +1057,12 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
position = point.getPosition();
points.add(new SimulatorWindDTO(position.getLatDeg(), position.getLngDeg(), speedWithBearing.getKnots(),
speedWithBearing.getBearing()
.getDegrees(), point.getTimePoint().asMillis(), isTurn));
speedWithBearing.getBearing().getDegrees(), point.getTimePoint().asMillis(), isTurn));
}
PathDTO result = new PathDTO(POLYLINE_PATH_NAME);
result.setPoints(points);
result.setAlgorithmTimedOut(false);
return result;
}
@@ -1081,17 +1149,18 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
endPoint = points.get(index + 1);
distanceMeters = startPoint.getDistance(endPoint).getMeters();
windAtTimePoint = useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(timepointAsMillis, gpsTrack)
: SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
windAtTimePoint = useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(timepointAsMillis,
gpsTrack) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
boatBearingDeg = SimulatorServiceUtils.getInitialBearing(startPoint, endPoint);
polarDiagram.setWind(windAtTimePoint);
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg)).getMetersPerSecond();
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg))
.getMetersPerSecond();
stepTimeMilliseconds = (long) ((distanceMeters / boatSpeedMetersPerSecond) * 1000);
// problem right here: boatSpeed might be 0 for very small distances
// this is a rough fix
if(boatSpeedMetersPerSecond == 0.0) {
if (boatSpeedMetersPerSecond == 0.0) {
stepTimeMilliseconds = 1000;
}
@@ -25,11 +25,12 @@ public class CompactRaceMapDataDTO implements IsSerializable {
private CoursePositionsDTO coursePositions;
private List<SidelineDTO> courseSidelines;
private List<CompactQuickRankDTO> quickRanks;
private int simulationResultVersion;
CompactRaceMapDataDTO() {}
public CompactRaceMapDataDTO(Map<CompetitorDTO, List<GPSFixDTO>> boatPositions, CoursePositionsDTO coursePositions,
List<SidelineDTO> courseSidelines, List<QuickRankDTO> quickRanks) {
List<SidelineDTO> courseSidelines, List<QuickRankDTO> quickRanks, int simulationResultVersion) {
this.boatPositionsByCompetitorIdAsString = new HashMap<String, List<GPSFixDTO>>();
for (Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : boatPositions.entrySet()) {
this.boatPositionsByCompetitorIdAsString.put(e.getKey().getIdAsString(), e.getValue());
@@ -43,6 +44,7 @@ public class CompactRaceMapDataDTO implements IsSerializable {
}
this.courseSidelines = courseSidelines;
this.coursePositions = coursePositions;
this.simulationResultVersion = simulationResultVersion;
}
public RaceMapDataDTO getRaceMapDataDTO(Iterable<CompetitorDTO> competitors) {
@@ -68,6 +70,7 @@ public class CompactRaceMapDataDTO implements IsSerializable {
result.boatPositions.put(competitor, e.getValue());
}
}
result.simulationResultVersion = simulationResultVersion;
return result;
}
}
@@ -13,6 +13,9 @@ public class CoursePositionsDTO implements IsSerializable {
public List<Position> finishMarkPositions;
public Set<MarkDTO> marks;
public int totalLegsCount;
public int currentLegNumber;
/**
* <code>null</code> if the start waypoint does not have exactly two marks with valid positions; in this case,
* {@link #startLineAdvantageousSide}, {@link #startLineAdvantageInMeters} and {@link #startLineLengthInMeters} are
@@ -7,9 +7,10 @@ import com.sap.sailing.domain.common.dto.NamedDTO;
public class PathDTO extends NamedDTO {
private static final long serialVersionUID = 2381814072763342186L;
private List<SimulatorWindDTO> points;
private boolean algorithmTimedOut;
private boolean mixedLeg;
public PathDTO() {
}
public PathDTO(final String name) {
@@ -31,5 +32,21 @@ public class PathDTO extends NamedDTO {
return points.get(points.size() - 1).timepoint - points.get(0).timepoint;
}
public boolean getAlgorithmTimedOut() {
return this.algorithmTimedOut;
}
public void setAlgorithmTimedOut(boolean algorithmTimedOut) {
this.algorithmTimedOut = algorithmTimedOut;
}
public boolean getMixedLeg() {
return this.mixedLeg;
}
public void setMixedLeg(boolean mixedLeg) {
this.mixedLeg = mixedLeg;
}
}
@@ -12,4 +12,5 @@ public class RaceMapDataDTO implements IsSerializable {
public CoursePositionsDTO coursePositions;
public List<SidelineDTO> courseSidelines;
public LinkedHashMap<CompetitorDTO, QuickRankDTO> quickRanks;
public int simulationResultVersion;
}
@@ -6,6 +6,7 @@ import com.google.gwt.user.client.rpc.IsSerializable;
public class SimulatorResultsDTO implements IsSerializable {
private int version;
private Date startTime;
private long timeStep;
private long legDuration;
@@ -15,6 +16,7 @@ public class SimulatorResultsDTO implements IsSerializable {
private String notificationMessage;
public SimulatorResultsDTO(){
this.version = 0;
this.startTime = null;
this.timeStep = 0;
this.legDuration = 0;
@@ -24,7 +26,8 @@ public class SimulatorResultsDTO implements IsSerializable {
this.notificationMessage = "";
}
public SimulatorResultsDTO(final Date startTime, final long timeStep, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
public SimulatorResultsDTO(final int version, final Date startTime, final long timeStep, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
this.version = version;
this.startTime = startTime;
this.timeStep = timeStep;
this.legDuration = legDuration;
@@ -34,6 +37,14 @@ public class SimulatorResultsDTO implements IsSerializable {
this.notificationMessage = notificationMessage;
}
public int getVersion() {
return this.version;
}
public void setVersion(int version) {
this.version = version;
}
public Date getStartTime() {
return this.startTime;
}
@@ -14,6 +14,8 @@ import com.google.gwt.maps.client.base.Point;
import com.google.gwt.maps.client.controls.ControlPosition;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.LegIdentifierImpl;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.gwt.ui.actions.GetSimulationAction;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -40,6 +42,8 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
public static final String GET_SIMULATION_CATEGORY = "getSimulation";
private final String textColor = "Black";
private final String textFont = "10pt OpenSansRegular";
private String algorithmTimedOutText = "out-of-bounds";
private String mixedLegText = "ambiguous wind";
private int xOffset = 0;
private int yOffset = 0; //150;
private double rectWidth = 20;
@@ -52,7 +56,6 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
private SimulatorResultsDTO simulationResult;
private PathDTO racePath;
private int raceLeg = 0;
private Date prevStartTime;
private Canvas simulationLegend;
public RaceSimulationOverlay(MapWidget map, int zIndex, RegattaAndRaceIdentifier raceIdentifier, SailingServiceAsync sailingService, StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor, CoordinateSystem coordinateSystem) {
@@ -64,16 +67,28 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
this.colors = new ColorPaletteGenerator();
}
public void updateLeg(int newLeg, Date newTime, boolean clearCanvas) {
if ((newLeg != raceLeg) && (this.isVisible())) {
public void updateLeg(int newLeg, boolean clearCanvas, int newVersion) {
if (((newLeg != raceLeg)||((newLeg == raceLeg)&&(newVersion != (simulationResult==null ? 0 : simulationResult.getVersion())))) && (this.isVisible())) {
raceLeg = newLeg;
if (clearCanvas) {
this.clearCanvas();
}
this.simulate(newTime);
this.simulate(newLeg);
}
}
public LegIdentifier getLegIdentifier() {
return new LegIdentifierImpl(raceIdentifier, String.valueOf(raceLeg));
}
public int getVersion() {
if (this.simulationResult == null) {
return 0;
} else {
return this.simulationResult.getVersion();
}
}
@Override
public void setVisible(boolean isVisible) {
super.setVisible(isVisible);
@@ -146,8 +161,12 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
Context2d ctxt = canvas.getContext2d();
PathDTO[] paths = simulationResult.getPaths();
boolean first = true;
int colorIdx = paths.length - 1;
int colorIdx = paths.length;
for (PathDTO path : paths) {
colorIdx--;
if (path.getMixedLeg()) {
continue;
}
List<SimulatorWindDTO> points = path.getPoints();
ctxt.setLineWidth(3.0);
ctxt.setGlobalAlpha(0.7);
@@ -175,7 +194,6 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
ctxt.closePath();
ctxt.stroke();
}
colorIdx--;
}
}
@@ -190,22 +208,57 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
txtmet = context2d.measureText("00:00:00");
double timewidth = txtmet.getWidth();
double txtmaxwidth = 0.0;
txtmet = context2d.measureText(racePath.getName().split("#")[1]);
txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth());
PathDTO[] paths = this.simulationResult.getPaths();
for (PathDTO path : paths) {
txtmet = context2d.measureText(path.getName());
if (racePath != null) {
txtmet = context2d.measureText(racePath.getName().split("#")[1]);
txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth());
}
//canvas.setSize(xOffset + rectWidth + txtmaxwidth + timewidth + 10.0+"px", rectHeight*(paths.length+1)+"px");
int canvasWidth = (int)Math.ceil(xOffset + rectWidth + txtmaxwidth + timewidth + 10.0 + 5.0);
int canvasHeight = (int)Math.ceil(yOffset + rectHeight*(paths.length+1));
setCanvasSize(canvas, canvasWidth, canvasHeight);
drawRectangleWithText(context2d, xOffset, yOffset, "rgba(255,255,255,0.8);",
racePath.getName().split("#")[1], getFormattedTime(racePath.getPathTime()), txtmaxwidth, timewidth);
boolean containsTimeOut = false;
boolean containsMixedLeg = false;
PathDTO[] paths = this.simulationResult.getPaths();
for (PathDTO path : paths) {
drawRectangleWithText(context2d, xOffset, yOffset + (paths.length-index) * rectHeight, this.colors.getColor(paths.length-1-index),
path.getName().split("#")[1], getFormattedTime(path.getPathTime()), txtmaxwidth, timewidth);
if (path.getAlgorithmTimedOut()) {
containsTimeOut = true;
}
if (path.getMixedLeg()) {
containsMixedLeg = true;
}
txtmet = context2d.measureText(path.getName().split("#")[1]);
txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth());
}
double newwidth = 0;
double deltaTime = 0;
double deltaMixedLeg = 0;
double deltaTimeOut = 0;
double mixedLegWidth = 0;
if (containsMixedLeg) {
txtmet = context2d.measureText(mixedLegText);
mixedLegWidth = txtmet.getWidth();
newwidth = Math.max(timewidth, mixedLegWidth);
}
double timeOutWidth = 0;
if (containsTimeOut) {
txtmet = context2d.measureText(algorithmTimedOutText);
timeOutWidth = txtmet.getWidth();
newwidth = Math.max(newwidth, timeOutWidth);
}
if (containsMixedLeg||containsTimeOut) {
deltaTime = newwidth - timewidth;
deltaMixedLeg = newwidth - mixedLegWidth;
deltaTimeOut = newwidth - timeOutWidth;
timewidth = newwidth;
}
//canvas.setSize(xOffset + rectWidth + txtmaxwidth + timewidth + 10.0+"px", rectHeight*(paths.length+1)+"px");
int canvasWidth = (int)Math.ceil(xOffset + rectWidth + txtmaxwidth + timewidth + 10.0 + 10.0);
int canvasHeight = (int)Math.ceil(yOffset + rectHeight*(paths.length + (racePath == null? 0 : 1)));
setCanvasSize(canvas, canvasWidth, canvasHeight);
if (racePath != null) {
drawRectangleWithText(context2d, xOffset, yOffset, null,
racePath.getName().split("#")[1], getFormattedTime(racePath.getPathTime()), txtmaxwidth, timewidth, deltaTime);
}
for (PathDTO path : paths) {
String timeText = (path.getMixedLeg() ? mixedLegText : (path.getAlgorithmTimedOut() ? algorithmTimedOutText : getFormattedTime(path.getPathTime())));
drawRectangleWithText(context2d, xOffset, yOffset + (paths.length-index-(racePath==null?1:0)) * rectHeight, this.colors.getColor(paths.length-1-index),
path.getName().split("#")[1], timeText, txtmaxwidth, timewidth, (path.getMixedLeg()?deltaMixedLeg:(path.getAlgorithmTimedOut()?deltaTimeOut:deltaTime)));
index++;
}
}
@@ -223,17 +276,19 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
context2d.fillRect(x, y, rectWidth, rectHeight);
}
protected void drawRectangleWithText(Context2d context2d, double x, double y, String color, String text, String time, double textmaxwidth, double timewidth) {
protected void drawRectangleWithText(Context2d context2d, double x, double y, String color, String text, String time, double textmaxwidth, double timewidth, double xdelta) {
double offset = 3.0;
context2d.setFont(textFont);
drawRectangle(context2d, x, y, color);
if (color != null) {
drawRectangle(context2d, x, y, color);
}
context2d.setGlobalAlpha(0.80);
context2d.setFillStyle("white");
context2d.fillRect(x + rectWidth, y, 15.0 + textmaxwidth + timewidth, rectHeight);
context2d.fillRect(x + (color==null?0:rectWidth), y, 20.0 + textmaxwidth + timewidth + (color==null?rectWidth:0), rectHeight);
context2d.setGlobalAlpha(1.0);
context2d.setFillStyle(textColor);
context2d.fillText(text, x + rectWidth + 5.0, y + 12.0 + offset);
context2d.fillText(time, x + rectWidth + textmaxwidth + 10.0, y + 12.0 + offset);
context2d.fillText(time, x + rectWidth + textmaxwidth + xdelta + 15.0, y + 12.0 + offset);
}
protected String getFormattedTime(long pathTime) {
@@ -244,8 +299,9 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
return pathTimeStr;
}
public void simulate(Date from) {
GetSimulationAction getSimulation = new GetSimulationAction(sailingService, raceIdentifier, from, prevStartTime);
public void simulate(int leg) {
LegIdentifier legIdentifier = new LegIdentifierImpl(raceIdentifier, String.valueOf(leg));
GetSimulationAction getSimulation = new GetSimulationAction(sailingService, legIdentifier);
asyncActionsExecutor.execute(getSimulation, GET_SIMULATION_CATEGORY,
new MarkedAsyncCallback<>(new AsyncCallback<SimulatorResultsDTO>() {
@Override
@@ -259,19 +315,26 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
public void onSuccess(SimulatorResultsDTO result) {
// store results
if (result != null) {
prevStartTime = result.getStartTime();
if (result.getPaths() != null) {
simulationResult = result;
PathDTO[] paths = result.getPaths();
racePath = new PathDTO();
List<SimulatorWindDTO> racePathPoints = new ArrayList<SimulatorWindDTO>();
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint));
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint + result.getLegDuration()));
racePath.setPoints(racePathPoints);
racePath.setName("0#Race");
if (result.getLegDuration() > 0) {
racePath = new PathDTO();
List<SimulatorWindDTO> racePathPoints = new ArrayList<SimulatorWindDTO>();
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint));
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint + result.getLegDuration()));
racePath.setPoints(racePathPoints);
racePath.setName("0#Race");
} else {
racePath = null;
}
clearCanvas();
drawPaths();
}
} else {
raceLeg = 0;
simulationResult = null;
clearCanvas();
}
}
}));
@@ -43,6 +43,8 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
private long totalTimeMilliseconds = 0;
private String name;
private boolean algorithmTimedOut;
private boolean mixedLeg;
private String pathColor = "Green";
@@ -56,9 +58,11 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
this.name = name;
}
public PathCanvasOverlay(MapWidget map, int zIndex, String name, Timer timer, WindFieldGenParamsDTO windParams, CoordinateSystem coordinateSystem) {
public PathCanvasOverlay(MapWidget map, int zIndex, String name, Timer timer, WindFieldGenParamsDTO windParams, boolean algorithmTimedOut, boolean mixedLeg, CoordinateSystem coordinateSystem) {
super(map, zIndex, timer, windParams, coordinateSystem);
this.name = name;
this.algorithmTimedOut = algorithmTimedOut;
this.mixedLeg = mixedLeg;
}
public PathCanvasOverlay(MapWidget map, int zIndex, String name, long totalTimeMilliseconds, CoordinateSystem coordinateSystem) {
@@ -250,6 +254,14 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
return name;
}
public boolean getAlgorithmTimedOut() {
return algorithmTimedOut;
}
public boolean getMixedLeg() {
return mixedLeg;
}
public long getPathTime() {
if (totalTimeIsGiven) {
return totalTimeMilliseconds;
@@ -40,6 +40,8 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay {
private String textColor = "Black";
private String textFont = "10pt OpenSansRegular";
private String algorithmTimedOutText = "out-of-bounds";
private String mixedLegText = "ambiguous wind";
public PathLegendCanvasOverlay(MapWidget map, int zIndex, char mode, CoordinateSystem coordinateSystem) {
super(map, zIndex, coordinateSystem);
@@ -82,13 +84,44 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay {
txtmet = context2d.measureText("00:00:00");
double timewidth = txtmet.getWidth();
double txtmaxwidth = 0.0;
boolean containsTimeOut = false;
boolean containsMixedLeg = false;
for (PathCanvasOverlay path : pathOverlays) {
if (path.getAlgorithmTimedOut()) {
containsTimeOut = true;
}
if (path.getMixedLeg()) {
containsMixedLeg = true;
}
txtmet = context2d.measureText(path.getName());
txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth());
}
double newwidth = 0;
double deltaTime = 0;
double deltaMixedLeg = 0;
double deltaTimeOut = 0;
double mixedLegWidth = 0;
if (containsMixedLeg) {
txtmet = context2d.measureText(mixedLegText);
mixedLegWidth = txtmet.getWidth();
newwidth = Math.max(timewidth, mixedLegWidth);
}
double timeOutWidth = 0;
if (containsTimeOut) {
txtmet = context2d.measureText(algorithmTimedOutText);
timeOutWidth = txtmet.getWidth();
newwidth = Math.max(newwidth, timeOutWidth);
}
if (containsMixedLeg||containsTimeOut) {
deltaTime = newwidth - timewidth;
deltaMixedLeg = newwidth - mixedLegWidth;
deltaTimeOut = newwidth - timeOutWidth;
timewidth = newwidth;
}
for (PathCanvasOverlay path : pathOverlays) {
String timeText = (path.getMixedLeg() ? mixedLegText : (path.getAlgorithmTimedOut() ? algorithmTimedOutText : getFormattedTime(path.getPathTime())));
drawRectangleWithText(xOffset, yOffset + (pathOverlays.size()-1-index) * rectHeight, path.getPathColor(),
path.getName(), getFormattedTime(path.getPathTime()),txtmaxwidth,timewidth);
path.getName(), timeText, txtmaxwidth, timewidth, (path.getMixedLeg()?deltaMixedLeg:(path.getAlgorithmTimedOut()?deltaTimeOut:deltaTime)));
index++;
}
//
@@ -170,7 +203,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay {
context2d.fillRect(x, y, rectWidth, rectHeight);
}
protected void drawRectangleWithText(double x, double y, String color, String text, String time, double textmaxwidth, double timewidth) {
protected void drawRectangleWithText(double x, double y, String color, String text, String time, double textmaxwidth, double timewidth, double xdelta) {
double offset = 3.0;
@@ -179,11 +212,11 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay {
drawRectangle(x, y, color);
context2d.setGlobalAlpha(0.80);
context2d.setFillStyle("white");
context2d.fillRect(x + rectWidth, y, 15.0 + textmaxwidth + timewidth, rectHeight);
context2d.fillRect(x + rectWidth, y, 20.0 + textmaxwidth + timewidth, rectHeight);
context2d.setGlobalAlpha(1.0);
context2d.setFillStyle(textColor);
context2d.fillText(text, x + rectWidth + 5.0, y + 12.0 + offset);
context2d.fillText(time, x + rectWidth + textmaxwidth + 10.0, y + 12.0 + offset);
context2d.fillText(time, x + rectWidth + textmaxwidth + xdelta + 15.0, y + 12.0 + offset);
}
protected String getFormattedTime(long pathTime) {
@@ -18,8 +18,8 @@ public class ReplayPathCanvasOverlay extends PathCanvasOverlay {
private List<SimulatorWindDTO> windDTOToDraw;
public ReplayPathCanvasOverlay(MapWidget map, int zIndex, final String name, final Timer timer,
WindFieldGenParamsDTO windParams, CoordinateSystem coordinateSystem) {
super(map, zIndex, name, timer, windParams, coordinateSystem);
WindFieldGenParamsDTO windParams, boolean algorithmTimedOut, boolean mixedLeg, CoordinateSystem coordinateSystem) {
super(map, zIndex, name, timer, windParams, algorithmTimedOut, mixedLeg, coordinateSystem);
this.displayWindAlongPath = false;
windDTOToDraw = null;
canvas.setStyleName("replayPanel");
@@ -34,6 +34,10 @@ public class ReplayPathCanvasOverlay extends PathCanvasOverlay {
@Override
public void timeChanged(final Date newTime, Date oldTime) {
if (windFieldDTO == null) {
return;
}
canvas.getContext2d().clearRect(0/* canvas.getAbsoluteLeft() */, 0/* canvas.getAbsoluteTop() */,
canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
@@ -50,7 +54,10 @@ public class ReplayPathCanvasOverlay extends PathCanvasOverlay {
@Override
public boolean shallStop() {
if (!isVisible() || windDTOToDraw == null || windFieldDTO == null) {
if (windDTOToDraw == null || windFieldDTO == null) {
return false;
}
if (!isVisible()) {
return true;
}
if (windDTOToDraw.size() >= (windFieldDTO.getMatrix().size()-1)) {
@@ -158,27 +158,31 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
PathDTO currentPath = null;
//String color = null;
String pathName = null;
boolean algorithmTimedOut = false;
int noOfPaths = paths.length;
for (int index = 0; index < noOfPaths; ++index) {
currentPath = paths[index];
pathName = currentPath.getName();
algorithmTimedOut = currentPath.getAlgorithmTimedOut();
//color = colorPalette.getColor(noOfPaths - 1 - index);
if (pathName.equals("Polyline")) {
pathPolyline = createPathPolyline(currentPath);
} else if (pathName.equals("GPS Poly")) {
continue;
}
else {
} else {
/* TODO Revisit for now creating a WindFieldDTO from the path */
WindFieldDTO pathWindDTO = new WindFieldDTO();
pathWindDTO.setMatrix(currentPath.getPoints());
WindFieldDTO pathWindDTO = null;
if (!currentPath.getMixedLeg()) {
pathWindDTO = new WindFieldDTO();
pathWindDTO.setMatrix(currentPath.getPoints());
}
ReplayPathCanvasOverlay replayPathCanvasOverlay = new ReplayPathCanvasOverlay(map, SimulatorMapOverlaysZIndexes.PATH_ZINDEX,
pathName.split("#")[1], timer, windParams, coordinateSystem);
pathName.split("#")[1], timer, windParams, algorithmTimedOut, currentPath.getMixedLeg(), coordinateSystem);
replayPathCanvasOverlays.add(replayPathCanvasOverlay);
replayPathCanvasOverlay.setPathColor(colorPalette.getColor(Integer.parseInt(pathName.split("#")[0])-1));
@@ -205,7 +209,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
legendCanvasOverlay.setPathOverlays(replayPathCanvasOverlays);
long tmpDurationTime = currentPath.getPathTime();
if (tmpDurationTime > maxDurationTime) {
if ((!currentPath.getMixedLeg())&&(!currentPath.getAlgorithmTimedOut())&&(tmpDurationTime > maxDurationTime)) {
maxDurationTime = tmpDurationTime;
}
}
@@ -4,12 +4,64 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - Strategy Simulator</h4>
<div class="innerContent">
<h5 class="articleSubheadline">September 2014</h5>
<h5 class="articleSubheadline">October 2014</h5>
<ul class="bulletList">
<li>Point 1...
</li>
<li>Simulation is being integrated into the race board (see What's New in Sailing Analytics)</li>
</ul>
<h5 class="articleSubheadline">August 2014</h5>
<ul class="bulletList">
<li>Accuracy of optimal course calculation has been improved</li>
<li>Streamlet motion is now calibrated to physical reality on large zoom levels of the map</li>
</ul>
<h5 class="articleSubheadline">April 2014</h5>
<ul class="bulletList">
<li>Wind is visualized using animated streamlets based on a swarm of particles which locally follow the wind</li>
</ul>
<h5 class="articleSubheadline">September 2013</h5>
<ul class="bulletList">
<li>Windbot based on RaspberryPi to construct real, spatially resolved wind vector fields from wind measurements</li>
</ul>
<h5 class="articleSubheadline">June 2013</h5>
<ul class="bulletList">
<li>Optimal courses shown as "Omniscient" are determined by an heuristic search algorithm greatly improving accuracy and runtime performance</li>
<li>Sailing angles are represented with full numeric accuracy</li>
<li>Water current is taken into account</li>
<li>Support up- and downwind legs</li>
<li>1-Click Simulation simplifies the user interaction</li>
<li>Race course map for Kieler Woche and Travemuender Woche</li>
</ul>
<h5 class="articleSubheadline">March 2013</h5>
<ul class="bulletList">
<li>What-if simulation comparing sailed GPS-track and simulated course with changed locations of turns for
answering questions like: would it have been advantageous to do the second turn a bit later?</li>
</ul>
<h5 class="articleSubheadline">February 2013</h5>
<ul class="bulletList">
<li>Strategy Simulator becomes a part of SAP's Sailing Analytics suite</li>
</ul>
<h5 class="articleSubheadline">December 2012</h5>
<ul class="bulletList">
<li>Polar diagram chart is available</li>
<li>Multiple boat classes are supported based on polar diagrams in tabular format provided as CSV-files</li>
<li>Several wind visualization approaches are supported: arrows on a grid, as well as, heatmap combined with bearing lines (either grid or integrated across race course)</li>
</ul>
<h5 class="articleSubheadline">June 2012</h5>
<ul class="bulletList">
<li>First upwind-only prototype of sailing simulation is available</li>
<li>Optimal courses shown as "Omniscient" are calculated with an implementation of Dijkstra's algorithm on a rectangular spatial grid</li>
<li>Wind is visualized by arrows which scale with wind speed</li>
<li>Race course definition with 2 mouse clicks for start and end</li>
</ul>
</div>
</div>
</body>
@@ -13,4 +13,5 @@ Require-Bundle: org.hamcrest;bundle-version="1.1.0",
com.sap.sailing.domain.test,
com.sap.sse.common,
com.sap.sailing.domain.tractracadapter,
com.tractrac.clientmodule;bundle-version="3.0.7"
com.tractrac.clientmodule;bundle-version="3.0.7",
com.sap.sailing.domain
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -49,7 +50,6 @@ import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
import com.sap.sailing.polars.mining.PolarDataMiner;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -12,8 +12,8 @@ import java.util.Random;
import org.junit.Test;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl;
public class IncrementalLeastSquaresTest {
@@ -7,8 +7,8 @@ import static org.junit.Assert.assertTrue;
import org.hamcrest.number.IsCloseTo;
import org.junit.Test;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl;
public class IncrementalLinearRegressionOrderOneTest {
@@ -12,7 +12,6 @@ import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -27,11 +26,11 @@ import com.sap.sailing.domain.common.confidence.impl.ScalableDouble;
import com.sap.sailing.domain.common.confidence.impl.ScalableWind;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.polars.impl.PolarDataServiceImpl;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl.ManeuverClassification;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.util.kmeans.Cluster;
@@ -117,7 +116,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException,
CreateModelException {
setUp("event_20110609_KielerWoch-505_Race_3");
ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(Executors.newFixedThreadPool(4)),
ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(),
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000, /* waitForLatest */ true);
final Map<Maneuver, Competitor> maneuvers = windTrack.getAllManeuvers(/* waitForLatest */ true);
final int numberOfClusters = 16;
@@ -153,7 +152,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
}
private Wind getManeuverBasedAverageWind() throws NotEnoughDataHasBeenAddedException {
ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(Executors.newFixedThreadPool(4)),
ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(),
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000, /* waitForLatest */ true);
ScalableWind windSum = null;
int count = 0;
@@ -7,9 +7,7 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .
Export-Package:
com.sap.sailing.polars,
com.sap.sailing.polars.data,
com.sap.sailing.polars.factory,
com.sap.sailing.polars.regression,
com.sap.sailing.polars.windestimation
Bundle-ActivationPolicy: lazy
@@ -21,7 +19,9 @@ Require-Bundle:
com.sap.sse.common,
com.sap.sse,
com.sap.sse.shared.android,
org.apache.commons.math;bundle-version="2.1.0"
org.apache.commons.math;bundle-version="2.1.0",
org.eclipse.osgi
Import-Package:
com.sap.sailing.util,
com.sap.sse.common
Bundle-Activator: com.sap.sailing.polars.impl.Activator
@@ -1,17 +0,0 @@
package com.sap.sailing.polars.factory;
import java.util.concurrent.Executor;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.factory.impl.PolarDataServiceImplFactory;
public abstract class PolarDataServiceFactory {
public static PolarDataService createStandardPolarDataService(Executor executor) {
PolarDataServiceFactory factory = new PolarDataServiceImplFactory();
return factory.createPolarDataService(executor);
}
protected abstract PolarDataService createPolarDataService(Executor executor);
}
@@ -1,16 +0,0 @@
package com.sap.sailing.polars.factory.impl;
import java.util.concurrent.Executor;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.factory.PolarDataServiceFactory;
import com.sap.sailing.polars.impl.PolarDataServiceImpl;
public class PolarDataServiceImplFactory extends PolarDataServiceFactory {
@Override
public PolarDataService createPolarDataService(Executor executor) {
return new PolarDataServiceImpl(executor);
}
}
@@ -0,0 +1,34 @@
package com.sap.sailing.polars.impl;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import com.sap.sailing.domain.polars.PolarDataService;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
private final Set<ServiceRegistration<?>> registrations = new HashSet<>();
@Override
public void start(BundleContext context) throws Exception {
logger.info("Registering PolarDataService");
registrations.add(context.registerService(PolarDataService.class, new PolarDataServiceImpl(), null));
}
@Override
public void stop(BundleContext context) throws Exception {
logger.info("Unregistering PolarDataService");
for (ServiceRegistration<?> reg : registrations) {
reg.unregister();
}
registrations.clear();
}
}
@@ -17,15 +17,18 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.aggregation.PolarFixAggregator;
import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.generation.PolarSheetGenerator;
import com.sap.sailing.polars.mining.PolarDataMiner;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.util.SmartFutureCache;
@@ -41,7 +44,7 @@ public class PolarDataServiceImpl implements PolarDataService {
private final PolarDataMiner polarDataMiner;
public PolarDataServiceImpl(Executor executor) {
public PolarDataServiceImpl() {
this.polarDataMiner = new PolarDataMiner();
}
@@ -158,4 +161,31 @@ public class PolarDataServiceImpl implements PolarDataService {
polarDataMiner.raceFinishedTracking(race);
}
@Override
public BearingWithConfidence<Void> getManeuverAngle(BoatClass boatClass, LegType legType, Speed windSpeed)
throws NotEnoughDataHasBeenAddedException {
if (legType != LegType.DOWNWIND && legType != LegType.UPWIND) {
throw new IllegalArgumentException("LegType needs to be upwind or downwind.");
}
if (boatClass == null || windSpeed == null) {
throw new IllegalArgumentException("Boatclass and windspeed cannot be null.");
}
SpeedWithBearingWithConfidence<Void> port = getAverageSpeedWithBearing(boatClass, windSpeed, legType, Tack.PORT);
SpeedWithBearingWithConfidence<Void> starboard = getAverageSpeedWithBearing(boatClass, windSpeed, legType,
Tack.STARBOARD);
double angleSumInDeg = Math.abs(port.getObject().getBearing().getDegrees())
+ Math.abs(starboard.getObject().getBearing().getDegrees());
Bearing bearing = new DegreeBearingImpl(angleSumInDeg);
double confidence = (port.getConfidence() + starboard.getConfidence()) / 2;
BearingWithConfidence<Void> bearingWithConfidence = new BearingWithConfidenceImpl<Void>(bearing, confidence,
null);
return bearingWithConfidence;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException {
return getAverageSpeedWithBearing(boatClass, windSpeed, legType, tack, true);
}
}
@@ -14,10 +14,10 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.polars.impl.CubicEquation;
import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
@@ -48,7 +48,7 @@ public class AngleAndSpeedRegression {
double estimatedAngle = angleRegression.getOrCreatePolynomialFunction().value(windSpeedInKnots);
Bearing bearing = new DegreeBearingImpl(estimatedAngle);
SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(estimatedSpeed, bearing);
return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, /*FIXME*/ 0.5, null);
return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, Math.min(1, speedRegression.getNumberOfAddedPoints() / 100.0), null);
}
public Set<SpeedWithBearingWithConfidence<Void>> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround) throws NotEnoughDataHasBeenAddedException {
@@ -13,7 +13,7 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.factories.GroupKeyFactory;
@@ -34,7 +34,6 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
private final TrackedRace race;
private final Competitor competitor;
private final ClusterGroup<Speed> windSpeedClusterGroup;
private final Set<WindSource> windSourcesToExcludeForBearing;
private final Set<WindSource> windSourcesToExcludeForSpeed;
private final ClusterGroup<Bearing> angleClusterGroup;
private final BearingWithConfidence<Integer> trueWindAngle;
@@ -48,7 +47,6 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
this.competitor = competitor;
this.windSpeedClusterGroup = windSpeedClusterGroup;
this.angleClusterGroup = angleClusterGroup;
this.windSourcesToExcludeForBearing = collectWindSourcesToIgnoreForBearing();
this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed();
this.trueWindAngle = computeTrueWindAngle();
this.wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed);
@@ -73,7 +71,7 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
private BearingWithConfidence<Integer> computeTrueWindAngle() {
SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint());
WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing);
WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint());
BearingWithConfidenceImpl<Integer> result = null;
if (wind != null && boatSpeed != null) {
@@ -105,32 +103,6 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe
track.getMillisecondsOverWhichToAverageSpeed()/2, /* minimumConfidence */ 0.00000001));
}
private Set<WindSource> collectWindSourcesToIgnoreForBearing() {
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
for (WindSource combinedSource : combinedSources) {
windSourcesToExclude.add(combinedSource);
}
Iterable<WindSource> courseSources = race.getWindSources(WindSourceType.COURSE_BASED);
for (WindSource courseSource : courseSources) {
windSourcesToExclude.add(courseSource);
}
Iterable<WindSource> expSources = race.getWindSources(WindSourceType.EXPEDITION);
for (WindSource expSource : expSources) {
windSourcesToExclude.add(expSource);
}
Iterable<WindSource> rcSources = race.getWindSources(WindSourceType.RACECOMMITTEE);
for (WindSource rcSource : rcSources) {
windSourcesToExclude.add(rcSource);
}
Iterable<WindSource> webSources = race.getWindSources(WindSourceType.WEB);
for (WindSource webSource : webSources) {
windSourcesToExclude.add(webSource);
}
return windSourcesToExclude;
}
private Set<WindSource> collectWindSourcesToIgnoreForSpeed() {
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
@@ -7,7 +7,7 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
@@ -20,9 +20,9 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.polars.regression.MovingAverageBoatSpeedEstimator;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
@@ -38,10 +38,10 @@ import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.impl.CubicEquation;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.datamining.components.FilterCriterion;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.data.ClusterGroup;
@@ -13,8 +13,8 @@ import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
@@ -2,6 +2,8 @@ package com.sap.sailing.polars.regression;
import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
/**
* Support incremental regression linear in one variable but potentially of higher order.
*
@@ -28,4 +30,6 @@ public interface IncrementalLeastSquares {
*/
public abstract PolynomialFunction getOrCreatePolynomialFunction() throws NotEnoughDataHasBeenAddedException;
public abstract long getNumberOfAddedPoints();
}
@@ -5,6 +5,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.mining.MovingAverageProcessorImpl;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
@@ -12,8 +12,8 @@ import org.apache.commons.math.linear.RealMatrixPreservingVisitor;
import org.apache.commons.math.linear.RealVector;
import org.apache.commons.math.linear.SingularMatrixException;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
@@ -297,4 +297,9 @@ public class IncrementalAnyOrderLeastSquaresImpl implements IncrementalLeastSqua
}
@Override
public long getNumberOfAddedPoints() {
return numberOfPointsAdded.get();
}
}
@@ -35,11 +35,11 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeed;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -5,7 +5,7 @@ import java.util.function.Function;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
import com.sap.sailing.domain.common.confidence.HasConfidenceAndIsScalable;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl.ManeuverClassification;
import com.sap.sse.common.scalablevalue.ScalableValue;
@@ -5,10 +5,7 @@ Bundle-SymbolicName: com.sap.sailing.server.gateway
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.regression,
com.sap.sailing.polars.windestimation,
com.sap.sailing.server,
Import-Package: com.sap.sailing.server,
com.sap.sailing.server.operationaltransformation,
javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
@@ -55,7 +52,8 @@ Require-Bundle: com.sap.sailing.domain,
org.apache.shiro.core;bundle-version="1.2.2",
org.apache.shiro.web;bundle-version="1.2.2",
org.apache.shiro.ehcache;bundle-version="1.2.3",
org.jvnet.mimepull;bundle-version="1.9.4"
org.jvnet.mimepull;bundle-version="1.9.4",
com.sap.sailing.polars
Bundle-ClassPath: .
Web-ContextPath: /sailingserver
Export-Package: com.sap.sailing.server.gateway,
@@ -31,9 +31,9 @@ import com.sap.sailing.domain.common.confidence.impl.ScalableDouble;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl;
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl.ManeuverClassification;
import com.sap.sailing.polars.windestimation.ScalableBearingAndScalableDouble;
@@ -6,9 +6,7 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sailing.server.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.factory,
com.sap.sailing.server.gateway.deserialization,
Import-Package: com.sap.sailing.server.gateway.deserialization,
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
@@ -24,14 +22,14 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.udpconnector,
com.sap.sailing.domain.shared.android,
com.sap.sailing.server.gateway.serialization,
com.sap.sailing.polars,
com.sap.sse.common,
com.sap.sse.operationaltransformation,
com.sap.sse.replication,
com.sap.sse,
com.sap.sailing.server.gateway.serialization.shared.android,
com.sap.sse.shared.android,
com.sap.sse.filestorage
com.sap.sse.filestorage,
com.sap.sailing.simulator;bundle-version="1.0.0"
Bundle-ClassPath: .
Export-Package: com.sap.sailing.server,
com.sap.sailing.server.impl;
@@ -43,5 +41,6 @@ Export-Package: com.sap.sailing.server,
com.sap.sailing.server.replication.test,
com.sap.sailing.domain.racelogtrackingadapter.test",
com.sap.sailing.server.masterdata,
com.sap.sailing.server.operationaltransformation
com.sap.sailing.server.operationaltransformation,
com.sap.sailing.server.simulation
@@ -55,6 +55,7 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -65,8 +66,8 @@ import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.server.masterdata.DataImportLockWithProgress;
import com.sap.sailing.server.simulation.SimulationService;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TypeBasedServiceFinderFactory;
import com.sap.sse.common.Util;
@@ -521,7 +522,9 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
WindStore getWindStore();
PolarDataService getPolarDataService();
SimulationService getSimulationService();
GPSFixStore getGPSFixStore();
RaceTracker getRaceTrackerById(Object id);
@@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.server.MasterDataImportClassLoaderService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceMXBean;
@@ -55,6 +56,8 @@ public class Activator implements BundleActivator {
private ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService> masterDataImportClassLoaderServiceTracker;
private ServiceTracker<PolarDataService, PolarDataService> polarDataServiceTracker;
public Activator() {
clearPersistentCompetitors = Boolean.valueOf(System.getProperty(CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME, ""+false));
logger.log(Level.INFO, "setting "+CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME+" to "+clearPersistentCompetitors);
@@ -77,6 +80,10 @@ public class Activator implements BundleActivator {
context, MasterDataImportClassLoaderService.class,
new MasterDataImportClassLoaderServiceTrackerCustomizer(context, racingEventService));
masterDataImportClassLoaderServiceTracker.open();
polarDataServiceTracker = new ServiceTracker<PolarDataService, PolarDataService>(context,
PolarDataService.class, new PolarDataServiceTrackerCustomizer(context, racingEventService));
polarDataServiceTracker.open();
// register the racing service in the OSGi registry
racingEventService.setBundleContext(context);
@@ -164,5 +171,34 @@ public class Activator implements BundleActivator {
}
}
private class PolarDataServiceTrackerCustomizer implements
ServiceTrackerCustomizer<PolarDataService, PolarDataService> {
private final BundleContext context;
private RacingEventServiceImpl racingEventService;
public PolarDataServiceTrackerCustomizer(BundleContext context, RacingEventServiceImpl racingEventService) {
this.context = context;
this.racingEventService = racingEventService;
}
@Override
public PolarDataService addingService(ServiceReference<PolarDataService> reference) {
PolarDataService service = context.getService(reference);
racingEventService.setPolarDataService(service);
return service;
}
@Override
public void modifiedService(ServiceReference<PolarDataService> reference, PolarDataService service) {
}
@Override
public void removedService(ServiceReference<PolarDataService> reference, PolarDataService service) {
racingEventService.unsetPolarDataService(service);
}
}
}
@@ -127,6 +127,7 @@ import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.persistence.media.MediaDB;
import com.sap.sailing.domain.persistence.media.MediaDBFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStoreFactory;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
@@ -153,8 +154,6 @@ import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.factory.PolarDataServiceFactory;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.Replicator;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
@@ -200,6 +199,8 @@ import com.sap.sailing.server.operationaltransformation.UpdateStartTimeReceived;
import com.sap.sailing.server.operationaltransformation.UpdateTrackedRaceStatus;
import com.sap.sailing.server.operationaltransformation.UpdateWindAveragingTime;
import com.sap.sailing.server.operationaltransformation.UpdateWindSourcesToExclude;
import com.sap.sailing.server.simulation.SimulationService;
import com.sap.sailing.server.simulation.SimulationServiceFactory;
import com.sap.sse.BuildVersion;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TypeBasedServiceFinderFactory;
@@ -351,7 +352,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
private final AbstractLogEventAuthor raceLogEventAuthorForServer = new LogEventAuthorImpl(
RacingEventService.class.getName(), 0);
private final PolarDataService polarDataService;
private PolarDataService polarDataService;
private final SimulationService simulationService;
/**
* Allow only one master data import at a time to avoid situation where multiple Imports override each other in
@@ -504,13 +507,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
operationExecutionListeners = new ConcurrentHashMap<>();
courseListeners = new ConcurrentHashMap<>();
persistentRegattasForRaceIDs = new ConcurrentHashMap<>();
final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
// TODO find out how many executors we have on server side and how to manage them
Executor polarExecutor = new ThreadPoolExecutor(/* corePoolSize */THREAD_POOL_SIZE,
/* maximumPoolSize */THREAD_POOL_SIZE,
final int SIMULATION_THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()/3, 1);
Executor simulatorExecutor = new ThreadPoolExecutor(/* corePoolSize */SIMULATION_THREAD_POOL_SIZE,
/* maximumPoolSize */SIMULATION_THREAD_POOL_SIZE,
/* keepAliveTime */60, TimeUnit.SECONDS,
/* workQueue */new LinkedBlockingQueue<Runnable>());
polarDataService = PolarDataServiceFactory.createStandardPolarDataService(polarExecutor);
// TODO: initialize smart-future-cache for simulation-results and add to simulation-service
simulationService = SimulationServiceFactory.INSTANCE.getService(simulatorExecutor, this);
this.raceLogReplicator = new RaceLogReplicator(this);
this.regattaLogReplicator = new RegattaLogReplicator(this);
this.raceLogScoringReplicator = new RaceLogScoringReplicator(this);
@@ -559,6 +562,11 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
return polarDataService;
}
@Override
public SimulationService getSimulationService() {
return simulationService;
}
@Override
public void clearState() throws Exception {
for (String leaderboardGroupName : new ArrayList<>(this.leaderboardGroupsByName.keySet())) {
@@ -1526,6 +1534,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
PolarFixCacheUpdater polarFixCacheUpdater = new PolarFixCacheUpdater(trackedRace);
polarFixCacheUpdaters.put(trackedRace, polarFixCacheUpdater);
trackedRace.addListener(polarFixCacheUpdater);
if (polarDataService != null) {
trackedRace.setPolarDataService(polarDataService);
}
}
}
@@ -1539,14 +1551,18 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
@Override
public void competitorPositionChanged(GPSFixMoving fix, Competitor item) {
polarDataService.competitorPositionChanged(fix, item, race);
if (polarDataService != null) {
polarDataService.competitorPositionChanged(fix, item, race);
}
}
@Override
public void statusChanged(TrackedRaceStatus newStatus, TrackedRaceStatus oldStatus) {
if (oldStatus.getStatus() == TrackedRaceStatusEnum.LOADING
&& newStatus.getStatus() != TrackedRaceStatusEnum.LOADING) {
polarDataService.raceFinishedLoading(race);
if (polarDataService != null) {
polarDataService.raceFinishedLoading(race);
}
}
}
@@ -3113,4 +3129,31 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
JoinedClassLoader joinedClassLoader = new JoinedClassLoader(masterDataClassLoaders);
return joinedClassLoader;
}
public void setPolarDataService(PolarDataService service) {
if (this.polarDataService == null) {
polarDataService = service;
setPolarDataServiceOnAllTrackedRaces(service);
}
}
private void setPolarDataServiceOnAllTrackedRaces(PolarDataService service) {
Iterable<Regatta> allRegattas = getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = getTrackedRegatta(regatta);
if (trackedRegatta != null) {
Iterable<DynamicTrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
trackedRace.setPolarDataService(service);
}
}
}
}
public void unsetPolarDataService(PolarDataService service) {
if (polarDataService == service) {
polarDataService = null;
setPolarDataService(null);
}
}
}
@@ -28,6 +28,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
@@ -583,4 +584,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
// TODO Auto-generated method stub
return 0;
}
@Override
public void setPolarDataService(PolarDataService polarDataService) {
}
}
@@ -0,0 +1,21 @@
package com.sap.sailing.server.simulation;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PathType;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.SimulationResults;
public interface SimulationService {
int getSimulationResultsVersion(LegIdentifier legIdentifier);
SimulationResults getSimulationResults(LegIdentifier legIdentifier);
Map<PathType, Path> getAllPathsEvenTimed(SimulationParameters simuPars, long millisecondsStep)
throws InterruptedException, ExecutionException;
}
@@ -0,0 +1,13 @@
package com.sap.sailing.server.simulation;
import java.util.concurrent.Executor;
import com.sap.sailing.server.RacingEventService;
public interface SimulationServiceFactory {
static SimulationServiceFactory INSTANCE = new SimulationServiceFactoryImpl();
SimulationService getService(Executor executor, RacingEventService racingEventService);
}
@@ -0,0 +1,13 @@
package com.sap.sailing.server.simulation;
import java.util.concurrent.Executor;
import com.sap.sailing.server.RacingEventService;
public class SimulationServiceFactoryImpl implements SimulationServiceFactory {
public SimulationService getService(Executor executor, RacingEventService racingEventService) {
return new SimulationServiceImpl(executor, racingEventService);
}
}
@@ -0,0 +1,472 @@
package com.sap.sailing.server.simulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.LegIdentifierImpl;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PathType;
import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.SimulationResults;
import com.sap.sailing.simulator.Simulator;
import com.sap.sailing.simulator.impl.PolarDiagramGPS;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
import com.sap.sailing.simulator.impl.SimulatorImpl;
import com.sap.sailing.simulator.impl.SparsePolarDataException;
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
import com.sap.sailing.simulator.windfield.WindFieldGenerator;
import com.sap.sailing.simulator.windfield.impl.WindFieldTrackedRaceImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.SmartFutureCache;
public class SimulationServiceImpl implements SimulationService {
private static final Logger logger = Logger.getLogger(SimulationService.class.getName());
final private Executor executor;
final private SmartFutureCache<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval> cache;
final private RacingEventService racingEventService;
final private ScheduledExecutorService scheduler;
final private HashMap<String, SimulationRaceListener> raceListeners;
final private HashMap<RaceIdentifier, LegChangeListener> legListeners;
final private long WAIT_MILLIS = 20000; // milliseconds to wait until earliest cache-update for simulation
public SimulationServiceImpl(Executor executor, RacingEventService racingEventService) {
this.executor = executor;
this.scheduler = Executors.newScheduledThreadPool(1);
this.racingEventService = racingEventService;
if (racingEventService != null) {
this.raceListeners = new HashMap<String, SimulationRaceListener>();
this.legListeners = new HashMap<RaceIdentifier, LegChangeListener>();
this.cache = new SmartFutureCache<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval>(
new SmartFutureCache.AbstractCacheUpdater<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval>() {
@Override
public SimulationResults computeCacheUpdate(LegIdentifier key, SmartFutureCache.EmptyUpdateInterval updateInterval) throws Exception {
logger.info("Simulation Started: \"" + key.toString() + "\"");
SimulationResults results = computeSimulationResults(key);
logger.info("Simulation Finished: \"" + key.toString() + "\", Results-Version: "+ (results==null?0:results.hashCode()));
return results;
}
}, "SmartFutureCache.simulationService (" + racingEventService.toString() + ")");
} else {
this.raceListeners = null;
this.legListeners = null;
this.cache = null;
}
}
private class SimulationRaceListener implements RaceListener {
@Override
public void raceAdded(TrackedRace trackedRace) {
}
@Override
public void raceRemoved(TrackedRace trackedRace) {
RaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier();
LegChangeListener listener = legListeners.get(raceIdentifier);
if (listener != null) {
trackedRace.removeListener(listener);
}
int legNumber = 1;
Iterator<TrackedLeg> iterator = trackedRace.getTrackedLegs().iterator();
while (iterator.hasNext()) {
LegIdentifier key = new LegIdentifierImpl((RegattaAndRaceIdentifier)raceIdentifier, ""+legNumber);
SimulationResults entry = cache.get(key, false);
if (entry != null) {
cache.remove(key);
}
legNumber++;
iterator.next();
}
legListeners.remove(raceIdentifier);
}
}
private class LegChangeListener extends AbstractRaceChangeListener {
private final TrackedRace trackedRace;
private LegIdentifier legIdentifier;
private final ScheduledExecutorService scheduler;
private boolean covered;
public LegChangeListener(TrackedRace trackedRace, ScheduledExecutorService scheduler) {
this.trackedRace = trackedRace;
this.legIdentifier = null;
this.scheduler = scheduler;
this.covered = false;
}
public boolean isLive() {
return trackedRace.isLive(MillisecondsTimePoint.now());
}
public void setLegIdentifier(LegIdentifier legIdentifier) {
if (this.legIdentifier != null) {
if (!this.legIdentifier.equals(legIdentifier)) {
this.legIdentifier = legIdentifier;
this.covered = false;
}
} else {
this.legIdentifier = legIdentifier;
}
}
@Override
protected void defaultAction() {
if ((!this.covered)&&(legIdentifier!=null)) {
this.covered = true;
LegIdentifier tmpLegIdentifier = new LegIdentifierImpl(legIdentifier, legIdentifier.getLegName());
scheduler.schedule(() -> triggerUpdate(tmpLegIdentifier), WAIT_MILLIS, TimeUnit.MILLISECONDS);
}
}
private void triggerUpdate(LegIdentifier legIdentifier) {
if (this.legIdentifier.equals(legIdentifier)) {
this.covered = false;
}
logger.info("Simulation Scheduled Update Triggered: \"" + legIdentifier.toString() + "\"");
cache.triggerUpdate(legIdentifier, null);
}
@Override
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
// relevant for simulation: start of leg 1 changes; requires update of leg 1
// TODO: update leg 1
}
@Override
public void windSourcesToExcludeChanged(Iterable<? extends WindSource> windSourcesToExclude) {
// relevant for simulation: update all legs when wind changes overall
// TODO: update all legs
}
@Override
public void startOfTrackingChanged(TimePoint startOfTracking) {
// irrelevant for simulation
}
@Override
public void endOfTrackingChanged(TimePoint endOfTracking) {
// irrelevant for simulation
}
@Override
public void startTimeReceivedChanged(TimePoint startTimeReceived) {
// irrelevant for simulation: should be covered by startOfRaceChanged()
}
@Override
public void markPositionChanged(GPSFix fix, Mark mark, boolean firstInTrack) {
// relevant for simulation
// TODO: identify influenced legs and update these legs
}
@Override
public void windDataReceived(Wind wind, WindSource windSource) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void windDataRemoved(Wind wind, WindSource windSource) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void competitorPositionChanged(GPSFixMoving fix, Competitor item) {
// irrelevant for simulation: covered by wind estimation
}
@Override
public void markPassingReceived(Competitor competitor, Map<Waypoint, MarkPassing> oldMarkPassings, Iterable<MarkPassing> markPassings) {
// relevant for simulation: update start- and end-times of legs
// TODO: update influenced legs; "live" update current leg
}
@Override
public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {
// irrelevant for simulation
}
@Override
public void delayToLiveChanged(long delayToLiveInMillis) {
// irrelevant for simulation
}
@Override
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
// relevant for simulation: update legs influenced waypoint
// TODO: update influenced legs; "live" update current leg
}
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
// relevant for simulation: update legs influenced waypoint
// TODO: update influenced legs; "live" update current leg
}
}
@Override
public int getSimulationResultsVersion(LegIdentifier legIdentifier) {
SimulationResults result = cache.get(legIdentifier, false);
int version = (result == null ? 0 : result.hashCode());
logger.fine("Simulation Results-Version: " + + version);
return version;
}
@Override
public SimulationResults getSimulationResults(LegIdentifier legIdentifier) {
SimulationResults result = cache.get(legIdentifier, false);
if (result == null) {
logger.fine("Simulation Get: Cache Empty: \"" + legIdentifier.toString() + "\"");
if (!raceListeners.containsKey(legIdentifier.getRegattaName())) {
Regatta regatta = racingEventService.getRegattaByName(legIdentifier.getRegattaName());
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
SimulationRaceListener raceListener = new SimulationRaceListener();
raceListeners.put(legIdentifier.getRegattaName(), raceListener);
trackedRegatta.addRaceListener(raceListener);
}
if (!legListeners.containsKey(legIdentifier.getRaceIdentifier())) {
TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
if (trackedRace != null) {
LegChangeListener listener = new LegChangeListener(trackedRace, scheduler);
if (listener.isLive()) {
listener.setLegIdentifier(legIdentifier);
}
legListeners.put(legIdentifier.getRaceIdentifier(), listener);
trackedRace.addListener(listener);
}
} else {
LegChangeListener listener = legListeners.get(legIdentifier.getRaceIdentifier());
if (listener.isLive()) {
listener.setLegIdentifier(legIdentifier);
}
}
logger.info("Simulation Get: Update Triggered: \"" + legIdentifier.toString() + "\"");
cache.triggerUpdate(legIdentifier, null);
result = cache.get(legIdentifier, true); // take first simulation result that becomes available
}
if (result == null) {
logger.fine("Simulation Get: Null-Result: \"" + legIdentifier.toString() + "\"");
}
return result;
}
public SimulationResults computeSimulationResults(LegIdentifier legIdentifier) throws InterruptedException,
ExecutionException {
SimulationResults result = null;
TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
if (trackedRace != null) {
Leg leg = trackedRace.getRace().getCourse().getLegs().get(legIdentifier.getLegNumber());
// get previous mark or start line as start-position
Waypoint fromWaypoint = leg.getFrom();
// get next mark as end-position
Waypoint toWaypoint = leg.getTo();
TimePoint startTimePoint = null;
TimePoint endTimePoint = null;
MarkPassing markPassing;
Iterator<MarkPassing> markPassingIterator = trackedRace.getMarkPassingsInOrder(fromWaypoint).iterator();
if (markPassingIterator.hasNext()) {
markPassing = markPassingIterator.next();
} else {
markPassing = null;
}
if (markPassing != null) {
startTimePoint = markPassing.getTimePoint();
}
markPassingIterator = trackedRace.getMarkPassingsInOrder(toWaypoint).iterator();
if (markPassingIterator.hasNext()) {
markPassing = markPassingIterator.next();
} else {
markPassing = null;
}
if (markPassing != null) {
endTimePoint = markPassing.getTimePoint();
}
long legDuration = 0;
if ((startTimePoint != null) && (endTimePoint != null)) {
legDuration = endTimePoint.asMillis() - startTimePoint.asMillis();
}
Position startPosition = null;
Position endPosition = null;
if (startTimePoint != null) {
startPosition = trackedRace.getApproximatePosition(fromWaypoint, startTimePoint);
}
if (endTimePoint != null) {
endPosition = trackedRace.getApproximatePosition(toWaypoint, endTimePoint);
} else if (startTimePoint != null) {
endPosition = trackedRace.getApproximatePosition(toWaypoint, startTimePoint);
}
// determine legtype upwind/downwind/reaching
LegType legType = null;
try {
legType = trackedRace.getTrackedLeg(leg).getLegType(startTimePoint);
} catch (NoWindException e) {
return null;
}
// get windfield
WindFieldGenerator windField = new WindFieldTrackedRaceImpl(trackedRace);
Duration timeStep = new MillisecondsDurationImpl(15 * 1000);
windField.generate(startTimePoint, null, timeStep);
// prepare simulation-parameters
List<Position> course = new ArrayList<Position>();
course.add(startPosition);
course.add(endPosition);
BoatClass boatClass = trackedRace.getRace().getBoatClass();
PolarDataService polarDataService = racingEventService.getPolarDataService();
PolarDiagram polarDiagram;
try {
polarDiagram = new PolarDiagramGPS(boatClass, polarDataService);
} catch (SparsePolarDataException e) {
polarDiagram = null;
// TODO: raise a UI message, to inform user about missing polar data resulting in unability to simulate
}
double simuStepSeconds = startPosition.getDistance(endPosition).getNauticalMiles()
/ ((PolarDiagramGPS) polarDiagram).getAvgSpeed() * 3600 / 100;
Duration simuStep = new MillisecondsDurationImpl(Math.round(simuStepSeconds) * 1000);
SimulationParameters simulationPars = new SimulationParametersImpl(course, polarDiagram, windField,
simuStep, SailingSimulatorConstants.ModeEvent, true, true, legType);
Map<PathType, Path> paths = null;
if (polarDiagram != null) {
paths = getAllPathsEvenTimed(simulationPars, timeStep.asMillis());
}
// prepare simulator-results
result = new SimulationResults(startTimePoint.asDate(), timeStep.asMillis(), legDuration, startPosition,
endPosition, paths, null);
}
return result;
}
public Map<PathType, Path> getAllPaths(SimulationParameters simulationParameters) throws InterruptedException,
ExecutionException {
Simulator simulator = new SimulatorImpl(simulationParameters);
Map<PathType, Path> result = new HashMap<PathType, Path>();
FutureTask<Path> taskOmniscient = null;
if (simulationParameters.showOmniscient()) {
// schedule omniscient task
taskOmniscient = new FutureTask<Path>(() -> simulator.getPath(PathType.OMNISCIENT));
executor.execute(taskOmniscient);
}
// schedule 1-turner tasks
FutureTask<Path> task1TurnerLeft = new FutureTask<Path>(() -> simulator.getPath(PathType.ONE_TURNER_LEFT));
FutureTask<Path> task1TurnerRight = new FutureTask<Path>(() -> simulator.getPath(PathType.ONE_TURNER_RIGHT));
executor.execute(task1TurnerLeft);
executor.execute(task1TurnerRight);
FutureTask<Path> taskOpportunistLeft = null;
FutureTask<Path> taskOpportunistRight = null;
if (simulationParameters.showOpportunist()) {
// schedule opportunist tasks (which depend on 1-turner results)
taskOpportunistLeft = new FutureTask<Path>(() -> simulator.getPath(PathType.OPPORTUNIST_LEFT));
taskOpportunistRight = new FutureTask<Path>(() -> simulator.getPath(PathType.OPPORTUNIST_RIGHT));
executor.execute(taskOpportunistLeft);
executor.execute(taskOpportunistRight);
}
// collect 1-turner results
result.put(PathType.ONE_TURNER_LEFT, task1TurnerLeft.get());
result.put(PathType.ONE_TURNER_RIGHT, task1TurnerRight.get());
if (simulationParameters.showOpportunist()) {
// collect opportunist results
Path pathOpportunistLeft = taskOpportunistLeft.get();
if (pathOpportunistLeft.getTurnCount() == 1) {
pathOpportunistLeft = result.get(PathType.ONE_TURNER_LEFT);
}
result.put(PathType.OPPORTUNIST_LEFT, pathOpportunistLeft);
Path pathOpportunistRight = taskOpportunistRight.get();
if (pathOpportunistRight.getTurnCount() == 1) {
pathOpportunistRight = result.get(PathType.ONE_TURNER_RIGHT);
}
result.put(PathType.OPPORTUNIST_RIGHT, pathOpportunistRight);
}
if (simulationParameters.showOmniscient()) {
// collect omniscient result (last, since usually slowest calculation)
Path pathOmniscient = taskOmniscient.get();
if (pathOmniscient.getFinalTime().after(result.get(PathType.ONE_TURNER_LEFT).getFinalTime())) {
pathOmniscient = result.get(PathType.ONE_TURNER_LEFT);
}
if (pathOmniscient.getFinalTime().after(result.get(PathType.ONE_TURNER_RIGHT).getFinalTime())) {
pathOmniscient = result.get(PathType.ONE_TURNER_RIGHT);
}
result.put(PathType.OMNISCIENT, pathOmniscient);
}
// return combined result
return result;
}
public Map<PathType, Path> getAllPathsEvenTimed(SimulationParameters simuPars, long millisecondsStep)
throws InterruptedException, ExecutionException {
Map<PathType, Path> allTimedPaths = new TreeMap<PathType, Path>();
Map<PathType, Path> allPaths = this.getAllPaths(simuPars);
for (Entry<PathType, Path> entry : allPaths.entrySet()) {
PathType pathType = entry.getKey();
Path pathValue = entry.getValue();
if (pathValue != null) {
allTimedPaths.put(pathType, pathValue.getEvenTimedPath(millisecondsStep));
}
}
return allTimedPaths;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.simulator.test;
import java.sql.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -13,12 +14,14 @@ import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PathType;
import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.impl.PolarDiagram49STG;
import com.sap.sailing.simulator.impl.RectangularGrid;
import com.sap.sailing.simulator.impl.SailingSimulatorImpl;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
import com.sap.sailing.simulator.impl.SimulatorImpl;
import com.sap.sailing.simulator.impl.SparsePolarDataException;
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
import com.sap.sailing.simulator.windfield.WindControlParameters;
import com.sap.sailing.simulator.windfield.WindFieldGenerator;
@@ -31,18 +34,18 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
public class SimulatorTest {
@Test
public void testSailingSimulatorALL() {
public void testSailingSimulatorALL() throws SparsePolarDataException {
// race course: copacabana, rio de janeiro, brasil
Position start = new DegreePosition(-22.975779,-43.17421);
Position end = new DegreePosition(-22.99016,-43.156013);
//System.out.println("race course size: "+start.getDistance(end).getKilometers());
Position start = new DegreePosition(-22.975779, -43.17421);
Position end = new DegreePosition(-22.99016, -43.156013);
// System.out.println("race course size: "+start.getDistance(end).getKilometers());
List<Position> course = new LinkedList<Position>();
course.add(start);
course.add(end);
PolarDiagram pd = new PolarDiagram49STG();//PolarDiagram49.CreateStandard49();
PolarDiagram pd = new PolarDiagram49STG();// PolarDiagram49.CreateStandard49();
RectangularGrid bd = new RectangularGrid(start, end);
Position[][] positions = bd.generatePositions(10, 10, 0, 0);
Bearing windBear = end.getBearingGreatCircle(start);
@@ -54,16 +57,18 @@ public class SimulatorTest {
Duration timeStep = new MillisecondsDurationImpl(30000);
wf.generate(startTime, null, timeStep);
SimulationParameters param = new SimulationParametersImpl(course, pd, wf, SailingSimulatorConstants.ModeFreestyle, true, true);
SailingSimulatorImpl sailingSim = new SailingSimulatorImpl(param);
SimulationParameters param = new SimulationParametersImpl(course, pd, wf, null,
SailingSimulatorConstants.ModeFreestyle, true, true);
SimulatorImpl sailingSim = new SimulatorImpl(param);
//Map<String, Path> paths = sailingSim.getAllPathsForLeg(new SimulatorUISelectionImpl(0, 0, 0, 0));
Map<String, Path> paths = sailingSim.getAllPaths();
//System.out.println("opportunistic path points: "+paths.get("2#Opportunist Left").getPathPoints().size());
Assert.assertNotNull(paths.get("2#Opportunist Left").getPathPoints());
//System.out.println("omnciscient path points: "+paths.get("1#Omniscient").getPathPoints().size());
Assert.assertNotNull(paths.get("1#Omniscient").getPathPoints());
Path pathOmniscient = sailingSim.getPath(PathType.OMNISCIENT);
Path pathOpportunist = sailingSim.getPath(PathType.OPPORTUNIST_LEFT);
Map<PathType, Path> paths = new HashMap<PathType, Path>();
paths.put(PathType.OMNISCIENT, pathOmniscient);
paths.put(PathType.OPPORTUNIST_LEFT, pathOpportunist);
Assert.assertNotNull(paths.get(PathType.OPPORTUNIST_LEFT).getPathPoints());
Assert.assertNotNull(paths.get(PathType.OMNISCIENT).getPathPoints());
}
}
@@ -34,44 +34,48 @@ public class TreeGrowTest {
@Test
public void testSailingSimulatorALL() {
Position start = new DegreePosition(54.001917,10.82222);
//Position end = new DegreePosition(54.023806,10.822048);
SpeedWithBearing bearNorth = new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(33.0));
Position end = bearNorth.travelTo(start, new MillisecondsTimePoint(0), new MillisecondsTimePoint(10*60*1000));
//System.out.println(start.getDistance(end).getKilometers());
Position start = new DegreePosition(54.001917, 10.82222);
// Position end = new DegreePosition(54.023806,10.822048);
SpeedWithBearing bearNorth = new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(33.0));
Position end = bearNorth.travelTo(start, new MillisecondsTimePoint(0),
new MillisecondsTimePoint(10 * 60 * 1000));
// System.out.println(start.getDistance(end).getKilometers());
List<Position> course = new LinkedList<Position>();
course.add(start);
course.add(end);
PolarDiagram pd = new PolarDiagram49STG();//PolarDiagram49.CreateStandard49();
PolarDiagram pd = new PolarDiagram49STG();// PolarDiagram49.CreateStandard49();
RectangularGrid bd = new RectangularGrid(start, end);
Position[][] positions = bd.generatePositions(10, 10, 0, 0);
//RectangularBoundary new_bd = new RectangularBoundary(start, end, 0.1);
//Speed knotSpeed = new KnotSpeedImpl(8);
WindControlParameters windParameters = new WindControlParameters(12, start.getBearingGreatCircle(end).reverse().getDegrees());
// RectangularBoundary new_bd = new RectangularBoundary(start, end, 0.1);
// Speed knotSpeed = new KnotSpeedImpl(8);
WindControlParameters windParameters = new WindControlParameters(12, start.getBearingGreatCircle(end).reverse()
.getDegrees());
WindFieldGenerator wf = new WindFieldGeneratorOscillationImpl(bd, windParameters);
wf.setPositionGrid(positions);
Date startDate = new Date(0);
TimePoint startTime = new MillisecondsTimePoint(startDate.getTime());
Duration timeStep = new MillisecondsDurationImpl(20000);
wf.generate(startTime, null, timeStep);
SimulationParameters param = new SimulationParametersImpl(course, pd, wf, SailingSimulatorConstants.ModeFreestyle, true, true);
SimulationParameters param = new SimulationParametersImpl(course, pd, wf, null,
SailingSimulatorConstants.ModeFreestyle, true, true);
/*param.setProperty("Heuristic.targetTolerance[double]", 0.05);
param.setProperty("Heuristic.timeResolution[long]", 30000.0);
param.setProperty("Djikstra.gridv[int]", 10.0);
param.setProperty("Djikstra.gridh[int]", 100.0);*/
/*
* param.setProperty("Heuristic.targetTolerance[double]", 0.05);
* param.setProperty("Heuristic.timeResolution[long]", 30000.0); param.setProperty("Djikstra.gridv[int]", 10.0);
* param.setProperty("Djikstra.gridh[int]", 100.0);
*/
PathGeneratorTreeGrow treeGrow = new PathGeneratorTreeGrow(param);
Path path = treeGrow.getPath();
//System.out.println("tree-grow path points: "+path.getPathPoints().size());
// System.out.println("tree-grow path points: "+path.getPathPoints().size());
Assert.assertNotNull(path.getPathPoints());
//for(TimedPositionWithSpeed pos : path.getPathPoints()) {
// System.out.println(""+pos.getPosition().getLatDeg()+", "+pos.getPosition().getLngDeg());
//}
// for(TimedPositionWithSpeed pos : path.getPathPoints()) {
// System.out.println(""+pos.getPosition().getLatDeg()+", "+pos.getPosition().getLngDeg());
// }
}
}
@@ -41,11 +41,12 @@ public class PathGenerator1TurnerEval {
public static void main(String[] args) throws IOException {
System.out.println("Collection of test methods for evaluating path segments.");
}
public void initialize() throws IOException, ClassNotFoundException {
this._polarDiagram = new PolarDiagramCSV(POLAR_DIAGRAM_49_STG_CSV_FILE_PATH);
this._simulationParameters = new SimulationParametersImpl(null, this._polarDiagram, null, SailingSimulatorConstants.ModeMeasured, true, true);
this._simulationParameters = new SimulationParametersImpl(null, this._polarDiagram, null, null,
SailingSimulatorConstants.ModeMeasured, true, true);
this._pathGenerator = new PathGenerator1Turner(this._simulationParameters);
this._windField = readWindFieldGeneratorFromExternalFile("windField.dat");
@@ -60,8 +61,8 @@ public class PathGenerator1TurnerEval {
int stepMax = 300;
long timeStep = 6666;
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, start, end, startTime, leftSide,
stepMax, timeStep);
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, start, end,
startTime, leftSide, stepMax, timeStep);
Assert.assertNotNull(result);
Assert.assertNotNull(result.getPosition());
@@ -85,8 +86,8 @@ public class PathGenerator1TurnerEval {
int stepMax = 300;
long timeStep = 1000;
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, start, end, startTime, leftSide,
stepMax, timeStep);
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, start, end,
startTime, leftSide, stepMax, timeStep);
Assert.assertNotNull(result);
Assert.assertNotNull(result.getPosition());
@@ -112,7 +113,8 @@ public class PathGenerator1TurnerEval {
int stepMax = 300;
long timeStep = 1000;
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, end, start, startTime, leftSide, stepMax, timeStep);
TimedPositionWithSpeed result = this._pathGenerator.get1Turner(this._windField, this._polarDiagram, end, start,
startTime, leftSide, stepMax, timeStep);
Assert.assertNotNull(result);
Assert.assertNotNull(result.getPosition());
@@ -143,7 +145,8 @@ public class PathGenerator1TurnerEval {
file.close();
}
} catch (final ClassNotFoundException ex) {
System.err.println("[ERROR][PathGenerator1Turner][readFromExternalFile][ClassNotFoundException] " + ex.getMessage());
System.err.println("[ERROR][PathGenerator1Turner][readFromExternalFile][ClassNotFoundException] "
+ ex.getMessage());
result = null;
} catch (final IOException ex) {
System.err.println("[ERROR][PathGenerator1Turner][readFromExternalFile][IOException] " + ex.getMessage());
@@ -152,7 +155,7 @@ public class PathGenerator1TurnerEval {
return result;
}
private File getFile(String fileName) {
return new File(RESOURCES + fileName);
}
@@ -18,7 +18,6 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.domain.tractracadapter.impl.TracTracAdapterFactoryImpl;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.simulator.impl.SimulatorUtils;
@SuppressWarnings("restriction")
public class RaceDataAccessEval {
@@ -26,10 +25,14 @@ public class RaceDataAccessEval {
private static final Logger logger = Logger.getLogger(RaceDataAccessEval.class.getName());
protected static final boolean tractracTunnel = Boolean.valueOf(System.getProperty("tractrac.tunnel", "false"));
protected static final String tractracTunnelHost = System.getProperty("tractrac.tunnel.host", "localhost");
@SuppressWarnings("unused")
private static URL paramUrl;
@SuppressWarnings("unused")
private static URI liveUri;
@SuppressWarnings("unused")
private static URI storedUri;
private static RacingEventServiceImpl service;
@SuppressWarnings("unused")
private static TracTracAdapterFactory tracTracAdapterFactory;
private static RaceHandle raceHandle;
public boolean storedend = false;
@@ -102,8 +105,7 @@ public class RaceDataAccessEval {
service = new RacingEventServiceImpl();
tracTracAdapterFactory = new TracTracAdapterFactoryImpl();
logger.info("Calling service.addTracTracRace");
raceHandle = SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramUrl, liveUri, storedUri, null, null,
60000);
raceHandle = null; //SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramUrl, liveUri, storedUri, null, null, 60000);
logger.info("Calling raceHandle.getRaces(): " + raceHandle);
RaceDefinition race = raceHandle.getRace(); // wait for RaceDefinition to be completely wired in Regatta
logger.info("Obtained race: " + race);
@@ -21,7 +21,6 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.domain.tractracadapter.impl.TracTracAdapterFactoryImpl;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.simulator.impl.SimulatorUtils;
@SuppressWarnings("restriction")
public class RacingEventPersistence {
@@ -48,12 +47,14 @@ public class RacingEventPersistence {
service = new RacingEventServiceImpl();
tracTracAdapterFactory = new TracTracAdapterFactoryImpl();
@SuppressWarnings("unused")
URL paramURL = new URL(paramURLStr);
@SuppressWarnings("unused")
URI liveURI = new URI(liveURIStr);
@SuppressWarnings("unused")
URI storedURI = new URI(storedURIStr);
raceHandle = SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI, null, null,
60000);
//raceHandle = SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI, null, null, 60000);
System.out.println("Done loading race.");
@@ -13,7 +13,6 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.domain.tractracadapter.impl.TracTracAdapterFactoryImpl;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.simulator.impl.SimulatorUtils;
@SuppressWarnings("restriction")
public class TracTracReaderFromURLs implements TracTracReader {
@@ -52,19 +51,23 @@ public class TracTracReaderFromURLs implements TracTracReader {
}
@Override
@SuppressWarnings("null")
public List<TrackedRace> read() throws Exception {
System.setProperty("mongo.port", mongoPortStr);
System.setProperty("http.proxyHost", proxyHostStr);
System.setProperty("http.proxyPort", proxyPortStr);
RacingEventServiceImpl service = new RacingEventServiceImpl();
@SuppressWarnings("unused")
final TracTracAdapterFactory tracTracAdapterFactory = new TracTracAdapterFactoryImpl();
@SuppressWarnings("unused")
URI liveURI = new URI(liveURIStr);
@SuppressWarnings("unused")
URI storedURI = new URI(storedURIStr);
List<TrackedRace> racesList = new ArrayList<TrackedRace>();
for (String paramURLStr : tracTracParamURLs) {
@SuppressWarnings("unused")
URL paramURL = new URL(paramURLStr);
RaceHandle raceHandle = SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI,
null, null, 60000);
RaceHandle raceHandle = null; //SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI, null, null, 60000);
String regatta = raceHandle.getRegatta().getName();
RaceDefinition r = raceHandle.getRace();
RegattaAndRaceIdentifier raceIdentifier = new RegattaNameAndRaceName(regatta, r.getName());
@@ -15,7 +15,6 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.domain.tractracadapter.impl.TracTracAdapterFactoryImpl;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.simulator.impl.SimulatorUtils;
@SuppressWarnings("restriction")
public class TracTracWriter {
@@ -30,19 +29,23 @@ public class TracTracWriter {
}
@SuppressWarnings("null")
public void write() throws Exception {
RacingEventServiceImpl service = new RacingEventServiceImpl();
@SuppressWarnings("unused")
final TracTracAdapterFactory tracTracAdapterFactory = new TracTracAdapterFactoryImpl();
System.setProperty("mongo.port", "10200");
System.setProperty("http.proxyHost", "proxy.wdf.sap.corp");
System.setProperty("http.proxyPort", "8080");
@SuppressWarnings("unused")
URI liveURI = new URI("tcp://10.18.22.156:1520");
@SuppressWarnings("unused")
URI storedURI = new URI("tcp://10.18.22.156:1521");
for (String paramURLStr : sources) {
@SuppressWarnings("unused")
URL paramURL = new URL(paramURLStr);
RaceHandle raceHandle = SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI,
null, null, 60000);
RaceHandle raceHandle = null; //SimulatorUtils.loadRace(service, tracTracAdapterFactory, paramURL, liveURI, storedURI, null, null, 60000);
String regatta = raceHandle.getRegatta().getName();
RaceDefinition r = raceHandle.getRace();
List<TrackedRace> racesList = new ArrayList<TrackedRace>();
@@ -7,7 +7,6 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0",
com.sap.sailing.domain;bundle-version="1.0.0",
com.sap.sailing.server;bundle-version="1.0.0",
com.sap.sailing.domain.tractracadapter,
com.sap.sse,
com.sap.sse.common,
@@ -1,3 +1,4 @@
49er
wind speed,0,6,8,10,12,14,16,20
beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9
beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734
1 wind speed 49er 0 6 8 10 12 14 16 20
1 49er
2 wind speed wind speed,0,6,8,10,12,14,16,20 0 6 8 10 12 14 16 20
3 beat angles beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9 45 43.4 41.8 38.9 37.5 36.8 36.3 36.9
4 beat sog beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 0 6.435 7.678 8.129 8.448 8.679 8.712 8.734
@@ -1,3 +1,4 @@
49er
wind speed,0,6,9,12,25
beat angles,43,50.24,49.4,47.35,48.01
beat sog,0,7.53,9.39,11.96,13.08
1 wind speed 49er 0 6 9 12 25
1 49er
2 wind speed wind speed,0,6,9,12,25 0 6 9 12 25
3 beat angles beat angles,43,50.24,49.4,47.35,48.01 43 50.24 49.4 47.35 48.01
4 beat sog beat sog,0,7.53,9.39,11.96,13.08 0 7.53 9.39 11.96 13.08
@@ -1,3 +1,4 @@
49er
wind speed,0,6,8,10,12,14,16,20
beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9
beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734
1 wind speed 49er 0 6 8 10 12 14 16 20
1 49er
2 wind speed wind speed,0,6,8,10,12,14,16,20 0 6 8 10 12 14 16 20
3 beat angles beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9 45 43.4 41.8 38.9 37.5 36.8 36.3 36.9
4 beat sog beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 0 6.435 7.678 8.129 8.448 8.679 8.712 8.734
@@ -1,3 +1,4 @@
49er
wind speed,0,6,8,10,12,14,16,20
beat angles,43,43,43,43,44,45,45.6,47
beat sog,0,4.5,6.25,8,8.5,9,9.3,10
1 wind speed 49er 0 6 8 10 12 14 16 20
1 49er
2 wind speed wind speed,0,6,8,10,12,14,16,20 0 6 8 10 12 14 16 20
3 beat angles beat angles,43,43,43,43,44,45,45.6,47 43 43 43 43 44 45 45.6 47
4 beat sog beat sog,0,4.5,6.25,8,8.5,9,9.3,10 0 4.5 6.25 8 8.5 9 9.3 10
@@ -1,3 +1,4 @@
5O5
wind speed,0,6,8,10,12,14,16,20
beat angles,44,44,46.5,50,52,54,55,56
beat sog,0,5,5.3,5.6,6,6.5,7,7
1 wind speed 5O5 0 6 8 10 12 14 16 20
1 5O5
2 wind speed wind speed,0,6,8,10,12,14,16,20 0 6 8 10 12 14 16 20
3 beat angles beat angles,44,44,46.5,50,52,54,55,56 44 44 46.5 50 52 54 55 56
4 beat sog beat sog,0,5,5.3,5.6,6,6.5,7,7 0 5 5.3 5.6 6 6.5 7 7
@@ -0,0 +1,22 @@
package com.sap.sailing.simulator;
public enum BoatDirection {
NONE("None"),
BEAT_LEFT("Beat Left"),
BEAT_RIGHT("Beat Right"),
REACH_LEFT("Reach Left"),
REACH_RIGHT("Reach Right"),
JIBE_LEFT("Jibe Left"),
JIBE_RIGHT("Jibe Right");
private String txtId;
BoatDirection(String txtId) {
this.txtId = txtId;
}
public String getTxtId() {
return txtId;
}
}
@@ -3,16 +3,29 @@ package com.sap.sailing.simulator;
import java.util.List;
import com.sap.sailing.simulator.windfield.WindField;
import com.sap.sse.common.TimePoint;
public interface Path {
List<TimedPositionWithSpeed> getPathPoints();
void setPathPoints(List<TimedPositionWithSpeed> pointsList);
public long getMaxTurnTime();
public void setMaxTurnTime(long maxTurnTime);
void setWindField(WindField wf);
List<TimedPositionWithSpeed> getTurns();
TimePoint getFinalTime();
Path getEvenTimedPath(long timeStep);
int getTurnCount();
boolean getAlgorithmTimedOut();
boolean getMixedLeg();
}
@@ -1,12 +1,17 @@
package com.sap.sailing.simulator;
import com.sap.sailing.simulator.impl.SparsePolarDataException;
public interface PathGenerator {
void setSimulationParameters(SimulationParameters params);
SimulationParameters getSimulationParameters();
Path getPath();
Path getPath() throws SparsePolarDataException;
Path getPathEvenTimed(long stepMilliseconds) throws SparsePolarDataException;
public boolean isTimedOut();
Path getPathEvenTimed(long stepMilliseconds);
}
@@ -0,0 +1,20 @@
package com.sap.sailing.simulator;
public enum PathType {
OMNISCIENT("1#Omniscient"),
OPPORTUNIST_LEFT("2#Opportunist Left"),
OPPORTUNIST_RIGHT("3#Opportunist Right"),
ONE_TURNER_LEFT("4#1-Turner Left"),
ONE_TURNER_RIGHT("5#1-Turner Right");
private String txtId;
PathType(String txtId) {
this.txtId = txtId;
}
public String getTxtId() {
return txtId;
}
}
@@ -0,0 +1,18 @@
package com.sap.sailing.simulator;
public enum PointOfSail {
TACKING("Tacking"),
JIBING("Jibing"),
REACHING("Reaching");
private String txtId;
PointOfSail(String txtId) {
this.txtId = txtId;
}
public String getTxtId() {
return txtId;
}
}
Loaded 100 of 136 files, more files were not shown because too many files have changed in this diff. Show more