mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
Merge remote-tracking branch 'server/windestimation' into windestimation
Change-Id: Id300413c84e80ae2c82a1dc52b538d834aebfe43
This commit is contained in:
commit
4c88e5f287
62 files changed
+1017
-304
No files matched your search
+2
-2
@@ -83,7 +83,7 @@ import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -1235,7 +1235,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindEstimation(IncrementalWindEstimationTrack windEstimation) {
|
||||
public void setWindEstimation(IncrementalWindEstimation windEstimation) {
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -64,7 +64,7 @@ import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -783,7 +783,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindEstimation(IncrementalWindEstimationTrack windEstimation) {
|
||||
public void setWindEstimation(IncrementalWindEstimation windEstimation) {
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -38,7 +38,7 @@ import com.sap.sailing.domain.ranking.RankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -722,7 +722,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindEstimation(IncrementalWindEstimationTrack windEstimation) {
|
||||
public void setWindEstimation(IncrementalWindEstimation windEstimation) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import com.sap.sailing.domain.ranking.RankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -1152,6 +1152,6 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
* {@code null}, the wind estimation will be disabled for the tracked race. After the call of this method, maneuver
|
||||
* cache and wind cache will be reset and its recalculation will be triggered.
|
||||
*/
|
||||
void setWindEstimation(IncrementalWindEstimationTrack windEstimation);
|
||||
void setWindEstimation(IncrementalWindEstimation windEstimation);
|
||||
|
||||
}
|
||||
+41
-1
@@ -1,8 +1,13 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -19,6 +24,8 @@ import com.sap.sailing.domain.tracking.impl.LegMiddleWindTrackImpl;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
import difflib.PatchFailedException;
|
||||
|
||||
|
||||
public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
|
||||
@@ -47,7 +54,7 @@ public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
* intra-leg computations are done dynamically based on wind information, selecting a different wind information
|
||||
* source can alter the intra-leg results. See {@link #currentWindSource}.
|
||||
*/
|
||||
protected final ConcurrentMap<WindSource, WindTrack> windTracks;
|
||||
protected transient ConcurrentMap<WindSource, WindTrack> windTracks;
|
||||
|
||||
protected final RaceDefinition race;
|
||||
|
||||
@@ -185,4 +192,37 @@ public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
protected NamedReentrantReadWriteLock getSerializationLock() {
|
||||
return serializationLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* For serialization, maneuver based estimation wind track should be removed because its replication is redundant.
|
||||
* The wind estimation track is calculated automatically when maneuver cache recalculation is triggered. Due to the
|
||||
* custom deserialization of maneuver cache, this is always the case, also if replication is performed. Furthermore,
|
||||
* it might introduce issues in context of already running wind estimation.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream s) throws IOException {
|
||||
LockUtil.lockForWrite(getSerializationLock());
|
||||
try {
|
||||
s.defaultWriteObject();
|
||||
Map<WindSource, WindTrack> windTracksToSerialize = new HashMap<>();
|
||||
synchronized (this.windTracks) {
|
||||
for (Entry<WindSource, WindTrack> entry : windTracks.entrySet()) {
|
||||
if (entry.getKey().getType() != WindSourceType.MANEUVER_BASED_ESTIMATION) {
|
||||
windTracksToSerialize.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
s.writeObject(windTracksToSerialize);
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(getSerializationLock());
|
||||
}
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException, PatchFailedException {
|
||||
ois.defaultReadObject();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<WindSource, WindTrack> windTracks = (Map<WindSource, WindTrack>) ois.readObject();
|
||||
this.windTracks = new ConcurrentHashMap<>(windTracks);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+23
-16
@@ -146,7 +146,7 @@ import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -393,7 +393,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
private transient PolarDataService polarDataService;
|
||||
|
||||
private transient volatile IncrementalWindEstimationTrack windEstimation;
|
||||
private transient volatile IncrementalWindEstimation windEstimation;
|
||||
|
||||
private transient ShortTimeAfterLastHitCache<Competitor, IncrementalManeuverDetector> maneuverDetectorPerCompetitorCache;
|
||||
|
||||
@@ -3611,27 +3611,34 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
@Override
|
||||
public void setPolarDataService(PolarDataService polarDataService) {
|
||||
this.polarDataService = polarDataService;
|
||||
if(polarDataService != null && windEstimation != null) {
|
||||
updateManeuversAndWindWithNewWindEstimation(windEstimation, windEstimation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindEstimation(IncrementalWindEstimationTrack windEstimation) {
|
||||
IncrementalWindEstimationTrack previousWindEstimation = this.windEstimation;
|
||||
public void setWindEstimation(IncrementalWindEstimation windEstimation) {
|
||||
IncrementalWindEstimation previousWindEstimation = this.windEstimation;
|
||||
if (previousWindEstimation != windEstimation) {
|
||||
if (previousWindEstimation != null) {
|
||||
windTracks.remove(previousWindEstimation.getWindSource());
|
||||
}
|
||||
if (windEstimation != null) {
|
||||
windTracks.put(windEstimation.getWindSource(), windEstimation);
|
||||
}
|
||||
this.windEstimation = windEstimation;
|
||||
// TODO Make more efficient by reusing the state of incremental maneuver detectors. The already computed
|
||||
// complete maneuver curves can be fed directly to the windEstimation.
|
||||
maneuverDetectorPerCompetitorCache.clearCache();
|
||||
shortTimeWindCache.clearCache();
|
||||
triggerManeuverCacheRecalculationForAllCompetitors();
|
||||
updateManeuversAndWindWithNewWindEstimation(windEstimation, previousWindEstimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateManeuversAndWindWithNewWindEstimation(IncrementalWindEstimation windEstimation,
|
||||
IncrementalWindEstimation previousWindEstimation) {
|
||||
WindSource windSource = new WindSourceImpl(WindSourceType.MANEUVER_BASED_ESTIMATION);
|
||||
windTracks.remove(windSource);
|
||||
if (windEstimation != null) {
|
||||
windTracks.put(windSource, windEstimation.getWindTrack());
|
||||
}
|
||||
this.windEstimation = windEstimation;
|
||||
// TODO Make more efficient by reusing the state of incremental maneuver detectors. The already computed
|
||||
// complete maneuver curves can be fed directly into the windEstimation.
|
||||
maneuverDetectorPerCompetitorCache.clearCache();
|
||||
shortTimeWindCache.clearCache();
|
||||
triggerManeuverCacheRecalculationForAllCompetitors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the {@link #raceLogResolver}.
|
||||
*/
|
||||
|
||||
+7
-2
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.tracking.WindTrack;
|
||||
|
||||
/**
|
||||
* Wind estimator which interacts with maneuver detector and maintains a wind track with estimated wind fixes. All in
|
||||
* all, this instance can be seen as a wind source with corresponding wind track. The underlying wind track is prone to
|
||||
* all, this instance can be seen as a wind source with corresponding wind track. The managed wind track is prone to
|
||||
* changes which are communicated to tracked race via its
|
||||
* {@link TrackedRace#recordWind(com.sap.sailing.domain.common.Wind, WindSource, boolean)} and
|
||||
* {@link TrackedRace#removeWind(com.sap.sailing.domain.common.Wind, WindSource)}.
|
||||
@@ -14,11 +14,16 @@ import com.sap.sailing.domain.tracking.WindTrack;
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public interface IncrementalWindEstimationTrack extends WindTrack, WindEstimationInteraction {
|
||||
public interface IncrementalWindEstimation extends WindEstimationInteraction {
|
||||
|
||||
/**
|
||||
* @return The wind source assigned to this wind track
|
||||
*/
|
||||
WindSource getWindSource();
|
||||
|
||||
/**
|
||||
* Gets the produced wind track of this wind estimation
|
||||
*/
|
||||
WindTrack getWindTrack();
|
||||
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.windestimation.integration;
|
||||
package com.sap.sailing.domain.windestimation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
@@ -13,8 +14,9 @@ import com.sap.sse.common.Util.Pair;
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class TimePointAndPositionWithToleranceComparator implements Comparator<Pair<Position, TimePoint>> {
|
||||
public class TimePointAndPositionWithToleranceComparator implements Comparator<Pair<Position, TimePoint>>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3240742353261384636L;
|
||||
private static final double DOUBLE_TOLERANCE = 0.0000001;
|
||||
|
||||
@Override
|
||||
+1
-1
@@ -31,7 +31,7 @@ public interface WindEstimationFactoryService {
|
||||
* @see #isReady()
|
||||
* @see #addWindEstimationModelsChangedListenerAndReceiveUpdate(WindEstimationModelsChangedListener)
|
||||
*/
|
||||
IncrementalWindEstimationTrack createIncrementalWindEstimationTrack(TrackedRace trackedRace);
|
||||
IncrementalWindEstimation createIncrementalWindEstimationTrack(TrackedRace trackedRace);
|
||||
|
||||
/**
|
||||
* Checks whether the wind estimation is ready for its use. The wind estimation maintains multiple internal machine
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.sap.sailing.domain.windestimation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
* Wind track implementation which uses the provided map with {@link WindWithConfidence}-instances to derive the
|
||||
* confidence for each wind fix within this wind track.
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class WindTrackWithConfidenceForEachWindFixImpl extends WindTrackImpl {
|
||||
|
||||
private static final long serialVersionUID = 8135949043175649443L;
|
||||
private final Map<Pair<Position, TimePoint>, WindWithConfidence<Pair<Position, TimePoint>>> windTrackWithConfidences;
|
||||
|
||||
public WindTrackWithConfidenceForEachWindFixImpl(long millisecondsOverWhichToAverage, double baseConfidence,
|
||||
boolean useSpeed, String nameForReadWriteLock, boolean losslessCompaction,
|
||||
Map<Pair<Position, TimePoint>, WindWithConfidence<Pair<Position, TimePoint>>> windTrackWithConfidences) {
|
||||
super(millisecondsOverWhichToAverage, baseConfidence, useSpeed, nameForReadWriteLock, losslessCompaction);
|
||||
this.windTrackWithConfidences = windTrackWithConfidences;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getConfidenceOfInternalWindFixUnsynchronized(Wind windFix) {
|
||||
WindWithConfidence<Pair<Position, TimePoint>> windWithConfidence = windTrackWithConfidences
|
||||
.get(new Pair<>(windFix.getPosition(), windFix.getTimePoint()));
|
||||
return super.getConfidenceOfInternalWindFixUnsynchronized(windFix) * windWithConfidence.getConfidence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockForWrite() {
|
||||
super.lockForWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockAfterWrite() {
|
||||
super.unlockAfterWrite();
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -71,7 +71,7 @@ import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -931,7 +931,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindEstimation(IncrementalWindEstimationTrack windEstimation) {
|
||||
public void setWindEstimation(IncrementalWindEstimation windEstimation) {
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
|
||||
<booleanAttribute key="append.args" value="true"/>
|
||||
<booleanAttribute key="automaticAdd" value="false"/>
|
||||
<booleanAttribute key="automaticValidate" value="false"/>
|
||||
<stringAttribute key="bootstrap" value=""/>
|
||||
<stringAttribute key="checked" value="[NONE]"/>
|
||||
<booleanAttribute key="clearConfig" value="false"/>
|
||||
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
|
||||
<booleanAttribute key="default" value="true"/>
|
||||
<booleanAttribute key="default_auto_start" value="true"/>
|
||||
<intAttribute key="default_start_level" value="4"/>
|
||||
<booleanAttribute key="includeOptional" value="true"/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2011 -Xmx6000m -XX:+UseG1GC -Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy -Dhttps.proxyPort=8080 -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar -Dmongo.dbName=replica -Dpolardata.source.url=http://127.0.0.1:8888 -Dwindestimation.source.url=http://127.0.0.1:8888"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<booleanAttribute key="show_selected_only" value="false"/>
|
||||
<stringAttribute key="target_bundles" value="com.fasterxml.jackson.core.jackson-annotations@default:default,com.fasterxml.jackson.core.jackson-core@default:default,com.fasterxml.jackson.core.jackson-databind@default:default,com.rabbitmq.client@default:default,com.sun.activation.javax.activation@default:default,com.sun.istack.commons-runtime@default:default,com.sun.jersey.contribs.jersey-multipart@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,com.sun.xml.bind.jaxb-impl@default:default,jackson-core-asl@default:default,jackson-jaxrs@default:default,jackson-mapper-asl@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,javax.xml.stream@default:default,javax.xml.ws@default:default,javax.xml@default:default,jaxb-api@default:default,jcl.over.slf4j@default:default,lz4-java@default:default,org.apache.commons.beanutils@default:default,org.apache.commons.codec@default:default,org.apache.commons.collections@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.math@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.felix.shell.remote@0:true,org.apache.felix.shell@0:true,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.poi.ooxml.schemas@default:default,org.apache.poi.ooxml@default:default,org.apache.poi@default:default,org.apache.servicemix.bundles.aws-java-sdk@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.servicemix.bundles.zxing@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.apache.xmlbeans@default:default,org.dom4j@default:default,org.eclipse.equinox.cm@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.simpleconfigurator@2:true,org.eclipse.jetty.client@default:default,org.eclipse.jetty.deploy@4:true,org.eclipse.jetty.http@4:true,org.eclipse.jetty.io@4:true,org.eclipse.jetty.jmx@4:true,org.eclipse.jetty.osgi.boot@3:true,org.eclipse.jetty.security@4:true,org.eclipse.jetty.server@4:true,org.eclipse.jetty.servlet@4:true,org.eclipse.jetty.util@4:true,org.eclipse.jetty.webapp@4:true,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.xml@4:true,org.eclipse.osgi.services@default:default,org.eclipse.osgi.util@default:default,org.eclipse.osgi@-1:true,org.hyperic.sigar@default:default,org.jdom@default:default,org.jvnet.mimepull@default:default,org.mongodb.mongo-java-driver@default:default,org.objectweb.asm@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
<booleanAttribute key="useCustomFeatures" value="false"/>
|
||||
<booleanAttribute key="useDefaultConfigArea" value="false"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.servlet@default:default,com.googlecode.java-diff-utils@default:default,com.sap.sailing.barbados.resultimport@4:true,com.sap.sailing.competitorimport@default:default,com.sap.sailing.dashboards.gwt@6:true,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@5:true,com.sap.sailing.declination@default:default,com.sap.sailing.domain.bravoadapter@5:true,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@5:true,com.sap.sailing.domain.expeditionadapter@5:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@4:true,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@4:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@4:true,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@4:true,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.domain.windfinderadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@4:true,com.sap.sailing.expeditionconnector.common@default:default,com.sap.sailing.expeditionconnector.persistence@4:true,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@4:true,com.sap.sailing.geocoding@default:default,com.sap.sailing.grib@default:default,com.sap.sailing.gwt.ui@6:true,com.sap.sailing.kiworesultimport@4:true,com.sap.sailing.manage2sail.resultimport@4:true,com.sap.sailing.manage2sail@default:default,com.sap.sailing.monitoring@7:true,com.sap.sailing.news@4:true,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.polars.datamining@5:true,com.sap.sailing.polars@5:true,com.sap.sailing.resultimport@4:true,com.sap.sailing.routeconverterjava11extension@default:false,com.sap.sailing.sailwave.resultimport@4:true,com.sap.sailing.server.gateway.serialization.shared.android@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.interface@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@5:true,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@4:true,com.sap.sailing.windestimation@5:true,com.sap.sailing.www@5:true,com.sap.sailing.xmlexport@5:true,com.sap.sailing.xrr.resultimport@4:true,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.yachtscoring.resultimport@4:true,com.sap.sse.common@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining.ui@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@4:true,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.jersey.jaxbdependencyfragment@default:false,com.sap.sse.jettyextensions@default:false,com.sap.sse.mail@5:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication.interfaces@default:default,com.sap.sse.replication@6:true,com.sap.sse.security.common@default:default,com.sap.sse.security.persistence@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@default:default,com.sap.sse.shared.android@default:default,com.sap.sse.threadmanager@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default,org.mp4parser.isoparser@default:default"/>
|
||||
<stringAttribute key="yk-options" value=" additional-options2=onexit\=snapshot "/>
|
||||
</launchConfiguration>
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
|
||||
<booleanAttribute key="append.args" value="true"/>
|
||||
<booleanAttribute key="automaticAdd" value="false"/>
|
||||
<booleanAttribute key="automaticValidate" value="false"/>
|
||||
<stringAttribute key="bootstrap" value=""/>
|
||||
<stringAttribute key="checked" value="[NONE]"/>
|
||||
<booleanAttribute key="clearConfig" value="false"/>
|
||||
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
|
||||
<booleanAttribute key="default" value="true"/>
|
||||
<booleanAttribute key="default_auto_start" value="true"/>
|
||||
<intAttribute key="default_start_level" value="4"/>
|
||||
<booleanAttribute key="includeOptional" value="true"/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2011 -Xmx6000m -XX:+UseG1GC -Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy -Dhttps.proxyPort=8080 -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar -Dmongo.dbName=replica -Dpolardata.source.url=http://127.0.0.1:8888"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<booleanAttribute key="show_selected_only" value="false"/>
|
||||
<stringAttribute key="target_bundles" value="routeconverter@default:default,org.apache.commons.codec@default:default,org.apache.poi@default:default,org.apache.poi.ooxml@default:default,org.apache.poi.ooxml.schemas@default:default,org.dom4j@default:default,org.apache.xmlbeans@default:default,org.apache.commons.math@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.objectweb.asm@default:default,org.hyperic.sigar@default:default,com.sun.jersey.contribs.jersey-multipart@default:default,javax.validation@default:default,org.apache.commons.fileupload@default:default,org.jdom@default:default,org.jvnet.mimepull@default:default,org.mongodb.mongo-java-driver@default:default,org.eclipse.jetty.osgi.boot@3:true,slf4j.jdk14@default:default,org.apache.felix.shell@0:true,org.apache.felix.shell.remote@0:true,lz4-java@default:default,javax.servlet@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.eclipse.jetty.deploy@4:true,org.eclipse.jetty.http@4:true,org.eclipse.jetty.io@4:true,org.eclipse.jetty.jmx@4:true,org.eclipse.jetty.security@4:true,org.eclipse.jetty.server@4:true,org.eclipse.jetty.servlet@4:true,org.eclipse.jetty.util@4:true,org.eclipse.jetty.webapp@4:true,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.xml@4:true,slf4j.api@default:default,org.apache.servicemix.bundles.zxing@default:default,com.fasterxml.jackson.core.jackson-annotations@default:default,com.fasterxml.jackson.core.jackson-core@default:default,com.fasterxml.jackson.core.jackson-databind@default:default,org.apache.commons.io@default:default,jcl.over.slf4j@default:default,org.apache.servicemix.bundles.aws-java-sdk@default:default,com.sun.mail.javax.mail@default:default,com.rabbitmq.client@default:default,org.apache.commons.lang@default:default,jackson-jaxrs@default:default,com.sun.jersey@default:default,javax.ws.rs@default:default,org.apache.commons.beanutils@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,jackson-core-asl@default:default,jackson-mapper-asl@default:default,org.apache.commons.collections@default:default,org.eclipse.jetty.client@default:default,javax.xml@default:default,com.sun.activation.javax.activation@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.simpleconfigurator@2:true,org.eclipse.osgi@default:default,org.eclipse.osgi.services@default:default,org.eclipse.equinox.cm@default:default,com.sun.istack.commons-runtime@default:default,jaxb-api@default:default,com.sun.xml.bind.jaxb-impl@default:default,javax.xml.stream@default:default,javax.xml.ws@default:default,org.eclipse.osgi.util@default:default"/>
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
<booleanAttribute key="useCustomFeatures" value="false"/>
|
||||
<booleanAttribute key="useDefaultConfigArea" value="false"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.sap.sailing.geocoding@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain@default:default,com.sap.sailing.news@4:true,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.domain.windfinderadapter@4:true,com.sap.sailing.server@5:true,com.sap.sailing.server.gateway@5:true,com.tractrac.clientmodule@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingadapter.persistence@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@4:true,com.sap.sailing.gwt.ui@6:true,com.sap.sailing.udpconnector@default:default,com.sap.sailing.xmlexport@5:true,com.sap.sailing.simulator@default:default,com.sap.sailing.www@5:true,com.sap.sailing.resultimport@4:true,com.sap.sailing.kiworesultimport@4:true,com.sap.sailing.ess40.resultimport@4:true,com.sap.sailing.freg.resultimport@4:true,com.sap.sailing.barbados.resultimport@4:true,com.sap.sailing.sailwave.resultimport@4:true,com.sap.sailing.manage2sail.resultimport@4:true,com.sap.sailing.yachtscoring.resultimport@4:true,com.sap.sailing.velum.resultimport@4:true,com.sap.sailing.monitoring@7:true,com.sap.sailing.xrr.resultimport@4:true,com.sap.sailing.domain.igtimiadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@4:true,com.sap.sailing.domain.deckmanadapter@5:true,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.server.gateway.serialization.shared.android@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.dashboards.gwt@6:true,com.sap.sailing.dashboards.gwt@6:true,com.sap.sailing.datamining@5:true,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.polars@5:true,com.sap.sailing.windestimation@5:true,com.sap.sailing.polars.datamining@5:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.competitorimport@default:default,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.grib@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.domain.expeditionadapter@5:true,com.sap.sailing.expeditionconnector.persistence@4:true,com.sap.sailing.expeditionconnector.common@default:default,com.sap.sailing.domain.bravoadapter@5:true,net.sf.marineapi@default:default,com.sap.sailing.routeconverterjava11extension@default:default,com.sap.sailing.server.interface@default:default,com.sap.sse.datamining.ui@default:default,com.tractrac.clientmodule@default:default,com.sap.sse.gwt@default:default,com.google.gwt.servlet@default:default,com.sap.sse.security@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@6:true,com.sap.sse.filestorage@4:true,com.sap.sse.shared.android@default:default,com.sap.sse.mail@5:true,com.sap.sse.threadmanager@default:default,com.sap.sse.security.common@default:default,org.json.simple@default:default,com.sap.sse.jersey.jaxbdependencyfragment@default:default,org.moxieapps.gwt.highcharts@default:default,com.googlecode.java-diff-utils@default:default,org.mp4parser.isoparser@default:default,com.sap.sse.replication.interfaces@default:default,com.sap.sse.security.persistence@default:default,com.sap.sse.jettyextensions@default:default"/>
|
||||
<stringAttribute key="yk-options" value=" additional-options2=onexit\=snapshot "/>
|
||||
</launchConfiguration>
|
||||
+7
-2
@@ -124,6 +124,7 @@ import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
|
||||
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.common.dto.AnniversaryType;
|
||||
import com.sap.sailing.domain.common.dto.EventType;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
@@ -2078,12 +2079,16 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
|
||||
@Override
|
||||
public void windDataReceived(Wind wind, WindSource windSource) {
|
||||
replicate(new RecordWindFix(getRaceIdentifier(), windSource, wind));
|
||||
if (windSource.getType() != WindSourceType.MANEUVER_BASED_ESTIMATION) {
|
||||
replicate(new RecordWindFix(getRaceIdentifier(), windSource, wind));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind, WindSource windSource) {
|
||||
replicate(new RemoveWindFix(getRaceIdentifier(), windSource, wind));
|
||||
if (windSource.getType() != WindSourceType.MANEUVER_BASED_ESTIMATION) {
|
||||
replicate(new RemoveWindFix(getRaceIdentifier(), windSource, wind));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/ManeuverAndWindImporter.java"/>
|
||||
<listEntry value="/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/model/SimpleModelsTrainingPart1.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.windestimation.data.importer.ManeuverAndWindImporter"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.windestimation.model.SimpleModelsTrainingPart1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.windestimation.lab"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx16G"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms16G -Xmx16G"/>
|
||||
</launchConfiguration>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/model/SimpleModelsTrainingPart2.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.windestimation.model.SimpleModelsTrainingPart2"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.windestimation.lab"/>
|
||||
</launchConfiguration>
|
||||
+8
-2
@@ -9,12 +9,18 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.SingleDimen
|
||||
public class AggregatedDistanceBasedAbsTwdTransitionImporter {
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DISTANCE);
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = createPersistenceManagerAndEnsureIndex();
|
||||
SingleDimensionTwdTransitionAggregationImporter importer = new SingleDimensionTwdTransitionAggregationImporter(
|
||||
singleDimensionBasedTwdTransitionPersistenceManager, AggregatedSingleDimensionType.DISTANCE_ABS,
|
||||
new AnnealingNextThresholdCalculator(10, 1.5));
|
||||
importer.runAggregation();
|
||||
}
|
||||
|
||||
public static SingleDimensionBasedTwdTransitionPersistenceManager createPersistenceManagerAndEnsureIndex()
|
||||
throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DISTANCE);
|
||||
return singleDimensionBasedTwdTransitionPersistenceManager;
|
||||
}
|
||||
|
||||
}
|
||||
+8
-2
@@ -9,12 +9,18 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.SingleDimen
|
||||
public class AggregatedDistanceBasedTwdTransitionImporter {
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DISTANCE);
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = createPersistenceManagerAndEnsureIndex();
|
||||
SingleDimensionTwdTransitionAggregationImporter importer = new SingleDimensionTwdTransitionAggregationImporter(
|
||||
singleDimensionBasedTwdTransitionPersistenceManager, AggregatedSingleDimensionType.DISTANCE,
|
||||
new AnnealingNextThresholdCalculator(10, 1.5));
|
||||
importer.runAggregation();
|
||||
}
|
||||
|
||||
public static SingleDimensionBasedTwdTransitionPersistenceManager createPersistenceManagerAndEnsureIndex()
|
||||
throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DISTANCE);
|
||||
return singleDimensionBasedTwdTransitionPersistenceManager;
|
||||
}
|
||||
|
||||
}
|
||||
+8
-2
@@ -9,12 +9,18 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.SingleDimen
|
||||
public class AggregatedDurationBasedAbsTwdTransitionImporter {
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DURATION);
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = createPersistenceManagerAndEnsureIndex();
|
||||
SingleDimensionTwdTransitionAggregationImporter importer = new SingleDimensionTwdTransitionAggregationImporter(
|
||||
singleDimensionBasedTwdTransitionPersistenceManager, AggregatedSingleDimensionType.DURATION_ABS,
|
||||
DurationBasedTwdTransitionImporter.getThresholdCalculator());
|
||||
importer.runAggregation();
|
||||
}
|
||||
|
||||
public static SingleDimensionBasedTwdTransitionPersistenceManager createPersistenceManagerAndEnsureIndex()
|
||||
throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DURATION);
|
||||
return singleDimensionBasedTwdTransitionPersistenceManager;
|
||||
}
|
||||
|
||||
}
|
||||
+8
-2
@@ -9,12 +9,18 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.SingleDimen
|
||||
public class AggregatedDurationBasedTwdTransitionImporter {
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DURATION);
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = createPersistenceManagerAndEnsureIndex();
|
||||
SingleDimensionTwdTransitionAggregationImporter importer = new SingleDimensionTwdTransitionAggregationImporter(
|
||||
singleDimensionBasedTwdTransitionPersistenceManager, AggregatedSingleDimensionType.DURATION,
|
||||
DurationBasedTwdTransitionImporter.getThresholdCalculator());
|
||||
importer.runAggregation();
|
||||
}
|
||||
|
||||
public static SingleDimensionBasedTwdTransitionPersistenceManager createPersistenceManagerAndEnsureIndex()
|
||||
throws UnknownHostException {
|
||||
SingleDimensionBasedTwdTransitionPersistenceManager singleDimensionBasedTwdTransitionPersistenceManager = new SingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
SingleDimensionType.DURATION);
|
||||
return singleDimensionBasedTwdTransitionPersistenceManager;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-4
@@ -36,7 +36,8 @@ public class DistanceBasedTwdTransitionImporter {
|
||||
.getIterator(); iterator.hasNext();) {
|
||||
long percent = windSourceNumber * 100 / numberOfWindSources;
|
||||
WindSourceWithFixes windSource = iterator.next();
|
||||
LoggingUtil.logInfo("Processing " + windSourceNumber++ + "/" + numberOfWindSources + " (" + percent + "%)");
|
||||
LoggingUtil.logInfo("Processing wind source " + windSourceNumber++ + "/" + numberOfWindSources + " ("
|
||||
+ percent + "%) for distance dimension");
|
||||
List<SingleDimensionBasedTwdTransition> entries = new ArrayList<>();
|
||||
List<WindSourceWithFixes> otherWindSources = new ArrayList<>();
|
||||
for (PersistedElementsIterator<WindSourceWithFixes> otherWindSourcesIterator = windSourcesPersistenceManager
|
||||
@@ -109,16 +110,17 @@ public class DistanceBasedTwdTransitionImporter {
|
||||
} while (currentFix != null && currentOtherFix != null);
|
||||
}
|
||||
if (entries.isEmpty()) {
|
||||
LoggingUtil.logInfo("No TWD transitions to import");
|
||||
LoggingUtil.logInfo("No distance based TWD transitions to import");
|
||||
} else {
|
||||
distanceBasedTwdTransitionPersistenceManager.add(entries);
|
||||
int totalEntries = entries.size();
|
||||
totalValuesCount += totalEntries;
|
||||
LoggingUtil.logInfo(totalEntries + " TWD transitions imported, " + totalValuesCount + " in total");
|
||||
LoggingUtil.logInfo(
|
||||
totalEntries + " distance based TWD transitions imported, " + totalValuesCount + " in total");
|
||||
}
|
||||
}
|
||||
LoggingUtil.logInfo("###################\r\nDistance based TWD transitions Import finished");
|
||||
LoggingUtil.logInfo("Totally " + totalValuesCount + " TWD transitions imported");
|
||||
LoggingUtil.logInfo("Totally " + totalValuesCount + " distance based TWD transitions imported");
|
||||
}
|
||||
|
||||
}
|
||||
+6
-5
@@ -32,7 +32,8 @@ public class DurationBasedTwdTransitionImporter {
|
||||
.getIterator(); iterator.hasNext();) {
|
||||
long percent = windSourceNumber * 100 / numberOfWindSources;
|
||||
WindSourceWithFixes windSource = iterator.next();
|
||||
LoggingUtil.logInfo("Processing " + windSourceNumber++ + "/" + numberOfWindSources + " (" + percent + "%)");
|
||||
LoggingUtil.logInfo("Processing wind source " + windSourceNumber++ + "/" + numberOfWindSources + " ("
|
||||
+ percent + "%) for duration dimension");
|
||||
TimePoint timePointOfLastConsideredWindFix = null;
|
||||
List<SingleDimensionBasedTwdTransition> entries = new ArrayList<>();
|
||||
int windFixIndex = 0;
|
||||
@@ -58,17 +59,17 @@ public class DurationBasedTwdTransitionImporter {
|
||||
}
|
||||
}
|
||||
if (entries.isEmpty()) {
|
||||
LoggingUtil.logInfo("No TWD transitions to import");
|
||||
LoggingUtil.logInfo("No duration based TWD transitions to import");
|
||||
} else {
|
||||
durationBasedTwdTransitionPersistenceManager.add(entries);
|
||||
int totalEntries = entries.size();
|
||||
totalValuesCount += totalEntries;
|
||||
LoggingUtil
|
||||
.logInfo(totalEntries + " TWD transition entries imported, " + totalValuesCount + " in total");
|
||||
LoggingUtil.logInfo(totalEntries + " duration based TWD transition entries imported, "
|
||||
+ totalValuesCount + " in total");
|
||||
}
|
||||
}
|
||||
LoggingUtil.logInfo("###################\r\nDuration based TWD transitions Import finished");
|
||||
LoggingUtil.logInfo("Totally " + totalValuesCount + " TWD transitions imported");
|
||||
LoggingUtil.logInfo("Totally " + totalValuesCount + " duration based TWD transitions imported");
|
||||
}
|
||||
|
||||
public static NextThresholdCalculator getThresholdCalculator() {
|
||||
|
||||
+97
-42
@@ -13,6 +13,10 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
@@ -40,11 +44,11 @@ import com.sap.sailing.windestimation.data.persistence.maneuver.RaceWithManeuver
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.RaceWithManeuverForEstimationPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.twdtransition.RaceWithWindSourcesPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.serialization.CompetitorTrackWithEstimationDataJsonDeserializer;
|
||||
import com.sap.sailing.windestimation.data.serialization.ManeuverForDataAnalysisJsonSerializer;
|
||||
import com.sap.sailing.windestimation.data.serialization.LabeledManeuverForEstimationJsonSerializer;
|
||||
import com.sap.sailing.windestimation.data.serialization.ManeuverForDataAnalysisJsonSerializer;
|
||||
import com.sap.sailing.windestimation.data.transformer.CompetitorTrackTransformer;
|
||||
import com.sap.sailing.windestimation.data.transformer.CompleteManeuverCurveWithEstimationDataToManeuverForDataAnalysisTransformer;
|
||||
import com.sap.sailing.windestimation.data.transformer.CompleteManeuverCurveWithEstimationDataToLabelledManeuverForEstimationTransformer;
|
||||
import com.sap.sailing.windestimation.data.transformer.CompleteManeuverCurveWithEstimationDataToManeuverForDataAnalysisTransformer;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
/**
|
||||
@@ -54,6 +58,7 @@ import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
*/
|
||||
public class ManeuverAndWindImporter {
|
||||
|
||||
private static final int CONNECTION_TIMEOUT_MILLIS = 60000 * 5;
|
||||
public static final String REST_API_BASE_URL = "https://www.sapsailing.com/sailingserver/api/v1";
|
||||
public static final String REST_API_REGATTAS_PATH = "/regattas";
|
||||
public static final String REST_API_RACES_PATH = "/races";
|
||||
@@ -70,6 +75,8 @@ public class ManeuverAndWindImporter {
|
||||
private final ManeuverForDataAnalysisJsonSerializer maneuverForDataAnalysisJsonSerializer;
|
||||
private final LabeledManeuverForEstimationJsonSerializer maneuverForEstimationJsonSerializer;
|
||||
private boolean skipRace;
|
||||
private static final int NUMBER_OF_THREADS = 10; // high number due to HTTP requests
|
||||
private final ThreadPoolExecutor executorService;
|
||||
|
||||
public ManeuverAndWindImporter() throws UnknownHostException {
|
||||
this.completeManeuverCurvePersistanceManager = new RaceWithCompleteManeuverCurvePersistenceManager();
|
||||
@@ -80,12 +87,28 @@ public class ManeuverAndWindImporter {
|
||||
this.maneuverForEstimationTransformer = new CompleteManeuverCurveWithEstimationDataToLabelledManeuverForEstimationTransformer();
|
||||
this.maneuverForDataAnalysisJsonSerializer = new ManeuverForDataAnalysisJsonSerializer();
|
||||
this.maneuverForEstimationJsonSerializer = new LabeledManeuverForEstimationJsonSerializer();
|
||||
this.executorService = new ThreadPoolExecutor(NUMBER_OF_THREADS, NUMBER_OF_THREADS, 0L, TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<>(NUMBER_OF_THREADS));
|
||||
this.executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
||||
// this will block if the queue is full
|
||||
try {
|
||||
executor.getQueue().put(r);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public HttpClient createNewHttpClient() {
|
||||
HttpParams httpParams = new BasicHttpParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT_MILLIS);
|
||||
HttpClient client = new SystemDefaultHttpClient(httpParams);
|
||||
client.getParams().setParameter("http.socket.timeout", CONNECTION_TIMEOUT_MILLIS);
|
||||
client.getParams().setParameter("http.connection.timeout", CONNECTION_TIMEOUT_MILLIS);
|
||||
client.getParams().setParameter("http.connection-manager.timeout", new Long(CONNECTION_TIMEOUT_MILLIS));
|
||||
client.getParams().setParameter("http.protocol.head-body-timeout", CONNECTION_TIMEOUT_MILLIS);
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -94,8 +117,8 @@ public class ManeuverAndWindImporter {
|
||||
importer.importAllRegattas();
|
||||
}
|
||||
|
||||
public void importAllRegattas()
|
||||
throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException {
|
||||
public void importAllRegattas() throws IllegalStateException, ClientProtocolException, IOException, ParseException,
|
||||
URISyntaxException, InterruptedException {
|
||||
skipRace = startFromRegattaName != null;
|
||||
LoggingUtil.logInfo("Importer for CompleteManeuverCurveWithEstimationData just started");
|
||||
LoggingUtil.logInfo("Dropping old database");
|
||||
@@ -118,9 +141,17 @@ public class ManeuverAndWindImporter {
|
||||
+ Math.round(100.0 * i / numberOfRegattas) + "%): \"" + regattaName + "\"");
|
||||
importRegatta(regattaName, importStatistics);
|
||||
}
|
||||
LoggingUtil.logInfo("Import finished");
|
||||
importStatistics.regattasCount = regattasJson.size();
|
||||
logImportStatistics(importStatistics);
|
||||
executorService.shutdown();
|
||||
boolean success = executorService.awaitTermination(2, TimeUnit.HOURS);
|
||||
if (success) {
|
||||
LoggingUtil.logInfo("Import finished");
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.regattasCount = regattasJson.size();
|
||||
logImportStatistics(importStatistics);
|
||||
}
|
||||
} else {
|
||||
new InterruptedException("Thread-pool was terminated after two hours waiting time");
|
||||
}
|
||||
}
|
||||
|
||||
private void logImportStatistics(ImportStatistics importStatistics) {
|
||||
@@ -139,14 +170,15 @@ public class ManeuverAndWindImporter {
|
||||
private void importRegatta(String regattaName, ImportStatistics importStatistics)
|
||||
throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException {
|
||||
String encodedRegattaName = encodeUrlPathPart(regattaName);
|
||||
// TODO The encoding of some regatta names with special characters does not work well
|
||||
HttpGet getRegatta = new HttpGet(REST_API_BASE_URL + REST_API_REGATTAS_PATH + "/" + encodedRegattaName);
|
||||
JSONObject regattaJson = null;
|
||||
try {
|
||||
regattaJson = (JSONObject) getHttpResponseAsJson(regattaName, null, getRegatta);
|
||||
} catch (Exception e) {
|
||||
importStatistics.ignoredRegattas += 1;
|
||||
LoggingUtil.logInfo("Error while processing regatta: " + regattaName);
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.ignoredRegattas++;
|
||||
}
|
||||
LoggingUtil.logInfo("Error while processing regatta: " + regattaName + "\r\n" + getRegatta);
|
||||
return;
|
||||
}
|
||||
for (Object seriesJson : (JSONArray) regattaJson.get("series")) {
|
||||
@@ -161,17 +193,29 @@ public class ManeuverAndWindImporter {
|
||||
if ((boolean) race.get("isTracked") && !(boolean) race.get("isLive")
|
||||
&& (boolean) race.get("hasGpsData") && (boolean) race.get("hasWindData")) {
|
||||
String trackedRaceName = (String) race.get("trackedRaceName");
|
||||
LoggingUtil.logInfo("Processing race nr. " + ++i + ": \"" + trackedRaceName + "\"");
|
||||
try {
|
||||
importRace(regattaName, trackedRaceName, importStatistics);
|
||||
} catch (Exception e) {
|
||||
importStatistics.ingoredRaces += 1;
|
||||
LoggingUtil
|
||||
.logInfo("Error while processing race nr. " + i + ": \"" + trackedRaceName + "\"");
|
||||
}
|
||||
i++;
|
||||
final int raceNumber = i;
|
||||
LoggingUtil.logInfo("Processing race nr. " + raceNumber + ": \"" + trackedRaceName + "\"");
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
importRace(regattaName, trackedRaceName, importStatistics);
|
||||
} catch (Exception e) {
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.ingoredRaces += 1;
|
||||
}
|
||||
String extraLog = "";
|
||||
if (e instanceof HttpClientException) {
|
||||
extraLog = "\r\n" + ((HttpClientException) e).getRequest();
|
||||
}
|
||||
LoggingUtil.logInfo("Error while processing race nr. " + raceNumber + ": \""
|
||||
+ trackedRaceName + "\"" + extraLog);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
importStatistics.racesCount += racesJson.size();
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.racesCount += racesJson.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,11 +259,14 @@ public class ManeuverAndWindImporter {
|
||||
JSONArray windFixesJson = (JSONArray) ((JSONObject) windSourceObj).get(RaceWindJsonSerializer.FIXES);
|
||||
windFixesCount += windFixesJson.size();
|
||||
}
|
||||
LoggingUtil.logInfo(
|
||||
"Imported " + windFixesCount + " wind fixes from " + windSourcesJson.size() + " wind sources");
|
||||
importStatistics.racesWithHighQualityWindData++;
|
||||
LoggingUtil.logInfo("Imported " + windFixesCount + " wind fixes from " + windSourcesJson.size()
|
||||
+ " wind sources in race \"" + trackedRaceName + "\" of regatta \"" + regattaName + "\"");
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.racesWithHighQualityWindData++;
|
||||
}
|
||||
} else {
|
||||
LoggingUtil.logInfo("No high quality wind fixes contained");
|
||||
LoggingUtil.logInfo("No high quality wind fixes contained in race \"" + trackedRaceName + "\" of regatta \""
|
||||
+ regattaName + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +319,12 @@ public class ManeuverAndWindImporter {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LoggingUtil.logInfo(
|
||||
"Imported " + competitorTracks.size() + " competitor tracks with " + maneuversCount + " maneuvers");
|
||||
importStatistics.competitorTracksCount += competitorTracks.size();
|
||||
importStatistics.maneuversCount += maneuversCount;
|
||||
LoggingUtil.logInfo("Imported " + competitorTracks.size() + " competitor tracks with " + maneuversCount
|
||||
+ " maneuvers in race \"" + trackedRaceName + "\" of regatta \"" + regattaName + "\"");
|
||||
synchronized (importStatistics) {
|
||||
importStatistics.competitorTracksCount += competitorTracks.size();
|
||||
importStatistics.maneuversCount += maneuversCount;
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject getHttpResponseAsJson(String trackedRegattaName, String trackedRaceName,
|
||||
@@ -285,7 +334,8 @@ public class ManeuverAndWindImporter {
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
try {
|
||||
httpResponse = createNewHttpClient().execute(getEstimationData);
|
||||
break;
|
||||
JSONObject resultJson = (JSONObject) getJsonFromResponse(httpResponse);
|
||||
return resultJson;
|
||||
} catch (Exception e) {
|
||||
Thread.sleep(10000);
|
||||
lastException = e;
|
||||
@@ -298,17 +348,7 @@ public class ManeuverAndWindImporter {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (httpResponse == null) {
|
||||
throw lastException;
|
||||
}
|
||||
JSONObject resultJson;
|
||||
try {
|
||||
resultJson = (JSONObject) getJsonFromResponse(httpResponse);
|
||||
} catch (Exception e) {
|
||||
System.out.println(getEstimationData);
|
||||
throw e;
|
||||
}
|
||||
return resultJson;
|
||||
throw new HttpClientException(getEstimationData.toString(), lastException);
|
||||
}
|
||||
|
||||
private <ToType> void addTransformedElementsToCompetitorTrackJson(List<JSONObject> competitorTracks,
|
||||
@@ -331,14 +371,13 @@ public class ManeuverAndWindImporter {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME duplicated code taken from ConnectivityUtils
|
||||
public static Object getJsonFromResponse(HttpResponse response)
|
||||
throws IllegalStateException, IOException, ParseException {
|
||||
JSONParser jsonParser = new JSONParser();
|
||||
final Header contentEncoding = response.getEntity().getContentEncoding();
|
||||
final Reader reader;
|
||||
if (contentEncoding == null) {
|
||||
reader = new InputStreamReader(response.getEntity().getContent());
|
||||
reader = new InputStreamReader(response.getEntity().getContent(), "UTF-8");
|
||||
} else {
|
||||
reader = new InputStreamReader(response.getEntity().getContent(), contentEncoding.getValue());
|
||||
}
|
||||
@@ -358,4 +397,20 @@ public class ManeuverAndWindImporter {
|
||||
private int racesWithHighQualityWindData = 0;
|
||||
}
|
||||
|
||||
private static class HttpClientException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 4948532287832868768L;
|
||||
private final String request;
|
||||
|
||||
public HttpClientException(String request, Exception e) {
|
||||
super(e);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public String getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
-5
@@ -35,7 +35,7 @@ public class SingleDimensionTwdTransitionAggregationImporter {
|
||||
public void runAggregation() throws UnknownHostException {
|
||||
long totalValuesCount = 0;
|
||||
List<AggregatedSingleDimensionBasedTwdTransition> aggregates = new ArrayList<>();
|
||||
LoggingUtil.logInfo("Aggregating persisted entries");
|
||||
LoggingUtil.logInfo("Aggregating persisted entries for dimension " + dimensionType);
|
||||
TDoubleList entries = new TDoubleArrayList();
|
||||
double currentBucketThreshold = thresholdCalculator.getInitialThresholdValue();
|
||||
double nextBucketThreshold = thresholdCalculator.getNextThresholdValue(currentBucketThreshold);
|
||||
@@ -61,7 +61,8 @@ public class SingleDimensionTwdTransitionAggregationImporter {
|
||||
}
|
||||
entries.add(twdChange);
|
||||
if (entries.size() % 10000 == 0) {
|
||||
LoggingUtil.logInfo((entries.size() + totalValuesCount) + " Entries aggregated");
|
||||
LoggingUtil.logInfo(
|
||||
(entries.size() + totalValuesCount) + " Entries aggregated for dimension " + dimensionType);
|
||||
}
|
||||
}
|
||||
if (entries.size() >= MIN_NUMBER_OF_VALUES_PER_BUCKET) {
|
||||
@@ -69,14 +70,16 @@ public class SingleDimensionTwdTransitionAggregationImporter {
|
||||
aggregates.add(aggregate);
|
||||
totalValuesCount += entries.size();
|
||||
}
|
||||
LoggingUtil.logInfo("Persisting " + aggregates.size() + " aggregates");
|
||||
LoggingUtil.logInfo("Persisting " + aggregates.size() + " aggregates for dimension " + dimensionType);
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager aggregatedDurationBasedTwdTransitionPersistenceManager = new AggregatedSingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
dimensionType);
|
||||
aggregatedDurationBasedTwdTransitionPersistenceManager.dropCollection();
|
||||
aggregatedDurationBasedTwdTransitionPersistenceManager.add(aggregates);
|
||||
LoggingUtil.logInfo("###################\r\n" + dimensionType + " based TWD transitions Import finished");
|
||||
LoggingUtil.logInfo("Totally " + totalValuesCount + " TWD transitions imported");
|
||||
LoggingUtil.logInfo("Totally " + aggregates.size() + " TWD transition aggregates imported");
|
||||
LoggingUtil.logInfo(
|
||||
"Totally " + totalValuesCount + " " + dimensionType + " based TWD transitions have been processed");
|
||||
LoggingUtil.logInfo(
|
||||
"Totally " + aggregates.size() + " TWD transition aggregates imported for dimension " + dimensionType);
|
||||
}
|
||||
|
||||
private double getFinalBucketThreshold(double currentBucketThreshold, double nextBucketThreshold) {
|
||||
|
||||
+6
-23
@@ -17,20 +17,16 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.RaceWindJsonSerializer;
|
||||
import com.sap.sailing.windestimation.data.RaceWithWindSources;
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.AbstractPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.PersistedElementsIterator;
|
||||
import com.sap.sailing.windestimation.data.serialization.RaceWithEstimationDataDeserializer;
|
||||
import com.sap.sailing.windestimation.data.serialization.RaceWithWindSourcesDeserializer;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceManager<RaceWithWindSources> {
|
||||
|
||||
private static final String COLLECTION_NAME = "racesWithWind";
|
||||
private static final String WIND_SOURCES_COLLECTION_NAME = "windSources";
|
||||
static final String COLLECTION_NAME = "racesWithWind";
|
||||
private final WindSourcesPersistenceManager windSourcesPersistenceManager;
|
||||
|
||||
public RaceWithWindSourcesPersistenceManager() throws UnknownHostException {
|
||||
Document indexes = new Document(RaceWindJsonSerializer.START_TIME_POINT, 1);
|
||||
indexes.put(RaceWindJsonSerializer.END_TIME_POINT, 1);
|
||||
getCollection().createIndex(indexes);
|
||||
windSourcesPersistenceManager = new WindSourcesPersistenceManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,7 +37,7 @@ public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceMa
|
||||
@Override
|
||||
public void dropCollection() {
|
||||
super.dropCollection();
|
||||
getDb().getCollection(getWindSourcesCollectionName()).drop();
|
||||
windSourcesPersistenceManager.dropCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,7 +48,7 @@ public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceMa
|
||||
JSONArray windSourceIdsJson = (JSONArray) raceJson
|
||||
.get(RaceWithEstimationDataDeserializer.COMPETITOR_TRACKS);
|
||||
JSONArray windSourcesJson = new JSONArray();
|
||||
MongoCollection<Document> windSourcesCollection = getDb().getCollection(getWindSourcesCollectionName());
|
||||
MongoCollection<Document> windSourcesCollection = windSourcesPersistenceManager.getCollection();
|
||||
for (Object idObject : windSourceIdsJson) {
|
||||
Document dbWindSource = windSourcesCollection
|
||||
.find(new Document(FIELD_DB_ID, new ObjectId((String) idObject))).first();
|
||||
@@ -69,10 +65,6 @@ public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceMa
|
||||
};
|
||||
}
|
||||
|
||||
public static String getWindSourcesCollectionName() {
|
||||
return COLLECTION_NAME + "." + WIND_SOURCES_COLLECTION_NAME;
|
||||
}
|
||||
|
||||
public void add(String regattaName, String raceName, JSONObject raceWithWindSourcesJson) {
|
||||
|
||||
JSONArray windSourcesJson = (JSONArray) raceWithWindSourcesJson.get(RaceWindJsonSerializer.WIND_SOURCES);
|
||||
@@ -81,7 +73,7 @@ public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceMa
|
||||
Document entry = parseJsonString(windSourceObj.toString());
|
||||
dbWindSources.add(entry);
|
||||
}
|
||||
MongoCollection<Document> windSourcesCollection = getDb().getCollection(getWindSourcesCollectionName());
|
||||
MongoCollection<Document> windSourcesCollection = windSourcesPersistenceManager.getCollection();
|
||||
windSourcesCollection.insertMany(dbWindSources);
|
||||
BasicDBList dbWindSourceIds = new BasicDBList();
|
||||
for (Document dbWindSource : dbWindSources) {
|
||||
@@ -96,13 +88,4 @@ public class RaceWithWindSourcesPersistenceManager extends AbstractPersistenceMa
|
||||
races.insertOne(dbObject);
|
||||
}
|
||||
|
||||
public PersistedElementsIterator<RaceWithWindSources> getIteratorForEntriesIntersectingPeriod(
|
||||
TimePoint startTimePoint, TimePoint endTimePoint, double toleranceInSeconds) {
|
||||
double maxStartTime = endTimePoint.asMillis() + toleranceInSeconds * 1000;
|
||||
double maxEndTime = startTimePoint.asMillis() - toleranceInSeconds * 1000;
|
||||
String query = "{$and: [{'" + RaceWindJsonSerializer.START_TIME_POINT + "': {$lte: " + maxStartTime + "}}, {'"
|
||||
+ RaceWindJsonSerializer.END_TIME_POINT + "': {$gte: " + maxEndTime + "}}]}";
|
||||
return getIterator(query);
|
||||
}
|
||||
|
||||
}
|
||||
+6
-3
@@ -21,15 +21,18 @@ import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class WindSourcesPersistenceManager extends AbstractPersistenceManager<WindSourceWithFixes> {
|
||||
|
||||
private static final String WIND_SOURCES_COLLECTION_NAME = "windSources";
|
||||
|
||||
public WindSourcesPersistenceManager() throws UnknownHostException {
|
||||
BasicDBObject indexes = new BasicDBObject(RaceWindJsonSerializer.START_TIME_POINT, 1);
|
||||
indexes.put(RaceWindJsonSerializer.END_TIME_POINT, 1);
|
||||
BasicDBObject indexes = new BasicDBObject("_id", 1);
|
||||
indexes.put(RaceWindJsonSerializer.START_TIME_POINT, 1);
|
||||
indexes.put(RaceWindJsonSerializer.END_TIME_POINT, -1);
|
||||
getCollection().createIndex(indexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectionName() {
|
||||
return RaceWithWindSourcesPersistenceManager.getWindSourcesCollectionName();
|
||||
return RaceWithWindSourcesPersistenceManager.COLLECTION_NAME + "." + WIND_SOURCES_COLLECTION_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -7,11 +7,17 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedS
|
||||
|
||||
public class AggregatedDistanceDimensionPlot {
|
||||
|
||||
private static SingleDimensionAggregatesPlottingFrame plot;
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager persistenceManager = new AggregatedSingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
AggregatedSingleDimensionType.DISTANCE);
|
||||
SingleDimensionAggregatesPlottingFrame plot = new SingleDimensionAggregatesPlottingFrame(persistenceManager);
|
||||
plot = new SingleDimensionAggregatesPlottingFrame(persistenceManager);
|
||||
plot.renderChart();
|
||||
}
|
||||
|
||||
public static void awaitWindowClosed() throws InterruptedException {
|
||||
plot.awaitWindowClosed();
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -7,11 +7,17 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedS
|
||||
|
||||
public class AggregatedDurationDimensionPlot {
|
||||
|
||||
private static SingleDimensionAggregatesPlottingFrame plot;
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager persistenceManager = new AggregatedSingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
AggregatedSingleDimensionType.DURATION);
|
||||
SingleDimensionAggregatesPlottingFrame plot = new SingleDimensionAggregatesPlottingFrame(persistenceManager);
|
||||
plot = new SingleDimensionAggregatesPlottingFrame(persistenceManager);
|
||||
plot.renderChart();
|
||||
}
|
||||
|
||||
public static void awaitWindowClosed() throws InterruptedException {
|
||||
plot.awaitWindowClosed();
|
||||
}
|
||||
|
||||
}
|
||||
+37
-1
@@ -3,9 +3,13 @@ package com.sap.sailing.windestimation.datavisualization;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Shape;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -40,27 +44,54 @@ public class SingleDimensionAggregatesPlottingFrame extends JFrame {
|
||||
private XYSeries valuesSeries;
|
||||
private XYSeries stdSeries;
|
||||
private XYSeries zeroMeanStdSeries;
|
||||
private final CountDownLatch windowClosedLatch = new CountDownLatch(1);
|
||||
|
||||
public SingleDimensionAggregatesPlottingFrame(
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager persistenceManager) {
|
||||
super(persistenceManager.getCollectionName());
|
||||
this.persistenceManager = persistenceManager;
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent event) {
|
||||
windowClosedLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
setLayout(null);
|
||||
setVisible(false);
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
initializeDataWithCharts();
|
||||
}
|
||||
|
||||
private void initializeDataWithCharts() {
|
||||
createSeries();
|
||||
fillSeries();
|
||||
DefaultTableXYDataset mainDataset = createMainDataset();
|
||||
DefaultTableXYDataset histoDataset = createHistoDataset();
|
||||
mainChart = createMainChart(mainDataset);
|
||||
histoChart = createHistoChart(histoDataset);
|
||||
// Create Panel
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
ChartPanel mainChartPanel = new ChartPanel(mainChart);
|
||||
ChartPanel histoChartPanel = new ChartPanel(histoChart);
|
||||
JButton refreshButton = new JButton("Refresh charts");
|
||||
refreshButton.addActionListener(event -> refreshCharts());
|
||||
panel.add(refreshButton);
|
||||
panel.add(mainChartPanel);
|
||||
panel.add(histoChartPanel);
|
||||
setContentPane(panel);
|
||||
}
|
||||
|
||||
public void refreshCharts() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
initializeDataWithCharts();
|
||||
revalidate();
|
||||
});
|
||||
}
|
||||
|
||||
private void createSeries() {
|
||||
meanSeries = new XYSeries("Mean", true, false);
|
||||
medianSeries = new XYSeries("Median", true, false);
|
||||
@@ -191,4 +222,9 @@ public class SingleDimensionAggregatesPlottingFrame extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
public void awaitWindowClosed() throws InterruptedException {
|
||||
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
windowClosedLatch.await();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-5
@@ -12,8 +12,8 @@ import com.sap.sailing.windestimation.data.persistence.maneuver.PersistedElement
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.RaceWithCompleteManeuverCurvePersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.polars.PolarDataServiceAccessUtil;
|
||||
import com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverFeatures;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
public class WindEstimatorManeuverNumberDependentEvaluationRunner {
|
||||
@@ -38,18 +38,15 @@ public class WindEstimatorManeuverNumberDependentEvaluationRunner {
|
||||
LoggingUtil.logInfo("Loading polar data");
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
LoggingUtil.logInfo("Wind estimator evaluation started...");
|
||||
ModelStore modelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// ModelStore modelStore = new MongoDbModelStoreImpl(persistenceManager.getDb());
|
||||
ModelStore modelStore = new MongoDbModelStoreImpl(persistenceManager.getDb());
|
||||
WindEstimatorFactories estimatorFactories = new WindEstimatorFactories(polarService,
|
||||
new ManeuverFeatures(ENABLE_POLARS, ENABLE_SCALED_SPEED, ENABLE_MARKS_INFORMATION), modelStore);
|
||||
|
||||
double[] avgErrorDegreesPerManeuverCount = new double[MAX_MANEUVERS];
|
||||
double[] avgConfidencePerManeuverCount = new double[MAX_MANEUVERS];
|
||||
double[] avgConfidenceOfCorrectEstimationsPerManeuverCount = new double[MAX_MANEUVERS];
|
||||
double[] avgConfidenceOfIncorrectEstimationsPerManeuverCount = new double[MAX_MANEUVERS];
|
||||
double[] accuracyPerManeuverCount = new double[MAX_MANEUVERS];
|
||||
double[] emptyEstimationsPercentagePerManeuverCount = new double[MAX_MANEUVERS];
|
||||
|
||||
for (int fixedNumberOfManeuvers = 1; fixedNumberOfManeuvers <= MAX_MANEUVERS; fixedNumberOfManeuvers++) {
|
||||
LoggingUtil.logInfo("Running evaluation with " + fixedNumberOfManeuvers + " maneuvers");
|
||||
WindEstimationEvaluator<CompleteManeuverCurveWithEstimationData> evaluator = new WindEstimationEvaluatorImpl<>(
|
||||
@@ -113,6 +110,7 @@ public class WindEstimatorManeuverNumberDependentEvaluationRunner {
|
||||
out.write(line);
|
||||
}
|
||||
}
|
||||
LoggingUtil.logInfo("CSV with evaluation results have been stored in: " + csvFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.sap.sailing.windestimation.model;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.windestimation.integration.ExportedModels;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelDomainType;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
public class ExportedModelsGenerator {
|
||||
|
||||
private static final File file = new File("windEstimationModels.dat");
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException, IOException {
|
||||
ModelStore modelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// RegularManeuversForEstimationPersistenceManager persistenceManager = new
|
||||
// RegularManeuversForEstimationPersistenceManager();
|
||||
// ModelStore modelStore = new MongoDbModelStoreImpl(persistenceManager.getDb());
|
||||
LoggingUtil.logInfo("Generation a single file with all trained models started");
|
||||
ExportedModels exportedModels = new ExportedModels();
|
||||
List<ModelDomainType> nonEmptyDomainTypes = new ArrayList<>();
|
||||
for (ModelDomainType domainType : ModelDomainType.values()) {
|
||||
LoggingUtil.logInfo("Started serialization of models belonging to domain type " + domainType);
|
||||
Map<String, byte[]> exportedModelsForDomainType = modelStore.exportAllPersistedModels(domainType);
|
||||
if (!exportedModelsForDomainType.isEmpty()) {
|
||||
exportedModels.addSerializedModelsForDomainType(domainType, exportedModelsForDomainType);
|
||||
LoggingUtil.logInfo("Finished serialization of " + exportedModelsForDomainType.size()
|
||||
+ " models belonging to domain type " + domainType);
|
||||
nonEmptyDomainTypes.add(domainType);
|
||||
}
|
||||
}
|
||||
LoggingUtil.logInfo("Serializing the exported models and writing it into a single file");
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
|
||||
oos.writeObject(exportedModels);
|
||||
}
|
||||
LoggingUtil.logInfo("Models have been generated");
|
||||
LoggingUtil.logInfo("The following domain types were considered: " + nonEmptyDomainTypes);
|
||||
LoggingUtil.logInfo("File with all exported models was persisted in: " + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
package com.sap.sailing.windestimation.model;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.sap.sailing.windestimation.data.importer.AggregatedDistanceBasedTwdTransitionImporter;
|
||||
import com.sap.sailing.windestimation.data.importer.AggregatedDurationBasedTwdTransitionImporter;
|
||||
import com.sap.sailing.windestimation.data.importer.DistanceBasedTwdTransitionImporter;
|
||||
import com.sap.sailing.windestimation.data.importer.DurationBasedTwdTransitionImporter;
|
||||
import com.sap.sailing.windestimation.data.importer.ManeuverAndWindImporter;
|
||||
import com.sap.sailing.windestimation.data.importer.PolarDataImporter;
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.ManeuverForEstimationPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.RegularManeuversForEstimationPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedSingleDimensionBasedTwdTransitionPersistenceManager.AggregatedSingleDimensionType;
|
||||
import com.sap.sailing.windestimation.datavisualization.AggregatedDistanceDimensionPlot;
|
||||
import com.sap.sailing.windestimation.datavisualization.AggregatedDurationDimensionPlot;
|
||||
import com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifierTrainer;
|
||||
import com.sap.sailing.windestimation.model.classifier.maneuver.PersistedManeuverClassifiersScorePrinter;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext.DistanceValueRange;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext.DurationValueRange;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class SimpleModelsTrainingPart1 {
|
||||
|
||||
private static final int NUMBER_OF_THREADS = 3;
|
||||
private static ExecutorService executorService;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new ManeuverForEstimationPersistenceManager().dropCollection();
|
||||
new RegularManeuversForEstimationPersistenceManager().dropCollection();
|
||||
executeInThreadPool(() -> PolarDataImporter.main(args));
|
||||
executeInThreadPool(() -> ManeuverAndWindImporter.main(args));
|
||||
awaitThreadPoolCompletion();
|
||||
executeInThreadPool(() -> {
|
||||
ManeuverClassifierTrainer.main(args);
|
||||
Thread.sleep(1000);
|
||||
PersistedManeuverClassifiersScorePrinter.main(args);
|
||||
});
|
||||
executeInThreadPool(() -> {
|
||||
DistanceBasedTwdTransitionImporter.main(args);
|
||||
});
|
||||
executeInThreadPool(() -> {
|
||||
DurationBasedTwdTransitionImporter.main(args);
|
||||
});
|
||||
awaitThreadPoolCompletion();
|
||||
AggregatedDurationBasedTwdTransitionImporter.createPersistenceManagerAndEnsureIndex();
|
||||
AggregatedDistanceBasedTwdTransitionImporter.createPersistenceManagerAndEnsureIndex();
|
||||
executeInThreadPool(() -> {
|
||||
AggregatedDurationBasedTwdTransitionImporter.main(args);
|
||||
});
|
||||
executeInThreadPool(() -> {
|
||||
AggregatedDistanceBasedTwdTransitionImporter.main(args);
|
||||
});
|
||||
awaitThreadPoolCompletion();
|
||||
do {
|
||||
AggregatedDurationDimensionPlot.main(args);
|
||||
showInfoAboutDataCleaning(AggregatedSingleDimensionType.DURATION);
|
||||
AggregatedDurationDimensionPlot.awaitWindowClosed();
|
||||
} while (JOptionPane.YES_OPTION != askDataCleaningFinished(AggregatedSingleDimensionType.DURATION));
|
||||
showInfoAboutIntervalAdjustments(DurationBasedTwdTransitionRegressorModelContext.class,
|
||||
DurationValueRange.class);
|
||||
do {
|
||||
AggregatedDistanceDimensionPlot.main(args);
|
||||
showInfoAboutDataCleaning(AggregatedSingleDimensionType.DISTANCE);
|
||||
AggregatedDistanceDimensionPlot.awaitWindowClosed();
|
||||
} while (JOptionPane.YES_OPTION != askDataCleaningFinished(AggregatedSingleDimensionType.DISTANCE));
|
||||
showInfoAboutIntervalAdjustments(DistanceBasedTwdTransitionRegressorModelContext.class,
|
||||
DistanceValueRange.class);
|
||||
showInfoAboutRunPart2();
|
||||
}
|
||||
|
||||
private static int askDataCleaningFinished(AggregatedSingleDimensionType dimension) {
|
||||
Object[] options = { "Continue with model training", "Restart Graphical Tool" };
|
||||
int res = JOptionPane.showOptionDialog(null,
|
||||
"Have you finished the data cleansing for the " + dimension + " dimension?",
|
||||
"Continue with model training?", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null,
|
||||
options, options[0]);
|
||||
switch (res) {
|
||||
case 0:
|
||||
return JOptionPane.YES_OPTION;
|
||||
case 1:
|
||||
return JOptionPane.NO_OPTION;
|
||||
}
|
||||
return JOptionPane.CLOSED_OPTION;
|
||||
}
|
||||
|
||||
private static void showInfoAboutRunPart2() {
|
||||
JOptionPane.showMessageDialog(null, "Now, run the class \"" + SimpleModelsTrainingPart2.class.getName()
|
||||
+ "\".\nThis will complete the training process.");
|
||||
}
|
||||
|
||||
private static void showInfoAboutDataCleaning(AggregatedSingleDimensionType dimension) {
|
||||
JOptionPane.showMessageDialog(null, "Now, clean the data for the " + dimension
|
||||
+ " dimension.\nRemove instances from MongoDB collection \"" + dimension.getCollectioName()
|
||||
+ "\"\nwhich do not make sense and cause implausible zig zag sections\nwithin \"Zero mean sigma\" curve.\n\nClose the graphical tool, when you are done to resume the model training.");
|
||||
}
|
||||
|
||||
private static void showInfoAboutIntervalAdjustments(Class<?> classToAdjust, Class<?> valueRangeEnum) {
|
||||
JOptionPane.showMessageDialog(null, "Now, open the source code of the class \"" + classToAdjust.getName()
|
||||
+ "\".\nScroll down to the definition of the inner enum \"" + valueRangeEnum.getSimpleName()
|
||||
+ "\",\nread its JavaDoc and adjust its interval definitions so that\neach interval can be learned by the adjusted regressor model configuration\nwith minimal error.\n\nPress OK after you are done.");
|
||||
}
|
||||
|
||||
private static void awaitThreadPoolCompletion() throws InterruptedException {
|
||||
executorService.shutdown();
|
||||
boolean success = executorService.awaitTermination(24, TimeUnit.HOURS);
|
||||
if (!success) {
|
||||
new InterruptedException("Thread-pool was terminated after 24 hours waiting time");
|
||||
}
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
|
||||
private static void executeInThreadPool(RunnableWithExceptionsCatch runnable) {
|
||||
if (executorService == null || executorService.isShutdown()) {
|
||||
createNewThreadPool();
|
||||
}
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
System.exit(1);
|
||||
LoggingUtil.logInfo("FAILURE: Caught unexpected exception. Model training aborted");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void createNewThreadPool() {
|
||||
executorService = new ThreadPoolExecutor(NUMBER_OF_THREADS, NUMBER_OF_THREADS, 0, TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<>(NUMBER_OF_THREADS), new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
private static interface RunnableWithExceptionsCatch {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.sap.sailing.windestimation.model;
|
||||
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionStdRegressorTrainer;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionStdRegressorTrainer;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class SimpleModelsTrainingPart2 {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
DurationBasedTwdTransitionStdRegressorTrainer.main(args);
|
||||
DistanceBasedTwdTransitionStdRegressorTrainer.main(args);
|
||||
Thread.sleep(1000);
|
||||
ExportedModelsGenerator.main(args);
|
||||
LoggingUtil.logInfo("Model training finished. You can upload the generated file to a server instance.");
|
||||
}
|
||||
|
||||
}
|
||||
+2
-4
@@ -19,9 +19,9 @@ import com.sap.sailing.windestimation.data.persistence.maneuver.TransformedManeu
|
||||
import com.sap.sailing.windestimation.data.persistence.polars.PolarDataServiceAccessUtil;
|
||||
import com.sap.sailing.windestimation.model.classifier.LabelExtraction;
|
||||
import com.sap.sailing.windestimation.model.classifier.TrainableClassificationModel;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelDomainType;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
@@ -66,7 +66,6 @@ public class ManeuverClassifierTrainer {
|
||||
LoggingUtil.logInfo("Training with " + trainManeuvers.size() + " maneuvers...");
|
||||
double[][] x = modelContext.getXMatrix(maneuvers);
|
||||
int[] y = labelExtraction.getYVector(maneuvers);
|
||||
classifierModel.resetTrainingStats();
|
||||
classifierModel.train(x, y);
|
||||
LoggingUtil.logInfo("Training finished. Validating on train dataset...");
|
||||
ManeuverClassifierScoring classifierScoring = new ManeuverClassifierScoring(classifierModel);
|
||||
@@ -112,8 +111,7 @@ public class ManeuverClassifierTrainer {
|
||||
public static void main(String[] args) throws Exception {
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
RegularManeuversForEstimationPersistenceManager persistenceManager = new RegularManeuversForEstimationPersistenceManager();
|
||||
ModelStore classifierModelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// ModelStore classifierModelStore = new MongoDbModelStore(persistenceManager.getDb());
|
||||
ModelStore classifierModelStore = new MongoDbModelStoreImpl(persistenceManager.getDb());
|
||||
classifierModelStore.deleteAll(ModelDomainType.MANEUVER_CLASSIFIER);
|
||||
ManeuverClassifierTrainer classifierTrainer = new ManeuverClassifierTrainer(persistenceManager,
|
||||
classifierModelStore);
|
||||
|
||||
+4
-4
@@ -13,11 +13,12 @@ import java.util.regex.Pattern;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.windestimation.data.ManeuverForEstimation;
|
||||
import com.sap.sailing.windestimation.data.persistence.maneuver.RegularManeuversForEstimationPersistenceManager;
|
||||
import com.sap.sailing.windestimation.data.persistence.polars.PolarDataServiceAccessUtil;
|
||||
import com.sap.sailing.windestimation.model.classifier.TrainableClassificationModel;
|
||||
import com.sap.sailing.windestimation.model.exception.ModelPersistenceException;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.util.LoggingUtil;
|
||||
|
||||
public class PersistedManeuverClassifiersScorePrinter {
|
||||
@@ -29,9 +30,8 @@ public class PersistedManeuverClassifiersScorePrinter {
|
||||
throws MalformedURLException, ClassNotFoundException, IOException, InterruptedException {
|
||||
PolarDataService polarService = PolarDataServiceAccessUtil.getPersistedPolarService();
|
||||
Set<BoatClass> allBoatClasses = polarService.getAllBoatClassesWithPolarSheetsAvailable();
|
||||
ModelStore classifierModelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// ModelStore classifierModelStore = new MongoDbModelStore(
|
||||
// new RegularManeuversForEstimationPersistenceManager().getDb());
|
||||
ModelStore classifierModelStore = new MongoDbModelStoreImpl(
|
||||
new RegularManeuversForEstimationPersistenceManager().getDb());
|
||||
List<TrainableClassificationModel<ManeuverForEstimation, ManeuverClassifierModelContext>> allClassifierModels = new ArrayList<>();
|
||||
ManeuverClassifierModelFactory classifierModelFactory = new ManeuverClassifierModelFactory();
|
||||
LoggingUtil.logInfo("### Loading all boat class classifiers:");
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ public class TwdTransitionClassifierTrainer {
|
||||
i++;
|
||||
}
|
||||
LoggingUtil.logInfo("Training with " + numberOfTrainingInstances + " instances...");
|
||||
classifierModel.resetTrainingStats();
|
||||
classifierModel.train(x, y);
|
||||
LoggingUtil.logInfo("Training finished. Validating on train dataset...");
|
||||
TwdTransitionClassifierScoring classifierScoring = new TwdTransitionClassifierScoring(classifierModel);
|
||||
|
||||
+2
-3
@@ -5,9 +5,9 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedS
|
||||
import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedSingleDimensionBasedTwdTransitionPersistenceManager.AggregatedSingleDimensionType;
|
||||
import com.sap.sailing.windestimation.model.regressor.IncrementalSingleDimensionPolynomialRegressor;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext.DistanceValueRange;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelDomainType;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
|
||||
/**
|
||||
* Trains TWD delta standard deviation by considering the distance passed between two measurements.
|
||||
@@ -26,8 +26,7 @@ public class DistanceBasedTwdTransitionStdRegressorTrainer extends TwdTransition
|
||||
public static void main(String[] args) throws Exception {
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager distanceBasedPersistenceManager = new AggregatedSingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
AggregatedSingleDimensionType.DISTANCE);
|
||||
ModelStore modelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// ModelStore modelStore = new MongoDbModelStore(distanceBasedPersistenceManager.getDb());
|
||||
ModelStore modelStore = new MongoDbModelStoreImpl(distanceBasedPersistenceManager.getDb());
|
||||
modelStore.deleteAll(ModelDomainType.DISTANCE_BASED_TWD_DELTA_STD_REGRESSOR);
|
||||
DistanceBasedTwdTransitionRegressorModelFactory distanceBasedTwdTransitionRegressorModelFactory = new DistanceBasedTwdTransitionRegressorModelFactory();
|
||||
for (DistanceValueRange distanceValueRange : DistanceValueRange.values()) {
|
||||
|
||||
+2
-3
@@ -5,9 +5,9 @@ import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedS
|
||||
import com.sap.sailing.windestimation.data.persistence.twdtransition.AggregatedSingleDimensionBasedTwdTransitionPersistenceManager.AggregatedSingleDimensionType;
|
||||
import com.sap.sailing.windestimation.model.regressor.IncrementalSingleDimensionPolynomialRegressor;
|
||||
import com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext.DurationValueRange;
|
||||
import com.sap.sailing.windestimation.model.store.FileSystemModelStoreImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ModelDomainType;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
|
||||
public class DurationBasedTwdTransitionStdRegressorTrainer extends TwdTransitionAggregatedStdRegressorTrainer {
|
||||
|
||||
@@ -20,8 +20,7 @@ public class DurationBasedTwdTransitionStdRegressorTrainer extends TwdTransition
|
||||
public static void main(String[] args) throws Exception {
|
||||
AggregatedSingleDimensionBasedTwdTransitionPersistenceManager durationBasedPersistenceManager = new AggregatedSingleDimensionBasedTwdTransitionPersistenceManager(
|
||||
AggregatedSingleDimensionType.DURATION);
|
||||
ModelStore modelStore = new FileSystemModelStoreImpl("trained_wind_estimation_models");
|
||||
// ModelStore modelStore = new MongoDbModelStoreImpl(durationBasedPersistenceManager.getDb());
|
||||
ModelStore modelStore = new MongoDbModelStoreImpl(durationBasedPersistenceManager.getDb());
|
||||
modelStore.deleteAll(ModelDomainType.DURATION_BASED_TWD_DELTA_STD_REGRESSOR);
|
||||
DurationBasedTwdTransitionRegressorModelFactory durationBasedTwdTransitionRegressorModelFactory = new DurationBasedTwdTransitionRegressorModelFactory();
|
||||
for (DurationValueRange durationValueRange : DurationValueRange.values()) {
|
||||
|
||||
+3
@@ -41,6 +41,9 @@ public class IncrementalSingleDimensionPolynomialRegressorTrainerHelper {
|
||||
}
|
||||
|
||||
public void incrementRmseCalculation(double[] x, double y) {
|
||||
if (!model.isModelReady()) {
|
||||
model.setModelAsReadyAfterSuccessfulTraining();
|
||||
}
|
||||
double predictedStd = model.getValue(x);
|
||||
double diff = predictedStd - y;
|
||||
squareErrorSum += diff * diff;
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@ public class TwdTransitionManualTrainingDataInputRegressorTrainer {
|
||||
throws Exception {
|
||||
IncrementalSingleDimensionPolynomialRegressorTrainerHelper trainerHelper = new IncrementalSingleDimensionPolynomialRegressorTrainerHelper(
|
||||
regressorModelStore, model);
|
||||
LoggingUtil
|
||||
.logInfo("########## Training of " + model.getModelContext().getId() + " started...");
|
||||
LoggingUtil.logInfo("########## Training of " + model.getModelContext().getId() + " started...");
|
||||
double[] modelInput = new double[1];
|
||||
for (int i = 0; i < inputOutputPairs.length; i++) {
|
||||
double xi = inputOutputPairs[i][0];
|
||||
|
||||
+4
-3
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sailing.domain.windestimation.TimePointAndPositionWithToleranceComparator;
|
||||
import com.sap.sailing.windestimation.ManeuverBasedWindEstimationComponentImpl;
|
||||
import com.sap.sailing.windestimation.aggregator.ManeuverClassificationsAggregatorFactory;
|
||||
import com.sap.sailing.windestimation.data.CompetitorTrackWithEstimationData;
|
||||
@@ -75,14 +76,14 @@ public class IncrementalMstHmmWindEstimationForTrackedRaceTest extends OnlineTra
|
||||
"SERIALIZATION.modelForManeuverClassifier.NeuralNetworkClassifier.ManeuverClassification-Basic-All.clf" };
|
||||
|
||||
protected final SimpleDateFormat dateFormat;
|
||||
private final WindEstimationFactoryServiceImpl windEstimationFactoryService;
|
||||
private WindEstimationFactoryServiceImpl windEstimationFactoryService;
|
||||
private ClassPathReadOnlyModelStoreImpl modelStore;
|
||||
|
||||
public IncrementalMstHmmWindEstimationForTrackedRaceTest()
|
||||
throws MalformedURLException, URISyntaxException, ModelPersistenceException {
|
||||
public IncrementalMstHmmWindEstimationForTrackedRaceTest() throws Exception {
|
||||
dateFormat = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+2")); // will result in CEST
|
||||
windEstimationFactoryService = new WindEstimationFactoryServiceImpl();
|
||||
windEstimationFactoryService.clearState();
|
||||
modelStore = new ClassPathReadOnlyModelStoreImpl("trained_wind_estimation_models", getClass().getClassLoader(),
|
||||
modelFilesNames);
|
||||
windEstimationFactoryService.importAllModelsFromModelStore(modelStore);
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
|
||||
import com.sap.sailing.domain.tracking.CompleteManeuverCurve;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sailing.domain.windestimation.TimePointAndPositionWithToleranceComparator;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.DistanceAndDurationAwareWindTransitionProbabilitiesCalculator;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.MstGraphLevel;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.MstManeuverGraphGenerator.MstManeuverGraphComponents;
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.sap.sailing.windestimation.jaxrs.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.windestimation.integration.ExportedModels;
|
||||
import com.sap.sailing.windestimation.integration.ReplicableWindEstimationFactoryService;
|
||||
import com.sap.sailing.windestimation.jaxrs.client.WindEstimationDataClient;
|
||||
import com.sap.sailing.windestimation.model.store.ModelDomainType;
|
||||
import com.sap.sailing.windestimation.model.store.ModelStore;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class WindEstimationDataClientMock extends WindEstimationDataClient {
|
||||
|
||||
private final ModelStore modelStore;
|
||||
|
||||
public WindEstimationDataClientMock(ModelStore modelStore,
|
||||
ReplicableWindEstimationFactoryService windEstimationFactoryService) {
|
||||
super(null, windEstimationFactoryService);
|
||||
this.modelStore = modelStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getContentFromResponse() throws IOException, ParseException {
|
||||
ExportedModels exportedModels = new ExportedModels();
|
||||
for (ModelDomainType domainType : ModelDomainType.values()) {
|
||||
Map<String, byte[]> exportedModelsForDomainType = modelStore.exportAllPersistedModels(domainType);
|
||||
exportedModels.addSerializedModelsForDomainType(domainType, exportedModelsForDomainType);
|
||||
}
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) {
|
||||
oos.writeObject(exportedModels);
|
||||
}
|
||||
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.sap.sailing.windestimation.jaxrs.api;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.windestimation.integration.IncrementalMstHmmWindEstimationForTrackedRaceTest;
|
||||
import com.sap.sailing.windestimation.integration.WindEstimationFactoryServiceImpl;
|
||||
import com.sap.sailing.windestimation.model.store.ClassPathReadOnlyModelStoreImpl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class WindEstimationDataResourceTest {
|
||||
|
||||
@Test
|
||||
public void testImportingFromMockFile() throws Exception {
|
||||
WindEstimationFactoryServiceImpl windEstimationFactoryService = new WindEstimationFactoryServiceImpl();
|
||||
windEstimationFactoryService.clearState();
|
||||
assertFalse(windEstimationFactoryService.isReady());
|
||||
ClassPathReadOnlyModelStoreImpl modelStore = new ClassPathReadOnlyModelStoreImpl(
|
||||
"trained_wind_estimation_models", getClass().getClassLoader(),
|
||||
IncrementalMstHmmWindEstimationForTrackedRaceTest.modelFilesNames);
|
||||
final WindEstimationDataClientMock client = new WindEstimationDataClientMock(modelStore,
|
||||
windEstimationFactoryService);
|
||||
client.updateWindEstimationModels();
|
||||
assertTrue(windEstimationFactoryService.isReady());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="resources"/>
|
||||
<classpathentry kind="lib" path="lib/smile-core-1.5.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/smile-data-1.5.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/smile-graph-1.5.2.jar"/>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
The library smile-score-1.5.2.jar has been patched!!!
|
||||
The class files smile.feature.FeatureTransform and smile.feature.Standardizer
|
||||
were removed from jar in order to overwrite those classes and make them Serializable
|
||||
(see com.sap.sailing.windestimation/src/smile/feature/). The removal was necessary
|
||||
due to OSGI environment. The next release version of SMILE will already include
|
||||
Serialization support for the aforementioned classes, which means that the patch will
|
||||
be obsolete. When 1.5.3 comes out, replace the 1.5.2 jars with the official 1.5.3 jars.
|
||||
Furthermore, delete com.sap.sailing.windestimation/src/smile folder with patched
|
||||
source files.
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
[main]
|
||||
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
|
||||
credentialsMatcher.storedCredentialsHexEncoded = false
|
||||
credentialsMatcher.hashIterations = 1024
|
||||
|
||||
# Realm configuration:
|
||||
# --------------------
|
||||
|
||||
permissionsForRoleProvider = com.sap.sailing.domain.common.security.SailingPermissionsForRoleProvider
|
||||
upRealm = com.sap.sse.security.UsernamePasswordRealm
|
||||
upRealm.credentialsMatcher = $credentialsMatcher
|
||||
upRealm.permissionsForRoleProvider = $permissionsForRoleProvider
|
||||
oauthRealm = com.sap.sse.security.OAuthRealm
|
||||
oauthRealm.permissionsForRoleProvider = $permissionsForRoleProvider
|
||||
bearerTokenRealm = com.sap.sse.security.BearerTokenRealm
|
||||
bearerTokenRealm.permissionsForRoleProvider = $permissionsForRoleProvider
|
||||
|
||||
# Putting it all together:
|
||||
sessionManager = com.sap.sse.security.SecurityWebSessionManager
|
||||
securityManager.sessionManager = $sessionManager
|
||||
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
|
||||
securityManager.sessionManager.sessionDAO = $sessionDAO
|
||||
subjectDAO = com.sap.sse.security.NoSessionStorageSessionDAO
|
||||
securityManager.subjectDAO = $subjectDAO
|
||||
# 1y timeout
|
||||
securityManager.sessionManager.globalSessionTimeout = 31536000000
|
||||
cacheManager = com.sap.sse.security.SessionCacheManager
|
||||
securityManager.cacheManager = $cacheManager
|
||||
|
||||
# Authentication Filter Configurations
|
||||
# ------------------------------------
|
||||
|
||||
# A filter that requires the user to have at least one of the roles specified as
|
||||
# filter parameter. This makes it different from the default roles[...] filter
|
||||
# which requires the user to have ALL of the roles specified as parameters.
|
||||
anyofroles = com.sap.sse.security.AnyOfRolesFilter
|
||||
|
||||
# A custom filter for GWT pages that require an authenticated user.
|
||||
# Forwards unauthenticated users to the default login page. When
|
||||
# addressed directly (and not based on a redirect), users who logged
|
||||
# on successfully will be redirected to the UserManagement.html page
|
||||
# where they can adjust their profile.
|
||||
customGwt = com.sap.sse.security.CustomFilter
|
||||
customGwt.loginUrl = /security/ui/Login.html
|
||||
customGwt.successUrl = /UserManagement.html
|
||||
|
||||
# Configuration for the default shiro HTTP form authentication filter.
|
||||
# It assumes that form-based login parameters are sent to the
|
||||
# /api/restsecurity/login RESTlet and forwards successful login attempts
|
||||
# to the /api/restsecurity/hello service which is expected to respond
|
||||
# with a JSON document containing the authenticated subject's properties.
|
||||
authc.loginUrl = /api/restsecurity/login
|
||||
authc.successUrl = /api/restsecurity/hello
|
||||
|
||||
# This authentication filter accepts a bearer access token in the HTTP
|
||||
# Authorization header field, as in
|
||||
# Authorization: Bearer 1029741026501365024376093245
|
||||
# If no such bearer token is provided, the filter falls back to basic HTTP
|
||||
# authentication ("Authentication: Basic ...") and then regular form-based
|
||||
# authentication with POST parameters "username" and "password".
|
||||
bearerToken = com.sap.sse.security.BearerTokenOrBasicOrFormAuthenticationFilter
|
||||
|
||||
# Specifying filter chains for URL patterns
|
||||
|
||||
[urls]
|
||||
# Uncomment the following to require the permission "event:view" in order to use /api/v1/events:
|
||||
#/api/v1/events = bearerToken
|
||||
|
||||
# Example: Secure access to leaderboard race column factor updates; read access should require no permissions
|
||||
#/api/v1/leaderboards/*/racecolumnfactors/** = bearerToken
|
||||
|
||||
# Example: Secure the access (in particular the POST/write access) to posting race log events
|
||||
#/rc/racelog = bearerToken
|
||||
+28
-1
@@ -3,6 +3,7 @@ package com.sap.sailing.windestimation.integration;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -105,7 +106,7 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera
|
||||
|
||||
@Override
|
||||
public ObjectInputStream createObjectInputStreamResolvingAgainstCache(InputStream is) throws IOException {
|
||||
ObjectInputStream ois = new ObjectInputStream(is);
|
||||
ObjectInputStream ois = new ObjectInputStreamWithContextClassLoaderAsResolver(is);
|
||||
return ois;
|
||||
}
|
||||
|
||||
@@ -139,4 +140,30 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera
|
||||
this.operationsSentToMasterForReplication.add(operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link ObjectInputStream} which is necessary in OSGI context in order to load the class
|
||||
* {@link WindEstimationModelsUpdateOperation} without {@link ClassNotFoundException}.
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
private class ObjectInputStreamWithContextClassLoaderAsResolver extends ObjectInputStream {
|
||||
|
||||
public ObjectInputStreamWithContextClassLoaderAsResolver(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
|
||||
String name = desc.getName();
|
||||
try {
|
||||
return Class.forName(name, /* initialize */true, Thread.currentThread().getContextClassLoader());
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// fall back on super class in order to load primitive types
|
||||
return super.resolveClass(desc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -88,10 +88,10 @@ public class Activator implements BundleActivator {
|
||||
|
||||
private void importWindEstimationModelsFromUrl(String windEstimationModelDataSourceURL) {
|
||||
logger.info("Importing wind estimation data from URL: " + windEstimationModelDataSourceURL);
|
||||
WindEstimationDataClient polarDataClient = new WindEstimationDataClient(windEstimationModelDataSourceURL,
|
||||
service);
|
||||
WindEstimationDataClient windEstimationDataClient = new WindEstimationDataClient(
|
||||
windEstimationModelDataSourceURL, service);
|
||||
try {
|
||||
polarDataClient.updateWindEstimationModels();
|
||||
windEstimationDataClient.updateWindEstimationModels();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Exception while trying to import wind estimation model data from "
|
||||
+ windEstimationModelDataSourceURL, e);
|
||||
|
||||
+17
-17
@@ -17,9 +17,11 @@ import com.sap.sailing.domain.maneuverdetection.TrackTimeInfo;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.tracking.CompleteManeuverCurve;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sailing.domain.windestimation.TimePointAndPositionWithToleranceComparator;
|
||||
import com.sap.sailing.domain.windestimation.WindTrackWithConfidenceForEachWindFixImpl;
|
||||
import com.sap.sailing.windestimation.aggregator.hmm.GraphLevelInference;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.DistanceAndDurationAwareWindTransitionProbabilitiesCalculator;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.MstBestPathsCalculator;
|
||||
@@ -48,10 +50,8 @@ import com.sap.sse.common.Util.Pair;
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public class IncrementalMstHmmWindEstimationForTrackedRace extends WindTrackImpl
|
||||
implements IncrementalWindEstimationTrack {
|
||||
public class IncrementalMstHmmWindEstimationForTrackedRace implements IncrementalWindEstimation {
|
||||
|
||||
private static final long serialVersionUID = 6654969432545619955L;
|
||||
private static final double WIND_COURSE_TOLERANCE_IN_DEGREES_TO_IGNORE_FOR_REUSE = 1.0;
|
||||
private static final double DEFAULT_BASE_CONFIDENCE = 0.01;
|
||||
private final IncrementalMstManeuverGraphGenerator mstManeuverGraphGenerator;
|
||||
@@ -60,15 +60,17 @@ public class IncrementalMstHmmWindEstimationForTrackedRace extends WindTrackImpl
|
||||
private final Map<Pair<Position, TimePoint>, WindWithConfidence<Pair<Position, TimePoint>>> windTrackWithConfidences = new TreeMap<>(
|
||||
new TimePointAndPositionWithToleranceComparator());
|
||||
private final TrackedRace trackedRace;
|
||||
private WindSource windSource;
|
||||
private final WindSource windSource;
|
||||
private final WindTrackWithConfidenceForEachWindFixImpl estimatedWindTrack;
|
||||
|
||||
public IncrementalMstHmmWindEstimationForTrackedRace(TrackedRace trackedRace, WindSource windSource,
|
||||
PolarDataService polarDataService, long millisecondsOverWhichToAverage,
|
||||
ManeuverClassifiersCache maneuverClassifiersCache,
|
||||
GaussianBasedTwdTransitionDistributionCache gaussianBasedTwdTransitionDistributionCache) {
|
||||
super(millisecondsOverWhichToAverage, DEFAULT_BASE_CONFIDENCE,
|
||||
this.estimatedWindTrack = new WindTrackWithConfidenceForEachWindFixImpl(millisecondsOverWhichToAverage,
|
||||
DEFAULT_BASE_CONFIDENCE,
|
||||
WindSourceType.MANEUVER_BASED_ESTIMATION.useSpeed() && polarDataService != null,
|
||||
IncrementalMstHmmWindEstimationForTrackedRace.class.getName(), false);
|
||||
IncrementalMstHmmWindEstimationForTrackedRace.class.getName(), false, windTrackWithConfidences);
|
||||
this.trackedRace = trackedRace;
|
||||
this.windSource = windSource;
|
||||
DistanceAndDurationAwareWindTransitionProbabilitiesCalculator transitionProbabilitiesCalculator = new DistanceAndDurationAwareWindTransitionProbabilitiesCalculator(
|
||||
@@ -82,11 +84,16 @@ public class IncrementalMstHmmWindEstimationForTrackedRace extends WindTrackImpl
|
||||
: new PolarsBasedTwsCalculatorImpl(polarDataService));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getWindTrack() {
|
||||
return estimatedWindTrack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newManeuverSpotsDetected(Competitor competitor, Iterable<CompleteManeuverCurve> newManeuvers,
|
||||
TrackTimeInfo trackTimeInfo) {
|
||||
List<ManeuverWithEstimatedType> maneuversWithEstimatedType = new ArrayList<>();
|
||||
lockForWrite();
|
||||
estimatedWindTrack.lockForWrite();
|
||||
try {
|
||||
for (CompleteManeuverCurve newManeuverSpot : newManeuvers) {
|
||||
mstManeuverGraphGenerator.add(competitor, newManeuverSpot, trackTimeInfo);
|
||||
@@ -134,7 +141,7 @@ public class IncrementalMstHmmWindEstimationForTrackedRace extends WindTrackImpl
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlockAfterWrite();
|
||||
estimatedWindTrack.unlockAfterWrite();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,13 +153,6 @@ public class IncrementalMstHmmWindEstimationForTrackedRace extends WindTrackImpl
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getConfidenceOfInternalWindFixUnsynchronized(Wind windFix) {
|
||||
WindWithConfidence<Pair<Position, TimePoint>> windWithConfidence = windTrackWithConfidences
|
||||
.get(new Pair<>(windFix.getPosition(), windFix.getTimePoint()));
|
||||
return super.getConfidenceOfInternalWindFixUnsynchronized(windFix) * windWithConfidence.getConfidence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindSource getWindSource() {
|
||||
return windSource;
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ package com.sap.sailing.windestimation.integration;
|
||||
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.windestimation.WindEstimationFactoryService;
|
||||
import com.sap.sailing.windestimation.model.exception.ModelPersistenceException;
|
||||
import com.sap.sse.replication.ReplicableWithObjectInputStream;
|
||||
|
||||
/**
|
||||
@@ -11,5 +12,7 @@ import com.sap.sse.replication.ReplicableWithObjectInputStream;
|
||||
*
|
||||
*/
|
||||
public interface ReplicableWindEstimationFactoryService extends WindEstimationFactoryService,
|
||||
ReplicableWithObjectInputStream<WindEstimationFactoryServiceImpl, WindEstimationModelsUpdateOperation> {
|
||||
ReplicableWithObjectInputStream<ReplicableWindEstimationFactoryService, WindEstimationModelsUpdateOperation> {
|
||||
|
||||
void updateWindEstimationModels(ExportedModels exportedModels) throws ModelPersistenceException;
|
||||
}
|
||||
+16
-9
@@ -11,7 +11,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimationTrack;
|
||||
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
|
||||
import com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifiersCache;
|
||||
import com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverFeatures;
|
||||
import com.sap.sailing.windestimation.model.exception.ModelPersistenceException;
|
||||
@@ -23,7 +23,7 @@ import com.sap.sailing.windestimation.model.store.MongoDbModelStoreImpl;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
|
||||
public class WindEstimationFactoryServiceImpl extends
|
||||
AbstractReplicableWithObjectInputStream<WindEstimationFactoryServiceImpl, WindEstimationModelsUpdateOperation>
|
||||
AbstractReplicableWithObjectInputStream<ReplicableWindEstimationFactoryService, WindEstimationModelsUpdateOperation>
|
||||
implements ReplicableWindEstimationFactoryService {
|
||||
|
||||
private static final boolean PRELOAD_ALL_MODELS = true;
|
||||
@@ -52,6 +52,7 @@ public class WindEstimationFactoryServiceImpl extends
|
||||
PRESERVE_LOADED_MODELS_MILLIS, MAX_MANEUVER_FEATURES);
|
||||
gaussianBasedTwdTransitionDistributionCache = new GaussianBasedTwdTransitionDistributionCache(MODEL_STORE,
|
||||
PRELOAD_ALL_MODELS, PRESERVE_LOADED_MODELS_MILLIS);
|
||||
modelsReady = maneuverClassifiersCache.isReady() && gaussianBasedTwdTransitionDistributionCache.isReady();
|
||||
}
|
||||
|
||||
private boolean isMaster() {
|
||||
@@ -59,8 +60,8 @@ public class WindEstimationFactoryServiceImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public IncrementalWindEstimationTrack createIncrementalWindEstimationTrack(TrackedRace trackedRace) {
|
||||
IncrementalWindEstimationTrack windEstimation = new IncrementalMstHmmWindEstimationForTrackedRace(trackedRace,
|
||||
public IncrementalWindEstimation createIncrementalWindEstimationTrack(TrackedRace trackedRace) {
|
||||
IncrementalWindEstimation windEstimation = new IncrementalMstHmmWindEstimationForTrackedRace(trackedRace,
|
||||
new WindSourceImpl(WindSourceType.MANEUVER_BASED_ESTIMATION), trackedRace.getPolarDataService(),
|
||||
trackedRace.getMillisecondsOverWhichToAverageWind(), maneuverClassifiersCache,
|
||||
gaussianBasedTwdTransitionDistributionCache);
|
||||
@@ -79,15 +80,17 @@ public class WindEstimationFactoryServiceImpl extends
|
||||
updateWindEstimationModels(exportedModels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindEstimationModels(ExportedModels exportedModels) throws ModelPersistenceException {
|
||||
for (ModelDomainType domainType : modelDomainTypesRequiredByWindEstimation) {
|
||||
Map<String, byte[]> serializedModelsForDomainType = exportedModels
|
||||
.getSerializedModelsForDomainType(domainType);
|
||||
if (serializedModelsForDomainType != null) {
|
||||
MODEL_STORE.deleteAll(domainType);
|
||||
MODEL_STORE.importPersistedModels(serializedModelsForDomainType, domainType);
|
||||
}
|
||||
}
|
||||
clearState();
|
||||
reloadWindEstimationModels();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,15 +138,19 @@ public class WindEstimationFactoryServiceImpl extends
|
||||
for (ModelDomainType domainType : modelDomainTypesRequiredByWindEstimation) {
|
||||
MODEL_STORE.deleteAll(domainType);
|
||||
}
|
||||
clearState();
|
||||
reloadWindEstimationModels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearState() throws Exception {
|
||||
clearReplicaState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears/Reloads all caches with machine learning models. Notifies ready state change listeners about the new ready
|
||||
* state of this wind estimation instance.
|
||||
*/
|
||||
@Override
|
||||
public void clearState() {
|
||||
public void reloadWindEstimationModels() {
|
||||
maneuverClassifiersCache.clearCache();
|
||||
gaussianBasedTwdTransitionDistributionCache.clearCache();
|
||||
List<WindEstimationModelsChangedListener> modelsChangedListeners;
|
||||
@@ -166,7 +173,7 @@ public class WindEstimationFactoryServiceImpl extends
|
||||
MODEL_STORE.deleteAll(domainType);
|
||||
MODEL_STORE.importPersistedModels(exportedModels, domainType);
|
||||
}
|
||||
clearState();
|
||||
reloadWindEstimationModels();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+17
-4
@@ -3,13 +3,26 @@ package com.sap.sailing.windestimation.integration;
|
||||
import com.sap.sse.replication.OperationWithResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* Needed in the context of the initial replication of the wind estimation factory service.
|
||||
* Updates wind estimation models with provided exported models.
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
*/
|
||||
public interface WindEstimationModelsUpdateOperation
|
||||
extends OperationWithResult<WindEstimationFactoryServiceImpl, Void> {
|
||||
public class WindEstimationModelsUpdateOperation
|
||||
implements OperationWithResult<ReplicableWindEstimationFactoryService, Void> {
|
||||
|
||||
private static final long serialVersionUID = 6407877837605630090L;
|
||||
|
||||
private final ExportedModels exportedModels;
|
||||
|
||||
public WindEstimationModelsUpdateOperation(ExportedModels exportedModels) {
|
||||
this.exportedModels = exportedModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalApplyTo(ReplicableWindEstimationFactoryService toState) throws Exception {
|
||||
toState.updateWindEstimationModels(exportedModels);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.sap.sailing.windestimation.integration;
|
||||
|
||||
public class WindEstimationModelsUpdateOperationImpl implements WindEstimationModelsUpdateOperation {
|
||||
|
||||
private static final long serialVersionUID = 6407877837605630090L;
|
||||
|
||||
private final ExportedModels exportedModels;
|
||||
|
||||
public WindEstimationModelsUpdateOperationImpl(ExportedModels exportedModels) {
|
||||
this.exportedModels = exportedModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalApplyTo(WindEstimationFactoryServiceImpl toState) throws Exception {
|
||||
toState.updateWindEstimationModels(exportedModels);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -18,10 +18,10 @@ import com.sap.sailing.domain.common.security.Permission;
|
||||
import com.sap.sailing.domain.common.security.Permission.Mode;
|
||||
import com.sap.sailing.windestimation.integration.ExportedModels;
|
||||
import com.sap.sailing.windestimation.integration.WindEstimationModelsUpdateOperation;
|
||||
import com.sap.sailing.windestimation.integration.WindEstimationModelsUpdateOperationImpl;
|
||||
import com.sap.sailing.windestimation.jaxrs.AbstractWindEstimationDataResource;
|
||||
|
||||
/**
|
||||
* Resource which manages wind estimation model metadata. Data export and import are supported.
|
||||
*
|
||||
* @author Vladislav Chumak (D069712)
|
||||
*
|
||||
@@ -45,7 +45,7 @@ public class WindEstimationDataResource extends AbstractWindEstimationDataResour
|
||||
ObjectInputStream ois = getWindEstimationFactoryServiceImpl()
|
||||
.createObjectInputStreamResolvingAgainstCache(inputStream);
|
||||
ExportedModels exportedModels = (ExportedModels) ois.readObject();
|
||||
WindEstimationModelsUpdateOperation windEstimationModelsUpdateOperation = new WindEstimationModelsUpdateOperationImpl(
|
||||
WindEstimationModelsUpdateOperation windEstimationModelsUpdateOperation = new WindEstimationModelsUpdateOperation(
|
||||
exportedModels);
|
||||
getWindEstimationFactoryServiceImpl().apply(windEstimationModelsUpdateOperation);
|
||||
return Response.ok("Wind estimation models accepted").build();
|
||||
|
||||
+5
-1
@@ -51,12 +51,16 @@ public abstract class AbstractTrainableModel<InstanceType, MC extends ModelConte
|
||||
|
||||
@Override
|
||||
public void setStatsAfterSuccessfulTraining(double trainScore, double testScore, long numberOfTrainingInstances) {
|
||||
trainingFinished = true;
|
||||
this.trainScore = trainScore;
|
||||
this.testScore = testScore;
|
||||
this.numberOfTrainingInstances = numberOfTrainingInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModelAsReadyAfterSuccessfulTraining() {
|
||||
trainingFinished = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetTrainingStats() {
|
||||
trainingFinished = false;
|
||||
|
||||
+13
-2
@@ -46,8 +46,9 @@ public interface TrainableModel<InstanceType, MC extends ModelContext<InstanceTy
|
||||
long getNumberOfTrainingInstances();
|
||||
|
||||
/**
|
||||
* Sets the training statistics for this model and makes this model ready (see {@link #isModelReady()}) for
|
||||
* prediction tasks. This method must be called only after model training has been successfully completed.
|
||||
* Sets the training statistics for this model after successful training which should have been previously signaled
|
||||
* with {@link #setModelAsReadyAfterSuccessfulTraining()}. This method must be called only after model training has
|
||||
* been successfully completed.
|
||||
*
|
||||
* @param trainScore
|
||||
* Macro-averaged F2-Score with training data (see {@link #getTrainScore()})
|
||||
@@ -58,6 +59,16 @@ public interface TrainableModel<InstanceType, MC extends ModelContext<InstanceTy
|
||||
*/
|
||||
void setStatsAfterSuccessfulTraining(double trainScore, double testScore, long numberOfTrainingInstances);
|
||||
|
||||
/**
|
||||
* Marks this model as ready for prediction tasks. This method should be called after successful training of this
|
||||
* model. Afterwards, model evaluation should be performed to determine the training and test score of the model and
|
||||
* set it with {@link #setStatsAfterSuccessfulTraining(double, double, long)}.
|
||||
*
|
||||
* @see #isModelReady()
|
||||
* @see #setStatsAfterSuccessfulTraining(double, double, long)
|
||||
*/
|
||||
void setModelAsReadyAfterSuccessfulTraining();
|
||||
|
||||
/**
|
||||
* Resets the training stats and marks this model as not ready (see {@link #isModelReady()}). Should be called
|
||||
* before model training starts.
|
||||
|
||||
+2
@@ -38,6 +38,7 @@ public abstract class AbstractSmileClassificationModel<InstanceType, MC extends
|
||||
|
||||
@Override
|
||||
public void train(double[][] x, int[] y) {
|
||||
resetTrainingStats();
|
||||
PreprocessingConfig preprocessingConfig = getPreprocessingConfig();
|
||||
scaler = null;
|
||||
if (preprocessingConfig.isScaling()) {
|
||||
@@ -56,6 +57,7 @@ public abstract class AbstractSmileClassificationModel<InstanceType, MC extends
|
||||
x = pca.project(x);
|
||||
}
|
||||
internalModel = trainInternalModel(x, y);
|
||||
setModelAsReadyAfterSuccessfulTraining();
|
||||
}
|
||||
|
||||
protected abstract SoftClassifier<double[]> trainInternalModel(double[][] x, int[] y);
|
||||
|
||||
@@ -1,67 +1,29 @@
|
||||
# Training of internal Wind Estimation models
|
||||
|
||||
This document describes the generation process of Machine Learning (ML) models which are used internally by wind estimation. It is highly recommended to proceed this howto step by step considering the order of sections.
|
||||
|
||||
## Overview
|
||||
In total, there are the following three categories of ML models used by wind estimation:
|
||||
1. **Maneuver Classifiers**
|
||||
2. **Regressors** of TWD delta standard deviation for the dimension **duration**
|
||||
3. **Regressors** of TWD delta standard deviation for the dimension **distance**
|
||||
|
||||
Each of the model categories are composed of multiple models where each model targets a specific context. A context for a maneuver classifier is determined by the following attributes:
|
||||
* Maneuver features
|
||||
* Polar features enabled: yes/no
|
||||
* Mark features enabled: yes/no
|
||||
* Scaled speed features enabled: yes/no
|
||||
* Boat class filtering for the data on which the classifier is trained, such as a specific boat class, or with all boat classes included
|
||||
|
||||
The context of regressor models is represented by its assigned input interval responsibility, e.g. [0 seconds; 62 seconds) for duration, or [80 meters; 1368 meters) for distance.
|
||||
|
||||
Each of the ML model categories must be trained individually. The common workflow looks as follows:
|
||||
1. Get the training data from REST API of sapsailing.com
|
||||
2. Preprocess data
|
||||
3. Train the model category
|
||||
|
||||
For each of the steps, appropriate Java classes must be executed per *Run with...->Java Application*. All referenced classes are located in *com.sap.sailing.windestimation.lab* Java project. Each class execution must finish without uncaught exceptions before proceeding to next instructions. After model training, all trained models can be collected in *./trained_wind_estimation_models*, which is normally */path/to/workspace/com.sap.sailing.windestimation/trained_wind_estimation_models* if you start the training classes in Eclipse per *Run with...->Java Application*.
|
||||
|
||||
The details of the training process for each model category are described in the following sections.
|
||||
This document describes the generation process of Machine Learning (ML) models which are used internally by wind estimation. It is highly recommended to proceed this howto step by step considering the order of sections. At the end of this howto, you will generate a file containing the representation of internal models used by ``com.sap.sailing.windestimation`` bundle. You can use this file to update the wind estimation models of a running server instance. If you are interested in a more advanced tutorial which requires all the execution steps contained in ``SimpleModelsTrainingPart...`` classes to be executed manually, then you might be interested in [Advanced Guide for training of internal Wind Estimation models](./windestimationAdvanced.md)
|
||||
|
||||
## Prerequisites
|
||||
To complete the training process successfully, you need to make sure that you have the following stuff:
|
||||
* A complete onboarding setup for SAP Sailing Analytics development
|
||||
* MongoDB (**3.4 or higher!**) is up and running (same MongoDB instance as required in onboarding howto)
|
||||
* MongoDB (**3.4 or higher!**) is up and running (can be the same MongoDB instance as required in onboarding howto)
|
||||
* At least 100 GB free space on the partition, where MongoDB is operating
|
||||
* Installed graphical MongoDB client such as MongoDB Compass (Community version)
|
||||
* 16 GB RAM
|
||||
* 24+ operating hours of your computer
|
||||
|
||||
## Get the training data from sapsailing.com
|
||||
The following steps import all the data required from sapsailing.com into the local MongoDB. These steps constitute a preprequisite for training of all ML model categories:
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.ManeuverAndWindImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.PolarDataImporter*
|
||||
|
||||
## Maneuver classifiers training
|
||||
1. Run *com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifierTrainer*. Within the this step, the maneuver data is preprocessed and all maneuver classifiers are trained for each supported context.
|
||||
2. Optionally run *com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifierScoring* to print the performance of the trained classifiers. After this step, a list with macro-averaged F2-score of each trained classifier will be stored in *./maneuverClassifierScores.csv*
|
||||
|
||||
## Duration-based TWD delta standard deviation regressor
|
||||
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.DurationBasedTwdTransitionImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.AggregatedDurationBasedTwdTransitionImporter*
|
||||
3. Run *com.sap.sailing.windestimation.datavisualization.AggregatedDurationDimensionPlot* to visualize the wind data. A Swing-based GUI-Window must open with two charts, one XY-chart where the x-axis represents **seconds**, and the y-axis represents TWD delta-based series measures (e.g. standard deviation or mean). Below the chart, a histogram for data points of the XY-Chart is provided. You can zoom-in and zoom-out in each of the chart by mouse dragging. Be aware that currently the zoom level of both charts is not synchronized
|
||||
4. Open your graphical MongoDB client and connect to *windEstimation* database hosted by your local MongoDB. Open the collection with name *aggregatedDurationTwdTransition*. Within the collection you will see all the instances/data points visualized in the previous step. The total number of the points must not exceed 100.
|
||||
5. Delete all the instances within the collection which do not make sense. For this, use the data visualization tool from step 3 to identify such instances. Pay a special attention to the instances in the beginnning and end. Some of the instances are not representative due to small number of supporting instances which is visualized in the histogram. Restart the data visualization tool as often as need to visualize the changed data.
|
||||
6. Open the source code of the class *com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext*. Scroll down to the definition of the inner class/enum *DurationValueRange*. The enum defines the intervals for which a separate regressor model will be trained. Adjust the intervals accordingly in order to allow the regressor model to learn the data curve with minimal error. Make sure that there are at least 2 data points available within each interval. Datapoint with x = 0, y = 0 will be created automatically.
|
||||
7. Run *com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionStdRegressorTrainer*
|
||||
8. Verify the trained regressor functions. They are printed in the console output of the previous step. For instance, you can visualize the polynoms by means of https://www.wolframalpha.com/
|
||||
|
||||
## Distance-based TWD delta standard deviation regressor
|
||||
|
||||
The steps of this sections are similar to the steps of the previous section. It is recommended to traverse through the previous section before starting with this one, because due to similarity of the steps, the similar steps in this section are described with less details and hints.
|
||||
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.DistanceBasedTwdTransitionImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.AggregatedDistanceBasedTwdTransitionImporter* with at least 10 GB JVM memory.
|
||||
3. Run *com.sap.sailing.windestimation.datavisualization.AggregatedDistanceDimensionPlot* to visualize the wind data. Here, the x-axis of the XY-chart represents **meters**
|
||||
4. Open your graphical MongoDB client and connect to *windEstimation* database hosted by your local MongoDB. Open collection *aggregatedDistanceTwdTransition* collection. Within the collection you will see all the instances/data points visualized in the previous step. The total number of the points must not exceed 100.
|
||||
5. Delete all the instances within the collection which do not make sense.
|
||||
6. Open the source code of the class *com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext*. Scroll down to the definition of the inner class/enum *DistanceValueRange*. The enum defines the intervals for which a separate regressor model will be trained. Adjust the intervals accordingly in order to allow the regressor model to learn the data curve with minimal error.
|
||||
7. Run *com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionStdRegressorTrainer*
|
||||
8. Verify the trained regressor functions. They are printed in the console output of the previous step.
|
||||
## Model training process
|
||||
1. Run ``com.sap.sailing.windestimation.model.SimpleModelsTrainingPart1`` as a normal Java Application. After this, all the necessary maneuver and wind data will be downloaded, pre-processed and maneuver classifiers get trained.
|
||||
2. Make sure that the launched program does not get termined by an uncaught exception. Wait until graphical info dialog shows up which requests you to perform data cleansing for duration dimension.
|
||||

|
||||
Press OK. Afterwards, a graphical window must open with two charts. The top chart is an XY-chart where the x-axis represents **seconds** and the y-axis represents various TWD delta-based measures (e.g. standard deviation or mean). Below the XY-chart, a histogram for the data points of the XY-Chart is provided. You can zoom-in and zoom-out in each of the charts by mouse dragging. Be aware that currently, the zoom level of both charts is not synchronizing.
|
||||

|
||||
3. Open your graphical MongoDB client and connect to ``windEstimation`` database hosted within your local MongoDB. Open the collection with name ``aggregatedDurationTwdTransition``. Within the collection, you will see all the instances/data points visualized in the previous step. The attribute used for the x-axis is ``value``.
|
||||

|
||||
4. Delete all the instances within the collection which do not make sense. For this, use the data visualization tool from step 2 to identify such instances. Some of the instances are not representative due to the small number of supporting instances which is visualized in the histogram. Such instances can produce unreasonable bumps in the XY-chart. The desired output of this step is that the curve ``Zero mean sigma`` looks smooth and always growing, e.g. as depicted below:
|
||||

|
||||
Use the ``Refresh charts`` button as often as needed to update the charts with the modified data in MongoDB. Close the graphical visualization tool window after you are done with data cleansing to resume the training process. A confirmation dialog shows up. Confirm it by pressing *"Continue with model training"* button.
|
||||

|
||||
5. A new information dialog shows up (**do not press OK yet!**) requesting you to open the source code of the class ``com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext``. Open it and scroll down to the definition of the inner enum ``DurationValueRange``. The enum defines the intervals for which a separate regressor model will be trained. Read the Javadoc of ``DurationValueRange`` and adjust the intervals accordingly in order to allow the regressor model to learn the ``Zero mean sigma`` curve with minimal error. You can also configure the polynomial which will be used for regressor training. Make sure that there are at least 2 data points contained within each configured interval. The data point with x = 0, y = 0 will be created automatically within model training procedure. Press OK in information dialog after you are done.
|
||||
6. A graphical info dialog shows up which requests you to perform data cleansing for the *distance* dimension. Press OK. All steps for data cleansing for the distance dimension are analogous to the steps of the duration dimension described from step 2. until step 5. Thus, consult these steps in order to complete the data cleansing for the distance dimension. The unit used for the distance representation is **meters**. The collection name required in step 3. is ``aggregatedDistanceTwdTransition``. The class required in step 5. is ``com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext`` and its inner enum is ``DistanceValueRange``.
|
||||
7. Run ``com.sap.sailing.windestimation.model.SimpleModelsTrainingPart2`` as a normal Java Application. Wait until the model training finishes and the program terminates normally. A new file with serialized representation of internal wind estimation models should be located in ``./windEstimationModels.dat``. The absolute path of the file must be printed in the console output of the program. You can upload the file via HTTP POST to http://sapsailing.com/windestimation/api/windestimation_data (see ``com.sap.sailing.windestimation.jaxrs.api.WindEstimationDataResource``) to update the wind estimation of a server instance. If you changed the source files of ``DurationValueRange`` or ``DistanceValueRange``, then you will need to update ``com.sap.sailing.windestimation`` bundle of the server instance which is meant to receive the new wind estimation models.
|
||||
8. Optionally, run ``com.sap.sailing.windestimation.evaluation.WindEstimatorManeuverNumberDependentEvaluationRunner`` as normal Java Application to evaluate the wind estimation with the new trained models. The evaluation score will be stored as CSV in ``./maneuverNumberDependentEvaluation.csv``.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Training of internal Wind Estimation models
|
||||
|
||||
This document describes the generation process of Machine Learning (ML) models which are used internally by wind estimation. It is highly recommended to proceed this howto step by step considering the order of sections. At the end of this howto, you will generate a file containing the representation of internal models used by ``com.sap.sailing.windestimation`` bundle. You can use this file to update the wind estimation models of a running server instance. If you are interested in a simpler tutorial with less execution steps thanks to automation, then you might be interested in [Simple Guide for training of internal Wind Estimation models](./windestimation.md)
|
||||
|
||||
## Overview
|
||||
In total, there are the following three categories of ML models used by wind estimation:
|
||||
1. **Maneuver Classifiers**
|
||||
2. **Regressors** of TWD delta standard deviation for the dimension **duration**
|
||||
3. **Regressors** of TWD delta standard deviation for the dimension **distance**
|
||||
|
||||
Each of the model categories are composed of multiple models where each model targets a specific context. A context for a maneuver classifier is determined by the following attributes:
|
||||
* Maneuver features
|
||||
* Polar features enabled: yes/no
|
||||
* Mark features enabled: yes/no
|
||||
* Scaled speed features enabled: yes/no
|
||||
* Boat class filtering for the data on which the classifier is trained, such as a specific boat class, or with all boat classes included
|
||||
|
||||
The context of regressor models is represented by its assigned input interval responsibility, e.g. [0 seconds; 62 seconds) for duration, or [80 meters; 1368 meters) for distance.
|
||||
|
||||
Each of the ML model categories must be trained individually. The common workflow looks as follows:
|
||||
1. Get the training data from REST API of sapsailing.com
|
||||
2. Preprocess data
|
||||
3. Train the model category
|
||||
|
||||
For each of the steps, appropriate Java classes must be executed per *Run with...->Java Application*. All referenced classes are located in *com.sap.sailing.windestimation.lab* Java project. Each class execution must finish without termination due to uncaught exceptions before proceeding to next instruction. You can skip the training of a model category if you do not want to update the models on server of that category. After model training, A new file with serialized representation of internal wind estimation models should be located in ``./windEstimationModels.dat``, which is normally */path/to/workspace/com.sap.sailing.windestimation/trained_wind_estimation_models* if you start the training classes in Eclipse per *Run with...->Java Application*.
|
||||
|
||||
The details of the training process for each model category are described in the following sections.
|
||||
|
||||
## Prerequisites
|
||||
To complete the training process successfully, you need to make sure that you have the following stuff:
|
||||
* A complete onboarding setup for SAP Sailing Analytics development
|
||||
* MongoDB (**3.4 or higher!**) is up and running
|
||||
* At least 100 GB free space on the partition, where MongoDB is operating
|
||||
* Installed graphical MongoDB client such as MongoDB Compass (Community version)
|
||||
|
||||
## Get the training data from sapsailing.com
|
||||
The following steps import all the data required from sapsailing.com into the local MongoDB. These steps constitute a preprequisite for training of all ML model categories:
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.ManeuverAndWindImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.PolarDataImporter*
|
||||
|
||||
## Maneuver classifiers training
|
||||
1. Run *com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifierTrainer*. Within the this step, the maneuver data is preprocessed and all maneuver classifiers are trained for each supported context.
|
||||
2. Optionally run *com.sap.sailing.windestimation.model.classifier.maneuver.ManeuverClassifierScoring* to print the performance of the trained classifiers. After this step, a list with macro-averaged F2-score of each trained classifier will be stored in *./maneuverClassifierScores.csv*
|
||||
|
||||
## Duration-based TWD delta standard deviation regressor
|
||||
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.DurationBasedTwdTransitionImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.AggregatedDurationBasedTwdTransitionImporter*
|
||||
3. Run *com.sap.sailing.windestimation.datavisualization.AggregatedDurationDimensionPlot* to visualize the wind data. A Swing-based GUI-Window must open with two charts, one XY-chart where the x-axis represents **seconds**, and the y-axis represents TWD delta-based series measures (e.g. standard deviation or mean). Below the chart, a histogram for data points of the XY-Chart is provided. You can zoom-in and zoom-out in each of the chart by mouse dragging. Be aware that currently the zoom level of both charts is not synchronized
|
||||
4. Open your graphical MongoDB client and connect to *windEstimation* database hosted by your local MongoDB. Open the collection with name *aggregatedDurationTwdTransition*. Within the collection you will see all the instances/data points visualized in the previous step. The total number of the points must not exceed 100.
|
||||
5. Delete all the instances within the collection which do not make sense. For this, use the data visualization tool from step 3 to identify such instances. Some of the instances are not representative due to the small number of supporting instances which is visualized in the histogram. Such instances can produce unreasonable bumps in the XY-chart. The desired output of this step is that the curve ``Zero mean sigma`` looks smooth and always growing. Use the ``Refresh charts`` button as often as needed to update the charts with the modified data in MongoDB.
|
||||
6. Open the source code of the class ``com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionRegressorModelContext``. Open it and scroll down to the definition of the inner enum ``DurationValueRange``. The enum defines the intervals for which a separate regressor model will be trained. Read the Javadoc of ``DurationValueRange`` and adjust the intervals accordingly in order to allow the regressor model to learn the ``Zero mean sigma`` curve with minimal error. You can also configure the polynomial which will be used for regressor training. Make sure that there are at least 2 data points contained within each configured interval. The data point with x = 0, y = 0 will be created automatically within model training procedure.
|
||||
7. Run *com.sap.sailing.windestimation.model.regressor.twdtransition.DurationBasedTwdTransitionStdRegressorTrainer*
|
||||
8. Verify the trained regressor functions. They are printed in the console output of the previous step. For instance, you can visualize the polynoms by means of https://www.wolframalpha.com/
|
||||
|
||||
## Distance-based TWD delta standard deviation regressor
|
||||
|
||||
The steps of this sections are similar to the steps of the previous section. It is recommended to traverse through the previous section before starting with this one, because due to similarity of the steps, the similar steps in this section are described with less details and hints.
|
||||
|
||||
1. Run *com.sap.sailing.windestimation.data.importer.DistanceBasedTwdTransitionImporter*
|
||||
2. Run *com.sap.sailing.windestimation.data.importer.AggregatedDistanceBasedTwdTransitionImporter* with at least 10 GB JVM memory.
|
||||
3. Run *com.sap.sailing.windestimation.datavisualization.AggregatedDistanceDimensionPlot* to visualize the wind data. Here, the x-axis of the XY-chart represents **meters**
|
||||
4. Open your graphical MongoDB client and connect to *windEstimation* database hosted by your local MongoDB. Open collection *aggregatedDistanceTwdTransition* collection. Within the collection you will see all the instances/data points visualized in the previous step. The total number of the points must not exceed 100.
|
||||
5. Delete all the instances within the collection which do not make sense.
|
||||
6. Open the source code of the class *com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionRegressorModelContext*. Scroll down to the definition of the inner class/enum *DistanceValueRange*. The enum defines the intervals for which a separate regressor model will be trained. Adjust the intervals accordingly in order to allow the regressor model to learn the data curve with minimal error.
|
||||
7. Run *com.sap.sailing.windestimation.model.regressor.twdtransition.DistanceBasedTwdTransitionStdRegressorTrainer*
|
||||
8. Verify the trained regressor functions. They are printed in the console output of the previous step.
|
||||
|
||||
## Generate the models file
|
||||
|
||||
Within this section, all the trained models produce within previous sections are aggregated and stored as one single file.
|
||||
|
||||
1. Run ``com.sap.sailing.windestimation.model.ExportedModelsGenerator``. Wait until the model training finishes and the program terminates normally. A new file with serialized representation of internal wind estimation models should be located in ``./windEstimationModels.dat``. The absolute path of the file must be printed in the console output of the program. You can upload the file via HTTP POST to http://sapsailing.com/windestimation/api/windestimation_data (see ``com.sap.sailing.windestimation.jaxrs.api.WindEstimationDataResource``) to update the wind estimation of a server instance. If you changed the source files of ``DurationValueRange`` or ``DistanceValueRange``, then you will need to update ``com.sap.sailing.windestimation`` bundle of the server instance which is meant to receive the new wind estimation models.
|
||||
|
||||
|
||||
## Model Evaluation
|
||||
|
||||
This step is optional. However, it is recommended, to evaluate the performance of wind estimation with new trained models.
|
||||
|
||||
1. Run ``com.sap.sailing.windestimation.evaluation.WindEstimatorManeuverNumberDependentEvaluationRunner`` to evaluate the wind estimation with the new trained models. The evaluation score will be stored as CSV in ``./maneuverNumberDependentEvaluation.csv``.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Reference in new issue
Block a user