mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-06 02:52:22 +00:00
Merge branch 'master' into translation
Change-Id: I9fc0dd2c41c922acf14fe60a1018d74d405b2131
This commit is contained in:
commit
cffcfa3d41
87 files changed
+1290
-522
No files matched your search
@@ -38,7 +38,7 @@
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
|
||||
</launchConfiguration>
|
||||
+4
-3
@@ -16,7 +16,7 @@ Race=Race
|
||||
Competitor=Competitor
|
||||
Boat=Boat
|
||||
Team=Team
|
||||
CompetitorSearchTag=Competitor Search Tag
|
||||
SearchTag=Search Tag
|
||||
LeaderboardSailingDomainRetrieverChain=Leaderboards
|
||||
RaceSailingDomainRetrieverChain=Races
|
||||
LegSailingDomainRetrieverChain=Legs
|
||||
@@ -102,7 +102,7 @@ WindTrack=Wind Track
|
||||
timeSpentFoiling=Time spent foiling
|
||||
yaw=Yaw
|
||||
pitch=Pitch
|
||||
roll=Heel
|
||||
heel=Heel
|
||||
rideHeight=Ride Height
|
||||
rideHeightPort=Ride Height Port Hull
|
||||
rideHeightStarboard=Ride Height Starboard Hull
|
||||
@@ -122,4 +122,5 @@ EndsOnLegType=Type of leg at landing
|
||||
FoilingSegments=Foiled Segments
|
||||
AverageAbsoluteTrueWindAngle=Average absolute true wind angle
|
||||
AbsoluteTrueWindAngle=Absolute true wind angle (TWA)
|
||||
Wind=Wind
|
||||
Wind=Wind
|
||||
VMG=Velocity Made Good
|
||||
+4
-3
@@ -3,7 +3,7 @@ DistanceTraveled=zur
|
||||
Boat=Boot
|
||||
Team=Team
|
||||
Name=Name
|
||||
CompetitorSearchTag=Teilnehmer Such-Tag
|
||||
CompetitorSearchTag=Such-Tag
|
||||
Nationality=Nationalitäts
|
||||
Acronym=Abkürzung
|
||||
SailID=Segelnummer
|
||||
@@ -102,7 +102,7 @@ WindTrack=Windmessreihe
|
||||
timeSpentFoiling=Zeit auf Foils
|
||||
yaw=Gierung
|
||||
pitch=Neigung
|
||||
roll=Krängung
|
||||
heel=Krängung
|
||||
rideHeight=Foiling-Höhe
|
||||
rideHeightPort=Foiling-Höhe Backbord-Rumpf
|
||||
rideHeightStarboard=Foiling-Höhe Steuerbord-Rumpf
|
||||
@@ -122,4 +122,5 @@ EndsOnLegType=Bahnschenkeltyp beim Landen
|
||||
FoilingSegments=Gefoilte Abschnitte
|
||||
AverageAbsoluteTrueWindAngle=Durchschnittlicher absoluter Winkel zum wahren Wind (TWA)
|
||||
AbsoluteTrueWindAngle=Absoluter Winkel zum wahren Wind (TWA)
|
||||
Wind=Wind
|
||||
Wind=Wind
|
||||
VMG=Luv-/Leegeschwindigkeit (VMG)
|
||||
+1
-1
@@ -16,7 +16,7 @@ Race=レース
|
||||
Competitor=競技者
|
||||
Boat=艇
|
||||
Team=チーム
|
||||
CompetitorSearchTag=競技者検索タグ
|
||||
SearchTag=競技者検索タグ
|
||||
LeaderboardSailingDomainRetrieverChain=リーダーボード
|
||||
RaceSailingDomainRetrieverChain=レース
|
||||
LegSailingDomainRetrieverChain=レグ
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ Race=Гонка
|
||||
Competitor=Участник
|
||||
Boat=Лодка
|
||||
Team=Команда
|
||||
CompetitorSearchTag=Тег поиска участника
|
||||
SearchTag=Тег поиска участника
|
||||
LeaderboardSailingDomainRetrieverChain=Таблицы лидеров
|
||||
RaceSailingDomainRetrieverChain=Гонки
|
||||
LegSailingDomainRetrieverChain=Отрезки
|
||||
|
||||
+2
-2
@@ -65,8 +65,8 @@
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="Team" resname="Team">
|
||||
<source>Team</source>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="CompetitorSearchTag" resname="CompetitorSearchTag">
|
||||
<source>Competitor Search Tag</source>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="SearchTag" resname="SearchTag">
|
||||
<source>Search Tag</source>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="LeaderboardSailingDomainRetrieverChain" resname="LeaderboardSailingDomainRetrieverChain">
|
||||
<source>Leaderboards</source>
|
||||
|
||||
Regular → Executable
+2
-2
@@ -83,8 +83,8 @@
|
||||
<source>Team</source>
|
||||
<target state="signed-off">チーム</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="CompetitorSearchTag" resname="CompetitorSearchTag">
|
||||
<source>Competitor Search Tag</source>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="SearchTag" resname="SearchTag">
|
||||
<source>Search Tag</source>
|
||||
<target state="signed-off">競技者検索タグ</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="LeaderboardSailingDomainRetrieverChain" resname="LeaderboardSailingDomainRetrieverChain">
|
||||
|
||||
+2
-2
@@ -83,8 +83,8 @@
|
||||
<source>Team</source>
|
||||
<target state="signed-off">Команда</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="CompetitorSearchTag" resname="CompetitorSearchTag">
|
||||
<source>Competitor Search Tag</source>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="SearchTag" resname="SearchTag">
|
||||
<source>Search Tag</source>
|
||||
<target state="signed-off">Тег поиска участника</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="LeaderboardSailingDomainRetrieverChain" resname="LeaderboardSailingDomainRetrieverChain">
|
||||
|
||||
+2
-2
@@ -83,8 +83,8 @@
|
||||
<source>Team</source>
|
||||
<target state="signed-off">团队</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="CompetitorSearchTag" resname="CompetitorSearchTag">
|
||||
<source>Competitor Search Tag</source>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="SearchTag" resname="SearchTag">
|
||||
<source>Search Tag</source>
|
||||
<target>参赛队搜索标记</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="LeaderboardSailingDomainRetrieverChain" resname="LeaderboardSailingDomainRetrieverChain">
|
||||
|
||||
+3
-3
@@ -3,20 +3,20 @@ package com.sap.sailing.datamining;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.sap.sailing.datamining.data.ClusterFormatter;
|
||||
import com.sap.sailing.datamining.impl.data.LinearDoubleClusterGroup;
|
||||
import com.sap.sailing.datamining.impl.data.PercentageClusterFormatter;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.data.ClusterBoundary;
|
||||
import com.sap.sse.datamining.data.ClusterFormatter;
|
||||
import com.sap.sse.datamining.data.ClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparableClusterBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
|
||||
import com.sap.sse.datamining.impl.data.FixClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.LinearDoubleClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.LocalizedCluster;
|
||||
import com.sap.sse.datamining.impl.data.PercentageClusterFormatter;
|
||||
|
||||
public class SailingClusterGroups {
|
||||
|
||||
|
||||
+4
@@ -2,6 +2,7 @@ package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
@@ -29,4 +30,7 @@ public interface HasBravoFixContext extends HasWindOnTrackedLeg {
|
||||
default TimePoint getTimePoint() {
|
||||
return getBravoFix().getTimePoint();
|
||||
}
|
||||
|
||||
@Connector(messageKey="VMG")
|
||||
Speed getVelocityMadeGood();
|
||||
}
|
||||
@@ -5,8 +5,11 @@ import com.sap.sailing.domain.common.Positioned;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
|
||||
public interface HasManeuver extends Positioned, Timed {
|
||||
|
||||
@Connector
|
||||
Maneuver getManeuver();
|
||||
|
||||
@Override
|
||||
|
||||
+2
-16
@@ -3,8 +3,6 @@ package com.sap.sailing.datamining.data;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
@@ -13,27 +11,15 @@ public interface HasManeuverContext extends HasWindOnTrackedLeg, HasManeuver, Ha
|
||||
@Connector(scanForStatistics=false)
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
//TODO Clean-Up:
|
||||
// Move Dimensions and Statistics to Maneuver and connect to it
|
||||
@Dimension(messageKey="ManeuverType", ordinal=12)
|
||||
ManeuverType getManeuverType();
|
||||
|
||||
@Dimension(messageKey="Tack", ordinal=13)
|
||||
Tack getTack();
|
||||
|
||||
@Dimension(messageKey="ToSide")
|
||||
@Dimension(messageKey="ToSide", ordinal=16)
|
||||
NauticalSide getToSide();
|
||||
|
||||
@Connector(messageKey="SpeedBefore", ordinal=0)
|
||||
SpeedWithBearing getSpeedBefore();
|
||||
@Connector(messageKey="SpeedAfter", ordinal=1)
|
||||
SpeedWithBearing getSpeedAfter();
|
||||
|
||||
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
|
||||
Double getDirectionChangeInDegrees();
|
||||
@Statistic(messageKey="AbsoluteDirectionChange", resultDecimals=2, ordinal=3)
|
||||
Double getAbsoluteDirectionChangeInDegrees();
|
||||
|
||||
|
||||
@Statistic(messageKey="ManeuverLoss", resultDecimals=0, ordinal=4)
|
||||
Distance getManeuverLoss();
|
||||
|
||||
|
||||
+3
-19
@@ -2,8 +2,6 @@ package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
@@ -11,27 +9,13 @@ import com.sap.sse.datamining.annotations.Statistic;
|
||||
public interface HasMarkPassingContext extends HasWindOnTrackedLeg, HasManeuver, HasTrackedLegOfCompetitor {
|
||||
@Connector(scanForStatistics=false)
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
//TODO Clean-Up:
|
||||
// Move Dimensions and Statistics to Maneuver/MarkPassingManeuver and connect to it
|
||||
// Find a way to implement HasManeuverContext and HasMarkPassingManeuver with a base class
|
||||
@Dimension(messageKey="Tack", ordinal=12)
|
||||
Tack getTack();
|
||||
|
||||
@Connector(messageKey="Waypoint", ordinal=13)
|
||||
@Connector(messageKey="Waypoint", ordinal=14)
|
||||
Waypoint getWaypoint();
|
||||
|
||||
@Dimension(messageKey="PassingSide", ordinal=13)
|
||||
|
||||
@Dimension(messageKey="PassingSide", ordinal=15)
|
||||
NauticalSide getPassingSide();
|
||||
|
||||
@Connector(messageKey="SpeedBefore", ordinal=0)
|
||||
SpeedWithBearing getSpeedBefore();
|
||||
@Connector(messageKey="SpeedAfter", ordinal=1)
|
||||
SpeedWithBearing getSpeedAfter();
|
||||
|
||||
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
|
||||
Double getDirectionChangeInDegrees();
|
||||
|
||||
@Statistic(messageKey="RelativeScore", ordinal=3, resultDecimals=2)
|
||||
Double getRelativeRank();
|
||||
|
||||
|
||||
-3
@@ -19,9 +19,6 @@ public interface HasRaceOfCompetitorContext {
|
||||
@Statistic(messageKey="")
|
||||
public Competitor getCompetitor();
|
||||
|
||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
||||
public String getCompetitorSearchTag();
|
||||
|
||||
@Dimension(messageKey="TackAtStart", ordinal=12)
|
||||
public Tack getTackAtStart() throws NoWindException;
|
||||
|
||||
|
||||
-3
@@ -21,9 +21,6 @@ public interface HasRaceResultOfCompetitorContext {
|
||||
@Dimension(messageKey="Regatta", ordinal=4)
|
||||
String getRegattaName();
|
||||
|
||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=5) // TODO Clean me: Move Dimension to Competitor when possible
|
||||
public String getCompetitorSearchTag();
|
||||
|
||||
@Dimension(messageKey="RelativeScoreInPercent", ordinal=6)
|
||||
public ClusterDTO getPercentageClusterForRelativeScore();
|
||||
|
||||
|
||||
-3
@@ -14,9 +14,6 @@ public interface HasTrackedLegOfCompetitorContext extends HasWindOnTrackedLeg {
|
||||
|
||||
public TrackedLegOfCompetitor getTrackedLegOfCompetitor();
|
||||
|
||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
||||
public String getCompetitorSearchTag();
|
||||
|
||||
@Dimension(messageKey="RelativeScoreInRaceInPercent", ordinal=12)
|
||||
public ClusterDTO getPercentageClusterForRelativeScoreInRace();
|
||||
|
||||
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.datamining.data.ClusterFormatter;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.data.ClusterBoundary;
|
||||
import com.sap.sse.datamining.impl.data.AbstractCluster;
|
||||
import com.sap.sse.datamining.impl.data.AbstractClusterBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
|
||||
import com.sap.sse.datamining.impl.data.LocalizedCluster;
|
||||
|
||||
//TODO Clean and move to sse bundle after 49er analysis
|
||||
public abstract class AbstractClusterFormatter<T extends Serializable> implements ClusterFormatter<T> {
|
||||
|
||||
private static final char INFINITE = '\u221e';
|
||||
|
||||
private final ClusterBoundariesAccessor boundariesAccessor = new ClusterBoundariesAccessor();
|
||||
private final BoundaryValueAccessor valueAccessor = new BoundaryValueAccessor();
|
||||
|
||||
@Override
|
||||
public String format(Cluster<T> cluster) {
|
||||
if (cluster instanceof LocalizedCluster) {
|
||||
// TODO recursive call format(localizedCluster.getInnerCluster()) after 49er analysis
|
||||
}
|
||||
if (cluster instanceof ClusterWithSingleBoundary) {
|
||||
return formatClusterWithSingleBoundary((ClusterWithSingleBoundary<T>) cluster);
|
||||
}
|
||||
if (cluster instanceof ClusterWithLowerAndUpperBoundaries) {
|
||||
return formatClusterWithLowerAndUpperBoundaries((ClusterWithLowerAndUpperBoundaries<T>) cluster);
|
||||
}
|
||||
throw new IllegalArgumentException("Can't format clusters of type " + cluster.getClass().getName());
|
||||
}
|
||||
|
||||
protected String formatClusterWithSingleBoundary(ClusterWithSingleBoundary<T> cluster) {
|
||||
List<ClusterBoundary<T>> boundaries = boundariesAccessor.getBoundaries(cluster);
|
||||
if (boundaries == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
ClusterBoundary<T> boundary = boundaries.get(0);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (boundary.getStrategy() == ComparisonStrategy.LOWER_EQUALS_THAN ||
|
||||
boundary.getStrategy() == ComparisonStrategy.LOWER_THAN) {
|
||||
builder.append("-" + INFINITE + " - ");
|
||||
}
|
||||
builder.append(formatBoundary(boundary));
|
||||
if (boundary.getStrategy() == ComparisonStrategy.GREATER_EQUALS_THAN ||
|
||||
boundary.getStrategy() == ComparisonStrategy.GREATER_THAN) {
|
||||
builder.append(" - " + INFINITE);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected String formatClusterWithLowerAndUpperBoundaries(ClusterWithLowerAndUpperBoundaries<T> cluster) {
|
||||
List<ClusterBoundary<T>> boundaries = boundariesAccessor.getBoundaries(cluster);
|
||||
if (boundaries == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
return String.format("%s - %s", formatBoundary(boundaries.get(0)), formatBoundary(boundaries.get(1)));
|
||||
}
|
||||
|
||||
protected String formatBoundary(ClusterBoundary<T> boundary) {
|
||||
String value = formatValue(valueAccessor.getValue((AbstractClusterBoundary<T>) boundary));
|
||||
if (value == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ComparisonStrategy strategy = boundary.getStrategy();
|
||||
if (strategy == ComparisonStrategy.GREATER_EQUALS_THAN || strategy == ComparisonStrategy.GREATER_THAN) {
|
||||
builder.append(strategy.getSignifier());
|
||||
}
|
||||
builder.append(value);
|
||||
if (strategy == ComparisonStrategy.LOWER_EQUALS_THAN || strategy == ComparisonStrategy.LOWER_THAN) {
|
||||
builder.append(strategy.getSignifier());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected abstract String formatValue(T value);
|
||||
|
||||
//FIXME Please delete me
|
||||
private class ClusterBoundariesAccessor {
|
||||
|
||||
private static final String boundariesFieldName = "boundaries";
|
||||
|
||||
public List<ClusterBoundary<T>> getBoundaries(AbstractCluster<T> cluster) {
|
||||
List<ClusterBoundary<T>> boundaries = null;
|
||||
Field boundariesField = null;
|
||||
try {
|
||||
boundariesField = AbstractCluster.class.getDeclaredField(boundariesFieldName);
|
||||
boundariesField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<ClusterBoundary<T>> reflectedBoundaries = (Collection<ClusterBoundary<T>>) boundariesField.get(cluster);
|
||||
boundaries = new ArrayList<>();
|
||||
for (ClusterBoundary<T> boundary : reflectedBoundaries) {
|
||||
boundaries.add(boundary);
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
} finally {
|
||||
if (boundariesField != null) {
|
||||
boundariesField.setAccessible(false);
|
||||
}
|
||||
}
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//FIXME Please delete me
|
||||
private class BoundaryValueAccessor {
|
||||
|
||||
private static final String valueFieldName = "boundaryValue";
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getValue(AbstractClusterBoundary<T> boundary) {
|
||||
T value = null;
|
||||
Field valueField = null;
|
||||
try {
|
||||
valueField = AbstractClusterBoundary.class.getDeclaredField(valueFieldName);
|
||||
valueField.setAccessible(true);
|
||||
value = (T) valueField.get(boundary);
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
} finally {
|
||||
if (valueField != null) {
|
||||
valueField.setAccessible(false);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -6,12 +6,14 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
|
||||
public class BravoFixWithContext implements HasBravoFixContext {
|
||||
private static final long serialVersionUID = -4537126043228674949L;
|
||||
@@ -51,6 +53,11 @@ public class BravoFixWithContext implements HasBravoFixContext {
|
||||
return getGpsFixTrack().getEstimatedSpeed(getTimePoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getVelocityMadeGood() {
|
||||
return getTrackedLegOfCompetitorContext().getTrackedLegOfCompetitor().getVelocityMadeGood(getTimePoint(), WindPositionMode.EXACT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getWind() {
|
||||
return getTrackedRace().getWind(getGpsFixTrack().getEstimatedPosition(getTimePoint(), /* extrapolate */ true), getTimePoint());
|
||||
|
||||
+3
-25
@@ -5,8 +5,6 @@ import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
|
||||
@@ -37,35 +35,15 @@ public class ManeuverWithContext implements HasManeuverContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack() {
|
||||
return getManeuver().getNewTack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedBefore() {
|
||||
return getManeuver().getSpeedWithBearingBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedAfter() {
|
||||
return getManeuver().getSpeedWithBearingAfter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDirectionChangeInDegrees() {
|
||||
return getManeuver().getDirectionChangeInDegrees();
|
||||
public NauticalSide getToSide() {
|
||||
return getManeuver().getDirectionChangeInDegrees() >= 0 ? NauticalSide.STARBOARD : NauticalSide.PORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAbsoluteDirectionChangeInDegrees() {
|
||||
return Math.abs(getManeuver().getDirectionChangeInDegrees());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getToSide() {
|
||||
return getManeuver().getDirectionChangeInDegrees() >= 0 ? NauticalSide.STARBOARD : NauticalSide.PORT;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Distance getManeuverLoss() {
|
||||
return getManeuver().getManeuverLoss();
|
||||
|
||||
-22
@@ -5,8 +5,6 @@ import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
@@ -36,11 +34,6 @@ public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
public MarkPassingManeuver getManeuver() {
|
||||
return maneuver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack() {
|
||||
return getManeuver().getNewTack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getWaypoint() {
|
||||
@@ -52,21 +45,6 @@ public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
return getManeuver().getSide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedBefore() {
|
||||
return getManeuver().getSpeedWithBearingBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedAfter() {
|
||||
return getManeuver().getSpeedWithBearingAfter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDirectionChangeInDegrees() {
|
||||
return getManeuver().getDirectionChangeInDegrees();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRelativeRank() {
|
||||
Leaderboard leaderboard = getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getLeaderboardContext().getLeaderboard();
|
||||
|
||||
-5
@@ -52,11 +52,6 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
public Competitor getCompetitor() {
|
||||
return competitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompetitorSearchTag() {
|
||||
return getCompetitor().getSearchTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTackAtStart() throws NoWindException {
|
||||
|
||||
-5
@@ -77,11 +77,6 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
|
||||
final String result = leaderboard.getName();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompetitorSearchTag() {
|
||||
return getCompetitor().getSearchTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getPercentageClusterForRelativeScore() {
|
||||
|
||||
-5
@@ -51,11 +51,6 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
||||
public Competitor getCompetitor() {
|
||||
return competitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompetitorSearchTag() {
|
||||
return getCompetitor().getSearchTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getPercentageClusterForRelativeScoreInRace() {
|
||||
|
||||
+9
-1
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
|
||||
|
||||
/**
|
||||
* Possible sources for wind data. Used to key and select between different {@link WindTrack}s. Literals
|
||||
* are given in descending order of precedence. Particularly, the {@link #COURSE_BASED} source should
|
||||
@@ -43,7 +45,13 @@ public enum WindSourceType {
|
||||
* their measures are stored in the race log.
|
||||
*
|
||||
*/
|
||||
RACECOMMITTEE(false, 0.9, /* useSpeed */ true);
|
||||
RACECOMMITTEE(false, 0.9, /* useSpeed */ true),
|
||||
|
||||
/**
|
||||
* Like {@link #COMBINED}, only that when combined with an ID into a {@link WindSourceWithAdditionalID} then the
|
||||
* position for the wind is taken to be the middle of the tracked leg whose number is identified by the ID.
|
||||
*/
|
||||
LEG_MIDDLE(/* can be stored */ false, /* base confidence */ 0.9, /* useSpeed */ true);
|
||||
|
||||
private final boolean canBeStored;
|
||||
|
||||
|
||||
+58
-28
@@ -5,49 +5,79 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Metadata that defines the column structure of {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}es when imported.
|
||||
* Metadata that defines the column structure of {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}es when
|
||||
* imported.
|
||||
*
|
||||
* The current implementation only stores a subset of the information available during the import.
|
||||
*/
|
||||
public enum BravoSensorDataMetadata {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final String RIDE_HEIGHT = "RideHeight";
|
||||
private static final String RIDE_HEIGHT_PORT_HULL = "RideHeightPortHull";
|
||||
private static final String RIDE_HEIGHT_STBD_HULL = "RideHeightStbdHull";
|
||||
private static final String YAW = "ImuSensor_Yaw";
|
||||
private static final String ROLL = "ImuSensor_Roll";
|
||||
private static final String HEEL = "Heel";
|
||||
private static final String PITCH = "ImuSensor_Pitch";
|
||||
|
||||
private final int HEADER_COLUMN_OFFSET = 3;
|
||||
|
||||
private final List<String> columns = Collections.unmodifiableList(Arrays.asList(RIDE_HEIGHT, RIDE_HEIGHT_PORT_HULL,
|
||||
RIDE_HEIGHT_STBD_HULL, "Heel", "Trim", "ImuSensor_GyroX", "ImuSensor_GyroY", "ImuSensor_GyroZ",
|
||||
PITCH, ROLL, YAW, "ImuSensor_LinearAccX", "ImuSensor_LinearAccY",
|
||||
"ImuSensor_LinearAccZ", "Hb_Z", "Dn_Z", "Db_Z", "LKF_ride_hgh", "LKF_ride_hgh_Position",
|
||||
"LKF_ride_hgh_Velocity", "LKF_ride_hgh_Acceleration", "LKF_ride_hgh_PositionError",
|
||||
"LKF_ride_hgh_VelocityError", "LKF_ride_hgh_AccelerationError", "Gps_Gga_PosFixTime", "Gps_Gga_Lat",
|
||||
"Gps_Gga_Lon", "Gps_Gga_QI", "Gps_Gga_HDOP", "Gps_Gga_AntHeight", "Gps_Vtg_TMG", "Gps_Vtg_SOGKnots",
|
||||
"Gps_Rmc_MagVar", "Gps_EastVelocity", "Gps_NorthVelocity", "Gps_UpVelocity", "BravoNet_Node0x0A0_Ch0",
|
||||
"BravoNet_Node0x0A0_Voltage", "BravoNet_Node0x0A0_Temperature", "BravoNet_Node0x0A0_Current"));
|
||||
|
||||
public final int rideHeightColumn = getColumnIndex(RIDE_HEIGHT);
|
||||
public final int rideHeightPortHullColumn = getColumnIndex(RIDE_HEIGHT_PORT_HULL);
|
||||
public final int rideHeightStarboardHullColumn = getColumnIndex(RIDE_HEIGHT_STBD_HULL);
|
||||
public final int pitchColumn = getColumnIndex(PITCH);
|
||||
public final int rollColumn = getColumnIndex(ROLL);
|
||||
public final int yawColumn = getColumnIndex(YAW);
|
||||
public final int columnCount = columns.size();
|
||||
/**
|
||||
*
|
||||
* Available information in file: <br/>
|
||||
* The columns field defines which information will be loaded into the system
|
||||
*
|
||||
*/
|
||||
private final List<String> fileColumns = Collections.unmodifiableList(Arrays.asList( //
|
||||
"RideHeight", RIDE_HEIGHT_PORT_HULL, RIDE_HEIGHT_STBD_HULL, HEEL, "Trim", "ImuSensor_GyroX",
|
||||
"ImuSensor_GyroY", "ImuSensor_GyroZ", PITCH, "ImuSensor_Roll", "ImuSensor_Yaw", "ImuSensor_LinearAccX",
|
||||
"ImuSensor_LinearAccY", "ImuSensor_LinearAccZ",
|
||||
"Hb_Z", "Dn_Z", "Db_Z", "LKF_ride_hgh", "LKF_ride_hgh_Position", "LKF_ride_hgh_Velocity",
|
||||
"LKF_ride_hgh_Acceleration", "LKF_ride_hgh_PositionError", "LKF_ride_hgh_VelocityError",
|
||||
"LKF_ride_hgh_AccelerationError", "Gps_Gga_PosFixTime", "Gps_Gga_Lat", "Gps_Gga_Lon", "Gps_Gga_QI",
|
||||
"Gps_Gga_HDOP", "Gps_Gga_AntHeight", "Gps_Vtg_TMG", "Gps_Vtg_SOGKnots", "Gps_Rmc_MagVar", "Gps_EastVelocity",
|
||||
"Gps_NorthVelocity", "Gps_UpVelocity", "BravoNet_Node0x0A0_Ch0", "BravoNet_Node0x0A0_Voltage",
|
||||
"BravoNet_Node0x0A0_Temperature", "BravoNet_Node0x0A0_Current"
|
||||
));
|
||||
|
||||
public List<String> getColumns() {
|
||||
return columns;
|
||||
private final List<String> trackColumns = Collections.unmodifiableList(Arrays.asList( //
|
||||
RIDE_HEIGHT_PORT_HULL, //
|
||||
RIDE_HEIGHT_STBD_HULL, //
|
||||
HEEL, //
|
||||
PITCH //
|
||||
));
|
||||
|
||||
public final int trackColumnCount = trackColumns.size();
|
||||
public final int rideHeightPortHullColumn = getTrackColumnIndex(RIDE_HEIGHT_PORT_HULL);
|
||||
public final int rideHeightStarboardHullColumn = getTrackColumnIndex(RIDE_HEIGHT_STBD_HULL);
|
||||
public final int heelColumn = getTrackColumnIndex(HEEL);
|
||||
public final int pitchColumn = getTrackColumnIndex(PITCH);
|
||||
|
||||
/**
|
||||
* The fields available in the import file
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getFileColumns() {
|
||||
return fileColumns;
|
||||
}
|
||||
|
||||
public boolean hasColumn(String columnName) {
|
||||
return columns.contains(columnName);
|
||||
/**
|
||||
* The fields to be loaded into a track
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getTrackColumns() {
|
||||
return trackColumns;
|
||||
}
|
||||
|
||||
public int getColumnIndex(String columnName) {
|
||||
return columns.indexOf(columnName);
|
||||
public boolean trackHasColumn(String columnName) {
|
||||
return trackColumns.contains(columnName);
|
||||
}
|
||||
|
||||
|
||||
public int getTrackColumnIndex(String columnName) {
|
||||
return trackColumns.indexOf(columnName);
|
||||
}
|
||||
|
||||
public int getHeaderColumnOffset() {
|
||||
return HEADER_COLUMN_OFFSET;
|
||||
}
|
||||
|
||||
+2
-4
@@ -33,9 +33,7 @@ public interface BravoFix extends SensorFix {
|
||||
@Statistic(messageKey="pitch", resultDecimals=1)
|
||||
double getPitch();
|
||||
|
||||
@Statistic(messageKey="yaw", resultDecimals=1)
|
||||
double getYaw();
|
||||
@Statistic(messageKey = "heel", resultDecimals = 1)
|
||||
double getHeel();
|
||||
|
||||
@Statistic(messageKey="roll", resultDecimals=1)
|
||||
double getRoll();
|
||||
}
|
||||
+30
-19
@@ -6,23 +6,39 @@ import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Implementation of {@link BravoFix} that wraps a {@link DoubleVectorFix} which holds the actual sensor data.
|
||||
*/
|
||||
public class BravoFixImpl implements BravoFix {
|
||||
private static final long serialVersionUID = 2033254212013221160L;
|
||||
|
||||
private final DoubleVectorFix fix;
|
||||
private final MeterDistance computedRideHeight;
|
||||
private final MeterDistance rideHeightPortHull;
|
||||
private final MeterDistance rideHeightStarboardHull;
|
||||
private final double pitch;
|
||||
private final double heel;
|
||||
private final boolean computedIsFoiling;
|
||||
|
||||
public BravoFixImpl(DoubleVectorFix fix) {
|
||||
this.fix = fix;
|
||||
|
||||
pitch = fix.get(BravoSensorDataMetadata.INSTANCE.pitchColumn);
|
||||
heel = fix.get(BravoSensorDataMetadata.INSTANCE.heelColumn);
|
||||
double rideHeightPortHullasDouble = fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn);
|
||||
double rideHeightStarboardHullasDouble = fix
|
||||
.get(BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn);
|
||||
|
||||
rideHeightPortHull = new MeterDistance(rideHeightPortHullasDouble);
|
||||
rideHeightStarboardHull = new MeterDistance(rideHeightStarboardHullasDouble);
|
||||
|
||||
computedRideHeight = new MeterDistance(Math.min(rideHeightPortHullasDouble, rideHeightStarboardHullasDouble));
|
||||
computedIsFoiling = computedRideHeight.compareTo(MIN_FOILING_HEIGHT_THRESHOLD) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double get(String valueName) {
|
||||
int index = BravoSensorDataMetadata.INSTANCE.getColumnIndex(valueName);
|
||||
int index = BravoSensorDataMetadata.INSTANCE.getTrackColumnIndex(valueName);
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Unknown value \"" + valueName + "\" for " + getClass().getSimpleName());
|
||||
}
|
||||
@@ -36,36 +52,31 @@ public class BravoFixImpl implements BravoFix {
|
||||
|
||||
@Override
|
||||
public Distance getRideHeight() {
|
||||
return Util.min(getRideHeightPortHull(), getRideHeightStarboardHull());
|
||||
return computedRideHeight;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Distance getRideHeightPortHull() {
|
||||
return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn));
|
||||
return rideHeightPortHull;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Distance getRideHeightStarboardHull() {
|
||||
return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn));
|
||||
return rideHeightStarboardHull;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isFoiling() {
|
||||
return getRideHeight().compareTo(MIN_FOILING_HEIGHT_THRESHOLD) >= 0;
|
||||
return computedIsFoiling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPitch() {
|
||||
return fix.get(BravoSensorDataMetadata.INSTANCE.pitchColumn);
|
||||
return pitch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getYaw() {
|
||||
return fix.get(BravoSensorDataMetadata.INSTANCE.yawColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRoll() {
|
||||
return fix.get(BravoSensorDataMetadata.INSTANCE.rollColumn);
|
||||
public double getHeel() {
|
||||
return heel;
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -513,8 +513,7 @@ public class SensorFixStoreAndLoadTest {
|
||||
}
|
||||
|
||||
private DoubleVectorFix createBravoDoubleVectorFixWithRideHeight(long timestamp, double rideHeight) {
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.columnCount];
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightColumn] = rideHeight;
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.trackColumnCount];
|
||||
// fill the port/starboard columns as well because their minimum defines the true ride height
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn] = rideHeight;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn] = rideHeight;
|
||||
|
||||
+3
-2
@@ -330,8 +330,9 @@ public class SensorFixStoreTest {
|
||||
}
|
||||
|
||||
private DoubleVectorFix createBravoDoubleVectorFixWithRideHeight(long timestamp, double rideHeight) {
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.columnCount];
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightColumn] = rideHeight;
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.trackColumnCount];
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn] = rideHeight;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn] = rideHeight;
|
||||
return new DoubleVectorFixImpl(new MillisecondsTimePoint(timestamp), fixData);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,4 +32,4 @@ public class BravoDataFixMapper implements SensorFixMapper<BravoFix, DynamicSens
|
||||
public boolean isResponsibleFor(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
|
||||
return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -8,6 +8,7 @@ import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.NamedWithID;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
|
||||
public interface Competitor extends NamedWithID, IsManagedByCache<SharedDomainFactory> {
|
||||
@Connector(messageKey="Team", ordinal=9)
|
||||
@@ -22,6 +23,7 @@ public interface Competitor extends NamedWithID, IsManagedByCache<SharedDomainFa
|
||||
|
||||
boolean hasEmail();
|
||||
|
||||
@Dimension(messageKey="SearchTag", ordinal=11)
|
||||
String getSearchTag();
|
||||
|
||||
/**
|
||||
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.CombinedWindAsNavigableSet;
|
||||
import com.sap.sailing.domain.tracking.impl.CombinedWindTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.VirtualWindFixesAsNavigableSet;
|
||||
import com.sap.sse.InvalidDateException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.DateParser;
|
||||
|
||||
/**
|
||||
* Tests for {@link VirtualWindFixesAsNavigableSet}, based mainly on bug 4063.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class VirtualWindFixesTest {
|
||||
private CombinedWindTrackImpl windTrack;
|
||||
private CombinedWindAsNavigableSet virtualSet;
|
||||
private TrackedRace trackedRace;
|
||||
private TimePoint oddStart;
|
||||
private TimePoint evenStart;
|
||||
|
||||
@Before
|
||||
public void setUp() throws InvalidDateException {
|
||||
oddStart = new MillisecondsTimePoint(DateParser.parse("2017-02-15T09:59:59.9+01:00"));
|
||||
evenStart = new MillisecondsTimePoint(DateParser.parse("2017-02-15T10:00:00+01:00"));
|
||||
trackedRace = mock(TrackedRace.class);
|
||||
RaceDefinition race = mock(RaceDefinition.class);
|
||||
when(race.getName()).thenReturn("Race Name");
|
||||
when(trackedRace.getRace()).thenReturn(race);
|
||||
when(trackedRace.getStartOfRace()).thenReturn(oddStart);
|
||||
when(trackedRace.getWind(any(), any())).thenAnswer(invocation->new WindImpl(
|
||||
(Position) invocation.getArguments()[0], (TimePoint) invocation.getArguments()[1],
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
windTrack = new CombinedWindTrackImpl(trackedRace, /* baseConfidence */ 0.9) {
|
||||
private static final long serialVersionUID = 123L;
|
||||
|
||||
@Override
|
||||
protected CombinedWindAsNavigableSet createVirtualInternalRawFixes(TrackedRace trackedRace) {
|
||||
virtualSet = new CombinedWindAsNavigableSet(windTrack, trackedRace, 10000l) {
|
||||
private static final long serialVersionUID = -8903939477027009728L;
|
||||
|
||||
@Override
|
||||
protected CombinedWindTrackImpl getTrack() {
|
||||
return windTrack;
|
||||
}
|
||||
};
|
||||
return virtualSet;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsetWithInclusiveEnd() {
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
final Iterable<Wind> fixes = windTrack.getFixes(evenStart, /* fromInclusive */ true,
|
||||
evenStart.plus(virtualSet.getResolutionInMilliseconds()), /* toInclusive */ true);
|
||||
assertEquals(2, Util.size(fixes));
|
||||
Wind wind1 = fixes.iterator().next();
|
||||
assertEquals(evenStart, wind1.getTimePoint());
|
||||
Wind wind2 = Util.get(fixes, 1);
|
||||
assertEquals(evenStart.plus(virtualSet.getResolutionInMilliseconds()), wind2.getTimePoint());
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsetWithInclusiveEndAndExclusiveStart() {
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
final Iterable<Wind> fixes = windTrack.getFixes(evenStart, /* fromInclusive */ false,
|
||||
evenStart.plus(virtualSet.getResolutionInMilliseconds()), /* toInclusive */ true);
|
||||
assertEquals(1, Util.size(fixes));
|
||||
Wind wind1 = fixes.iterator().next();
|
||||
assertEquals(evenStart.plus(virtualSet.getResolutionInMilliseconds()), wind1.getTimePoint());
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubset() {
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
final Iterable<Wind> fixes = windTrack.getFixes(evenStart, /* fromInclusive */ true,
|
||||
evenStart.plus(virtualSet.getResolutionInMilliseconds()), /* toInclusive */ false);
|
||||
assertEquals(1, Util.size(fixes));
|
||||
Wind wind = fixes.iterator().next();
|
||||
assertEquals(evenStart, wind.getTimePoint());
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubsetWithExclusiveEndButToIsSlightlyAfterSecondFix() {
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
final Iterable<Wind> fixes = windTrack.getFixes(evenStart, /* fromInclusive */ true,
|
||||
evenStart.plus(virtualSet.getResolutionInMilliseconds()).plus(1), /* toInclusive */ false);
|
||||
assertEquals(2, Util.size(fixes));
|
||||
Wind wind1 = fixes.iterator().next();
|
||||
assertEquals(evenStart, wind1.getTimePoint());
|
||||
Wind wind2 = Util.get(fixes, 1);
|
||||
assertEquals(evenStart.plus(virtualSet.getResolutionInMilliseconds()), wind2.getTimePoint());
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -116,8 +116,7 @@ public class BravoFixTrackSerializationTest {
|
||||
}
|
||||
|
||||
private void addOrReplaceBravoFixToTrack(boolean replace) {
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.columnCount];
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightColumn] = rideHeight;
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.trackColumnCount];
|
||||
// fill the port/starboard columns as well because their minimum defines the true ride height
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn] = rideHeight;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn] = rideHeight;
|
||||
|
||||
+15
@@ -366,4 +366,19 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
|
||||
protected LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.FlexibleLeaderboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition to invoking the superclass implementation, a flexible leaderboard also
|
||||
* detaches all race logs from any tracked race currently linked to any of the race columns
|
||||
* of this leaderboard.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
for (final RaceColumn raceColumn : getRaceColumns()) {
|
||||
for (final Fleet fleet : raceColumn.getFleets()) {
|
||||
raceColumn.setTrackedRace(fleet, null); // this will in particular detach the race log
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,13 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
|
||||
*/
|
||||
Distance getRideHeight(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Tells if the competitor to which this track belongs is considered to be foiling at the time point
|
||||
* given. This depends on the {@link #getRideHeight} and a threshold above which a boat is considered
|
||||
* to be foiling.
|
||||
*/
|
||||
boolean isFoiling(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Calculates the average ride height for the given time range.
|
||||
*
|
||||
|
||||
@@ -5,16 +5,23 @@ import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
|
||||
public interface Maneuver extends GPSFix {
|
||||
ManeuverType getType();
|
||||
|
||||
|
||||
@Dimension(messageKey="Tack", ordinal=13)
|
||||
Tack getNewTack();
|
||||
|
||||
|
||||
@Connector(messageKey="SpeedBefore", ordinal=0)
|
||||
SpeedWithBearing getSpeedWithBearingBefore();
|
||||
|
||||
|
||||
@Connector(messageKey="SpeedAfter", ordinal=1)
|
||||
SpeedWithBearing getSpeedWithBearingAfter();
|
||||
|
||||
|
||||
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
|
||||
double getDirectionChangeInDegrees();
|
||||
|
||||
Distance getManeuverLoss();
|
||||
|
||||
@@ -376,15 +376,17 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
/**
|
||||
* Retrieves the wind sources used so far by this race that have the specified <code>type</code> as their
|
||||
* {@link WindSource#getType() type}. Always returns a non-<code>null</code> iterable which may be empty in case the
|
||||
* race does not use any wind source of the specified type (yet). Additional sources may be returned after
|
||||
* race does not use any wind source of the specified type (yet).<p>
|
||||
*
|
||||
* It is possible to ask for the {@link WindSourceType#COMBINED} and {@link WindSourceType#LEG_MIDDLE} types and
|
||||
* get a non-empty result although those sources are never returned by {@link #getWindSources()}.
|
||||
*/
|
||||
Set<WindSource> getWindSources(WindSourceType type);
|
||||
|
||||
/**
|
||||
* Retrieves all wind sources known to this race, including those {@link #getWindSourcesToExclude() to exclude}.
|
||||
* Callers can freely iterate because a copied collection is returned. The {@link WindSourceType#COMBINED} wind source
|
||||
* is never part of the result.
|
||||
* as well as the {@link WindSourceType#LEG_MIDDLE} sources are never part of the result.
|
||||
*/
|
||||
Set<WindSource> getWindSources();
|
||||
|
||||
|
||||
+35
-3
@@ -11,7 +11,10 @@ import java.util.logging.Logger;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
|
||||
import com.sap.sailing.domain.tracking.impl.CombinedWindTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.LegMiddleWindTrackImpl;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
@@ -75,6 +78,9 @@ public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
* @param delayForWindEstimationCacheInvalidation
|
||||
* if <code>-1</code> and the parameter is accessed, it will be replaced by
|
||||
* {@link #getMillisecondsOverWhichToAverageWind()}/2
|
||||
*
|
||||
* @return {@code null} in case the {@link WindSourceType#LEG_MIDDLE} type is requested for an
|
||||
* {@link WindSourceWithAdditionalID#getId() ID} that doesn't identify an existing leg
|
||||
*/
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
|
||||
@@ -84,6 +90,8 @@ public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
combinedWindTrack = new CombinedWindTrackImpl(this, WindSourceType.COMBINED.getBaseConfidence());
|
||||
}
|
||||
result = combinedWindTrack;
|
||||
} else if (windSource.getType() == WindSourceType.LEG_MIDDLE && windSource instanceof WindSourceWithAdditionalID) {
|
||||
result = getLegMiddleWindTrack((WindSourceWithAdditionalID) windSource);
|
||||
} else {
|
||||
synchronized (windTracks) {
|
||||
result = windTracks.get(windSource);
|
||||
@@ -121,12 +129,36 @@ public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
return result;
|
||||
}
|
||||
|
||||
private WindSource getLegMiddleWindSource(TrackedLeg trackedLeg) {
|
||||
return new WindSourceWithAdditionalID(WindSourceType.LEG_MIDDLE, getWindSourceIdForLegMiddle(trackedLeg));
|
||||
}
|
||||
|
||||
private String getWindSourceIdForLegMiddle(TrackedLeg trackedLeg) {
|
||||
return Integer.toString(getRace().getCourse().getIndexOfWaypoint(trackedLeg.getLeg().getFrom()));
|
||||
}
|
||||
|
||||
private WindTrack getLegMiddleWindTrack(WindSourceWithAdditionalID windSource) {
|
||||
assert windSource.getType() == WindSourceType.LEG_MIDDLE;
|
||||
final String id = windSource.getId();
|
||||
final Integer zeroBasedLegIndex = Integer.valueOf(id);
|
||||
final TrackedLeg trackedLeg = getTrackedLeg(getRace().getCourse().getLeg(zeroBasedLegIndex));
|
||||
return new LegMiddleWindTrackImpl(this, trackedLeg, WindSourceType.LEG_MIDDLE.getBaseConfidence());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<WindSource> getWindSources(WindSourceType type) {
|
||||
Set<WindSource> result = new HashSet<WindSource>();
|
||||
for (WindSource windSource : getWindSources()) {
|
||||
if (windSource.getType() == type) {
|
||||
result.add(windSource);
|
||||
if (type == WindSourceType.COMBINED) {
|
||||
result.add(new WindSourceImpl(WindSourceType.COMBINED));
|
||||
} else if (type == WindSourceType.LEG_MIDDLE) {
|
||||
for (final TrackedLeg trackedLeg : getTrackedLegs()) {
|
||||
result.add(getLegMiddleWindSource(trackedLeg));
|
||||
}
|
||||
} else {
|
||||
for (WindSource windSource : getWindSources()) {
|
||||
if (windSource.getType() == type) {
|
||||
result.add(windSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+7
-1
@@ -28,7 +28,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
* the name of the track by which it can be obtained from the {@link TrackedRace}.
|
||||
*/
|
||||
public BravoFixTrackImpl(ItemType trackedItem, String trackName) {
|
||||
super(trackedItem, trackName, BravoSensorDataMetadata.INSTANCE.getColumns(),
|
||||
super(trackedItem, trackName, BravoSensorDataMetadata.INSTANCE.getFileColumns(),
|
||||
BravoFixTrack.TRACK_NAME + " for " + trackedItem);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
return fixBefore.getRideHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFoiling(TimePoint timePoint) {
|
||||
final Distance rideHeight = getRideHeight(timePoint);
|
||||
return rideHeight != null && rideHeight.compareTo(BravoFix.MIN_FOILING_HEIGHT_THRESHOLD) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
|
||||
final Distance result;
|
||||
|
||||
+16
-1
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl.DummyWind;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
@@ -19,7 +20,7 @@ public class CombinedWindAsNavigableSet extends VirtualWindFixesAsNavigableSet {
|
||||
|
||||
private static final long serialVersionUID = -153959652212518644L;
|
||||
|
||||
public CombinedWindAsNavigableSet(WindTrack track, TrackedRace trackedRace, long resolutionInMilliseconds) {
|
||||
public CombinedWindAsNavigableSet(CombinedWindTrackImpl track, TrackedRace trackedRace, long resolutionInMilliseconds) {
|
||||
super(track, trackedRace, resolutionInMilliseconds);
|
||||
}
|
||||
|
||||
@@ -28,6 +29,11 @@ public class CombinedWindAsNavigableSet extends VirtualWindFixesAsNavigableSet {
|
||||
super(track, trackedRace, from, to, resolutionInMilliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CombinedWindTrackImpl getTrack() {
|
||||
return (CombinedWindTrackImpl) super.getTrack();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Wind getWind(Position p, TimePoint timePoint) {
|
||||
return getTrackedRace().getWind(p, timePoint);
|
||||
@@ -52,4 +58,13 @@ public class CombinedWindAsNavigableSet extends VirtualWindFixesAsNavigableSet {
|
||||
: ceilingToResolution(getTrackedRace().getEndOfRace()) : getToInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link TrackedRace#getCenterOfCourse(TimePoint) center of the course} as the position for which to
|
||||
* compute the combined wind
|
||||
*/
|
||||
@Override
|
||||
protected DummyWind createDummyWindFix(TimePoint timePoint) {
|
||||
return new DummyWind(timePoint, getTrack().getDefaultPosition(timePoint));
|
||||
}
|
||||
|
||||
}
|
||||
+17
-3
@@ -28,13 +28,27 @@ public class CombinedWindTrackImpl extends VirtualWindTrackImpl {
|
||||
public CombinedWindTrackImpl(TrackedRace trackedRace, double baseConfidence) {
|
||||
super(trackedRace, /* millisecondsOverWhichToAverage not used, see overridden method */ -1, baseConfidence,
|
||||
/* useSpeed */ WindSourceType.COMBINED.useSpeed());
|
||||
virtualInternalRawFixes = new CombinedWindAsNavigableSet(this, trackedRace, /* resolutionInMilliseconds */ 10000l);
|
||||
virtualInternalRawFixes = createVirtualInternalRawFixes(trackedRace);
|
||||
}
|
||||
|
||||
protected CombinedWindAsNavigableSet createVirtualInternalRawFixes(TrackedRace trackedRace) {
|
||||
return new CombinedWindAsNavigableSet(this, trackedRace, /* resolutionInMilliseconds */ 1000l);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CombinedWindAsNavigableSet getInternalRawFixes() {
|
||||
return virtualInternalRawFixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers the position to use when a caller of {@link #getAveragedWind(Position, TimePoint)} or
|
||||
* {@link #getAveragedWindWithConfidence(Position, TimePoint)} provides a {@code null} position. This position is
|
||||
* also used for the fixes produced by the fix iterators for this track. This implementation returns the
|
||||
* {@link TrackedRace#getCenterOfCourse(TimePoint) center of the course}.
|
||||
*/
|
||||
protected Position getDefaultPosition(TimePoint at) {
|
||||
return getTrackedRace().getCenterOfCourse(at);
|
||||
}
|
||||
|
||||
/**
|
||||
* The combined wind source already averages the wind from each wind source considered. There is no use in again
|
||||
@@ -49,11 +63,11 @@ public class CombinedWindTrackImpl extends VirtualWindTrackImpl {
|
||||
|
||||
@Override
|
||||
public Wind getAveragedWind(Position p, TimePoint at) {
|
||||
return getTrackedRace().getWind(p, at);
|
||||
return getTrackedRace().getWind(p==null?getDefaultPosition(at):p, at);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindWithConfidence<Util.Pair<Position, TimePoint>> getAveragedWindWithConfidence(Position p, TimePoint at) {
|
||||
return getTrackedRace().getWindWithConfidence(p, at);
|
||||
return getTrackedRace().getWindWithConfidence(p==null?getDefaultPosition(at):p, at);
|
||||
}
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class LegMiddleWindTrackImpl extends CombinedWindTrackImpl {
|
||||
private static final long serialVersionUID = -3014653185579457125L;
|
||||
private final TrackedLeg trackedLeg;
|
||||
|
||||
public LegMiddleWindTrackImpl(TrackedRace trackedRace, TrackedLeg trackedLeg, double baseConfidence) {
|
||||
super(trackedRace, baseConfidence);
|
||||
this.trackedLeg = trackedLeg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Position getDefaultPosition(TimePoint at) {
|
||||
return trackedLeg.getMiddleOfLeg(at);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -3851,8 +3851,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
public Position getCenterOfCourse(TimePoint at) {
|
||||
int count = 0;
|
||||
ScalablePosition sum = null;
|
||||
final MarkPositionAtTimePointCache cache = new MarkPositionAtTimePointCacheImpl(this, at);
|
||||
for (Waypoint waypoint : getRace().getCourse().getWaypoints()) {
|
||||
final Position waypointPosition = getApproximatePosition(waypoint, at);
|
||||
final Position waypointPosition = getApproximatePosition(waypoint, at, cache);
|
||||
if (waypointPosition != null) {
|
||||
ScalablePosition p = new ScalablePosition(waypointPosition);
|
||||
if (sum == null) {
|
||||
|
||||
+9
-5
@@ -122,7 +122,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
protected TimePoint higherToResolution(TimePoint timePoint) {
|
||||
TimePoint result;
|
||||
final TimePoint startOfTracking = getTrackedRace().getStartOfTracking();
|
||||
if (startOfTracking == null && timePoint.compareTo(startOfTracking) < 0) {
|
||||
if (startOfTracking != null && timePoint.compareTo(startOfTracking) < 0) {
|
||||
result = higherToResolution(startOfTracking);
|
||||
} else {
|
||||
result = new MillisecondsTimePoint((timePoint.asMillis() / getResolutionInMilliseconds() + 1)
|
||||
@@ -192,7 +192,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
@Override
|
||||
public Wind next() {
|
||||
Wind result = floor(new DummyWind(timePoint));
|
||||
Wind result = floor(createDummyWindFix(timePoint));
|
||||
timePoint = new MillisecondsTimePoint(timePoint.asMillis() + getResolutionInMilliseconds());
|
||||
return result;
|
||||
}
|
||||
@@ -235,7 +235,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> subSet(Wind fromElement, boolean fromInclusive, Wind toElement, boolean toInclusive) {
|
||||
return createSubset(track, getTrackedRace(), fromInclusive ? ceilingToResolution(fromElement.getTimePoint())
|
||||
return createSubset(getTrack(), getTrackedRace(), fromInclusive ? ceilingToResolution(fromElement.getTimePoint())
|
||||
: higherToResolution(fromElement.getTimePoint()), toInclusive ? floorToResolution(toElement.getTimePoint())
|
||||
: lowerToResolution(toElement.getTimePoint()));
|
||||
}
|
||||
@@ -247,13 +247,13 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> headSet(Wind toElement, boolean inclusive) {
|
||||
return createSubset(track, getTrackedRace(), /* from */ null,
|
||||
return createSubset(getTrack(), getTrackedRace(), /* from */ null,
|
||||
inclusive ? ceilingToResolution(toElement.getTimePoint()) : lowerToResolution(toElement.getTimePoint()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> tailSet(Wind fromElement, boolean inclusive) {
|
||||
return createSubset(track, getTrackedRace(), inclusive ? floorToResolution(fromElement.getTimePoint())
|
||||
return createSubset(getTrack(), getTrackedRace(), inclusive ? floorToResolution(fromElement.getTimePoint())
|
||||
: higherToResolution(fromElement.getTimePoint()),
|
||||
/* to */ null);
|
||||
}
|
||||
@@ -332,5 +332,9 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
protected TimePoint getToInternal() {
|
||||
return to;
|
||||
}
|
||||
|
||||
protected DummyWind createDummyWindFix(TimePoint timePoint) {
|
||||
return new DummyWind(timePoint);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -331,12 +331,17 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
|
||||
protected static class DummyWind extends DummyTimed implements Wind {
|
||||
private static final long serialVersionUID = -311172509910032149L;
|
||||
private final Position position;
|
||||
public DummyWind(TimePoint timePoint) {
|
||||
this(timePoint, /* position */ null);
|
||||
}
|
||||
public DummyWind(TimePoint timePoint, Position position) {
|
||||
super(timePoint);
|
||||
this.position = position;
|
||||
}
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return null;
|
||||
return position;
|
||||
}
|
||||
@Override
|
||||
public Bearing getBearing() {
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false -Dgwt.watchFileChanges=false"/>
|
||||
</launchConfiguration>
|
||||
+2
@@ -302,6 +302,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String details();
|
||||
String noGroupSelected();
|
||||
String combinedWindSourceTypeName();
|
||||
String legMiddleWindSourceTypeName();
|
||||
String courseBasedWindSourceTypeName();
|
||||
String trackBasedEstimationWindSourceTypeName();
|
||||
String windSensorWindSourceTypeName();
|
||||
@@ -925,6 +926,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String showWindSpeedSeriesTooltip();
|
||||
String showWindDirectionSeriesTooltip();
|
||||
String combinedWindSourceTypeTooltip();
|
||||
String legMiddleWindSourceTypeTooltip();
|
||||
String courseBasedWindSourceTypeTooltip();
|
||||
String trackBasedEstimationWindSourceTypeTooltip();
|
||||
String windSensorWindSourceTypeTooltip();
|
||||
|
||||
+2
@@ -310,6 +310,7 @@ notAvailable=Not available
|
||||
details=Details
|
||||
noGroupSelected=No group selected
|
||||
combinedWindSourceTypeName=Combined
|
||||
legMiddleWindSourceTypeName=Middle of Leg
|
||||
courseBasedWindSourceTypeName=Course Layout
|
||||
trackBasedEstimationWindSourceTypeName=Estimation
|
||||
windSensorWindSourceTypeName=Measured
|
||||
@@ -897,6 +898,7 @@ gribWindImport_Title=Import wind from GRIB files
|
||||
showWindSpeedSeriesTooltip=Toggles the visibility of the wind speed for all data sources
|
||||
showWindDirectionSeriesTooltip=Toggles the visibility of the wind direction for all data sources
|
||||
combinedWindSourceTypeTooltip=Combines all other wind sources to one data set
|
||||
legMiddleWindSourceTypeTooltip=Combined wind at the mid point of a leg''s rhumb line
|
||||
courseBasedWindSourceTypeTooltip=A value based on the course layout (under the assumption, that the race starts with an upwind leg)
|
||||
trackBasedEstimationWindSourceTypeTooltip=Calculated data from the competitor tracks
|
||||
windSensorWindSourceTypeTooltip=Data captured from mobile measuring systems
|
||||
|
||||
+4
@@ -8,6 +8,8 @@ public class WindSourceTypeFormatter {
|
||||
switch (windSourceType) {
|
||||
case COMBINED:
|
||||
return stringMessages.combinedWindSourceTypeName();
|
||||
case LEG_MIDDLE:
|
||||
return stringMessages.legMiddleWindSourceTypeName();
|
||||
case COURSE_BASED:
|
||||
return stringMessages.courseBasedWindSourceTypeName();
|
||||
case TRACK_BASED_ESTIMATION:
|
||||
@@ -26,6 +28,8 @@ public class WindSourceTypeFormatter {
|
||||
switch (windSourceType) {
|
||||
case COMBINED:
|
||||
return stringMessages.combinedWindSourceTypeTooltip();
|
||||
case LEG_MIDDLE:
|
||||
return stringMessages.legMiddleWindSourceTypeTooltip();
|
||||
case COURSE_BASED:
|
||||
return stringMessages.courseBasedWindSourceTypeTooltip();
|
||||
case TRACK_BASED_ESTIMATION:
|
||||
|
||||
+25
-5
@@ -42,6 +42,7 @@ import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.ColorMapImpl;
|
||||
import com.sap.sailing.gwt.ui.actions.GetWindInfoAction;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
@@ -60,7 +61,7 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
|
||||
|
||||
public class WindChart extends AbstractRaceChart<WindChartSettings> implements RequiresResize {
|
||||
public static final String LODA_WIND_CHART_DATA_CATEGORY = "loadWindChartData";
|
||||
public static final String LOAD_WIND_CHART_DATA_CATEGORY = "loadWindChartData";
|
||||
|
||||
private static final int LINE_WIDTH = 1;
|
||||
|
||||
@@ -435,15 +436,23 @@ public class WindChart extends AbstractRaceChart<WindChartSettings> implements R
|
||||
*/
|
||||
@Override
|
||||
public void updateSettings(WindChartSettings newSettings) {
|
||||
boolean clearCacheAndReload = false;
|
||||
final Set<String> oldWindSourceTypesToRequest = getNamesOfWindSourceTypesOfWhichToDisplaySpeedOrDirection();
|
||||
if (newSettings.getResolutionInMilliseconds() != settings.getResolutionInMilliseconds()) {
|
||||
settings.setResolutionInMilliseconds(newSettings.getResolutionInMilliseconds());
|
||||
clearCacheAndReload();
|
||||
clearCacheAndReload = true;
|
||||
}
|
||||
settings.setShowWindDirectionsSeries(newSettings.isShowWindDirectionsSeries());
|
||||
settings.setWindDirectionSourcesToDisplay(newSettings.getWindDirectionSourcesToDisplay());
|
||||
|
||||
settings.setShowWindSpeedSeries(newSettings.isShowWindSpeedSeries());
|
||||
settings.setWindSpeedSourcesToDisplay(newSettings.getWindSpeedSourcesToDisplay());
|
||||
if (!oldWindSourceTypesToRequest.equals(getNamesOfWindSourceTypesOfWhichToDisplaySpeedOrDirection())) {
|
||||
clearCacheAndReload = true;
|
||||
}
|
||||
if (clearCacheAndReload) {
|
||||
clearCacheAndReload();
|
||||
}
|
||||
updateVisibleSeries();
|
||||
}
|
||||
|
||||
@@ -453,7 +462,6 @@ public class WindChart extends AbstractRaceChart<WindChartSettings> implements R
|
||||
windSourceDirectionPoints.clear();
|
||||
windSourceSpeedPoints.clear();
|
||||
firstPointOfFirstSeries = null;
|
||||
|
||||
loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false);
|
||||
}
|
||||
|
||||
@@ -473,9 +481,10 @@ public class WindChart extends AbstractRaceChart<WindChartSettings> implements R
|
||||
showLoading(stringMessages.windChartLoading());
|
||||
}
|
||||
GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, selectedRaceIdentifier,
|
||||
from, to, settings.getResolutionInMilliseconds(), null, /* onlyUpToNewestEvent==true because we don't want
|
||||
from, to, settings.getResolutionInMilliseconds(), getNamesOfWindSourceTypesOfWhichToDisplaySpeedOrDirection(),
|
||||
/* onlyUpToNewestEvent==true because we don't want
|
||||
to overshoot the evidence so far */ true);
|
||||
asyncActionsExecutor.execute(getWindInfoAction, LODA_WIND_CHART_DATA_CATEGORY,
|
||||
asyncActionsExecutor.execute(getWindInfoAction, LOAD_WIND_CHART_DATA_CATEGORY,
|
||||
new AsyncCallback<WindInfoForRaceDTO>() {
|
||||
@Override
|
||||
public void onSuccess(WindInfoForRaceDTO result) {
|
||||
@@ -501,6 +510,17 @@ public class WindChart extends AbstractRaceChart<WindChartSettings> implements R
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getNamesOfWindSourceTypesOfWhichToDisplaySpeedOrDirection() {
|
||||
final Set<String> result = new HashSet<>();
|
||||
for (WindSourceType speedType : getSettings().getWindSpeedSourcesToDisplay()) {
|
||||
result.add(speedType.name());
|
||||
}
|
||||
for (WindSourceType speedType : getSettings().getWindDirectionSourcesToDisplay()) {
|
||||
result.add(speedType.name());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void clearChart() {
|
||||
chart.removeAllSeries();
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -75,9 +76,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
windSpeedSourcesPanel.add(showWindSpeedSeriesCheckbox);
|
||||
|
||||
showWindDirectionsSeriesCheckbox = dialog.createCheckbox(stringMessages.showWindDirectionSeries());
|
||||
showWindDirectionsSeriesCheckbox.setTitle(stringMessages.showWindDirectionSeriesTooltip());
|
||||
showWindDirectionsSeriesCheckbox.setValue(initialSettings.isShowWindDirectionsSeries());
|
||||
@@ -92,7 +91,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent
|
||||
|
||||
windDirectionSourcesPanel.add(showWindDirectionsSeriesCheckbox);
|
||||
|
||||
for (WindSourceType windSourceType : WindSourceType.values()) {
|
||||
for (WindSourceType windSourceType : getWindSourceTypesToOffer()) {
|
||||
CheckBox checkbox = dialog.createCheckbox(WindSourceTypeFormatter.format(windSourceType, stringMessages));
|
||||
checkbox.setTitle(WindSourceTypeFormatter.tooltipFor(windSourceType, stringMessages));
|
||||
checkbox.setEnabled(initialSettings.isShowWindDirectionsSeries());
|
||||
@@ -102,7 +101,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent
|
||||
windDirectionSourcesPanel.add(checkbox);
|
||||
}
|
||||
|
||||
for (WindSourceType windSourceType : WindSourceType.values()) {
|
||||
for (WindSourceType windSourceType : getWindSourceTypesToOffer()) {
|
||||
CheckBox checkbox = dialog.createCheckbox(WindSourceTypeFormatter.format(windSourceType, stringMessages));
|
||||
checkbox.setTitle(WindSourceTypeFormatter.tooltipFor(windSourceType, stringMessages));
|
||||
checkbox.setEnabled(initialSettings.isShowWindSpeedSeries() && windSourceType.useSpeed());
|
||||
@@ -115,6 +114,12 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent
|
||||
return vp;
|
||||
}
|
||||
|
||||
private Iterable<WindSourceType> getWindSourceTypesToOffer() {
|
||||
final Set<WindSourceType> result = new HashSet<>();
|
||||
result.addAll(Arrays.asList(WindSourceType.values()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindChartSettings getResult() {
|
||||
Set<WindSourceType> windDirectionSourcesToDisplay = new HashSet<WindSourceType>();
|
||||
|
||||
+19
-12
@@ -1365,6 +1365,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
* "best effort" readings are provided for the time interval requested, no matter if based on any sensor
|
||||
* evidence or not, regardless of {@link TrackedRace#getTimePointOfNewestEvent()
|
||||
* trackedRace.getTimePointOfNewestEvent()}.
|
||||
* @param windSourceTypeNames
|
||||
* if {@code null}, all wind sources delivered by {@link TrackedRace#getWindSources()} plus the
|
||||
* {@link WindSourceType#COMBINED} wind source are delivered. Note that this does not include
|
||||
* the {@link WindSourceType#LEG_MIDDLE} wind sources.
|
||||
* @param includeCombinedWindForAllLegMiddles
|
||||
* if <code>true</code>, the result will return non-<code>null</code> results for calls to
|
||||
* {@link WindInfoForRaceDTO#getCombinedWindOnLegMiddle(int)}.
|
||||
@@ -1384,20 +1388,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
result.windSourcesToExclude = windSourcesToExclude;
|
||||
Map<WindSource, WindTrackInfoDTO> windTrackInfoDTOs = new HashMap<WindSource, WindTrackInfoDTO>();
|
||||
result.windTrackInfoByWindSource = windTrackInfoDTOs;
|
||||
List<WindSource> windSourcesToDeliver = new ArrayList<WindSource>();
|
||||
Util.addAll(trackedRace.getWindSources(), windSourcesToDeliver);
|
||||
final WindSource combinedWindSource = new WindSourceImpl(WindSourceType.COMBINED);
|
||||
windSourcesToDeliver.add(combinedWindSource);
|
||||
final List<WindSource> windSourcesToDeliver = new ArrayList<WindSource>();
|
||||
final WindSourceImpl combinedWindSource = new WindSourceImpl(WindSourceType.COMBINED);
|
||||
if (windSourceTypeNames == null) {
|
||||
Util.addAll(trackedRace.getWindSources(), windSourcesToDeliver);
|
||||
windSourcesToDeliver.add(combinedWindSource);
|
||||
} else {
|
||||
for (final String windSourceTypeToAdd : windSourceTypeNames) {
|
||||
for (final WindSource windSource : trackedRace.getWindSources(WindSourceType.valueOf(windSourceTypeToAdd))) {
|
||||
windSourcesToDeliver.add(windSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final WindSource windSource : windSourcesToDeliver) {
|
||||
// TODO consider parallelizing
|
||||
if (windSourceTypeNames == null || windSourceTypeNames.contains(windSource.getType().name())) {
|
||||
WindTrackInfoDTO windTrackInfoDTO = createWindTrackInfoDTO(from, millisecondsStepWidth,
|
||||
numberOfFixes, trackedRace, onlyUpToNewestEvent, newestEvent, windSource,
|
||||
new PositionAtTimeProvider() { @Override public Position getPosition(TimePoint at) {
|
||||
return windSource == combinedWindSource ? trackedRace.getCenterOfCourse(at) : null;
|
||||
}});
|
||||
windTrackInfoDTOs.put(windSource, windTrackInfoDTO);
|
||||
}
|
||||
WindTrackInfoDTO windTrackInfoDTO = createWindTrackInfoDTO(from, millisecondsStepWidth,
|
||||
numberOfFixes, trackedRace, onlyUpToNewestEvent, newestEvent, windSource, /* use default positions */ at->null);
|
||||
windTrackInfoDTOs.put(windSource, windTrackInfoDTO);
|
||||
}
|
||||
if (includeCombinedWindForAllLegMiddles) {
|
||||
int zeroBasedLegNumber = 0;
|
||||
|
||||
+7
-2
@@ -1,8 +1,8 @@
|
||||
package com.sap.sailing.polars.datamining.components.aggregators;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
|
||||
@@ -33,7 +33,7 @@ import com.sap.sse.datamining.shared.GroupKey;
|
||||
public class PolarBackendDataAggregationProcessor extends AbstractParallelGroupedDataAggregationProcessor<HasBackendPolarBoatClassContext, PolarBackendData> {
|
||||
|
||||
private static final String POLARS_MESSAGE_KEY = "Polars";
|
||||
private final Map<GroupKey, PolarBackendData> resultMap = new HashMap<>();
|
||||
private final ConcurrentHashMap<GroupKey, PolarBackendData> resultMap = new ConcurrentHashMap<>();
|
||||
|
||||
public PolarBackendDataAggregationProcessor(ExecutorService executor,
|
||||
Collection<Processor<Map<GroupKey, PolarBackendData>, ?>> resultReceivers) {
|
||||
@@ -46,6 +46,11 @@ public class PolarBackendDataAggregationProcessor extends AbstractParallelGroupe
|
||||
public static AggregationProcessorDefinition<HasBackendPolarBoatClassContext, PolarBackendData> getDefinition() {
|
||||
return DEFINITION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsSynchronization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleElement(GroupedDataEntry<HasBackendPolarBoatClassContext> element) {
|
||||
|
||||
+3
@@ -17,4 +17,7 @@ public interface HasGPSFixPolarContext {
|
||||
@Connector(scanForStatistics=false)
|
||||
HasCompetitorPolarContext getCompetitorPolarContext();
|
||||
|
||||
@Dimension(messageKey="IsFoiling")
|
||||
boolean isFoiling();
|
||||
|
||||
}
|
||||
+12
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.polars.datamining.data.HasCompetitorPolarContext;
|
||||
@@ -51,4 +52,15 @@ public class GPSFixWithPolarContext implements HasGPSFixPolarContext {
|
||||
return competitorPolarContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFoiling() {
|
||||
final boolean result;
|
||||
final BravoFixTrack<Competitor> competitorBravoFixTrack = trackedRace.getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
if (competitorBravoFixTrack != null) {
|
||||
result = competitorBravoFixTrack.isFoiling(fix.getTimePoint());
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+7
-20
@@ -18,24 +18,23 @@ public class DefaultWindTrackJsonSerializer implements WindTrackJsonSerializer {
|
||||
public static final String FIELD_PUBLICATION_URL = "publicationUrl";
|
||||
public static final String FIELD_VENUE = "venue";
|
||||
|
||||
private TimePoint fromTime;
|
||||
private TimePoint toTime;
|
||||
private WindSource windSource;
|
||||
private final TimePoint fromTime;
|
||||
private final TimePoint toTime;
|
||||
private final WindSource windSource;
|
||||
|
||||
/**
|
||||
* -1 means unlimited.
|
||||
*/
|
||||
private final int maxNumberOfFixes;
|
||||
|
||||
public DefaultWindTrackJsonSerializer(int maxNumberOfFixes) {
|
||||
public DefaultWindTrackJsonSerializer(int maxNumberOfFixes, TimePoint fromTime, TimePoint toTime, WindSource windSource) {
|
||||
super();
|
||||
this.maxNumberOfFixes = maxNumberOfFixes;
|
||||
this.fromTime = fromTime;
|
||||
this.toTime = toTime;
|
||||
this.windSource = windSource;
|
||||
}
|
||||
|
||||
public DefaultWindTrackJsonSerializer() {
|
||||
this(-1);
|
||||
}
|
||||
|
||||
public JSONObject serialize(WindTrack windTrack) {
|
||||
JSONObject result = new JSONObject();
|
||||
JSONArray jsonWindFixes = new JSONArray();
|
||||
@@ -74,16 +73,4 @@ public class DefaultWindTrackJsonSerializer implements WindTrackJsonSerializer {
|
||||
result.put(windSource.getType() + (windSource.getId() != null ? "-"+windSource.getId().toString() : ""), jsonWindFixes);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setFromTime(TimePoint fromTime) {
|
||||
this.fromTime = fromTime;
|
||||
}
|
||||
|
||||
public void setToTime(TimePoint toTime) {
|
||||
this.toTime = toTime;
|
||||
}
|
||||
|
||||
public void setWindSource(WindSource windSource) {
|
||||
this.windSource = windSource;
|
||||
}
|
||||
}
|
||||
+9
-15
@@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.serialization.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
@@ -12,7 +13,6 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
|
||||
public static final String FIELD_ID = "id";
|
||||
@@ -24,16 +24,12 @@ public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
|
||||
|
||||
private final String windSourceToSerialize;
|
||||
private final String windSourceIdToSerialize;
|
||||
private final TimePoint fromTime;
|
||||
private final TimePoint toTime;
|
||||
private final WindTrackJsonSerializer windTrackSerializer;
|
||||
private final Function<WindSource, WindTrackJsonSerializer> windTrackSerializerProducer;
|
||||
|
||||
public TrackedRaceJsonSerializer(WindTrackJsonSerializer windTrackSerializer, String windSourceToSerialize, String windSourceIdToSerialize, TimePoint fromTime, TimePoint toTime) {
|
||||
this.windTrackSerializer = windTrackSerializer;
|
||||
public TrackedRaceJsonSerializer(Function<WindSource, WindTrackJsonSerializer> windTrackSerializer, String windSourceToSerialize, String windSourceIdToSerialize) {
|
||||
this.windTrackSerializerProducer = windTrackSerializer;
|
||||
this.windSourceToSerialize = windSourceToSerialize;
|
||||
this.windSourceIdToSerialize = windSourceIdToSerialize;
|
||||
this.fromTime = fromTime;
|
||||
this.toTime = toTime;
|
||||
windSourceToSerialize = WindSourceType.COMBINED.name();
|
||||
}
|
||||
|
||||
@@ -43,7 +39,7 @@ public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
|
||||
jsonRace.put(FIELD_NAME, trackedRace.getRace().getName());
|
||||
jsonRace.put(FIELD_REGATTA, trackedRace.getRaceIdentifier().getRegattaName());
|
||||
|
||||
if(windTrackSerializer != null) {
|
||||
if (windTrackSerializerProducer != null) {
|
||||
JSONArray windTracks = new JSONArray();
|
||||
|
||||
List<WindSource> windSources = getAvailableWindSources(trackedRace);
|
||||
@@ -60,18 +56,13 @@ public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
|
||||
if (windSourceIdToSerialize != null && windSource.getId() != null && !windSource.getId().toString().equalsIgnoreCase(windSourceIdToSerialize)) {
|
||||
continue;
|
||||
}
|
||||
windTrackSerializer.setFromTime(fromTime);
|
||||
windTrackSerializer.setToTime(toTime);
|
||||
windTrackSerializer.setWindSource(windSource);
|
||||
|
||||
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
JSONObject jsonWindTrack = windTrackSerializer.serialize(windTrack);
|
||||
JSONObject jsonWindTrack = windTrackSerializerProducer.apply(windSource).serialize(windTrack);
|
||||
windTracks.add(jsonWindTrack);
|
||||
}
|
||||
}
|
||||
jsonRace.put(FIELD_WINDSOURCES, windTracks);
|
||||
}
|
||||
|
||||
return jsonRace;
|
||||
}
|
||||
|
||||
@@ -84,6 +75,9 @@ public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
|
||||
windSources.remove(windSourceToExclude);
|
||||
}
|
||||
windSources.add(new WindSourceImpl(WindSourceType.COMBINED));
|
||||
for (final WindSource trackedLegMiddleWindSource : trackedRace.getWindSources(WindSourceType.LEG_MIDDLE)) {
|
||||
windSources.add(trackedLegMiddleWindSource);
|
||||
}
|
||||
return windSources;
|
||||
}
|
||||
}
|
||||
-5
@@ -1,12 +1,7 @@
|
||||
package com.sap.sailing.server.gateway.serialization.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public interface WindTrackJsonSerializer extends JsonSerializer<WindTrack> {
|
||||
void setFromTime(TimePoint fromTime);
|
||||
void setToTime(TimePoint toTime);
|
||||
void setWindSource(WindSource windSource);
|
||||
}
|
||||
+3
-3
@@ -758,10 +758,10 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
// Crop request interval to startOfTracking / [endOfTracking|timePointOfLastEvent]
|
||||
from = Util.getLatestOfTimePoints(from, trackedRace.getStartOfTracking());
|
||||
to = Util.getEarliestOfTimePoints(to, Util.getEarliestOfTimePoints(trackedRace.getEndOfTracking(), trackedRace.getTimePointOfNewestEvent()));
|
||||
final TimePoint finalFrom = Util.getLatestOfTimePoints(from, trackedRace.getStartOfTracking());
|
||||
final TimePoint finalTo = Util.getEarliestOfTimePoints(to, Util.getEarliestOfTimePoints(trackedRace.getEndOfTracking(), trackedRace.getTimePointOfNewestEvent()));
|
||||
TrackedRaceJsonSerializer serializer = new TrackedRaceJsonSerializer(
|
||||
new DefaultWindTrackJsonSerializer(/* maxNumberOfFixes */ 10000), windSource, windSourceId, from, to);
|
||||
ws->new DefaultWindTrackJsonSerializer(/* maxNumberOfFixes */ 10000, finalFrom, finalTo, ws), windSource, windSourceId);
|
||||
|
||||
JSONObject jsonWindTracks = serializer.serialize(trackedRace);
|
||||
String json = jsonWindTracks.toJSONString();
|
||||
|
||||
+3
-2
@@ -307,9 +307,10 @@ public class MasterDataImportTest {
|
||||
logTimePoint4, author, competitor, deviceIdentifier2, logTimePoint,
|
||||
logTimePoint3);
|
||||
regatta.getRegattaLog().add(bravoMappingEvent);
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.columnCount];
|
||||
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.trackColumnCount];
|
||||
double rideHeightValue = 1337.0;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightColumn] = rideHeightValue;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightPortHullColumn] = rideHeightValue;
|
||||
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightStarboardHullColumn] = rideHeightValue;
|
||||
DoubleVectorFixImpl doubleVectorFix = new DoubleVectorFixImpl(logTimePoint2, fixData);
|
||||
sourceService.getSensorFixStore().storeFix(deviceIdentifier2, doubleVectorFix);
|
||||
|
||||
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
package com.sap.sailing.server.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
||||
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveEvent;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardGroup;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class RemoveLeaderboardTest {
|
||||
private static final String R1 = "R1";
|
||||
private RacingEventServiceImpl server;
|
||||
private static final String LEADERBOARD_NAME = "A Flexible Leaderboard";
|
||||
private static final String RACENAME1 = "racedef1";
|
||||
private final static String EVENTNAME = "TESTEVENT";
|
||||
private final static String BOATCLASSNAME = "HAPPYBOATCLASS";
|
||||
private final static String SERIES_NAME = "Opening";
|
||||
|
||||
private Regatta regatta;
|
||||
private BoatClass boatClass;
|
||||
private RaceDefinition raceDef1;
|
||||
private DynamicTrackedRace trackedRace;
|
||||
private Fleet defaultFleet;
|
||||
private FlexibleLeaderboard leaderboard;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
server = new RacingEventServiceImpl();
|
||||
List<Event> allEvents = new ArrayList<>();
|
||||
Util.addAll(server.getAllEvents(), allEvents);
|
||||
for (final Event e : allEvents) {
|
||||
server.apply(new RemoveEvent(e.getId()));
|
||||
}
|
||||
Map<String, Leaderboard> allLeaderboards = new HashMap<>(server.getLeaderboards());
|
||||
for (final String leaderboardName : allLeaderboards.keySet()) {
|
||||
server.apply(new RemoveLeaderboard(leaderboardName));
|
||||
}
|
||||
Map<String, LeaderboardGroup> allLeaderboardGroups = new HashMap<>(server.getLeaderboardGroups());
|
||||
for (final String leaderboardGroupName : allLeaderboardGroups.keySet()) {
|
||||
server.apply(new RemoveLeaderboardGroup(leaderboardGroupName));
|
||||
}
|
||||
leaderboard = server.apply(
|
||||
new CreateFlexibleLeaderboard(LEADERBOARD_NAME, /* display name */ null, new int[0], new LowPoint(), /* default course area ID */ null));
|
||||
leaderboard.addRaceColumn(R1, /* medalRace */ false);
|
||||
|
||||
boatClass = new BoatClassImpl(BOATCLASSNAME, /* typicallyStartsUpwind */ true);
|
||||
regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
|
||||
RegattaImpl.getDefaultName(EVENTNAME, boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */
|
||||
null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), UUID.randomUUID(), null);
|
||||
TrackedRegatta trackedRegatta1 = server.getOrCreateTrackedRegatta(regatta);
|
||||
defaultFleet = new FleetImpl("Default");
|
||||
regatta.addSeries(new SeriesImpl(SERIES_NAME, /* is medal */ false, /* can fleets run in parallel */ true,
|
||||
Collections.singleton(defaultFleet), Collections.singleton(R1),
|
||||
/* regatta registry */ server));
|
||||
raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList<Waypoint>()), boatClass, new ArrayList<Competitor>());
|
||||
regatta.addRace(raceDef1);
|
||||
trackedRace = trackedRegatta1.createTrackedRace(raceDef1, Collections.<Sideline> emptyList(),
|
||||
/* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l,
|
||||
/* millisecondsOverWhichToAverageWind */ 0l,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaceLogOfRaceColumnInSeriesOfRegattaIsAttached() {
|
||||
regatta.getRaceColumnByName(R1).setTrackedRace(defaultFleet, trackedRace);
|
||||
assertEquals(1, Util.size(trackedRace.getAttachedRaceLogs()));
|
||||
RaceLog raceLog = trackedRace.getAttachedRaceLogs().iterator().next();
|
||||
assertSame(regatta.getRaceColumnByName(R1).getRaceLog(defaultFleet), raceLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaceLogOfRaceColumnInSeriesDetachedWhenRegattaIsRemoved() {
|
||||
regatta.getRaceColumnByName(R1).setTrackedRace(defaultFleet, trackedRace);
|
||||
assertEquals(1, Util.size(trackedRace.getAttachedRaceLogs()));
|
||||
server.apply(new RemoveRegatta(new RegattaName(regatta.getName())));
|
||||
assertTrue(Util.isEmpty(trackedRace.getAttachedRaceLogs()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaceLogOfRaceColumnInFlexibleLeaderboardIsAttached() {
|
||||
leaderboard.getRaceColumnByName(R1).setTrackedRace(leaderboard.getRaceColumnByName(R1).getFleets().iterator().next(), trackedRace);
|
||||
assertEquals(1, Util.size(trackedRace.getAttachedRaceLogs()));
|
||||
RaceLog raceLog = trackedRace.getAttachedRaceLogs().iterator().next();
|
||||
assertSame(leaderboard.getRaceColumnByName(R1).getRaceLog(leaderboard.getRaceColumnByName(R1).getFleets().iterator().next()), raceLog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovingFlexibleLeaderboardDetachesAllRaceLogsFromTrackedRaces() {
|
||||
leaderboard.getRaceColumnByName(R1).setTrackedRace(leaderboard.getRaceColumnByName(R1).getFleets().iterator().next(), trackedRace);
|
||||
assertEquals(1, Util.size(trackedRace.getAttachedRaceLogs()));
|
||||
server.apply(new RemoveLeaderboard(LEADERBOARD_NAME));
|
||||
assertTrue(Util.isEmpty(trackedRace.getAttachedRaceLogs()));
|
||||
}
|
||||
}
|
||||
+28
-11
@@ -9,15 +9,31 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.common.tracking.impl.BravoFixImpl;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter;
|
||||
import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
|
||||
import com.sap.sailing.server.trackfiles.impl.BravoDataImporterImpl;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DownsamplerTo1HzProcessor;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.LearningBatchProcessor;
|
||||
|
||||
public class BravoDataImportTest {
|
||||
|
||||
private final DoubleVectorFixImporter bravoDataImporter = new BravoDataImporterImpl();
|
||||
private LearningBatchProcessor batchProcessor;
|
||||
private DownsamplerTo1HzProcessor downsampler;
|
||||
|
||||
private final DoubleVectorFixImporter bravoDataImporter = new BravoDataImporterImpl() {
|
||||
protected com.sap.sailing.server.trackfiles.impl.doublefix.DoubleFixProcessor createProcessor(
|
||||
BravoSensorDataMetadata metadata,
|
||||
DoubleVectorFixImporter.Callback callback,
|
||||
TrackFileImportDeviceIdentifier trackIdentifier) {
|
||||
batchProcessor = new LearningBatchProcessor(5000, 5000, callback, trackIdentifier);
|
||||
downsampler = new DownsamplerTo1HzProcessor(metadata.trackColumnCount, batchProcessor);
|
||||
return downsampler;
|
||||
};
|
||||
};
|
||||
private int callbackCallCount = 0;
|
||||
private double sumRideHeightInMeters = 0.0;
|
||||
|
||||
@@ -48,39 +64,40 @@ public class BravoDataImportTest {
|
||||
}
|
||||
|
||||
private void testImport(ImportData importData) throws FormatNotSupportedException, IOException {
|
||||
|
||||
bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> {
|
||||
for (DoubleVectorFix fix : fixes) {
|
||||
callbackCallCount++;
|
||||
sumRideHeightInMeters += new BravoFixImpl(fix).getRideHeight().getMeters();
|
||||
}
|
||||
}, "filename", "source");
|
||||
Assert.assertEquals(importData.expectedFixesCount, callbackCallCount);
|
||||
Assert.assertEquals(importData.expectedAverageRideHeight,
|
||||
callbackCallCount == 0 ? sumRideHeightInMeters : sumRideHeightInMeters / callbackCallCount, 0.00001);
|
||||
Assert.assertEquals(importData.expectedFixesCount, downsampler.getCountSourceTtl());
|
||||
Assert.assertEquals(importData.expectedFixesConsolidated, downsampler.getCountImportedTtl());
|
||||
Assert.assertEquals(importData.expectedFixesConsolidated, callbackCallCount);
|
||||
}
|
||||
|
||||
private enum ImportData {
|
||||
// find out the number of fixes using the following bash line:
|
||||
// tail -n +5 Undefined\ Race\ -\ BRAVO.txt | awk '{if ($5<$6) print v=$5; else v=$6; sum+=v; count++; } END {print "Sum: ", sum, "Count: ", count, "Average:", sum/count;}'
|
||||
FILE_UNDEFINED_RACE_BRAVO(870, 0.661037) {
|
||||
FILE_UNDEFINED_RACE_BRAVO(870, 89) {
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return getClass().getResourceAsStream("/Undefined Race - BRAVO.txt");
|
||||
}
|
||||
},
|
||||
DUMMY_DEFAULT_HEADER_NO_DATA(0, 0.0) {
|
||||
DUMMY_DEFAULT_HEADER_NO_DATA(0, 0) {
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return new ByteArrayInputStream(HEADER_ORDER_DEFAULT.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
},
|
||||
DUMMY_DEAFULT_HEADER_ONE_LINE(1, 0.661044) {
|
||||
DUMMY_DEAFULT_HEADER_ONE_LINE(1, 1) {
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return new ByteArrayInputStream((HEADER_ORDER_DEFAULT + DUMMY_CONTENT).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
},
|
||||
DUMMY_RANDOM_HAEDER_ONE_LINE(1, 0.092727) {
|
||||
DUMMY_RANDOM_HAEDER_ONE_LINE(1, 1) {
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return new ByteArrayInputStream((HEADER_ORDER_RANDOM + DUMMY_CONTENT).getBytes(StandardCharsets.UTF_8));
|
||||
@@ -88,11 +105,11 @@ public class BravoDataImportTest {
|
||||
};
|
||||
|
||||
private final int expectedFixesCount;
|
||||
private final double expectedAverageRideHeight;
|
||||
private final int expectedFixesConsolidated;
|
||||
|
||||
private ImportData(int expectedFixesCount, double expectedAverageRideHeight) {
|
||||
private ImportData(int expectedFixesCount, int expectedFixesConsolidated) {
|
||||
this.expectedFixesCount = expectedFixesCount;
|
||||
this.expectedAverageRideHeight = expectedAverageRideHeight;
|
||||
this.expectedFixesConsolidated = expectedFixesConsolidated;
|
||||
}
|
||||
|
||||
protected abstract InputStream getInputStream();
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.sap.sailing.server.trackfiles.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DoubleFixProcessor;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DoubleVectorFixData;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DownsamplerTo1HzProcessor;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class DownsamplerTo1HzProcessorTest {
|
||||
|
||||
@Test
|
||||
public void testConsolidation() {
|
||||
|
||||
MyProcessor delegate = new MyProcessor();
|
||||
DownsamplerTo1HzProcessor processor = new DownsamplerTo1HzProcessor(4, delegate);
|
||||
int second = 1 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(second + 100, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 300, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 550, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 900, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 999, new double[] { 0d, 0d, 0d, 0d }));
|
||||
second = 10 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(second + 130, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 200, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 560, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 600, new double[] { 0d, 0d, 0d, 0d }));
|
||||
second = 30 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(second + 130, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 200, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 560, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 561, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 562, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 563, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 564, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 600, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 700, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 800, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 900, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 910, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 920, new double[] { 0d, 0d, 0d, 0d }));
|
||||
second = 50 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(second + 130, new double[] { 0d, 0d, 0d, 0d }));
|
||||
Assert.assertEquals("Count consolidated fixes", 3, delegate.countAccepted);
|
||||
Assert.assertFalse("Not finished yet", delegate.fineshedWasCalled);
|
||||
|
||||
processor.finish();
|
||||
|
||||
Assert.assertEquals("Count consolidated fixes", 4, delegate.countAccepted);
|
||||
Assert.assertTrue("Finished", delegate.fineshedWasCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAverageComputation() {
|
||||
MyProcessor delegate = new MyProcessor();
|
||||
DownsamplerTo1HzProcessor processor = new DownsamplerTo1HzProcessor(4, delegate);
|
||||
int second = 1 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(second + 100, new double[] { 12.40d, 1d, 1d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 300, new double[] { 0.01d, 2d, 1d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 550, new double[] { -10.00d, 3d, 1d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 900, new double[] { 1.00d, 2d, 1d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(second + 999, new double[] { 0.00d, 1d, 1d, 0d }));
|
||||
|
||||
processor.finish();
|
||||
Assert.assertTrue("Finished", delegate.fineshedWasCalled);
|
||||
Assert.assertEquals("Count consolidated fixes", 1, delegate.countAccepted);
|
||||
DoubleVectorFixData lastFix = delegate.lastFix;
|
||||
Assert.assertNotNull("Lastfix", lastFix);
|
||||
Assert.assertEquals("Avg col 1", (12.4d + 0.01d - 10d + 1d + 0d) / 5d, lastFix.getFix()[0]);
|
||||
Assert.assertEquals("Avg col 2", 9d / 5d, lastFix.getFix()[1]);
|
||||
Assert.assertEquals("Avg col 3", 1d, lastFix.getFix()[2]);
|
||||
Assert.assertEquals("Avg col 4", 0d, lastFix.getFix()[3]);
|
||||
}
|
||||
|
||||
private final class MyProcessor implements DoubleFixProcessor {
|
||||
public int countAccepted = 0;
|
||||
public boolean fineshedWasCalled = false;
|
||||
public DoubleVectorFixData lastFix;
|
||||
@Override
|
||||
public void finish() {
|
||||
fineshedWasCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DoubleVectorFixData fix) {
|
||||
lastFix = fix;
|
||||
countAccepted++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.sap.sailing.server.trackfiles.test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DoubleVectorFixData;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.LearningBatchProcessor;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class LearningBatchProcessorTest {
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testConsolidation() {
|
||||
|
||||
MyCallback delegate = new MyCallback();
|
||||
LearningBatchProcessor processor = new LearningBatchProcessor(5, 10, delegate, device);
|
||||
int currentSecondAsMillis = 1 * 1000;
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 100, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 300, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 550, new double[] { 0d, 0d, 0d, 0d }));
|
||||
Assert.assertEquals("Batch not full", 0, delegate.batchesDelivered);
|
||||
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 900, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 999, new double[] { 0d, 0d, 0d, 0d }));
|
||||
Assert.assertEquals("Batch not full, should be learing", 0, delegate.batchesDelivered);
|
||||
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 130, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 200, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 560, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 600, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 130, new double[] { 0d, 0d, 0d, 0d }));
|
||||
Assert.assertEquals("Batches after learning", 2, delegate.batchesDelivered);
|
||||
Assert.assertEquals("Last batch after learning", 5, delegate.lastBatchSize);
|
||||
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 200, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 560, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 561, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 562, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 563, new double[] { 0d, 0d, 0d, 0d }));
|
||||
Assert.assertEquals("Next batch received", 3, delegate.batchesDelivered);
|
||||
Assert.assertEquals("First batch after learning", 5, delegate.lastBatchSize);
|
||||
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 564, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 600, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 700, new double[] { 0d, 0d, 0d, 0d }));
|
||||
processor.accept(new DoubleVectorFixData(currentSecondAsMillis + 800, new double[] { 0d, 0d, 0d, 0d }));
|
||||
|
||||
Assert.assertEquals("Still on same batch", 3, delegate.batchesDelivered);
|
||||
processor.finish();
|
||||
|
||||
Assert.assertEquals("Batch count after finish", 4, delegate.batchesDelivered);
|
||||
Assert.assertEquals("Last batch size after finish", 4, delegate.lastBatchSize);
|
||||
Assert.assertEquals("Last fix ", (currentSecondAsMillis + 800), delegate.lastFix.getTimePoint().asMillis());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final class MyCallback implements DoubleVectorFixImporter.Callback {
|
||||
public int lastBatchSize = 0;
|
||||
public int batchesDelivered = 0;
|
||||
public DoubleVectorFix lastFix;
|
||||
|
||||
@Override
|
||||
public void addFixes(Iterable<DoubleVectorFix> fix, TrackFileImportDeviceIdentifier device) {
|
||||
lastBatchSize = Util.size(fix);
|
||||
batchesDelivered++;
|
||||
lastFix = Util.last(fix);
|
||||
}
|
||||
}
|
||||
|
||||
TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifier() {
|
||||
private static final long serialVersionUID = 870061505508090216L;
|
||||
private UUID uuid = UUID.randomUUID();
|
||||
private MillisecondsTimePoint uploadedAt = new MillisecondsTimePoint(0);
|
||||
|
||||
@Override
|
||||
public String getStringRepresentation() {
|
||||
return "stringRepresentation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierType() {
|
||||
return "identifierType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getUploadedAt() {
|
||||
return uploadedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrackName() {
|
||||
return "trackname";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return "filename";
|
||||
}
|
||||
};
|
||||
}
|
||||
+42
-50
@@ -5,12 +5,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -18,7 +12,6 @@ import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
@@ -26,13 +19,15 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompeti
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
|
||||
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter;
|
||||
import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DoubleFixProcessor;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DoubleVectorFixData;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.DownsamplerTo1HzProcessor;
|
||||
import com.sap.sailing.server.trackfiles.impl.doublefix.LearningBatchProcessor;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
@@ -42,10 +37,8 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
*/
|
||||
public class BravoDataImporterImpl implements DoubleVectorFixImporter {
|
||||
private final Logger LOG = Logger.getLogger(DoubleVectorFixImporter.class.getName());
|
||||
private final static DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyyMMdd.HHmmss.SSSSSS");
|
||||
private final BravoSensorDataMetadata metadata = BravoSensorDataMetadata.INSTANCE;
|
||||
private final String BOF = "jjlDATE\tjjlTIME\tEpoch";
|
||||
private static final int BATCH_SIZE = 5000;
|
||||
|
||||
public void importFixes(InputStream inputStream, Callback callback, final String filename, String sourceName)
|
||||
throws FormatNotSupportedException, IOException {
|
||||
@@ -79,22 +72,15 @@ public class BravoDataImporterImpl implements DoubleVectorFixImporter {
|
||||
}
|
||||
LOG.fine("Validate and parse header columns");
|
||||
final Map<String, Integer> colIndices = validateAndParseHeader(headerLine);
|
||||
LOG.fine("Parse and store data in batches of " + BATCH_SIZE + " items");
|
||||
final ArrayList<DoubleVectorFix> collectedFixes = new ArrayList<>(BATCH_SIZE);
|
||||
|
||||
DoubleFixProcessor downsampler = createProcessor(metadata, callback, trackIdentifier);
|
||||
|
||||
buffer.lines().forEach(line -> {
|
||||
lineNr.incrementAndGet();
|
||||
DoubleVectorFixImpl vectorFix = parseLine(lineNr.get(), filename, line, colIndices);
|
||||
if (vectorFix != null) {
|
||||
collectedFixes.add(vectorFix);
|
||||
if (collectedFixes.size() == BATCH_SIZE) {
|
||||
callback.addFixes(collectedFixes, trackIdentifier);
|
||||
collectedFixes.clear();
|
||||
}
|
||||
}
|
||||
downsampler.accept(parseLine(lineNr.get(), filename, line, colIndices));
|
||||
});
|
||||
if (!collectedFixes.isEmpty()) {
|
||||
callback.addFixes(collectedFixes, trackIdentifier);
|
||||
}
|
||||
|
||||
downsampler.finish();
|
||||
buffer.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -102,40 +88,46 @@ public class BravoDataImporterImpl implements DoubleVectorFixImporter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates the double fix processor chain used to downsample and batch the stream of fixes parsed by the
|
||||
* importer.
|
||||
*
|
||||
* This method is protected so it can be overridden by the test case.
|
||||
*
|
||||
* @param metadata
|
||||
* @param callback
|
||||
* @param trackIdentifier
|
||||
* @return
|
||||
*/
|
||||
protected DoubleFixProcessor createProcessor(BravoSensorDataMetadata metadata, Callback callback,
|
||||
final TrackFileImportDeviceIdentifier trackIdentifier) {
|
||||
LearningBatchProcessor batchProcessor = new LearningBatchProcessor(5000, 5000, callback, trackIdentifier);
|
||||
DoubleFixProcessor downsampler = new DownsamplerTo1HzProcessor(metadata.trackColumnCount, batchProcessor);
|
||||
return downsampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the CSV line and reads the double data values in the order defined by the col enums.
|
||||
*/
|
||||
private DoubleVectorFixImpl parseLine(long lineNr, String filename, String line, Map<String, Integer> colIndices) {
|
||||
private DoubleVectorFixData parseLine(long lineNr, String filename, String line, Map<String, Integer> columnsInFile) {
|
||||
try {
|
||||
String[] contentTokens = split(line);
|
||||
TimePoint fixTp;
|
||||
String epochColValue = contentTokens[2];
|
||||
String[] fileContentTokens = split(line);
|
||||
String epochColValue = fileContentTokens[2];
|
||||
long epoch;
|
||||
if (epochColValue != null && epochColValue.length() > 0) {
|
||||
epochColValue = epochColValue.substring(0, epochColValue.indexOf("."));
|
||||
long epoch = Long.parseLong(epochColValue);
|
||||
fixTp = new MillisecondsTimePoint(epoch);
|
||||
epoch = Long.parseLong(epochColValue);
|
||||
} else {
|
||||
String jjLDATE = contentTokens[0].substring(0, contentTokens[0].indexOf("."));
|
||||
StringBuilder dtb = new StringBuilder(jjLDATE);
|
||||
String jjlTIME = contentTokens[1];
|
||||
int offset = 6 - jjlTIME.indexOf(".");
|
||||
IntStream.range(0, offset).forEach(n -> dtb.append("0"));
|
||||
dtb.append(jjlTIME);
|
||||
LocalDateTime day = DATE_FMT.parse(dtb.toString(), LocalDateTime::from);
|
||||
Instant instant = day.toInstant(ZoneOffset.UTC);
|
||||
fixTp = new MillisecondsTimePoint(Date.from(instant));
|
||||
// we don't have epoch, skip the line
|
||||
return null;
|
||||
}
|
||||
// code for time adjustment for foiling test data
|
||||
// long r16TP = ZonedDateTime.of(LocalDateTime.of(2016, 3, 19, 11, 55), ZoneId.of("Europe/Berlin"))
|
||||
// .toEpochSecond() * 1000;
|
||||
// fixTp = fixTp.plus(r16TP - 1461828459589l);
|
||||
double[] fixData = new double[metadata.columnCount];
|
||||
for (int columnIndexInFix = 0; columnIndexInFix < fixData.length; columnIndexInFix++) {
|
||||
String columnName = metadata.getColumns().get(columnIndexInFix);
|
||||
Integer columnIndexInFile = colIndices.get(columnName);
|
||||
fixData[columnIndexInFix] = Double.parseDouble(contentTokens[columnIndexInFile]);
|
||||
double[] trackFixData = new double[metadata.trackColumnCount];
|
||||
for (int trackColumnIdx = 0; trackColumnIdx < metadata.trackColumnCount; trackColumnIdx++) {
|
||||
String columnNameToSearchForInFile = metadata.getTrackColumns().get(trackColumnIdx);
|
||||
Integer columnsInFileIdx = columnsInFile.get(columnNameToSearchForInFile);
|
||||
trackFixData[trackColumnIdx] = Double.parseDouble(fileContentTokens[columnsInFileIdx]);
|
||||
}
|
||||
return new DoubleVectorFixImpl(fixTp, fixData);
|
||||
return new DoubleVectorFixData(epoch, trackFixData);
|
||||
} catch (Exception e) {
|
||||
LOG.warning(
|
||||
"Error parsing line nr " + lineNr + " in file " + filename + "with exception: " + e.getMessage());
|
||||
@@ -150,7 +142,7 @@ public class BravoDataImporterImpl implements DoubleVectorFixImporter {
|
||||
String header = headerTokens[j];
|
||||
colIndicesInFile.put(header, j);
|
||||
}
|
||||
List<String> columnsInFix = metadata.getColumns();
|
||||
List<String> columnsInFix = metadata.getFileColumns();
|
||||
if (colIndicesInFile.size() != columnsInFix.size() || !colIndicesInFile.keySet().containsAll(columnsInFix)) {
|
||||
LOG.log(Level.SEVERE, "Missing headers");
|
||||
throw new RuntimeException("Missing headers in import files");
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.sap.sailing.server.trackfiles.impl.doublefix;
|
||||
|
||||
|
||||
public interface DoubleFixProcessor {
|
||||
void accept(DoubleVectorFixData fix);
|
||||
void finish();
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.sap.sailing.server.trackfiles.impl.doublefix;
|
||||
|
||||
public final class DoubleVectorFixData {
|
||||
private long timepointInMs;
|
||||
private double[] fix;
|
||||
|
||||
public DoubleVectorFixData(long timepoint, double[] fix) {
|
||||
super();
|
||||
this.timepointInMs = timepoint;
|
||||
this.fix = fix;
|
||||
}
|
||||
|
||||
public void correctTimepointBy(long offset) {
|
||||
timepointInMs += offset;
|
||||
}
|
||||
|
||||
public long getTimepointInMs() {
|
||||
return timepointInMs;
|
||||
}
|
||||
|
||||
public long getFixSecond() {
|
||||
return timepointInMs / 1000;
|
||||
}
|
||||
|
||||
public double[] getFix() {
|
||||
return fix;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.sap.sailing.server.trackfiles.impl.doublefix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter;
|
||||
|
||||
/**
|
||||
* This class consolidates fixes in a sub-second range to one fix per second and computes the average of all values.
|
||||
*
|
||||
*/
|
||||
public final class DownsamplerTo1HzProcessor implements DoubleFixProcessor {
|
||||
private final Logger LOG = Logger.getLogger(DoubleVectorFixImporter.class.getName());
|
||||
final private ArrayList<DoubleVectorFixData> fixesInTheCurrentSecond = new ArrayList<>();
|
||||
final private int nrOfColumsInTrack;
|
||||
final private DoubleFixProcessor delegateProcesssor;
|
||||
private long currentSecond = 0;
|
||||
private long currentOffsetInMs = 0;
|
||||
private long countSourceTtl = 0;
|
||||
private long countImportedTtl = 0;
|
||||
|
||||
public DownsamplerTo1HzProcessor(int nrOfColumsInTrack,
|
||||
DoubleFixProcessor consumerOfConsolidatedFixes) {
|
||||
this.nrOfColumsInTrack = nrOfColumsInTrack;
|
||||
this.delegateProcesssor = consumerOfConsolidatedFixes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DoubleVectorFixData fix) {
|
||||
if (fix == null)
|
||||
return;
|
||||
fix.correctTimepointBy(currentOffsetInMs);
|
||||
if (fix.getFixSecond() < currentSecond) {
|
||||
currentOffsetInMs = (currentSecond - fix.getFixSecond() + 1) * 1000;
|
||||
LOG.warning("Timepoint before last second, using offser of " + currentOffsetInMs + "ms from now on");
|
||||
}
|
||||
if (currentSecond != fix.getFixSecond()) {
|
||||
computeDownsampledFixForCurrentSecond();
|
||||
currentSecond = fix.getFixSecond();
|
||||
}
|
||||
countSourceTtl++;
|
||||
fixesInTheCurrentSecond.add(fix);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
computeDownsampledFixForCurrentSecond();
|
||||
delegateProcesssor.finish();
|
||||
LOG.fine("Imported " + countImportedTtl + " fixes from " + countSourceTtl);
|
||||
}
|
||||
|
||||
private void computeDownsampledFixForCurrentSecond() {
|
||||
final double[] computedAverage = new double[nrOfColumsInTrack];
|
||||
final int numberOfFixesInSecond = fixesInTheCurrentSecond.size();
|
||||
if (numberOfFixesInSecond == 0) {
|
||||
return;
|
||||
}
|
||||
for (DoubleVectorFixData d : fixesInTheCurrentSecond) {
|
||||
final double[] fix = d.getFix();
|
||||
for (int colIdx = 0; colIdx < nrOfColumsInTrack; colIdx++) {
|
||||
computedAverage[colIdx] += fix[colIdx];
|
||||
}
|
||||
}
|
||||
fixesInTheCurrentSecond.clear();
|
||||
for (int colIdx = 0; colIdx < nrOfColumsInTrack; colIdx++) {
|
||||
computedAverage[colIdx] /= (double) numberOfFixesInSecond;
|
||||
}
|
||||
delegateProcesssor.accept(new DoubleVectorFixData(currentSecond * 1000 + 500, computedAverage));
|
||||
countImportedTtl++;
|
||||
}
|
||||
|
||||
public long getCountImportedTtl() {
|
||||
return countImportedTtl;
|
||||
}
|
||||
|
||||
public long getCountSourceTtl() {
|
||||
return countSourceTtl;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.sap.sailing.server.trackfiles.impl.doublefix;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter.Callback;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public final class LearningBatchProcessor implements DoubleFixProcessor {
|
||||
private final int batchSize;
|
||||
private final ArrayList<DoubleVectorFixData> learnedFixes;
|
||||
private final ArrayList<DoubleVectorFix> collectedFixes;
|
||||
private final Callback callback;
|
||||
private final TrackFileImportDeviceIdentifier deviceIdentifier;
|
||||
private boolean isLearning = true;
|
||||
private long fixesToLearn;
|
||||
|
||||
public LearningBatchProcessor(int batchSize, int fixesToLearn, Callback callback,
|
||||
TrackFileImportDeviceIdentifier deviceIdentifier) {
|
||||
super();
|
||||
this.batchSize = batchSize;
|
||||
this.fixesToLearn = fixesToLearn;
|
||||
this.collectedFixes = new ArrayList<>(batchSize);
|
||||
this.learnedFixes = new ArrayList<>(fixesToLearn);
|
||||
this.callback = callback;
|
||||
this.deviceIdentifier = deviceIdentifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DoubleVectorFixData fix) {
|
||||
if (isLearning) {
|
||||
learnFix(fix);
|
||||
} else {
|
||||
processFix(fix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the learn phase the fixes just get collected for learning.
|
||||
*
|
||||
* @param fix
|
||||
*/
|
||||
private void learnFix(DoubleVectorFixData fix) {
|
||||
learnedFixes.add(fix);
|
||||
|
||||
if (learnedFixes.size() >= fixesToLearn) {
|
||||
finishLearning();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishLearning() {
|
||||
// Now we have enough data to process
|
||||
isLearning = false;
|
||||
// process the loaded learning fixes
|
||||
for (DoubleVectorFixData doubleVectorFix : learnedFixes) {
|
||||
processFix(doubleVectorFix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When processing a fix, data can be corrected before added to the collection of Fixes that will get stored in the
|
||||
* mongo....
|
||||
*
|
||||
* @param fix
|
||||
*/
|
||||
private void processFix(DoubleVectorFixData fix) {
|
||||
// TODO process fix
|
||||
collectedFixes.add(new DoubleVectorFixImpl(new MillisecondsTimePoint(fix.getTimepointInMs()), fix.getFix()));
|
||||
int currentlyCollectedFixes = collectedFixes.size();
|
||||
if (currentlyCollectedFixes >= batchSize) {
|
||||
pushCollectedFixes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (isLearning) {
|
||||
finishLearning();
|
||||
}
|
||||
pushCollectedFixes();
|
||||
}
|
||||
|
||||
private void pushCollectedFixes() {
|
||||
callback.addFixes(collectedFixes, deviceIdentifier);
|
||||
collectedFixes.clear();
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,5 @@
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
<booleanAttribute key="useCustomFeatures" value="false"/>
|
||||
<booleanAttribute key="useDefaultConfigArea" value="true"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.servlet@default:default,com.googlecode.java-diff-utils@default:default,com.googlecode.mgwt@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.competitorimport@default:default,com.sap.sailing.dashboards.gwt@5:true,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.news@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.polars.datamining@default:default,com.sap.sailing.polars@4:true,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,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.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.yachtscoring.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mail@4:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@4:true,com.sap.sse.security.common@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.sap.sse.shared.android@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.servlet@default:default,com.googlecode.java-diff-utils@default:default,com.googlecode.mgwt@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.competitorimport@default:default,com.sap.sailing.dashboards.gwt@5:true,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.grib@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.news@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.polars.datamining@default:default,com.sap.sailing.polars@4:true,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,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.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.yachtscoring.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mail@4:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@4:true,com.sap.sse.security.common@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.sap.sse.shared.android@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
|
||||
</launchConfiguration>
|
||||
@@ -38,6 +38,13 @@
|
||||
the GRIB files you use are adequate in resolution and region as well as adequate in time. Obtaining
|
||||
a forecast 72h in the future makes little sense for an in-shore race starting in 30min.
|
||||
</li>
|
||||
<li>The REST service for wind (/sailingservice/api/v1/regattas/{regatta}/races/{race}/wind) now considers
|
||||
the center of the course to calculate the COMBINED wind readings. Previously, all wind readings for the COMBINED
|
||||
wind track as published through the REST API were equally considered, regardless of how far away a
|
||||
wind sensor was from the course. Now, sensors closer to the course count more than those further away.
|
||||
Furthermore, the time between two readings in the COMBINED wind track in the REST output has been
|
||||
reduced from ten seconds to one second.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 class="articleSubheadline">January 2017</h2>
|
||||
<ul class="bulletList">
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
package com.sap.sse.datamining.impl.data;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
@@ -12,8 +12,8 @@ import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparableClusterBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
|
||||
import com.sap.sse.datamining.impl.data.LinearDoubleClusterGroup;
|
||||
|
||||
// TODO Move to sse bundle, after 49er analysis
|
||||
public class TestLinearDoubleClusterGroup {
|
||||
|
||||
private LinearDoubleClusterGroup hardPercentageClusterGroup;
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
package com.sap.sse.datamining.impl.data;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -8,6 +8,8 @@ import org.junit.Test;
|
||||
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.data.ClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.LinearDoubleClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.PercentageClusterFormatter;
|
||||
|
||||
public class TestPercentageClusterFormatter {
|
||||
|
||||
@@ -2,4 +2,3 @@ eclipse.preferences.version=1
|
||||
encoding//resources/stringmessages/StringMessages_ja.properties=UTF-8
|
||||
encoding//resources/stringmessages/StringMessages_ru.properties=UTF-8
|
||||
encoding//resources/stringmessages/StringMessages_zh.properties=UTF-8
|
||||
encoding//resources/stringmessages/StringMessages_zh_CN.properties=UTF-8
|
||||
@@ -18,6 +18,7 @@ public interface ClusterBoundary<ElementType extends Serializable> extends Seria
|
||||
public boolean contains(ElementType value);
|
||||
|
||||
public ComparisonStrategy getStrategy();
|
||||
public ElementType getBoundaryValue();
|
||||
|
||||
public Class<ElementType> getClusterElementsType();
|
||||
|
||||
|
||||
+1
-4
@@ -1,10 +1,7 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
package com.sap.sse.datamining.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
|
||||
// TODO Move to sse bundle, after 49er analysis
|
||||
public interface ClusterFormatter<T extends Serializable> {
|
||||
|
||||
String format(Cluster<T> cluster);
|
||||
+28
-8
@@ -27,17 +27,37 @@ public abstract class AbstractParallelAggregationProcessor<InputType, Aggregated
|
||||
|
||||
@Override
|
||||
protected AbstractProcessorInstruction<AggregatedType> createInstruction(final InputType element) {
|
||||
return new AbstractProcessorInstruction<AggregatedType>(this, ProcessorInstructionPriority.Aggregation) {
|
||||
@Override
|
||||
public AggregatedType computeResult() {
|
||||
synchronized (monitor) {
|
||||
handleElement(element);
|
||||
if (needsSynchronization()) {
|
||||
return new AbstractProcessorInstruction<AggregatedType>(this, ProcessorInstructionPriority.Aggregation) {
|
||||
@Override
|
||||
public AggregatedType computeResult() {
|
||||
synchronized (monitor) {
|
||||
handleElement(element);
|
||||
}
|
||||
return AbstractParallelAggregationProcessor.super.createInvalidResult();
|
||||
}
|
||||
return AbstractParallelAggregationProcessor.super.createInvalidResult();
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
return new AbstractProcessorInstruction<AggregatedType>(this, ProcessorInstructionPriority.Aggregation) {
|
||||
@Override
|
||||
protected AggregatedType computeResult() throws Exception {
|
||||
handleElement(element);
|
||||
return AbstractParallelAggregationProcessor.super.createInvalidResult();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if {@link #handleElement(Object) handleElement} needs to be called in a synchronized block.
|
||||
* Default is <code>true</code>.</br>
|
||||
* Override if the concrete aggregator implementation will handle the synchronization on its own.
|
||||
* @return
|
||||
*/
|
||||
protected boolean needsSynchronization() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to handle the element. This method is only called in a way that is thread safe so
|
||||
|
||||
+3
-15
@@ -14,9 +14,7 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.datamining.components.AdditionalResultDataBuilder;
|
||||
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
||||
import com.sap.sse.datamining.components.Processor;
|
||||
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
|
||||
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
|
||||
import com.sap.sse.datamining.impl.components.ProcessorInstructionPriority;
|
||||
import com.sap.sse.datamining.impl.components.SimpleAggregationProcessorDefinition;
|
||||
import com.sap.sse.datamining.shared.GroupKey;
|
||||
|
||||
@@ -45,20 +43,10 @@ public class ParallelGroupedDataCountDistinctAggregationProcessor
|
||||
super(executor, resultReceivers, "CountDistinct");
|
||||
countMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't need synchronization here because we're using a {@link ConcurrentHashSet} and a {@link ConcurrentHashMap} to
|
||||
* aggregate the results.
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected AbstractProcessorInstruction<Map<GroupKey, Number>> createInstruction(GroupedDataEntry<Object> element) {
|
||||
return new AbstractProcessorInstruction<Map<GroupKey, Number>>(this, ProcessorInstructionPriority.Aggregation) {
|
||||
@Override
|
||||
public Map<GroupKey, Number> computeResult() {
|
||||
handleElement(element);
|
||||
return createInvalidResult();
|
||||
}
|
||||
};
|
||||
protected boolean needsSynchronization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public abstract class AbstractCluster<ElementType extends Serializable> implemen
|
||||
return getBoundariesAsString();
|
||||
}
|
||||
|
||||
protected Collection<ClusterBoundary<ElementType>> getClusterBoundaries() {
|
||||
public Iterable<ClusterBoundary<ElementType>> getClusterBoundaries() {
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ public abstract class AbstractClusterBoundary<ElementType extends Serializable>
|
||||
|
||||
protected abstract int compare(ElementType value);
|
||||
|
||||
protected ElementType getBoundaryValue() {
|
||||
@Override
|
||||
public ElementType getBoundaryValue() {
|
||||
return boundaryValue;
|
||||
}
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.sap.sse.datamining.impl.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.data.ClusterBoundary;
|
||||
import com.sap.sse.datamining.data.ClusterFormatter;
|
||||
|
||||
public abstract class AbstractClusterFormatter<T extends Serializable> implements ClusterFormatter<T> {
|
||||
|
||||
private static final char INFINITE = '\u221e';
|
||||
|
||||
@Override
|
||||
public String format(Cluster<T> cluster) {
|
||||
if (cluster instanceof LocalizedCluster) {
|
||||
format(((LocalizedCluster<T>) cluster).getInnerCluster());
|
||||
}
|
||||
if (cluster instanceof ClusterWithSingleBoundary) {
|
||||
return formatClusterWithSingleBoundary((ClusterWithSingleBoundary<T>) cluster);
|
||||
}
|
||||
if (cluster instanceof ClusterWithLowerAndUpperBoundaries) {
|
||||
return formatClusterWithLowerAndUpperBoundaries((ClusterWithLowerAndUpperBoundaries<T>) cluster);
|
||||
}
|
||||
throw new IllegalArgumentException("Can't format clusters of type " + cluster.getClass().getName());
|
||||
}
|
||||
|
||||
protected String formatClusterWithSingleBoundary(ClusterWithSingleBoundary<T> cluster) {
|
||||
Iterable<ClusterBoundary<T>> boundaries = cluster.getClusterBoundaries();
|
||||
if (boundaries == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
ClusterBoundary<T> boundary = Util.first(boundaries);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (boundary.getStrategy() == ComparisonStrategy.LOWER_EQUALS_THAN ||
|
||||
boundary.getStrategy() == ComparisonStrategy.LOWER_THAN) {
|
||||
builder.append("-" + INFINITE + " - ");
|
||||
}
|
||||
builder.append(formatBoundary(boundary));
|
||||
if (boundary.getStrategy() == ComparisonStrategy.GREATER_EQUALS_THAN ||
|
||||
boundary.getStrategy() == ComparisonStrategy.GREATER_THAN) {
|
||||
builder.append(" - " + INFINITE);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected String formatClusterWithLowerAndUpperBoundaries(ClusterWithLowerAndUpperBoundaries<T> cluster) {
|
||||
Iterable<ClusterBoundary<T>> boundaries = cluster.getClusterBoundaries();
|
||||
if (boundaries == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
return String.format("%s - %s", formatBoundary(Util.get(boundaries, 0)), formatBoundary(Util.get(boundaries, 1)));
|
||||
}
|
||||
|
||||
protected String formatBoundary(ClusterBoundary<T> boundary) {
|
||||
String value = formatValue(boundary.getBoundaryValue());
|
||||
if (value == null) {
|
||||
return "<Unformattable>";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ComparisonStrategy strategy = boundary.getStrategy();
|
||||
if (strategy == ComparisonStrategy.GREATER_EQUALS_THAN || strategy == ComparisonStrategy.GREATER_THAN) {
|
||||
builder.append(strategy.getSignifier());
|
||||
}
|
||||
builder.append(value);
|
||||
if (strategy == ComparisonStrategy.LOWER_EQUALS_THAN || strategy == ComparisonStrategy.LOWER_THAN) {
|
||||
builder.append(strategy.getSignifier());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
protected abstract String formatValue(T value);
|
||||
|
||||
}
|
||||
+2
-6
@@ -1,16 +1,12 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
package com.sap.sse.datamining.impl.data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.data.ClusterGroup;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
|
||||
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparableClusterBoundary;
|
||||
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
|
||||
|
||||
// TODO: Improve and move to com.sap.sse.datamining
|
||||
// TODO: Cache the clusters
|
||||
public class LinearDoubleClusterGroup implements ClusterGroup<Double> {
|
||||
private static final long serialVersionUID = 1559316450183865115L;
|
||||
|
||||
@@ -31,5 +31,9 @@ public class LocalizedCluster<ElementType extends Serializable> implements Clust
|
||||
public Class<ElementType> getClusterElementsType() {
|
||||
return cluster.getClusterElementsType();
|
||||
}
|
||||
|
||||
public Cluster<ElementType> getInnerCluster() {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
package com.sap.sse.datamining.impl.data;
|
||||
|
||||
public class PercentageClusterFormatter extends AbstractClusterFormatter<Double> {
|
||||
|
||||
@@ -4,4 +4,3 @@ encoding//src/com/sap/sse/gwt/client/controls/filestorage/StringMessages_de.prop
|
||||
encoding//src/com/sap/sse/gwt/client/controls/filestorage/StringMessages_ja.properties=UTF-8
|
||||
encoding//src/com/sap/sse/gwt/client/controls/filestorage/StringMessages_ru.properties=UTF-8
|
||||
encoding//src/com/sap/sse/gwt/client/controls/filestorage/StringMessages_zh.properties=UTF-8
|
||||
encoding//src/com/sap/sse/gwt/client/controls/filestorage/StringMessages_zh_CN.properties=UTF-8
|
||||
@@ -5,7 +5,6 @@ encoding//src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.pr
|
||||
encoding//src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ja.properties=UTF-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ru.properties=UTF-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh.properties=UTF-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh_CN.properties=UTF-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/login/LoginView.java=utf-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/oauth/client/util/ClientUtils.java=utf-8
|
||||
encoding//src/main/java/com/sap/sse/security/ui/servlet/RedirectServlet.java=utf-8
|
||||
Reference in new issue
Block a user