@@ -37,6 +35,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
* {@link Object#equals(Object)} and {@link Object#hashCode()} should be implemented so that multiple
* deserializations of same device identifier (conceptually) result in objects that are be identified
* as being equal through these two methods.
+ *
* @author Fredrik Teschke
*
*/
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java
index 150ec7d4087..4435b336e9f 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java
@@ -138,7 +138,7 @@ public class LeaderboardEntryDTO implements Serializable {
return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE);
}
- public Duration getTimeSailedInMillis() {
+ public Duration getTimeSailed() {
final Duration result;
if (legDetails != null) {
long timeInMilliseconds = 0;
@@ -159,7 +159,7 @@ public class LeaderboardEntryDTO implements Serializable {
return result;
}
- public Distance getDistanceTraveledInMeters() {
+ public Distance getDistanceTraveled() {
Distance result = null;
if (legDetails != null) {
for (LegEntryDTO legDetail : legDetails) {
@@ -176,6 +176,47 @@ public class LeaderboardEntryDTO implements Serializable {
return result;
}
+ public Duration getDurationFoiled() {
+ final Duration result;
+ if (legDetails != null) {
+ Double acc = null;
+ for (LegEntryDTO legDetail : legDetails) {
+ if (legDetail != null) {
+ if (legDetail.currentDurationFoiledInSeconds != null) {
+ if (acc == null) {
+ acc = 0.0;
+ }
+ acc += legDetail.currentDurationFoiledInSeconds;
+ } else {
+ acc = null;
+ break;
+ }
+ }
+ }
+ result = acc == null ? null : new MillisecondsDurationImpl((long) (acc*1000.));
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ public Distance getDistanceFoiled() {
+ Distance result = null;
+ if (legDetails != null) {
+ for (LegEntryDTO legDetail : legDetails) {
+ if (legDetail != null) {
+ if (legDetail.currentDistanceFoiledInMeters != null) {
+ if (result == null) {
+ result = Distance.NULL;
+ }
+ result = result.add(new MeterDistance(legDetail.currentDistanceFoiledInMeters));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
public int getOneBasedCurrentLegNumber() {
int result = 0;
if (legDetails != null && !legDetails.isEmpty()) {
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java
index 4e60ad845c6..5ca9daaf00e 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java
@@ -26,6 +26,8 @@ public class LeaderboardRowDTO implements Serializable {
public Double totalTimeSailedUpwindInSeconds;
public Double totalTimeSailedReachingInSeconds;
public Double totalDistanceTraveledInMeters;
+ public Double totalDurationFoiledInSeconds;
+ public Double totalDistanceFoiledInMeters;
@Override
public int hashCode() {
@@ -38,6 +40,10 @@ public class LeaderboardRowDTO implements Serializable {
+ ((maximumSpeedOverGroundInKnots == null) ? 0 : maximumSpeedOverGroundInKnots.hashCode());
result = prime * result
+ ((totalDistanceTraveledInMeters == null) ? 0 : totalDistanceTraveledInMeters.hashCode());
+ result = prime * result
+ + ((totalDurationFoiledInSeconds == null) ? 0 : totalDurationFoiledInSeconds.hashCode());
+ result = prime * result
+ + ((totalDistanceFoiledInMeters == null) ? 0 : totalDistanceFoiledInMeters.hashCode());
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
result = prime * result
+ ((totalTimeSailedDownwindInSeconds == null) ? 0 : totalTimeSailedDownwindInSeconds.hashCode());
@@ -87,6 +93,16 @@ public class LeaderboardRowDTO implements Serializable {
return false;
} else if (!totalDistanceTraveledInMeters.equals(other.totalDistanceTraveledInMeters))
return false;
+ if (totalDurationFoiledInSeconds == null) {
+ if (other.totalDurationFoiledInSeconds != null)
+ return false;
+ } else if (!totalDurationFoiledInSeconds.equals(other.totalDurationFoiledInSeconds))
+ return false;
+ if (totalDistanceFoiledInMeters == null) {
+ if (other.totalDistanceFoiledInMeters != null)
+ return false;
+ } else if (!totalDistanceFoiledInMeters.equals(other.totalDistanceFoiledInMeters))
+ return false;
if (netPoints == null) {
if (other.netPoints != null)
return false;
@@ -120,11 +136,11 @@ public class LeaderboardRowDTO implements Serializable {
return true;
}
- public Distance getDistanceTraveledInMeters(String raceColumnName) {
+ public Distance getDistanceTraveled(String raceColumnName) {
final Distance result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
- result = fieldsForRace.getDistanceTraveledInMeters();
+ result = fieldsForRace.getDistanceTraveled();
} else {
result = null;
}
@@ -135,7 +151,29 @@ public class LeaderboardRowDTO implements Serializable {
final Duration result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
- result = fieldsForRace.getTimeSailedInMillis();
+ result = fieldsForRace.getTimeSailed();
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ public Distance getDistanceFoiled(String raceColumnName) {
+ final Distance result;
+ LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
+ if (fieldsForRace != null) {
+ result = fieldsForRace.getDistanceFoiled();
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ public Duration getDurationFoiled(String raceColumnName) {
+ final Duration result;
+ LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
+ if (fieldsForRace != null) {
+ result = fieldsForRace.getDurationFoiled();
} else {
result = null;
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java
index 255fbbb3a00..e545f1fe01a 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java
@@ -44,6 +44,8 @@ public class LegEntryDTO implements Serializable {
public Double currentHeelInDegrees;
public Double currentPitchInDegrees;
+ public Double currentDistanceFoiledInMeters;
+ public Double currentDurationFoiledInSeconds;
/**
* If the competitor has already finished the leg at the time point for which this entry was created, contains the
@@ -81,6 +83,8 @@ public class LegEntryDTO implements Serializable {
result = prime * result + ((currentHeelInDegrees == null) ? 0 : currentHeelInDegrees.hashCode());
result = prime * result + ((currentPitchInDegrees == null) ? 0 : currentPitchInDegrees.hashCode());
result = prime * result + ((currentRideHeightInMeters == null) ? 0 : currentRideHeightInMeters.hashCode());
+ result = prime * result + ((currentDurationFoiledInSeconds == null) ? 0 : currentDurationFoiledInSeconds.hashCode());
+ result = prime * result + ((currentDistanceFoiledInMeters == null) ? 0 : currentDistanceFoiledInMeters.hashCode());
result = prime * result
+ ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode());
result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode());
@@ -150,6 +154,16 @@ public class LegEntryDTO implements Serializable {
return false;
} else if (!currentRideHeightInMeters.equals(other.currentRideHeightInMeters))
return false;
+ if (currentDurationFoiledInSeconds == null) {
+ if (other.currentDurationFoiledInSeconds != null)
+ return false;
+ } else if (!currentDurationFoiledInSeconds.equals(other.currentDurationFoiledInSeconds))
+ return false;
+ if (currentDistanceFoiledInMeters == null) {
+ if (other.currentDistanceFoiledInMeters != null)
+ return false;
+ } else if (!currentDistanceFoiledInMeters.equals(other.currentDistanceFoiledInMeters))
+ return false;
if (currentSpeedOverGroundInKnots == null) {
if (other.currentSpeedOverGroundInKnots != null)
return false;
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DegreeBearingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DegreeBearingImpl.java
index 70e37a8717f..5ea889222d9 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DegreeBearingImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DegreeBearingImpl.java
@@ -9,7 +9,7 @@ public class DegreeBearingImpl extends AbstractBearing implements Bearing {
private final double bearingDeg;
/**
*
- * @param bearingDeg if a mount degrees over 360, then it will be mod 360 degrees
+ * @param bearingDeg if amount degrees over 360, then it will be mod 360 degrees
*/
public DegreeBearingImpl(double bearingDeg) {
super();
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalableDistance.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalableDistance.java
new file mode 100755
index 00000000000..065b6f4899f
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalableDistance.java
@@ -0,0 +1,44 @@
+package com.sap.sailing.domain.common.scalablevalue.impl;
+
+import com.sap.sailing.domain.common.Distance;
+import com.sap.sailing.domain.common.impl.MeterDistance;
+import com.sap.sse.common.scalablevalue.ScalableValue;
+import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
+
+public class ScalableDistance implements ScalableValueWithDistance {
+ private final double meters;
+
+ public ScalableDistance(Distance distance) {
+ this.meters = distance.getMeters();
+ }
+
+ private ScalableDistance(double meters) {
+ this.meters = meters;
+ }
+
+ @Override
+ public ScalableDistance multiply(double factor) {
+ return new ScalableDistance(factor*meters);
+ }
+
+ @Override
+ public ScalableDistance add(ScalableValue t) {
+ return new ScalableDistance(meters+t.getValue());
+ }
+
+ @Override
+ public Distance divide(double divisor) {
+ return new MeterDistance(meters / divisor);
+ }
+
+ @Override
+ public Double getValue() {
+ return meters;
+ }
+
+ @Override
+ public double getDistance(Distance other) {
+ return Math.abs(meters-other.getMeters());
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java
index f9c5eaa05ae..befba93a52f 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java
@@ -15,6 +15,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission {
MANAGE_COURSE_LAYOUT,
MANAGE_WIND,
MANAGE_IGTIMI_ACCOUNTS,
+ MANAGE_EXPEDITION_DEVICE_CONFIGURATIONS,
MANAGE_LEADERBOARDS,
MANAGE_LEADERBOARD_RESULTS,
MANAGE_LEADERBOARD_GROUPS,
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoExtendedSensorDataMetadata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoExtendedSensorDataMetadata.java
index f92f94ccf56..efd9c9a863d 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoExtendedSensorDataMetadata.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoExtendedSensorDataMetadata.java
@@ -1,39 +1,78 @@
package com.sap.sailing.domain.common.sensordata;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+
/**
- * Metadata that defines the column structure of BravoExtended fixes when imported as {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
+ * Metadata that defines the column structure of BravoExtended fixes when imported as
+ * {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
+ *
*
* The current implementation only stores a subset of the information available during the import.
+ *
*
- * The first columns need to be consistent with {@link BravoSensorDataMetadata}.
+ * The first columns need to be consistent with {@link BravoSensorDataMetadata}. Furthermore,
+ * once data has been stored in a sensor store, the order of literals must remain constant,
+ * and no literals must be deleted anymore because the ordinal is used to determine the index
+ * into the {@link DoubleVectorFix}.
*/
-public enum BravoExtendedSensorDataMetadata {
- RIDE_HEIGHT_PORT_HULL("RideHeightPort"), //
- RIDE_HEIGHT_STBD_HULL("RideHeightStbd"), //
- HEEL("Heel"), //
- PITCH("PitchRate"),
- DB_RAKE_PORT("DaggerBoardRakeAnglePort"), //
- DB_RAKE_STBD("DaggerBoardRakeAngleStbd"), //
- RUDDER_RAKE_PORT("RudderRakeAnglePort"), //
- RUDDER_RAKE_STBD("RudderRakeAngleStbd"), //
- MAST_ROTATION("MastRotation");
+public enum BravoExtendedSensorDataMetadata implements ColumnMetadata {
+ RIDE_HEIGHT_PORT_HULL("RideHeightPort", /* expectedInLogfile */ true), //
+ RIDE_HEIGHT_STBD_HULL("RideHeightStbd", /* expectedInLogfile */ true), //
+ HEEL("Heel", /* expectedInLogfile */ true), //
+ PITCH("PitchRate", /* expectedInLogfile */ true), //
+ DB_RAKE_PORT("DaggerBoardRakeAnglePort", /* expectedInLogfile */ true), //
+ DB_RAKE_STBD("DaggerBoardRakeAngleStbd", /* expectedInLogfile */ true), //
+ RUDDER_RAKE_PORT("RudderRakeAnglePort", /* expectedInLogfile */ true), //
+ RUDDER_RAKE_STBD("RudderRakeAngleStbd", /* expectedInLogfile */ true), //
+ MAST_ROTATION("MastRotation", /* expectedInLogfile */ true), //
+ LEEWAY(null, /* expectedInLogfile */ false), //
+ SET(null, /* expectedInLogfile */ false), //
+ DRIFT(null, /* expectedInLogfile */ false), //
+ DEPTH(null, /* expectedInLogfile */ false), //
+ RUDDER(null, /* expectedInLogfile */ false), //
+ FORESTAY_LOAD(null, /* expectedInLogfile */ false), //
+ FORESTAY_PRESSURE(null, /* expectedInLogfile */ false), //
+ TACK_ANGLE(null, /* expectedInLogfile */ false), //
+ RAKE_DEG(null, /* expectedInLogfile */ false), //
+ DEFLECTOR_PERCENTAGE(null, /* expectedInLogfile */ false), //
+ TARGET_HEEL(null, /* expectedInLogfile */ false), //
+ DEFLECTOR_MILLIMETERS(null, /* expectedInLogfile */ false), //
+ TARGET_BOATSPEED_P(null, /* expectedInLogfile */ false), //
+ LAT("Lat", /* expectedInLogfile */ true), //
+ LON("Lon", /* expectedInLogfile */ true), //
+ COG("COG", /* expectedInLogfile */ true), //
+ SOG("SOG", /* expectedInLogfile */ true), //
+ ;
- private String columnName;
+ private final String columnName;
- private BravoExtendedSensorDataMetadata(String columnName) {
+ /**
+ * Some values seem to only appear in the UDP stream but so far not in the log file. Therefore,
+ * those columns shall not be part of the response to {@link #getColumnNamesToIndexInDoubleFix()},
+ * indicated by this field being {@code false} for those attributes.
+ */
+ private final boolean expectedInLogFile;
+
+ private BravoExtendedSensorDataMetadata(String columnName, boolean expectedInLogfile) {
this.columnName = columnName;
+ this.expectedInLogFile = expectedInLogfile;
}
public String getColumnName() {
return columnName;
}
+ public boolean isExpectedInLogFile() {
+ return expectedInLogFile;
+ }
+
+ /**
+ * The index in the {@link DoubleVectorFix} where this data item will be stored
+ */
public int getColumnIndex() {
return this.ordinal();
}
@@ -51,21 +90,25 @@ public enum BravoExtendedSensorDataMetadata {
}
public static int getTrackColumnCount() {
- return BravoExtendedSensorDataMetadata.values().length;
- }
-
- public static List getTrackColumnNames() {
- ArrayList colNames = new ArrayList<>(getTrackColumnCount());
- for (BravoExtendedSensorDataMetadata item : BravoExtendedSensorDataMetadata.values()) {
- colNames.add(item.getColumnName());
+ int result = 0;
+ for (final BravoExtendedSensorDataMetadata m : BravoExtendedSensorDataMetadata.values()) {
+ if (m.isExpectedInLogFile()) {
+ result++;
+ }
}
- return colNames;
+ return result;
}
+ /**
+ * @return namens of those columns with {@link #isExpectedInLogFile()} being {@code true}, mapped to their
+ * {@link #getColumnIndex() column index}
+ */
public static Map getColumnNamesToIndexInDoubleFix() {
final Map columnNamesToIndexInDoubleFix = new HashMap<>();
for (final BravoExtendedSensorDataMetadata column : BravoExtendedSensorDataMetadata.values()) {
- columnNamesToIndexInDoubleFix.put(column.getColumnName(), column.getColumnIndex());
+ if (column.isExpectedInLogFile()) {
+ columnNamesToIndexInDoubleFix.put(column.getColumnName(), column.getColumnIndex());
+ }
}
return columnNamesToIndexInDoubleFix;
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
index 9f9e112d48d..f7c7a853a37 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/BravoSensorDataMetadata.java
@@ -7,18 +7,18 @@ import java.util.Map;
import java.util.Objects;
/**
- * Metadata that defines the column structure of Bravo fixes when imported as {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
+ * Metadata that defines the column structure of Bravo fixes when imported as
+ * {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
+ *
*
* The current implementation only stores a subset of the information available during the import.
*/
-public enum BravoSensorDataMetadata {
-
+public enum BravoSensorDataMetadata implements ColumnMetadata {
RIDE_HEIGHT_PORT_HULL("RideHeightPortHull"), //
RIDE_HEIGHT_STBD_HULL("RideHeightStbdHull"), //
HEEL("Heel"), //
PITCH("ImuSensor_Pitch");
-
private String columnName;
private BravoSensorDataMetadata(String columnName) {
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ColumnMetadata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ColumnMetadata.java
new file mode 100644
index 00000000000..5380fa447be
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ColumnMetadata.java
@@ -0,0 +1,14 @@
+package com.sap.sailing.domain.common.sensordata;
+
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+import com.sap.sailing.domain.common.tracking.SensorFix;
+
+/**
+ * Definition of column metadata used by {@link SensorFix} implementations to resolve columns in underlying
+ * {@link DoubleVectorFix DoubleVectorFixes}. Once data has been stored in a sensor store according to such a
+ * specification, the order of literals in implementing {@code enum} types must remain constant, and no literals must be
+ * deleted anymore because the ordinal is used to determine the index into the {@link DoubleVectorFix}.
+ */
+public interface ColumnMetadata {
+ int getColumnIndex();
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ExpeditionExtendedSensorDataMetadata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ExpeditionExtendedSensorDataMetadata.java
new file mode 100755
index 00000000000..3858832e652
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sensordata/ExpeditionExtendedSensorDataMetadata.java
@@ -0,0 +1,109 @@
+package com.sap.sailing.domain.common.sensordata;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+
+/**
+ * Metadata that defines the column structure of BravoExtended fixes when imported as
+ * {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
+ *
+ *
+ * The current implementation only stores a subset of the information available during the import.
+ *
+ *
+ * The first columns need to be consistent with {@link BravoSensorDataMetadata}.
+ */
+public enum ExpeditionExtendedSensorDataMetadata {
+ HEEL("Heel", BravoExtendedSensorDataMetadata.HEEL), //
+ TRIM("Trim", BravoExtendedSensorDataMetadata.PITCH), //
+ LEEWAY("Leeway", BravoExtendedSensorDataMetadata.LEEWAY, /* expected in logfile */ true), //
+ SET("Set", BravoExtendedSensorDataMetadata.SET, /* expected in logfile */ true), //
+ DRIFT("Drift", BravoExtendedSensorDataMetadata.DRIFT, /* expected in logfile */ true), //
+ DEPTH("Depth", BravoExtendedSensorDataMetadata.DEPTH, /* expected in logfile */ true), //
+ RUDDER("Rudder", BravoExtendedSensorDataMetadata.RUDDER, /* expected in logfile */ true), //
+ FORESTAY_LOAD("Forestay", BravoExtendedSensorDataMetadata.FORESTAY_LOAD, /* expected in logfile */ true), //
+ FORESTAY_PRESSURE("ForestayPres", BravoExtendedSensorDataMetadata.FORESTAY_PRESSURE, /* expected in logfile */ true), //
+ TACK_ANGLE(null, BravoExtendedSensorDataMetadata.TACK_ANGLE), //
+ RAKE_DEG("RakeDeg", BravoExtendedSensorDataMetadata.RAKE_DEG, /* expected in logfile */ true), //
+ DEFLECTOR_PERCENTAGE("DflectrPP", BravoExtendedSensorDataMetadata.DEFLECTOR_PERCENTAGE, /* expected in logfile */ true), //
+ TARGET_HEEL("TG Heell", BravoExtendedSensorDataMetadata.TARGET_HEEL, /* expected in logfile */ true), //
+ DEFLECTOR_MILLIMETERS("DflectrMM", BravoExtendedSensorDataMetadata.DEFLECTOR_MILLIMETERS, /* expected in logfile */ true), //
+ TARGET_BOATSPEED_P("BspTr", BravoExtendedSensorDataMetadata.TARGET_BOATSPEED_P, /* expected in logfile */ true), //
+ ;
+
+ private String columnName;
+
+ /**
+ * Expedition extended fixes are mapped to Bravo extended fixes whose metadata is described
+ * in {@link BravoExtendedSensorDataMetadata}. When parsing an Expedition file, the columns
+ * in the resulting {@link DoubleVectorFix} must be chosen such that they match up with the
+ * corresponding fields as described by {@link BravoExtendedSensorDataMetadata}. This turns
+ * the ordinals of this enum's literals insignificant; the position is taken solely from
+ * the Bravo metadata.
+ */
+ private final BravoExtendedSensorDataMetadata mappedToBravoField;
+
+ private final boolean expectedInLogfile;
+
+ private ExpeditionExtendedSensorDataMetadata(String columnName, BravoExtendedSensorDataMetadata mappedToBravoField) {
+ this(columnName, mappedToBravoField, mappedToBravoField.isExpectedInLogFile());
+ }
+
+ private ExpeditionExtendedSensorDataMetadata(String columnName, BravoExtendedSensorDataMetadata mappedToBravoField, boolean expectedInLogfile) {
+ this.columnName = columnName;
+ this.mappedToBravoField = mappedToBravoField;
+ this.expectedInLogfile = expectedInLogfile;
+ }
+
+ public String getColumnName() {
+ return columnName;
+ }
+
+ /**
+ * The index in the {@link DoubleVectorFix} where this data item will be stored
+ */
+ public int getColumnIndex() {
+ return mappedToBravoField.getColumnIndex();
+ }
+
+ public boolean isExpectedInLogFile() {
+ return expectedInLogfile;
+ }
+
+ public static ExpeditionExtendedSensorDataMetadata byColumnName(String valueName) {
+ ExpeditionExtendedSensorDataMetadata[] values = ExpeditionExtendedSensorDataMetadata.values();
+ for (ExpeditionExtendedSensorDataMetadata item : values) {
+ if (Objects.equals(item.getColumnName(), valueName)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public static int getTrackColumnCount() {
+ return ExpeditionExtendedSensorDataMetadata.values().length;
+ }
+
+ public static List getTrackColumnNames() {
+ ArrayList colNames = new ArrayList<>(getTrackColumnCount());
+ for (ExpeditionExtendedSensorDataMetadata item : ExpeditionExtendedSensorDataMetadata.values()) {
+ colNames.add(item.getColumnName());
+ }
+ return colNames;
+ }
+
+ public static Map getColumnNamesToIndexInDoubleFix() {
+ final Map columnNamesToIndexInDoubleFix = new HashMap<>();
+ for (final ExpeditionExtendedSensorDataMetadata column : ExpeditionExtendedSensorDataMetadata.values()) {
+ if (column.isExpectedInLogFile()) {
+ columnNamesToIndexInDoubleFix.put(column.getColumnName(), column.getColumnIndex());
+ }
+ }
+ return columnNamesToIndexInDoubleFix;
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoExtendedFix.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoExtendedFix.java
index 85fe90483d4..d38e8be6b5d 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoExtendedFix.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoExtendedFix.java
@@ -1,14 +1,28 @@
package com.sap.sailing.domain.common.tracking;
import com.sap.sailing.domain.common.Bearing;
+import com.sap.sailing.domain.common.Distance;
/**
* Extended version of {@link BravoFix} that provides access to more measures found in the extended data format.
*/
public interface BravoExtendedFix extends BravoFix {
- double getPortDaggerboardRake();
- double getStbdDaggerboardRake();
- double getPortRudderRake();
- double getStbdRudderRake();
+ Double getPortDaggerboardRake();
+ Double getStbdDaggerboardRake();
+ Double getPortRudderRake();
+ Double getStbdRudderRake();
Bearing getMastRotation();
+ Bearing getLeeway();
+ Double getSet();
+ Bearing getDrift();
+ Distance getDepth();
+ Bearing getRudder();
+ Double getForestayLoad();
+ Double getForestayPressure();
+ Bearing getTackAngle();
+ Bearing getRake();
+ Double getDeflectorPercentage();
+ Bearing getTargetHeel();
+ Distance getDeflector();
+ Double getTargetBoatspeedP();
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java
index 78e938466e7..3cb3a5e7302 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java
@@ -46,7 +46,4 @@ public interface BravoFix extends SensorFix {
@Statistic(messageKey = "heel", resultDecimals = 1)
Bearing getHeel();
-
-
-
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/DoubleVectorFix.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/DoubleVectorFix.java
index 4e955af6679..9367955be94 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/DoubleVectorFix.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/DoubleVectorFix.java
@@ -3,10 +3,15 @@ package com.sap.sailing.domain.common.tracking;
import com.sap.sse.common.Timed;
/**
- * A fix that simply holds an array of double values. The interpretation of the data depends on the concrete mapping in
- * the RegattaLog.
+ * A fix that simply holds an array of {@link Double} values. The interpretation of the data depends on the concrete
+ * mapping in the RegattaLog. Asking beyond the end of the vector using {@link #get(int)} will return {@code null} but
+ * not throw an exception.
*/
public interface DoubleVectorFix extends Timed {
- double[] get();
- double get(int index);
+ Double[] get();
+ Double get(int index);
+ /**
+ * Tells whether at least one component is not {@code null}
+ */
+ boolean hasValidData();
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoExtendedFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoExtendedFixImpl.java
index 1c32c3ae0f5..4fc24390061 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoExtendedFixImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoExtendedFixImpl.java
@@ -1,11 +1,18 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Bearing;
+import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
+import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.sensordata.BravoExtendedSensorDataMetadata;
+import com.sap.sailing.domain.common.sensordata.ColumnMetadata;
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
+import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+/**
+ * Implementation of {@link BravoExtendedFix}. {@link BravoExtendedFix} adds more measures compared to {@link BravoFix}.
+ */
public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedFix {
private static final long serialVersionUID = 5622321493028301922L;
@@ -14,28 +21,106 @@ public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedF
}
@Override
- public double getPortDaggerboardRake() {
+ protected ColumnMetadata resolveMetadataFromValueName(String valueName) {
+ return BravoExtendedSensorDataMetadata.byColumnName(valueName);
+ }
+
+ @Override
+ public Double getPortDaggerboardRake() {
return fix.get(BravoExtendedSensorDataMetadata.DB_RAKE_PORT.getColumnIndex());
}
@Override
- public double getStbdDaggerboardRake() {
+ public Double getStbdDaggerboardRake() {
return fix.get(BravoExtendedSensorDataMetadata.DB_RAKE_STBD.getColumnIndex());
}
@Override
- public double getPortRudderRake() {
+ public Double getPortRudderRake() {
return fix.get(BravoExtendedSensorDataMetadata.RUDDER_RAKE_PORT.getColumnIndex());
}
@Override
- public double getStbdRudderRake() {
+ public Double getStbdRudderRake() {
return fix.get(BravoExtendedSensorDataMetadata.RUDDER_RAKE_STBD.getColumnIndex());
}
@Override
public Bearing getMastRotation() {
- return new DegreeBearingImpl(fix.get(BravoExtendedSensorDataMetadata.MAST_ROTATION.getColumnIndex()));
+ final Double bearingDeg = fix.get(BravoExtendedSensorDataMetadata.MAST_ROTATION.getColumnIndex());
+ return bearingDeg == null ? null : new DegreeBearingImpl(bearingDeg);
}
+ @Override
+ public Bearing getLeeway() {
+ final Double leeway = fix.get(BravoExtendedSensorDataMetadata.LEEWAY.getColumnIndex());
+ return leeway == null ? null : new DegreeBearingImpl(leeway);
+ }
+
+ @Override
+ public Double getSet() {
+ return fix.get(BravoExtendedSensorDataMetadata.SET.getColumnIndex());
+ }
+
+ @Override
+ public Bearing getDrift() {
+ final Double drift = fix.get(BravoExtendedSensorDataMetadata.DRIFT.getColumnIndex());
+ return drift == null ? null : new DegreeBearingImpl(drift);
+ }
+
+ @Override
+ public Distance getDepth() {
+ final Double depthInMeters = fix.get(BravoExtendedSensorDataMetadata.DEPTH.getColumnIndex());
+ return depthInMeters == null ? null : new MeterDistance(depthInMeters);
+ }
+
+ @Override
+ public Bearing getRudder() {
+ final Double rudderAngleDeg = fix.get(BravoExtendedSensorDataMetadata.RUDDER.getColumnIndex());
+ return rudderAngleDeg == null ? null : new DegreeBearingImpl(rudderAngleDeg);
+ }
+
+ @Override
+ public Double getForestayLoad() {
+ return fix.get(BravoExtendedSensorDataMetadata.FORESTAY_LOAD.getColumnIndex());
+ }
+
+ @Override
+ public Double getForestayPressure() {
+ return fix.get(BravoExtendedSensorDataMetadata.FORESTAY_PRESSURE.getColumnIndex());
+ }
+
+ @Override
+ public Bearing getTackAngle() {
+ final Double tackAngleDeg = fix.get(BravoExtendedSensorDataMetadata.TACK_ANGLE.getColumnIndex());
+ return tackAngleDeg == null ? null : new DegreeBearingImpl(tackAngleDeg);
+ }
+
+ @Override
+ public Bearing getRake() {
+ final Double rakeDeg = fix.get(BravoExtendedSensorDataMetadata.RAKE_DEG.getColumnIndex());
+ return rakeDeg == null ? null : new DegreeBearingImpl(rakeDeg);
+ }
+
+ @Override
+ public Double getDeflectorPercentage() {
+ return fix.get(BravoExtendedSensorDataMetadata.DEFLECTOR_PERCENTAGE.getColumnIndex());
+ }
+
+ @Override
+ public Bearing getTargetHeel() {
+ final Double targetHeelDeg = fix.get(BravoExtendedSensorDataMetadata.TARGET_HEEL.getColumnIndex());
+ return targetHeelDeg == null ? null : new DegreeBearingImpl(targetHeelDeg);
+ }
+
+ @Override
+ public Distance getDeflector() {
+ final Double deflectorMillimeters = fix.get(BravoExtendedSensorDataMetadata.DEFLECTOR_MILLIMETERS.getColumnIndex());
+ return deflectorMillimeters == null ? null : new MeterDistance(deflectorMillimeters / 1000.);
+ }
+
+ @Override
+ public Double getTargetBoatspeedP() {
+ return fix.get(BravoExtendedSensorDataMetadata.TARGET_BOATSPEED_P.getColumnIndex());
+ }
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java
index ca8bcec9f48..7db57751513 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java
@@ -5,43 +5,37 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
+import com.sap.sailing.domain.common.sensordata.ColumnMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
-import com.sap.sse.common.TimePoint;
/**
* Implementation of {@link BravoFix} that wraps a {@link DoubleVectorFix} which holds the actual sensor data. The
* mapping metadata is stored in the {@link BravoSensorDataMetadata} enum.
*/
-public class BravoFixImpl implements BravoFix {
+public class BravoFixImpl extends SensorFixImpl implements BravoFix {
private static final long serialVersionUID = 2033254212013221160L;
- protected final DoubleVectorFix fix;
public BravoFixImpl(DoubleVectorFix fix) {
- this.fix = fix;
+ super(fix);
}
-
+
@Override
- public double get(String valueName) {
- BravoSensorDataMetadata colDefEnum = BravoSensorDataMetadata.byColumnName(valueName);
- if (colDefEnum == null) {
- throw new IllegalArgumentException("Unknown value \"" + valueName + "\" for " + getClass().getSimpleName());
- }
- int index = colDefEnum.getColumnIndex();
- return fix.get(index);
- }
-
- @Override
- public TimePoint getTimePoint() {
- return fix.getTimePoint();
+ protected ColumnMetadata resolveMetadataFromValueName(String valueName) {
+ return BravoSensorDataMetadata.byColumnName(valueName);
}
@Override
public Distance getRideHeight() {
- double rideHeightPortHullasDouble = fix.get(BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex());
- double rideHeightStarboardHullasDouble = fix
+ Double rideHeightPortHullasDouble = fix.get(BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex());
+ Double rideHeightStarboardHullAsDouble = fix
.get(BravoSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex());
- return new MeterDistance(Math.min(rideHeightPortHullasDouble, rideHeightStarboardHullasDouble));
+ return rideHeightPortHullasDouble == null ?
+ rideHeightStarboardHullAsDouble == null ? null :
+ new MeterDistance(rideHeightStarboardHullAsDouble) :
+ rideHeightStarboardHullAsDouble == null ?
+ new MeterDistance(rideHeightPortHullasDouble) :
+ new MeterDistance(Math.min(rideHeightPortHullasDouble, rideHeightStarboardHullAsDouble));
}
@Override
@@ -69,11 +63,13 @@ public class BravoFixImpl implements BravoFix {
@Override
public Bearing getPitch() {
- return new DegreeBearingImpl(fix.get(BravoSensorDataMetadata.PITCH.getColumnIndex()));
+ final Double bearingDeg = fix.get(BravoSensorDataMetadata.PITCH.getColumnIndex());
+ return bearingDeg == null ? null : new DegreeBearingImpl(bearingDeg);
}
@Override
public Bearing getHeel() {
- return new DegreeBearingImpl(fix.get(BravoSensorDataMetadata.HEEL.getColumnIndex()));
+ final Double bearingDeg = fix.get(BravoSensorDataMetadata.HEEL.getColumnIndex());
+ return bearingDeg == null ? null : new DegreeBearingImpl(bearingDeg);
}
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/DoubleVectorFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/DoubleVectorFixImpl.java
index 9c6b1ca99f3..031a5b197d9 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/DoubleVectorFixImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/DoubleVectorFixImpl.java
@@ -1,12 +1,16 @@
package com.sap.sailing.domain.common.tracking.impl;
import java.util.Arrays;
+import java.util.BitSet;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sse.common.TimePoint;
/**
- * Implementation of {@link DoubleVectorFix}.
+ * Implementation of {@link DoubleVectorFix}. In order to save some space and reduce the number of object headers,
+ * instead of storing {@link Double} objects the structure internally uses an array of {@code double} values and
+ * additionally remembers in a {@link BitSet} which components are {@code null}. The {@link #get()} and {@link #get(int)}
+ * methods then translate back to a {@link Double} representation accordingly.
*/
public class DoubleVectorFixImpl implements DoubleVectorFix {
@@ -14,10 +18,25 @@ public class DoubleVectorFixImpl implements DoubleVectorFix {
private final double[] fixData;
private final TimePoint timePoint;
+
+ /**
+ * The constructor accepts a {@code Double[]} for the data components, allowing for {@code null}
+ * values to be used. To save space, internally a {@code double[]} is used to represent the data.
+ * In order to encode the {@code null} values, this bit set stores which array components had
+ * valid, non-{@code null} data.
+ */
+ private final BitSet validComponents;
- public DoubleVectorFixImpl(TimePoint timePoint, double[] fixData) {
+ public DoubleVectorFixImpl(TimePoint timePoint, Double[] fixData) {
this.timePoint = timePoint;
- this.fixData = fixData;
+ this.fixData = new double[fixData.length];
+ this.validComponents = new BitSet(fixData.length);
+ for (int i=0; i
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.domain.expeditionadapter/.project b/java/com.sap.sailing.domain.expeditionadapter/.project
new file mode 100755
index 00000000000..a413084a0cd
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/.project
@@ -0,0 +1,28 @@
+
+
+ com.sap.sailing.domain.expeditionadapter
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 00000000000..f29e940a005
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/java/com.sap.sailing.domain.expeditionadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.expeditionadapter/META-INF/MANIFEST.MF
new file mode 100755
index 00000000000..43e36b46448
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Expeditionadapter
+Bundle-SymbolicName: com.sap.sailing.domain.expeditionadapter
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: SAP
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.osgi.framework
+Require-Bundle: com.sap.sailing.server.trackfiles,
+ com.sap.sailing.domain,
+ com.sap.sse.common,
+ com.sap.sailing.domain.common
+Bundle-Activator: com.sap.sailing.domain.expeditionadapter.impl.Activator
diff --git a/java/com.sap.sailing.domain.expeditionadapter/build.properties b/java/com.sap.sailing.domain.expeditionadapter/build.properties
new file mode 100755
index 00000000000..34d2e4d2dad
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/java/com.sap.sailing.domain.expeditionadapter/pom.xml b/java/com.sap.sailing.domain.expeditionadapter/pom.xml
new file mode 100755
index 00000000000..420cb8c34f6
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/pom.xml
@@ -0,0 +1,12 @@
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sailing.domain.expeditionadapter
+ eclipse-plugin
+
diff --git a/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/Activator.java b/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/Activator.java
new file mode 100755
index 00000000000..a73939a8ac8
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/Activator.java
@@ -0,0 +1,17 @@
+package com.sap.sailing.domain.expeditionadapter.impl;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.sap.sailing.server.trackfiles.common.GPSFixImporterRegistration;
+
+public class Activator implements BundleActivator {
+ @Override
+ public void start(BundleContext context) throws Exception {
+ GPSFixImporterRegistration.register(new ExpeditionGPSFixImporter(), context);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ }
+}
diff --git a/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/ExpeditionGPSFixImporter.java b/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/ExpeditionGPSFixImporter.java
new file mode 100755
index 00000000000..be38baee40d
--- /dev/null
+++ b/java/com.sap.sailing.domain.expeditionadapter/src/com/sap/sailing/domain/expeditionadapter/impl/ExpeditionGPSFixImporter.java
@@ -0,0 +1,61 @@
+package com.sap.sailing.domain.expeditionadapter.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
+import com.sap.sailing.domain.common.impl.DegreePosition;
+import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
+import com.sap.sailing.domain.common.tracking.GPSFixMoving;
+import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
+import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
+import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
+import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
+import com.sap.sailing.domain.trackimport.GPSFixImporter;
+import com.sap.sailing.server.trackfiles.impl.ExpeditionExtendedDataImporterImpl;
+
+public class ExpeditionGPSFixImporter implements GPSFixImporter {
+ private static final String LAT_COLUMN_HEADING = ExpeditionExtendedDataImporterImpl.COL_NAME_LAT;
+ private static final String LON_COLUMN_HEADING = ExpeditionExtendedDataImporterImpl.COL_NAME_LON;
+ private static final String COG_COLUMN_HEADING = "Cog";
+ private static final String SOG_COLUMN_HEADING = "Sog";
+ @Override
+ public void importFixes(InputStream inputStream, Callback callback, boolean inferSpeedAndBearing, final String sourceName)
+ throws FormatNotSupportedException, IOException {
+ TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifierImpl(sourceName, getType() + "@" + new Date());
+ final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+ final String headerLine = br.readLine();
+ final Map columnDefinitions = ExpeditionExtendedDataImporterImpl.parseHeader(headerLine);
+ final AtomicInteger lineNr = new AtomicInteger(0);
+ br.lines().forEach(line->{
+ if (!line.trim().isEmpty()) {
+ ExpeditionExtendedDataImporterImpl.parseLine(lineNr.incrementAndGet(), sourceName, line, columnDefinitions,
+ (timePoint, columnValues, columns)->{
+ final double latDeg = Double.parseDouble(columnValues[columns.get(LAT_COLUMN_HEADING)]);
+ final double lonDeg = Double.parseDouble(columnValues[columns.get(LON_COLUMN_HEADING)]);
+ final double cogDeg = Double.parseDouble(columnValues[columns.get(COG_COLUMN_HEADING)]);
+ final double sogKnots = Double.parseDouble(columnValues[columns.get(SOG_COLUMN_HEADING)]);
+ final GPSFixMoving fix = new GPSFixMovingImpl(new DegreePosition(latDeg, lonDeg), timePoint,
+ new KnotSpeedWithBearingImpl(sogKnots, new DegreeBearingImpl(cogDeg)));
+ callback.addFix(fix, device);
+ });
+ }
+ });
+ }
+
+ @Override
+ public Iterable getSupportedFileExtensions() {
+ return Arrays.asList(new String[] { "csv", "log", "txt" });
+ }
+
+ @Override
+ public String getType() {
+ return "Expedition";
+ }
+}
diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/FixFactoryTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/FixFactoryTest.java
new file mode 100755
index 00000000000..808ba392812
--- /dev/null
+++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/FixFactoryTest.java
@@ -0,0 +1,46 @@
+package com.sap.sailing.domain.igtimiadapter.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
+import com.sap.sailing.domain.igtimiadapter.impl.FixFactory;
+import com.sap.sse.common.Util;
+
+public class FixFactoryTest {
+ private FixFactory fixFactory;
+
+ @Before
+ public void setUp() {
+ fixFactory = new FixFactory();
+ }
+
+ @Test
+ public void testUnknownFixTypes() throws ParseException {
+ final String[] messages = new String[] {
+ "{\"DC-FE-AAKG\":{\"1\":{\"t\":[1510174025500],\"1\":[170.5044476],\"2\":[-45.87697]}}}",
+ "{\"DC-FE-AAKG\":{\"2\":{\"t\":[1510174025500],\"1\":[1]}}}",
+ "{\"DC-FE-AAKG\":{\"3\":{\"t\":[1510174025500],\"1\":[10]}}}",
+ "{\"DC-FE-AAKG\":{\"7\":{\"t\":[1510174025500],\"1\":[187.7]}}}",
+ "{\"DC-FE-AAKG\":{\"11\":{\"t\":[1510174025500],\"1\":[54]}}}",
+ "{\"DC-FE-AAKG\":{\"12\":{\"t\":[1510174025500],\"1\":[1.852]}}}",
+ "{\"DC-FE-AAKG\":{\"22\":{\"t\":[1510174025500],\"1\":[263.4]}}}",
+ "{\"DC-FE-AAKG\":{\"23\":{\"t\":[1510174025500],\"1\":[1.852]}}}",
+ "{\"DC-FE-AAKG\":{\"6\":{\"t\":[1510174025500],\"1\":[0]}}}",
+ "{\"DC-FE-AAKG\":{\"9\":{\"t\":[1510174025500],\"1\":[0]}}}" };
+ List fixes = new ArrayList<>();
+ for (final String message : messages) {
+ JSONObject jsonObject = (JSONObject) new JSONParser().parse(message);
+ Util.addAll(fixFactory.createFixes(jsonObject), fixes);
+ }
+ assertTrue(!Util.isEmpty(fixes)); // this largely asserts that no exception was thrown although there were unknown fix types (22/23)
+ }
+}
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactory.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactory.java
index 71700a8136e..83d1e869d5b 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactory.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactory.java
@@ -54,9 +54,12 @@ public class FixFactory {
Sensor sensor = new SensorImpl(deviceSerialNumber,
fixTypeAndOptionalColonSeparatedSensorsSubId.length < 2 ? 0
: Long.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[1]));
- Fix fix = createFix(sensor, Type.valueOf(fixType), timePoint, valuesPerSubindex);
- result.add(fix);
- fixIndex++;
+ final Type ft = Type.valueOf(fixType);
+ if (ft != null) {
+ Fix fix = createFix(sensor, ft, timePoint, valuesPerSubindex);
+ result.add(fix);
+ fixIndex++;
+ }
}
}
return result;
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
index 15879dfdb15..3753f48e93e 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
@@ -39,6 +39,7 @@ import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
+import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseAreaChangedEvent;
@@ -112,6 +113,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpe
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
+import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
@@ -154,6 +156,7 @@ import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.VenueImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
@@ -167,8 +170,8 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
-import com.sap.sailing.domain.common.dto.EventType;
import com.sap.sailing.domain.common.dto.AnniversaryType;
+import com.sap.sailing.domain.common.dto.EventType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -205,7 +208,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongo
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.PlaceHolderDeviceIdentifierSerializationHandler;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
@@ -1600,8 +1602,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final Long finishingTimePointAsMillis = (Long) dbObject.get(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name());
final TimePoint finishingTime = finishingTimePointAsMillis == null ? null : new MillisecondsTimePoint(finishingTimePointAsMillis);
final String comment = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name());
+ final String mergeStateAsString = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MERGE_STATE.name());
+ final MergeState mergeState;
+ if (mergeStateAsString == null) {
+ mergeState = MergeState.OK;
+ } else {
+ mergeState = MergeState.valueOf(mergeStateAsString);
+ }
CompetitorResultImpl positionedCompetitor = new CompetitorResultImpl(
- competitorId, competitorDisplayName, rank == null ? rankCounter : rank, maxPointsReason, score, finishingTime, comment);
+ competitorId, competitorDisplayName, rank == null ? rankCounter : rank, maxPointsReason, score, finishingTime, comment, mergeState);
positionedCompetitors.add(positionedCompetitor);
rankCounter++;
}
@@ -1713,6 +1722,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} else if (eventClass.equals(RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.class.getSimpleName())) {
return loadRegattaLogDeviceCompetitorBravoExtendedMappingEvent(createdAt, author, logicalTimePoint, id, dbObject,
regattaLogIdentifier, o);
+ } else if (eventClass.equals(RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.getSimpleName())) {
+ return loadRegattaLogDeviceCompetitorExpeditionExtendedMappingEvent(createdAt, author, logicalTimePoint, id, dbObject,
+ regattaLogIdentifier, o);
} else if (eventClass.equals(RegattaLogDeviceMarkMappingEvent.class.getSimpleName())) {
return loadRegattaLogDeviceMarkMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o);
} else if (eventClass.equals(RegattaLogCloseOpenEndedDeviceMappingEvent.class.getSimpleName())) {
@@ -1911,6 +1923,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
regattaLogIdentifier, outerDBObject, RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl::new);
}
+ private RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl loadRegattaLogDeviceCompetitorExpeditionExtendedMappingEvent(
+ TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id,
+ final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) {
+ return loadRegattaLogDeviceCompetitorSensorDataMappingEvent(createdAt, author, logicalTimePoint, id, dbObject,
+ regattaLogIdentifier, outerDBObject, RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl::new);
+ }
+
private MappingT loadRegattaLogDeviceCompetitorSensorDataMappingEvent(
TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id,
final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject,
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
index 2a2981141c0..a3dfe0b6343 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
@@ -80,7 +80,8 @@ public enum FieldNames {
LEADERBOARD_IS_MEDAL_RACE_COLUMN, LEADERBOARD_CARRIED_POINTS, LEADERBOARD_CARRIED_POINTS_BY_ID,
LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS,
LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP,
- LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
+ LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_SCORE_CORRECTION_MERGE_STATE,
+ LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
ELMINATED_COMPETITORS,
// leaderboard group
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
index 6c07b6ee67a..78c18534f25 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
@@ -86,6 +86,7 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMatcherSingle;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.common.Bearing;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.Positioned;
@@ -108,7 +109,6 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
@@ -1239,6 +1239,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name(), competitorResult.getFinishingTime() == null ? null : competitorResult.getFinishingTime().asMillis());
result.put(FieldNames.LEADERBOARD_RANK.name(), competitorResult.getOneBasedRank());
result.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name(), competitorResult.getComment());
+ result.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_MERGE_STATE.name(), competitorResult.getMergeState().name());
return result;
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/DoubleVectorFixMongoHandlerImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/DoubleVectorFixMongoHandlerImpl.java
index c39f4e58069..7d4043b0e56 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/DoubleVectorFixMongoHandlerImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/DoubleVectorFixMongoHandlerImpl.java
@@ -38,20 +38,20 @@ public class DoubleVectorFixMongoHandlerImpl implements FixMongoHandler dbValues = (List) dbObject;
- double[] result = new double[dbValues.size()];
- for(int i = 0 ; i < dbValues.size() ; i++) {
- result[i] = dbValues.get(i).doubleValue();
+ Double[] result = new Double[dbValues.size()];
+ for (int i = 0 ; i < dbValues.size() ; i++) {
+ result[i] = dbValues.get(i) == null ? null : dbValues.get(i).doubleValue();
}
return result;
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java
index eb813bc3351..9459a46b8ed 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java
@@ -20,6 +20,7 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -30,7 +31,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongo
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.Duration;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/PlaceHolderDeviceIdentifierMongoHandler.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/PlaceHolderDeviceIdentifierMongoHandler.java
index ee3ee4fe186..ccf0364559e 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/PlaceHolderDeviceIdentifierMongoHandler.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/PlaceHolderDeviceIdentifierMongoHandler.java
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.PlaceHolderDeviceIdentifierSerializationHandler;
public class PlaceHolderDeviceIdentifierMongoHandler extends PlaceHolderDeviceIdentifierSerializationHandler
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java
index 2a74622a15d..dcb29f6b6e5 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.impl.RegattaImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -38,7 +39,6 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockDeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
index 836726fb94e..4c9f475b7f3 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -47,7 +48,6 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/DeviceMappingsAndSensorFixStoreLockingTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/DeviceMappingsAndSensorFixStoreLockingTest.java
index 2a23fcdeda4..383942f7784 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/DeviceMappingsAndSensorFixStoreLockingTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/DeviceMappingsAndSensorFixStoreLockingTest.java
@@ -16,9 +16,9 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCo
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogDeviceMappings;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sse.common.MultiTimeRange;
@@ -76,7 +76,7 @@ public class DeviceMappingsAndSensorFixStoreLockingTest extends AbstractGPSFixSt
};
}.start();
- store.storeFix(device, new DoubleVectorFixImpl(new MillisecondsTimePoint(1), new double[]{0.0}));
+ store.storeFix(device, new DoubleVectorFixImpl(new MillisecondsTimePoint(1), new Double[]{0.0}));
}
}
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/GPSFixStoreListenerTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/GPSFixStoreListenerTest.java
index bab51469897..2174d0718e8 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/GPSFixStoreListenerTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/GPSFixStoreListenerTest.java
@@ -9,10 +9,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
public class GPSFixStoreListenerTest extends AbstractGPSFixStoreTest {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java
index a8aa39a5806..5558db801d9 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java
@@ -31,6 +31,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -45,7 +46,6 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
index fffd58bb725..383919426b2 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
@@ -54,7 +54,10 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.Distance;
+import com.sap.sailing.domain.common.Position;
+import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
@@ -75,12 +78,12 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.BravoDataFixMapper;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.BravoFixTrack;
+import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -92,6 +95,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.SensorFixTrackImpl;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util.Pair;
@@ -178,18 +182,12 @@ public class SensorFixStoreAndLoadTest {
public void testLoadAlreadyAddedFixes() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -197,18 +195,12 @@ public class SensorFixStoreAndLoadTest {
public void testAddFixesWhileTracking() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -216,18 +208,12 @@ public class SensorFixStoreAndLoadTest {
public void testNoFixesAreLoadedIfNoStoredFixIsInTimeRange() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(AFTER_LAST_FIX), new MillisecondsTimePoint(END_OF_TRACKING)));
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -235,18 +221,12 @@ public class SensorFixStoreAndLoadTest {
public void testFixesNotInMappedTimeRangeAreIgnoredWhileTracking() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(AFTER_LAST_FIX), new MillisecondsTimePoint(END_OF_TRACKING)));
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -254,18 +234,12 @@ public class SensorFixStoreAndLoadTest {
public void testCompetitorWithoutMappingHasNoTrack() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
assertNull(trackedRace.getSensorTrack(comp2, BravoFixTrack.TRACK_NAME));
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -275,19 +249,13 @@ public class SensorFixStoreAndLoadTest {
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp2,
device, new MillisecondsTimePoint(MID_OF_TRACKING + 1), new MillisecondsTimePoint(END_OF_TRACKING)));
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp2, BravoFixTrack.TRACK_NAME), 1);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -297,18 +265,12 @@ public class SensorFixStoreAndLoadTest {
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(MID_OF_TRACKING + 1), new MillisecondsTimePoint(END_OF_TRACKING)));
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -319,18 +281,12 @@ public class SensorFixStoreAndLoadTest {
new MillisecondsTimePoint(MID_OF_TRACKING));
regattaLog.add(mappingEvent);
regattaLog.add(new RegattaLogRevokeEventImpl(author, mappingEvent, "Test purposes"));
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
assertNull(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME));
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -342,23 +298,16 @@ public class SensorFixStoreAndLoadTest {
new MillisecondsTimePoint(END_OF_TRACKING));
regattaLog.add(mappingEvent);
regattaLog.add(new RegattaLogRevokeEventImpl(author, mappingEvent, "Test purposes"));
-
// non-revoked mapping timestamp 100-200
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
// Only Fixes from 100 to 200 may be included
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -371,25 +320,16 @@ public class SensorFixStoreAndLoadTest {
RegattaLogCloseOpenEndedDeviceMappingEventImpl closeEvent = new RegattaLogCloseOpenEndedDeviceMappingEventImpl(
new MillisecondsTimePoint(4), author, mappingEventId, new MillisecondsTimePoint(MID_OF_TRACKING));
regattaLog.add(closeEvent);
-
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
BravoFixTrack bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
-
testNumberOfRawFixes(bravoFixTrack, 2);
-
regattaLog.add(new RegattaLogRevokeEventImpl(author, closeEvent, "Test purposes"));
-
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(bravoFixTrack, 3);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -414,20 +354,14 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
addTestFixes();
addBravoFixes();
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME), 1);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -437,21 +371,14 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
-
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addTestFixes();
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME), 1);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -461,18 +388,12 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
-
-
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
-
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
-
addTestFixes();
addBravoFixes();
-
trackedRace.waitForLoadingToFinish();
-
BravoFixTrack bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
assertEquals(FIX_RIDE_HEIGHT,
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
@@ -480,11 +401,9 @@ public class SensorFixStoreAndLoadTest {
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP2)).getRideHeight());
assertEquals(FIX_RIDE_HEIGHT3,
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP3)).getRideHeight());
-
TestFixTrackImpl testFixTrack = trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME);
assertEquals(FIX_TEST_VALUE,
testFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getTestValue());
-
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -501,7 +420,6 @@ public class SensorFixStoreAndLoadTest {
}
private FixLoaderAndTracker createFixLoaderAndTracker() {
-
return new FixLoaderAndTracker(trackedRace, store, new SensorFixMapperFactory() {
private TestDataFixMapper testDataFixMapper = new TestDataFixMapper();
private BravoDataFixMapper bravoDataFixMapper = new BravoDataFixMapper();
@@ -542,7 +460,7 @@ public class SensorFixStoreAndLoadTest {
}
private DoubleVectorFix createBravoDoubleVectorFixWithRideHeight(long timestamp, double rideHeight) {
- double[] fixData = new double[BravoSensorDataMetadata.getTrackColumnCount()];
+ Double[] fixData = new Double[BravoSensorDataMetadata.getTrackColumnCount()];
// fill the port/starboard columns as well because their minimum defines the true ride height
fixData[BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex()] = rideHeight;
fixData[BravoSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex()] = rideHeight;
@@ -554,7 +472,7 @@ public class SensorFixStoreAndLoadTest {
}
private DoubleVectorFix createTestDoubleVectorFixWithTestValue(long timestamp, double testValue) {
- double[] fixData = new double[TestFixImpl.COLUMNS.size()];
+ Double[] fixData = new Double[TestFixImpl.COLUMNS.size()];
fixData[TestFixImpl.TEST_COLUMN_INDEX] = testValue;
return new DoubleVectorFixImpl(new MillisecondsTimePoint(timestamp), fixData);
}
@@ -747,6 +665,50 @@ public class SensorFixStoreAndLoadTest {
// only the initial 3 fixes are available
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
+ final DynamicGPSFixTrack gpsFixTrack = trackedRace.getTrack(comp);
+ final DegreePosition pos1 = new DegreePosition(0, 0);
+ final DegreeBearingImpl course = new DegreeBearingImpl(0);
+ final KnotSpeedWithBearingImpl speed = new KnotSpeedWithBearingImpl(10, course);
+ final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(FIX_TIMESTAMP);
+ final MillisecondsTimePoint timePoint2 = new MillisecondsTimePoint(FIX_TIMESTAMP2);
+ final MillisecondsTimePoint timePoint3 = new MillisecondsTimePoint(FIX_TIMESTAMP3);
+ final TimePoint timePointBetween2And3 = new MillisecondsTimePoint((FIX_TIMESTAMP2+FIX_TIMESTAMP3)/2);
+ final GPSFixMoving fix1 = new GPSFixMovingImpl(pos1, timePoint, speed);
+ final Position pos2 = pos1.translateGreatCircle(course, speed.travel(timePoint.until(timePoint2)));
+ final GPSFixMoving fix2 = new GPSFixMovingImpl(pos2, timePoint2, speed);
+ final Position pos3 = pos2.translateGreatCircle(course, speed.travel(timePoint2.until(timePoint3)));
+ final GPSFixMoving fix3 = new GPSFixMovingImpl(pos3, timePoint3, speed);
+ gpsFixTrack.add(fix1);
+ gpsFixTrack.add(fix2);
+ gpsFixTrack.add(fix3);
+ final BravoFixTrack bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
+ final Distance foilingDistance = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
+ final Distance distanceTraveled = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
+ assertEquals(distanceTraveled.getMeters(), foilingDistance.getMeters(), 0.001);
+ final Duration foilingDuration = bravoFixTrack.getTimeSpentFoiling(timePoint, timePoint3);
+ assertEquals(timePoint.until(timePoint3).asMillis(), foilingDuration.asMillis());
+
+ // now insert a GPS fix which leads to an extended distance traveled as it is not exactly on the previous track
+ final DegreeBearingImpl temporaryCourse = new DegreeBearingImpl(90);
+ final GPSFixMoving fixBetween2And3 = new GPSFixMovingImpl(
+ pos2.translateGreatCircle(temporaryCourse, speed.add(new KnotSpeedWithBearingImpl(5, temporaryCourse)).travel(timePoint2.until(timePointBetween2And3))),
+ timePointBetween2And3, speed);
+ gpsFixTrack.add(fixBetween2And3);
+ final Distance distanceTraveledDifferently = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
+ assertTrue(distanceTraveledDifferently.compareTo(distanceTraveled) > 0);
+ final Distance foilingDistanceDifferent = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
+ assertEquals(distanceTraveledDifferently.getMeters(), foilingDistanceDifferent.getMeters(), 0.001);
+
+ // now overwrite fix3 by a new one with increased speed, therefore extended distance:
+ final SpeedWithBearing doubledSpeed = speed.add(speed);
+ final GPSFixMoving fix3Faster = new GPSFixMovingImpl(
+ pos2.translateGreatCircle(course, doubledSpeed.travel(timePoint2.until(timePoint3))),
+ timePoint3, doubledSpeed);
+ gpsFixTrack.add(fix3Faster, /* replace */ true);
+ final Distance distanceTraveledFaster = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
+ assertTrue(distanceTraveledFaster.compareTo(distanceTraveled) > 0);
+ final Distance foilingDistanceFaster = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
+ assertEquals(distanceTraveledFaster.getMeters(), foilingDistanceFaster.getMeters(), 0.001);
}
@Test
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java
index 50f0204d706..1b11a7f9f3f 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java
@@ -14,6 +14,7 @@ import org.mockito.Mockito;
import com.mongodb.DB;
import com.mongodb.MongoException;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -30,7 +31,6 @@ import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Timed;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -384,7 +384,7 @@ public class SensorFixStoreTest {
}
private DoubleVectorFix createBravoDoubleVectorFixWithRideHeight(long timestamp, double rideHeight) {
- double[] fixData = new double[BravoSensorDataMetadata.getTrackColumnCount()];
+ Double[] fixData = new Double[BravoSensorDataMetadata.getTrackColumnCount()];
fixData[BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex()] = rideHeight;
fixData[BravoSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex()] = rideHeight;
return new DoubleVectorFixImpl(new MillisecondsTimePoint(timestamp), fixData);
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java
index f449b0dc1b0..56bc80be5bc 100755
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java
@@ -24,11 +24,11 @@ import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiIdentifier.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiIdentifier.java
index 9bfdebc3545..f0ec889b7ab 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiIdentifier.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiIdentifier.java
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
public class SmartphoneImeiIdentifier implements DeviceIdentifier {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiJsonHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiJsonHandler.java
index 3a770d9dfbb..71a6c7cfcc2 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiJsonHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiJsonHandler.java
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
public class SmartphoneImeiJsonHandler extends SmartphoneImeiSerializationHandler
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiMongoHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiMongoHandler.java
index 1ac23ea7e8f..d102c5e0280 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiMongoHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiMongoHandler.java
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
public class SmartphoneImeiMongoHandler extends SmartphoneImeiSerializationHandler
implements DeviceIdentifierMongoHandler {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiSerializationHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiSerializationHandler.java
index eaa539c68f6..bddce674e82 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiSerializationHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/SmartphoneImeiSerializationHandler.java
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.Util;
public class SmartphoneImeiSerializationHandler {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_fr.properties b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_fr.properties
index 70b07c43666..00a58568345 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_fr.properties
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_fr.properties
@@ -2,10 +2,10 @@ trackingInvitationFor=Invitation de suivi pour
scanQRCodeOrVisitUrlToRegisterAs=Si vous avez bien installé l''application {0}, scannez le code QR suivant ou cliquez sur le lien sur votre smartphone pour vous enregistrer comme
welcomeTo=Bienvenue dans
iOSUsers=Lancer l''application iOS
-androidUsers=Lancer l’application Android
+androidUsers=Lancer l''application Android
alternativelyVisitThisLink=Si vous consultez ce mail sur l''appareil sur lequel l''application est déjà installée, cliquez simplement sur le bouton ci-dessous pour commencer :
buoyTender=Navire baliseur
-appStoreInstallText=Si vous n''avez pas encore l''application, installez-la :
+appStoreInstallText=Si vous n''avez pas encore l''application, voici les liens pour l''installer :
appIos=Installer l''application iOS
appAndroid=Installer l''application Android
buoyPingerAppName=SAP Sailing Buoy Pinger
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifier.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifier.java
index 9312eb9f762..2aa2d8778f5 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifier.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifier.java
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
+import com.sap.sailing.domain.common.DeviceIdentifier;
+
/**
* A device identifier used to identify a non-existent, virtual device, which is used only once as an
* identifier to add a single fix to a track (i.e. pinging the location of that track's item).
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifierImpl.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifierImpl.java
index d08d7f72ff8..9c8f284f018 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifierImpl.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/PingDeviceIdentifierImpl.java
@@ -4,8 +4,6 @@ import java.util.UUID;
public class PingDeviceIdentifierImpl implements PingDeviceIdentifier {
private static final long serialVersionUID = -4049961972156611640L;
-
-
private final UUID id;
public PingDeviceIdentifierImpl() {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java
index fa8980c1f5f..8be4cc22076 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java
@@ -89,6 +89,7 @@ public class Activator implements BundleActivator {
new MasterDataImportClassLoaderServiceImpl(), null));
registrations.add(context.registerService(SensorFixMapper.class, new BravoDataFixMapper(), null));
registrations.add(context.registerService(SensorFixMapper.class, new BravoExtendedDataFixMapper(), null));
+ registrations.add(context.registerService(SensorFixMapper.class, new ExpeditionExtendedDataFixMapper(), null));
sensorFixMapperTracker = createSensorFixMapperServiceTracker(context);
racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java
index 7f822f11560..3415d44f166 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoDataFixMapper.java
@@ -20,7 +20,8 @@ public class BravoDataFixMapper implements SensorFixMapper getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
- () -> new BravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, false));
+ () -> new BravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
+ race.getTrack(key)));
}
@Override
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java
index 9aab573d347..f35509b8ec3 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/BravoExtendedDataFixMapper.java
@@ -21,7 +21,8 @@ public class BravoExtendedDataFixMapper implements SensorFixMapper getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
- () -> new BravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, true));
+ () -> new BravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
+ race.getTrack(key)));
}
@Override
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java
new file mode 100755
index 00000000000..5dcf9db8119
--- /dev/null
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/ExpeditionExtendedDataFixMapper.java
@@ -0,0 +1,41 @@
+package com.sap.sailing.domain.racelogtracking.impl;
+
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl;
+import com.sap.sailing.domain.base.Competitor;
+import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
+import com.sap.sailing.domain.common.tracking.BravoFix;
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl;
+import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
+import com.sap.sailing.domain.tracking.BravoFixTrack;
+import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
+import com.sap.sailing.domain.tracking.DynamicTrackedRace;
+import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
+
+/**
+ * {@link SensorFixMapper} implementation to product {@link BravoExtendedFix}es from {@link DoubleVectorFix}es
+ * interpreted as "Expedition" data. The data has significant overlap with what we typically receive from the
+ * "Bravo" devices. Ultimately, the naming may need to be adjusted, better reflecting that the "Bravo" extended
+ * tracks now assume general responsibility for a broader set of sensor values that may be assigned to a
+ * competitor, beyond just GPS tracking data.
+ */
+public class ExpeditionExtendedDataFixMapper implements SensorFixMapper, Competitor> {
+
+ @Override
+ public DynamicSensorFixTrack getTrack(DynamicTrackedRace race, Competitor key) {
+ return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
+ () -> new BravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
+ race.getTrack(key)));
+ }
+
+ @Override
+ public BravoExtendedFix map(DoubleVectorFix fix) {
+ return new BravoExtendedFixImpl(fix);
+ }
+
+ @Override
+ public boolean isResponsibleFor(Class extends RegattaLogDeviceMappingEvent>> eventType) {
+ return RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.class.isAssignableFrom(eventType);
+ }
+}
\ No newline at end of file
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierJsonHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierJsonHandler.java
index 60d1b57dfb7..072e91a1e4f 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierJsonHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierJsonHandler.java
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
public class PingDeviceIdentifierJsonHandler extends PingDeviceIdentifierSerializationHandler
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierMongoHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierMongoHandler.java
index efb7ecb08dc..9a5a7d37764 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierMongoHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierMongoHandler.java
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelogtracking.impl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
public class PingDeviceIdentifierMongoHandler extends PingDeviceIdentifierSerializationHandler
implements DeviceIdentifierMongoHandler {
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierSerializationHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierSerializationHandler.java
index 415af995c85..2d1e5578407 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierSerializationHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/PingDeviceIdentifierSerializationHandler.java
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.racelogtracking.impl;
import java.util.UUID;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
import com.sap.sse.common.Util;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/SmartphoneUUIDMongoHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/SmartphoneUUIDMongoHandler.java
index 8646b044541..e11f27f1d8e 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/SmartphoneUUIDMongoHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/SmartphoneUUIDMongoHandler.java
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelogtracking.impl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDSerializationHandler;
public class SmartphoneUUIDMongoHandler extends SmartphoneUUIDSerializationHandler
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierJsonHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierJsonHandler.java
index fcdb4f4876e..bff372f9a57 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierJsonHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierJsonHandler.java
@@ -4,8 +4,8 @@ import java.util.UUID;
import org.json.simple.JSONObject;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-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.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierMongoHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierMongoHandler.java
index a9b1e2cd8b5..1218089453f 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierMongoHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierMongoHandler.java
@@ -4,9 +4,9 @@ import java.util.UUID;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sse.common.TimePoint;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierStringSerializationHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierStringSerializationHandler.java
index e121abd0502..9f66f61d8e1 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierStringSerializationHandler.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackFileImportDeviceIdentifierStringSerializationHandler.java
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.racelogtracking.impl;
import java.util.UUID;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
index ddc341ea55e..d83e9517bb5 100755
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
@@ -23,6 +23,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMap
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
@@ -33,7 +34,6 @@ import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java
index 07cc2cefb8b..b837aa38419 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java
@@ -22,8 +22,8 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMap
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicTrack;
diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ConfirmedFinishPositioningListFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ConfirmedFinishPositioningListFinderTest.java
index 178e8dc026d..b818708212f 100644
--- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ConfirmedFinishPositioningListFinderTest.java
+++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ConfirmedFinishPositioningListFinderTest.java
@@ -12,6 +12,7 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
+import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -56,10 +57,10 @@ public class ConfirmedFinishPositioningListFinderTest extends
public void testMostRecent() {
RaceLogFinishPositioningConfirmedEvent event1 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1);
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 2);
- final CompetitorResultImpl oldResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.NONE, 1.0, /* finishing time */ null, /* comment */ null);
- final CompetitorResultImpl oldResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl oldResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.NONE, 1.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
+ final CompetitorResultImpl oldResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults olderResults = mockCompetitorResults(oldResultC1, oldResultC2);
- final CompetitorResultImpl newResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl newResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults newerResults = mockCompetitorResults(newResultC2);
when(event1.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(olderResults);
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(newerResults);
@@ -74,16 +75,16 @@ public class ConfirmedFinishPositioningListFinderTest extends
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(/* priority */ 1, RaceLogFinishPositioningConfirmedEvent.class, 2);
RaceLogFinishPositioningConfirmedEvent event3 = createEvent(/* priority */ 2, RaceLogFinishPositioningConfirmedEvent.class, 3);
RaceLogFinishPositioningConfirmedEvent event4 = createEvent(/* priority */ 1, RaceLogFinishPositioningConfirmedEvent.class, 4);
- final CompetitorResultImpl e1ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.DNC, 6.0, /* finishing time */ null, /* comment */ null);
- final CompetitorResultImpl e1ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl e1ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.DNC, 6.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
+ final CompetitorResultImpl e1ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e1Results = mockCompetitorResults(e1ResultC1, e1ResultC2);
- final CompetitorResultImpl e2ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl e2ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e2Results = mockCompetitorResults(e2ResultC2);
- final CompetitorResultImpl e3ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DSQ, 6.0, /* finishing time */ null, /* comment */ null);
- final CompetitorResultImpl e3ResultC3 = new CompetitorResultImpl("Comp3", "Comp 3", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl e3ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DSQ, 6.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
+ final CompetitorResultImpl e3ResultC3 = new CompetitorResultImpl("Comp3", "Comp 3", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e3Results = mockCompetitorResults(e3ResultC2, e3ResultC3);
- final CompetitorResultImpl e4ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, /* maxPointsReason */ null, /* score */ null, /* finishing time */ null, /* comment */ null);
- final CompetitorResultImpl e4ResultC4 = new CompetitorResultImpl("Comp4", "Comp 4", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
+ final CompetitorResultImpl e4ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, /* maxPointsReason */ null, /* score */ null, /* finishing time */ null, /* comment */ null, MergeState.OK);
+ final CompetitorResultImpl e4ResultC4 = new CompetitorResultImpl("Comp4", "Comp 4", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e4Results = mockCompetitorResults(e4ResultC1, e4ResultC4);
when(event1.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(e1Results);
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(e2Results);
diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java
index e9b28c32396..e43e8cece89 100644
--- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java
+++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/regattalog/tracking/analyzing/test/DeviceMappingFinderTest.java
@@ -17,9 +17,9 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCo
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/CompetitorResult.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/CompetitorResult.java
index a1743691ef4..73e556c861e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/CompetitorResult.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/CompetitorResult.java
@@ -14,6 +14,30 @@ import com.sap.sse.common.TimePoint;
*
*/
public interface CompetitorResult extends Serializable {
+ public static enum MergeState {
+ /**
+ * No merge was necessary at competitor level.
+ */
+ OK,
+
+ /**
+ * A field-wise merge was necessary because an update was received that affected this competitor result
+ * while locally this competitor result was in a "dirty" (modified) state. The merge was carried out
+ * automatically because the fields changed in the dirty state did not overlap with the fields changed
+ * in the version of the object received.
+ */
+ WARNING,
+
+ /**
+ * A field-wise merge was necessary because an update was received that affected this competitor result while
+ * locally this competitor result was in a "dirty" (modified) state. The fields changed in the dirty state
+ * overlap with the fields changed in the version of the object received. Field values from the local dirty state
+ * have been preferred or have been appended in case of {@link String}-typed fields and need manual
+ * intervention.
+ */
+ ERROR
+ };
+
Serializable getCompetitorId();
String getCompetitorDisplayName();
@@ -27,4 +51,9 @@ public interface CompetitorResult extends Serializable {
TimePoint getFinishingTime();
String getComment();
+
+ /**
+ * Always returns a valid, non-{@code null} result. See {@link MergeState} for details.
+ */
+ MergeState getMergeState();
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/CompetitorResultImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/CompetitorResultImpl.java
index da8728f267b..d5221e451a4 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/CompetitorResultImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/CompetitorResultImpl.java
@@ -23,8 +23,11 @@ public class CompetitorResultImpl implements CompetitorResult {
private final String comment;
+ private final MergeState mergeState;
+
public CompetitorResultImpl(Serializable competitorId, String competitorDisplayName, int oneBasedRank,
- MaxPointsReason maxPointsReason, Double score, TimePoint finishingTime, String comment) {
+ MaxPointsReason maxPointsReason, Double score, TimePoint finishingTime, String comment,
+ MergeState mergeState) {
super();
this.competitorId = competitorId;
this.competitorDisplayName = competitorDisplayName;
@@ -33,6 +36,7 @@ public class CompetitorResultImpl implements CompetitorResult {
this.score = score;
this.finishingTime = finishingTime;
this.comment = comment;
+ this.mergeState = mergeState;
}
@Override
@@ -70,6 +74,11 @@ public class CompetitorResultImpl implements CompetitorResult {
return comment;
}
+ @Override
+ public MergeState getMergeState() {
+ return mergeState == null ? MergeState.OK : mergeState; // default in case of having de-serialized an old version
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -81,6 +90,7 @@ public class CompetitorResultImpl implements CompetitorResult {
result = prime * result + ((maxPointsReason == null) ? 0 : maxPointsReason.hashCode());
result = prime * result + oneBasedRank;
result = prime * result + ((score == null) ? 0 : score.hashCode());
+ result = prime * result + ((mergeState == null) ? 0 : mergeState.hashCode());
return result;
}
@@ -122,6 +132,11 @@ public class CompetitorResultImpl implements CompetitorResult {
return false;
} else if (!score.equals(other.score))
return false;
+ if (mergeState == null) {
+ if (other.mergeState != null)
+ return false;
+ } else if (!mergeState.equals(other.mergeState))
+ return false;
return true;
}
@@ -129,7 +144,7 @@ public class CompetitorResultImpl implements CompetitorResult {
public String toString() {
return "CompetitorResultImpl [competitorId=" + competitorId + ", competitorName=" + competitorDisplayName + ", rank="
+ oneBasedRank + ", maxPointsReason=" + maxPointsReason + ", score=" + score + ", finishingTime="
- + finishingTime + ", comment=" + comment + "]";
+ + finishingTime + ", comment=" + comment + ", mergeState=" + mergeState + "]";
}
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java
index 269d14b692d..5a2a9e2c7dc 100755
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
+import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Function;
@@ -263,4 +264,11 @@ public class NoAddingRaceLogWrapper implements RaceLog {
public int size() {
return innerRaceLog.size();
}
+
+ @Override
+ public T getValueSum(TimePoint from, TimePoint to, T nullElement,
+ com.sap.sailing.domain.tracking.Track.Adder adder, TimeRangeCache cache,
+ com.sap.sailing.domain.tracking.Track.TimeRangeValueCalculator valueCalculator) {
+ return innerRaceLog.getValueSum(from, to, nullElement, adder, cache, valueCalculator);
+ }
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java
index 20501c69678..2fbd998177e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java
@@ -158,6 +158,7 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
public void forceNewDependentStartTime(TimePoint now, final Duration startTimeDifference, final SimpleRaceLogIdentifier dependentOnRace) {
raceLog.add(new RaceLogDependentStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), dependentOnRace, startTimeDifference));
}
+
@Override
public void setFinishingTime(TimePoint timePoint) {
@@ -166,7 +167,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
@Override
public void setFinishedTime(TimePoint timePoint) {
- raceLog.add(new RaceLogRaceStatusEventImpl(timePoint, author, raceLog.getCurrentPassId(), RaceLogRaceStatus.FINISHED));
+ raceLog.add(new RaceLogRaceStatusEventImpl(timePoint, author, raceLog.getCurrentPassId(),
+ RaceLogRaceStatus.FINISHED));
+ // ensure caches are synched
+ forceUpdate();
}
@Override
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEvent.java
index 8895db1fbea..7cac1817803 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEvent.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEvent.java
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java
index 88b011916d1..08f4e08c1d9 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/RegattaLogDeviceMappingEventImpl.java
@@ -5,7 +5,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.AbstractLogEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java
index a0891b9273a..dbe9f1e80ac 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl.java
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.java
index 43eb27f8faa..83fc2feb3e4 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoExtendedMappingEventImpl.java
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java
index 8c68721ceaf..831aa75a927 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorBravoMappingEventImpl.java
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.java
new file mode 100755
index 00000000000..eab4586d1f2
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl.java
@@ -0,0 +1,28 @@
+package com.sap.sailing.domain.abstractlog.regatta.events.impl;
+
+import java.io.Serializable;
+
+import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
+import com.sap.sailing.domain.base.Competitor;
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sse.common.TimePoint;
+
+/**
+ * Mapping event for devices mapped for "Expedition" extended type fixes, coming from the
+ * "Expedition" software that was originally used in the context of this solution only for
+ * handling wind data but now is also used to forward sensor data into competitor tracks.
+ */
+public class RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl extends AbstractRegattaLogDeviceCompetitorSensorDataMappingEventImpl {
+ private static final long serialVersionUID = -7865925670153838864L;
+
+ public RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
+ AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
+ TimePoint from, TimePoint to) {
+ super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
+ }
+
+ public RegattaLogDeviceCompetitorExpeditionExtendedMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
+ Competitor mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
+ super(logicalTimePoint, author, mappedTo, device, from, to);
+ }
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorMappingEventImpl.java
index ef7aea4c628..b9c8cbce426 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceCompetitorMappingEventImpl.java
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceCompetitorMappingEventImpl extends RegattaLogDeviceMappingEventImpl implements
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceMarkMappingEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceMarkMappingEventImpl.java
index e2da1cdca93..61312f84ba3 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceMarkMappingEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/events/impl/RegattaLogDeviceMarkMappingEventImpl.java
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.base.Mark;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceMarkMappingEventImpl extends RegattaLogDeviceMappingEventImpl implements
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/BaseRegattaLogDeviceMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/BaseRegattaLogDeviceMappingFinder.java
index 83cff11e8f6..62f138d79f5 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/BaseRegattaLogDeviceMappingFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/BaseRegattaLogDeviceMappingFinder.java
@@ -10,8 +10,8 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceCompetitorMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceCompetitorMappingFinder.java
index 6226305b798..c357cc9e26e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceCompetitorMappingFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceCompetitorMappingFinder.java
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompeti
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
index 448947529dc..983684147c1 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
@@ -13,7 +13,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingWithRegattaLogEventImpl;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMarkMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMarkMappingFinder.java
index b76ca947923..4e3d50a3cf8 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMarkMappingFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMarkMappingFinder.java
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMapping
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.base.Mark;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceIdentifierSerializationHandler.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceIdentifierSerializationHandler.java
index 57327658980..e3bc1a64e60 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceIdentifierSerializationHandler.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceIdentifierSerializationHandler.java
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.racelogtracking;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sse.common.Util;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceMapping.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceMapping.java
index 19fd46a3fed..afd702b188d 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceMapping.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/DeviceMapping.java
@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
import com.sap.sse.common.WithID;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/PlaceHolderDeviceIdentifier.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/PlaceHolderDeviceIdentifier.java
index 6adb0e30b79..1c4d9c2b16c 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/PlaceHolderDeviceIdentifier.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/PlaceHolderDeviceIdentifier.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelogtracking;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
+import com.sap.sailing.domain.common.DeviceIdentifier;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDIdentifier.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDIdentifier.java
index 05d6e8e3671..2b5655a8c58 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDIdentifier.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDIdentifier.java
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
+import com.sap.sailing.domain.common.DeviceIdentifier;
+
public interface SmartphoneUUIDIdentifier extends DeviceIdentifier {
String TYPE = "smartphoneUUID";
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDSerializationHandler.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDSerializationHandler.java
index 8a284d26c53..2661f799f5a 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDSerializationHandler.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/SmartphoneUUIDSerializationHandler.java
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl;
import com.sap.sse.common.Util;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingImpl.java
index 125b7c6a8b3..1a7bebc997f 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingImpl.java
@@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingWithRegattaLogEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingWithRegattaLogEventImpl.java
index 21096c83a03..454d2055597 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingWithRegattaLogEventImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/DeviceMappingWithRegattaLogEventImpl.java
@@ -4,7 +4,7 @@ import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/PlaceHolderDeviceIdentifierSerializationHandler.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/PlaceHolderDeviceIdentifierSerializationHandler.java
index 383c40a7e5c..182fa4d18a5 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/PlaceHolderDeviceIdentifierSerializationHandler.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelogtracking/impl/PlaceHolderDeviceIdentifierSerializationHandler.java
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
-import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PlaceHolderDeviceIdentifier;
import com.sap.sse.common.Util;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/FixAcceptancePredicate.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/FixAcceptancePredicate.java
new file mode 100755
index 00000000000..6788fc98915
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/FixAcceptancePredicate.java
@@ -0,0 +1,14 @@
+package com.sap.sailing.domain.tracking;
+
+/**
+ * A predicate for a fix, for use in
+ * {@link Track#getInterpolatedValue(com.sap.sse.common.TimePoint, com.sap.sse.common.Util.Function)}, used, e.g., to
+ * provide a rule for when a fix shall be accepted during the search for surrounding fixes.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ * @param
+ */
+public interface FixAcceptancePredicate {
+ boolean isAcceptFix(FixType fix);
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java
index adada1b8209..7bffcbefe56 100755
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java
@@ -5,6 +5,7 @@ import java.util.Iterator;
import java.util.concurrent.locks.ReadWriteLock;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
+import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
@@ -31,6 +32,29 @@ import com.sap.sse.common.scalablevalue.ScalableValue;
* @author Axel Uhl (d043530)
*/
public interface Track extends Serializable {
+ /**
+ * An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ * @param
+ */
+ static interface Adder {
+ /**
+ * Adds two elements of type {@code T}. Neither argument must be {@code null}.
+ */
+ T add(T t1, T t2);
+ }
+
+ static interface TimeRangeValueCalculator {
+ /**
+ * Calculates a value for fixes across a time range. When the method is called,
+ * a read lock will previously have been {@link Track#lockForRead obtained} before,
+ * so an implementing class does not need to worry about acquiring the lock.
+ */
+ T calculate(TimePoint from, TimePoint to);
+ }
+
/**
* Locks this track for reading by the calling thread. If the thread already holds the lock for this track,
* the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a finally
@@ -209,6 +233,8 @@ public interface Track extends Serializable {
*/
Duration getAverageIntervalBetweenRawFixes();
+ T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder adder, TimeRangeCache cache, TimeRangeValueCalculator valueCalculator);
+
/**
* @return the number of raw fixes contained in the Track.
*/
@@ -218,4 +244,5 @@ public interface Track extends Serializable {
* Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty
*/
boolean isEmpty();
+
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java
new file mode 100755
index 00000000000..de43e8d22f7
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java
@@ -0,0 +1,221 @@
+package com.sap.sailing.domain.tracking.impl;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+import java.util.NavigableSet;
+
+import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.Util;
+import com.sap.sse.common.Util.Pair;
+import com.sap.sse.concurrent.LockUtil;
+import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
+import com.sap.sse.util.impl.ArrayListNavigableSet;
+
+/**
+ * This cache looks "backwards." It contains pairs whose first component represents a to parameter used in
+ * a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of
+ * pairs where the first pair component represents a from parameter used in the calculation's time range
+ * and the second pair component represents the result of the calculation for this parameter combination.
+ *
+ *
+ * For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order
+ * to be able to efficiently extend a cache entry for a single to fix, the navigable sets containing the
+ * from fixes and results are ordered such that earlier fixes come later in the set. This way, extending
+ * the cache entry for a to fix to an earlier from fix only requires appending to the set.
+ *
+ *
+ * Invalidation: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's
+ * time point are removed from this cache. Additionally, the fix insertion may have an impact on the
+ * {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and
+ * therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the
+ * cache entries for the time points at or after the previous fix also need to be removed.
+ *
+ *
+ * Cache use: When a result across a time range is to be computed the calculating method should first look for a
+ * cache entry for the to parameter. If one is found, the earliest entry in the navigable set for the
+ * navigable set of from and result values that is at or after the requested from time point
+ * is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the
+ * from value found in the cache as the new to value, and the from value
+ * originally passed to the calculating method as from again. If no entry is found in the cache entry for
+ * to that is at or after the requested from time, the result has to be computed "from
+ * scratch."
+ *
+ *
+ * If a cache entry for to is not found, the latest cache entry before it is looked up. If one is found,
+ * the result for the time range between the to time point requested and the to time point
+ * found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is
+ * computed by iterating backwards all the way to from.
+ *
+ *
+ * Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the
+ * result to the cache.
+ *
+ * @author Axel Uhl (D043530)
+ */
+public class TimeRangeCache {
+ public static final int MAX_SIZE = 100;
+
+ private final NavigableSet>>> timeRangeCache;
+
+ /**
+ * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is
+ * defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or
+ * having been added by {@link #cache(TimePoint, TimePoint, Object)}.
+ *
+ *
+ * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements,
+ * the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading
+ * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash
+ * map that counts access as "use" has a modifying effect on its internal structures.
+ *
+ *
+ * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the
+ * alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.
+ *
+ * Read access is to be synchronized using this field's mutex; write access only happens under the
+ * {@link #lock write lock} and therefore will have no contenders.
+ */
+ private final LinkedHashMap, Void> lruCache;
+
+ private final NamedReentrantReadWriteLock lock;
+
+ private static final Comparator> timePointInPairComparator = new Comparator>() {
+ @Override
+ public int compare(Util.Pair o1, Util.Pair o2) {
+ return o1.getA().compareTo(o2.getA());
+ }
+ };
+
+ public TimeRangeCache(String nameForLockLogging) {
+ lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true);
+ this.timeRangeCache = new ArrayListNavigableSet>>>(timePointInPairComparator);
+ this.lruCache = new LinkedHashMap, Void>(/* initial capacity */ 10, /* load factor */ 0.75f,
+ /* access-based ordering */ true) {
+ private static final long serialVersionUID = -6568235517111733193L;
+
+ @Override
+ protected boolean removeEldestEntry(Entry, Void> eldest) {
+ final boolean expunge = size() > MAX_SIZE;
+ if (expunge) {
+ removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB());
+ }
+ return expunge;
+ }
+ };
+ }
+
+ public int size() {
+ return lruCache.size();
+ }
+
+ private void removeCacheEntry(TimePoint from, TimePoint to) {
+ assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock
+ Util.Pair>> entryForTo = timeRangeCache.floor(createDummy(to));
+ if (entryForTo.getA().equals(to)) {
+ Pair entryForFrom = entryForTo.getB().ceiling(new Util.Pair(from, null));
+ if (entryForFrom.getA().equals(from)) {
+ entryForTo.getB().remove(entryForFrom);
+ if (entryForTo.getB().isEmpty()) {
+ timeRangeCache.remove(entryForTo);
+ }
+ }
+ }
+ }
+
+ /**
+ * Looks up the entry closest to but no later than to. If not found, null is returned. If
+ * found, the earliest pair of from/result that is at or after from will be returned, together with
+ * the to value of the entry. If there is no entry that is at or after from,
+ * null is returned.
+ */
+ public Util.Pair> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) {
+ LockUtil.lockForRead(lock);
+ try {
+ Util.Pair> result = null;
+ Util.Pair>> entryForTo = timeRangeCache.floor(createDummy(to));
+ if (entryForTo != null) {
+ final Util.Pair fromCeiling = entryForTo.getB().ceiling(new Util.Pair(from, null));
+ if (fromCeiling != null) {
+ result = new Util.Pair>(entryForTo.getA(), fromCeiling);
+ }
+ }
+ // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using
+ // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry
+ // modifies the internal parts of the data structure which is not thread safe.
+ synchronized (lruCache) {
+ lruCache.get(new Util.Pair(from, to));
+ }
+ return result;
+ } finally {
+ LockUtil.unlockAfterRead(lock);
+ }
+ }
+
+ /**
+ * Removes all cache entries that have a to time point that is at or after timePoint.
+ */
+ public void invalidateAllAtOrLaterThan(TimePoint timePoint) {
+ LockUtil.lockForWrite(lock);
+ try {
+ Util.Pair>> dummy = createDummy(timePoint);
+ NavigableSet>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true);
+ for (Iterator>>> i=toRemove.iterator(); i.hasNext(); ) {
+ Util.Pair>> entryToRemove = i.next();
+ assert entryToRemove.getA().compareTo(timePoint) >= 0;
+ for (Pair fromAndResult : entryToRemove.getB()) {
+ lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA()));
+ }
+ i.remove();
+ }
+ } finally {
+ LockUtil.unlockAfterWrite(lock);
+ }
+ }
+
+ private NavigableSet> getEntryForTo(TimePoint to) {
+ NavigableSet> result = null;
+ Util.Pair>> dummyForTo = createDummy(to);
+ Util.Pair>> entryForTo = timeRangeCache.floor(dummyForTo);
+ if (entryForTo != null && entryForTo.getA().equals(to)) {
+ result = entryForTo.getB();
+ }
+ return result;
+ }
+
+ public void cache(TimePoint from, TimePoint to, T result) {
+ LockUtil.lockForWrite(lock);
+ try {
+ NavigableSet> entryForTo = getEntryForTo(to);
+ if (entryForTo == null) {
+ entryForTo = new ArrayListNavigableSet>(timePointInPairComparator);
+ Util.Pair>> pairForTo = new Util.Pair>>(
+ to, entryForTo);
+ timeRangeCache.add(pairForTo);
+ }
+ entryForTo.add(new Util.Pair(from, result));
+ lruCache.put(new Util.Pair(from, to), null);
+ } finally {
+ LockUtil.unlockAfterWrite(lock);
+ }
+ }
+
+ private Util.Pair>> createDummy(TimePoint to) {
+ return new Util.Pair>>(to, null);
+ }
+
+ /**
+ * Removes all contents from this cache
+ */
+ public void clear() {
+ LockUtil.lockForWrite(lock);
+ try {
+ timeRangeCache.clear();
+ lruCache.clear();
+ } finally {
+ LockUtil.unlockAfterWrite(lock);
+ }
+
+ }
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java
index 0685e176249..4f6a752bbcc 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java
@@ -6,10 +6,12 @@ import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NavigableSet;
+import com.sap.sailing.domain.tracking.FixAcceptancePredicate;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
+import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Function;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.scalablevalue.ScalableValue;
@@ -148,14 +150,25 @@ public class TrackImpl implements Track {
@Override
public FixType getLastFixAtOrBefore(TimePoint timePoint) {
+ return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null);
+ }
+
+ private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate fixAcceptancePredicate) {
lockForRead();
try {
- return (FixType) getInternalFixes().floor(getDummyFix(timePoint));
+ final NavigableSet headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true);
+ for (final Iterator i=headSet.descendingIterator(); i.hasNext(); ) {
+ final FixType next = i.next();
+ if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) {
+ return next;
+ }
+ }
+ return null;
} finally {
unlockAfterRead();
}
}
-
+
@Override
public FixType getLastFixBefore(TimePoint timePoint) {
lockForRead();
@@ -188,9 +201,20 @@ public class TrackImpl implements Track {
@Override
public FixType getFirstFixAtOrAfter(TimePoint timePoint) {
+ return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null);
+ }
+
+ private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate fixAcceptancePredicate) {
lockForRead();
try {
- return (FixType) getInternalFixes().ceiling(getDummyFix(timePoint));
+ final NavigableSet tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true);
+ for (final Iterator i=tailSet.iterator(); i.hasNext(); ) {
+ final FixType next = i.next();
+ if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) {
+ return next;
+ }
+ }
+ return null;
} finally {
unlockAfterRead();
}
@@ -254,9 +278,16 @@ public class TrackImpl implements Track {
}
}
- private Pair getSurroundingFixes(TimePoint timePoint) {
- FixType left = getLastFixAtOrBefore(timePoint);
- FixType right = getFirstFixAtOrAfter(timePoint);
+ /**
+ * @param fixAcceptancePredicate
+ * if not {@code null}, adjacent fixes will be skipped as long as this predicate does not
+ * {@link FixAcceptancePredicate#isAcceptFix(Object) accept} the fix. This can, e.g., be used to skip
+ * fixes that don't have values in a dimension required. If {@code null}, the next fixes left and right
+ * (including the exact {@code timePoint} if a fix exists there) will be used without further check.
+ */
+ private Pair getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate fixAcceptancePredicate) {
+ FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate);
+ FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate);
com.sap.sse.common.Util.Pair result = new com.sap.sse.common.Util.Pair<>(left, right);
return result;
}
@@ -274,9 +305,15 @@ public class TrackImpl implements Track {
}
@Override
- public ValueType getInterpolatedValue(TimePoint timePoint, Function> converter) {
+ public ValueType getInterpolatedValue(TimePoint timePoint,
+ Function> converter) {
+ return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null);
+ }
+
+ protected ValueType getInterpolatedValue(TimePoint timePoint,
+ Function> converter, FixAcceptancePredicate fixAcceptancePredicate) {
final ValueType result;
- Pair fixPair = getSurroundingFixes(timePoint);
+ Pair fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate);
if (fixPair.getA() == null) {
if (fixPair.getB() == null) {
result = null;
@@ -423,6 +460,64 @@ public class TrackImpl implements Track {
}
}
+ @Override
+ public T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder adder, TimeRangeCache cache, TimeRangeValueCalculator valueCalculator) {
+ return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator);
+ }
+
+ private T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement,
+ Adder adder, TimeRangeCache cache, TimeRangeValueCalculator valueCalculator) {
+ T result;
+ if (!from.before(to)) {
+ result = nullElement;
+ } else {
+ boolean perfectCacheHit = false;
+ lockForRead();
+ try {
+ Util.Pair> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to);
+ if (bestCacheEntry != null) {
+ perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below
+ // compute the missing stretches between best cache entry's "from" and our "from" and the cache
+ // entry's "to" and our "to"
+ T valueFromFromToBeginningOfCacheEntry = nullElement;
+ T valueFromEndOfCacheEntryToTo = nullElement;
+ if (!bestCacheEntry.getB().getA().equals(from)) {
+ assert bestCacheEntry.getB().getA().after(from);
+ perfectCacheHit = false;
+ valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry
+ .getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator);
+ }
+ if (!bestCacheEntry.getA().equals(to)) {
+ assert bestCacheEntry.getA().before(to);
+ perfectCacheHit = false;
+ valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to,
+ recursionDepth + 1, nullElement, adder, cache, valueCalculator);
+ }
+ if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) {
+ result = null;
+ } else {
+ result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()),
+ valueFromEndOfCacheEntryToTo);
+ }
+ } else {
+ if (from.compareTo(to) < 0) {
+ result = valueCalculator.calculate(from, to);
+ } else {
+ result = nullElement;
+ }
+ }
+ } finally {
+ unlockAfterRead();
+ }
+ if (!perfectCacheHit && recursionDepth == 0) {
+ cache.cache(from, to, result);
+ }
+ }
+ return result;
+ }
+
+
+
@Override
public int size() {
return fixes.size();
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BearingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BearingTest.java
index 2beb3229a9b..8c82f95ade9 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BearingTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BearingTest.java
@@ -46,6 +46,16 @@ public class BearingTest {
assertNull(new BearingWithConfidenceCluster(null).getAverage(null));
}
+ @Test
+ public void testNegativeAngle() {
+ assertEquals(-10., new DegreeBearingImpl(-10.).getDegrees(), 0.000001);
+ }
+
+ @Test
+ public void testNegativeAngleOverflow() {
+ assertEquals(-10., new DegreeBearingImpl(-370.).getDegrees(), 0.000001);
+ }
+
@Test
public void testZeroConfidenceLeadsToNullBearingAverage() {
BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(null);
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackInterpolationWithNullTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackInterpolationWithNullTest.java
new file mode 100755
index 00000000000..8fc789297ef
--- /dev/null
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackInterpolationWithNullTest.java
@@ -0,0 +1,49 @@
+package com.sap.sailing.domain.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.UUID;
+
+import org.junit.Test;
+
+import com.sap.sailing.domain.base.CourseArea;
+import com.sap.sailing.domain.base.impl.CourseAreaImpl;
+import com.sap.sailing.domain.common.sensordata.BravoExtendedSensorDataMetadata;
+import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl;
+import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
+import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
+import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
+import com.sap.sse.common.impl.MillisecondsTimePoint;
+
+public class BravoFixTrackInterpolationWithNullTest {
+ @Test
+ public void testFixesWithNonNullFieldsAreSelected() {
+ DynamicBravoFixTrack track = new BravoFixTrackImpl<>(new CourseAreaImpl("Test", UUID.randomUUID()),
+ "test", /* hasExtendedFixes */ true);
+ track.add(createFix(10l, 5., null));
+ track.add(createFix(20l, null, 7.));
+ track.add(createFix(30l, 7., null));
+ assertEquals(6., track.getHeel(new MillisecondsTimePoint(20l)).getDegrees(), 0.00001);
+ }
+
+ @Test
+ public void testFixesWithNonNullFieldsAreSelectedForMultipleNullsInARow() {
+ DynamicBravoFixTrack track = new BravoFixTrackImpl<>(new CourseAreaImpl("Test", UUID.randomUUID()),
+ "test", /* hasExtendedFixes */ true);
+ track.add(createFix(10l, 5., null));
+ track.add(createFix(20l, null, 7.));
+ track.add(createFix(30l, null, 7.));
+ track.add(createFix(40l, null, 7.));
+ track.add(createFix(50l, 7., null));
+ assertEquals(6., track.getHeel(new MillisecondsTimePoint(30l)).getDegrees(), 0.00001);
+ assertEquals(5., track.getHeel(new MillisecondsTimePoint(10l)).getDegrees(), 0.00001);
+ }
+
+ private BravoExtendedFixImpl createFix(long timePointAsMillis, Double heel, Double pitch) {
+ final Double[] fixData = new Double[Math.max(BravoExtendedSensorDataMetadata.HEEL.getColumnIndex(),
+ BravoExtendedSensorDataMetadata.PITCH.getColumnIndex())+1];
+ fixData[BravoExtendedSensorDataMetadata.HEEL.getColumnIndex()] = heel;
+ fixData[BravoExtendedSensorDataMetadata.PITCH.getColumnIndex()] = pitch;
+ return new BravoExtendedFixImpl(new DoubleVectorFixImpl(new MillisecondsTimePoint(timePointAsMillis), fixData));
+ }
+}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
index cbcfa7f6b7c..61002947418 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
@@ -207,27 +207,29 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
for (Competitor c : compLists[1]) {
raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
+ Set allCompetitors = new HashSet<>();
+ allCompetitors.addAll(compLists[0]);
+ allCompetitors.addAll(compLists[1]);
// expected are only the competitors in the RaceLog, because only one RaceColumn is
// registered, which has the competitors registered in the RaceLog.
- assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
+ assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
// Now we revoke that the race log provides the competitors for R1; the competitors should then
// snap back to those competitors taken from the regatta log for both, the rentire leaderboard
// as well as for the race column
raceLog.revokeEvent(author, usesCompetitorsFromRaceLogEvent);
- assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]));
+ assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]), new HashSet<>(compLists[0]));
// And now re-introduce per-race competitors and validate again that the cache adjusts properly:
raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId));
- assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
+ assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
}
- private void assertRegattaAndRaceCompetitors(Set expected) {
+ private void assertRegattaAndRaceCompetitors(Set expectedAllCompetitors, Set expectedForRace) {
Set actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
- assertEquals(expected, actual);
+ assertEquals(expectedAllCompetitors, actual);
Set actualForRace = new HashSet<>();
- //For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
- Set expectedForRace = new HashSet<>(expected);
+ // For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MarkPassingUpdateFromRaceLogFinishPositioingListTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MarkPassingUpdateFromRaceLogFinishPositioingListTest.java
index 51cb340ece3..c8fe26bb138 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MarkPassingUpdateFromRaceLogFinishPositioingListTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MarkPassingUpdateFromRaceLogFinishPositioingListTest.java
@@ -24,6 +24,7 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
+import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogFinishPositioningConfirmedEvent;
@@ -153,7 +154,7 @@ public class MarkPassingUpdateFromRaceLogFinishPositioingListTest extends Abstra
int rank = 1;
for (Pair finishingTime : finishingTimes) {
results.add(new CompetitorResultImpl(finishingTime.getA().getId(), finishingTime.getA().getName(), rank++, /* maxPointsReason */
- null, /* score */null, finishingTime.getB(), /* comment */null));
+ null, /* score */null, finishingTime.getB(), /* comment */null, MergeState.OK));
}
return results;
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
index 165585f853c..f0ce8699455 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
@@ -51,7 +51,7 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixTrack;
-import com.sap.sailing.domain.tracking.impl.DistanceCache;
+import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl;
@@ -304,7 +304,7 @@ public class TrackTest {
// The following getMaximumSpeedOverGround call will trigger a computeMaxSpeed(...) and a cache(...) call
new Thread(()->
assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
- getB().getKnots(), 0.001)).start(); // produces a cache entry that ends
+ getB().getKnots(), 0.01)).start(); // produces a cache entry that ends
// now don't release the cacheBarrier as yet but add more fixes
new Thread(() -> {
GPSFixMoving fix2 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(3600000),
@@ -442,7 +442,7 @@ public class TrackTest {
@Test
public void testDistanceTraveledBackwardsQuery() {
final Set> cacheEntries = new HashSet<>();
- final DistanceCache distanceCache = new DistanceCache("test-DistanceCache") {
+ final TimeRangeCache distanceCache = new TimeRangeCache("test-DistanceCache") {
@Override
public void cache(TimePoint from, TimePoint to, Distance distance) {
super.cache(from, to, distance);
@@ -453,7 +453,7 @@ public class TrackTest {
DynamicGPSFixTrack if there are no mark passings for the competitor in this race
* or if the competitor has not finished one of the legs in the race.
@@ -623,6 +624,18 @@ public interface TrackedRace extends Serializable, IsManagedByCachenull if there are no mark passings for the competitor in this race
+ * or if the competitor has not finished one of the legs in the race.
+ */
+ Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint);
+
+ /**
+ * @return null if there are no mark passings for the competitor in this race
+ * or if the competitor has not finished one of the legs in the race.
+ */
+ Duration getDurationFoiled(Competitor competitor, TimePoint timePoint);
+
/**
* See {@link TrackedLegOfCompetitor#getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)}
*/
@@ -1012,4 +1025,51 @@ public interface TrackedRace extends Serializable, IsManagedByCache getAttachedRaceLogs();
+ /**
+ * Computes the average speed over ground for a {@link Competitor} based on times and distances for all
+ * {@link TrackedLegOfCompetitor legs} the competitor {@link TrackedLegOfCompetitor#hasStartedLeg(TimePoint) has
+ * started} already at {@code timePoint}.
+ *
+ * @param timePoint
+ * time point up and until to compute the speed
+ */
+ Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint);
+
+ /**
+ * Computes the competitor's speed projected onto the wind (if wind data is available and the competitor is not
+ * between start and finish (not racing) or not on a {@link LegType#REACHING reaching} leg; if outside a race and no
+ * wind information is available, {@code null} is returned. Otherwise, the speed at {@code timePoint} is projected
+ * onto the course. The wind direction at the {@link WindPositionMode#EXACT exact} competitor position at
+ * {@code timePoint} is used for the calculation.
+ */
+ default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint) {
+ return getVelocityMadeGood(competitor, timePoint, new LeaderboardDTOCalculationReuseCache(timePoint));
+ }
+
+ /**
+ * Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a {@link WindPositionMode}
+ * other than the default {@link WindPositionMode#EXACT}. If {@link WindPositionMode#LEG_MIDDLE} is used and the
+ * competitor is not currently sailing on a leg (hasn't started or has already finished), {@code null} is returned.
+ */
+ default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
+ return getVelocityMadeGood(competitor, timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint));
+ }
+
+ /**
+ * Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a cache that can
+ * accelerate requests for wind directions, the leg type and the competitor's current leg's bearing.
+ */
+ default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
+ return getVelocityMadeGood(competitor, timePoint, WindPositionMode.EXACT, cache);
+ }
+
+ /**
+ * Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a
+ * {@link WindPositionMode} other than the default {@link WindPositionMode#EXACT} as well as a cache that can
+ * accelerate requests for wind directions, the leg type and the competitor's current leg's bearing. If
+ * {@link WindPositionMode#LEG_MIDDLE} is used and the competitor is not currently sailing on a leg (hasn't started
+ * or has already finished), {@code null} is returned.
+ */
+ SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode,
+ WindLegTypeAndLegBearingCache cache);
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java
index 2906508fcd8..bb6e27ca4c1 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java
@@ -1,19 +1,23 @@
package com.sap.sailing.domain.tracking.impl;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.function.Function;
-import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.confidence.impl.ScalableDouble;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
+import com.sap.sailing.domain.common.scalablevalue.impl.ScalableDistance;
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
import com.sap.sailing.domain.tracking.GPSFixTrack;
+import com.sap.sailing.domain.tracking.GPSTrackListener;
+import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -30,6 +34,37 @@ public class BravoFixTrackImpl extends S
private static final long serialVersionUID = 460944392510182976L;
private final boolean hasExtendedFixes;
+
+ private transient TimeRangeCache foilingTimeCache;
+
+ private transient TimeRangeCache foilingDistanceCache;
+
+ private transient TimeRangeCache averageRideHeightCache;
+
+ /**
+ * If a GPS track was provided at construction time, remember it non-transiently. It is needed when restoring
+ * the object after de-serialization, so the cache invalidation listener can be re-established.
+ */
+ private GPSFixTrack gpsTrack;
+
+ private class CacheInvalidationGpsTrackListener implements GPSTrackListener {
+ private static final long serialVersionUID = 6395529765232404414L;
+ @Override
+ public boolean isTransient() {
+ return true;
+ }
+
+ @Override
+ public void gpsFixReceived(GPSFixMoving fix, ItemType item, boolean firstFixInTrack) {
+ assert item == getTrackedItem();
+ foilingDistanceCache.invalidateAllAtOrLaterThan(fix.getTimePoint());
+ }
+
+ @Override
+ public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage,
+ long newMillisecondsOverWhichToAverage) {
+ }
+ }
/**
* @param trackedItem
@@ -38,68 +73,73 @@ public class BravoFixTrackImpl extends S
* the name of the track by which it can be obtained from the {@link TrackedRace}.
*/
public BravoFixTrackImpl(ItemType trackedItem, String trackName, boolean hasExtendedFixes) {
+ this(trackedItem, trackName, hasExtendedFixes, /* listen to GPS track for distance cache invalidation */ null);
+ }
+
+ /**
+ * @param gpsTrack
+ * if not {@code null}, this track will listen on that track for changes in order to invalidate this
+ * track's caches; for example, if a GPS fix is added, this track may need to invalidate its
+ * {@link #foilingDistanceCache} accordingly because the distance traveled between the fixes may have
+ * changed.
+ */
+ public BravoFixTrackImpl(ItemType trackedItem, String trackName, boolean hasExtendedFixes, GPSFixTrack gpsTrack) {
super(trackedItem, trackName, BravoFixTrack.TRACK_NAME + " for " + trackedItem);
this.hasExtendedFixes = hasExtendedFixes;
+ this.foilingTimeCache = createFoilingTimeCache(trackedItem);
+ this.foilingDistanceCache = createFoilingDistanceCache(trackedItem);
+ this.averageRideHeightCache = createAverageRideHeightCache(trackedItem);
+ this.gpsTrack = gpsTrack;
+ if (gpsTrack != null) {
+ gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
+ }
+ }
+
+ private TimeRangeCache createAverageRideHeightCache(ItemType trackedItem) {
+ return createTimeRangeCache(trackedItem, "averageRideHeightCache");
+ }
+
+ private TimeRangeCache createTimeRangeCache(ItemType trackedItem, final String cacheName) {
+ return new TimeRangeCache<>(cacheName+" for "+trackedItem);
+ }
+
+ private TimeRangeCache createFoilingDistanceCache(ItemType trackedItem) {
+ return createTimeRangeCache(trackedItem, "foilingDistanceCache");
+ }
+
+ private TimeRangeCache createFoilingTimeCache(ItemType trackedItem) {
+ return createTimeRangeCache(trackedItem, "foilingTimeCache");
+ }
+
+ /**
+ * After reading this object from an {@link ObjectInputStream}, initialize the caches properly.
+ */
+ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ this.foilingTimeCache = createFoilingTimeCache(getTrackedItem());
+ this.foilingDistanceCache = createFoilingDistanceCache(getTrackedItem());
+ this.averageRideHeightCache = createAverageRideHeightCache(getTrackedItem());
+ if (gpsTrack != null) {
+ gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
+ }
}
@Override
public Distance getRideHeight(TimePoint timePoint) {
- BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
- if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixAfter.getRideHeight();
- }
- BravoFix fixBefore = getLastFixAtOrBefore(timePoint);
- if (fixBefore != null && fixBefore.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixBefore.getRideHeight();
- }
- if (fixAfter == null || fixBefore == null) {
- // the fix is out of the TimeRange where we have fixes
- return null;
- }
- // TODO interpolate if necessary
- return fixBefore.getRideHeight();
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoFix::getRideHeight,
+ ScalableDistance::new);
}
@Override
public Bearing getHeel(TimePoint timePoint) {
- BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
- if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixAfter.getHeel();
- }
- BravoFix fixBefore = getLastFixAtOrBefore(timePoint);
- if (fixBefore != null && fixBefore.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixBefore.getHeel();
- }
- if (fixAfter == null || fixBefore == null) {
- // the fix is out of the TimeRange where we have fixes
- return null;
- }
- // TODO interpolate if necessary
- return fixBefore.getHeel();
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoFix::getHeel,
+ NaivelyScalableBearing::new);
}
@Override
public Bearing getPitch(TimePoint timePoint) {
- BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
- if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixAfter.getPitch();
- }
- BravoFix fixBefore = getLastFixAtOrBefore(timePoint);
- if (fixBefore != null && fixBefore.getTimePoint().compareTo(timePoint) == 0) {
- // exact match of timepoint -> no interpolation necessary
- return fixBefore.getPitch();
- }
- if (fixAfter == null || fixBefore == null) {
- // the fix is out of the TimeRange where we have fixes
- return null;
- }
- // TODO interpolate if necessary
- return fixBefore.getPitch();
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoFix::getPitch,
+ NaivelyScalableBearing::new);
}
@Override
@@ -108,26 +148,43 @@ public class BravoFixTrackImpl extends S
return rideHeight != null && rideHeight.compareTo(BravoFix.MIN_FOILING_HEIGHT_THRESHOLD) >= 0;
}
+
+ @Override
+ public boolean add(BravoFix fix, boolean replace) {
+ final boolean added = super.add(fix, replace);
+ if (added) {
+ final TimePoint fixTimePoint = fix.getTimePoint();
+ averageRideHeightCache.invalidateAllAtOrLaterThan(fixTimePoint);
+ foilingDistanceCache.invalidateAllAtOrLaterThan(fixTimePoint);
+ foilingTimeCache.invalidateAllAtOrLaterThan(fixTimePoint);
+ }
+ return added;
+ }
+
@Override
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
- final Distance result;
- lockForRead();
- try {
- Distance sum = Distance.NULL;
- int count = 0;
- for (final BravoFix fix : getFixes(from, true, to, true)) {
- sum = sum.add(fix.getRideHeight());
- count++;
+ return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, averageRideHeightCache,
+ /* valueCalculator */ new Track.TimeRangeValueCalculator() {
+ @Override
+ public Distance calculate(TimePoint from, TimePoint to) {
+ Distance result;
+ Distance sum = Distance.NULL;
+ int count = 0;
+ for (final BravoFix fix : getFixes(from, true, to, true)) {
+ final Distance rideHeight = fix.getRideHeight();
+ if (rideHeight != null) {
+ sum = sum.add(rideHeight);
+ count++;
+ }
+ }
+ if (count > 0) {
+ result = sum.scale(1./count);
+ } else {
+ result = null;
+ }
+ return result;
}
- if (count > 0) {
- result = sum.scale(1./count);
- } else {
- result = null;
- }
- } finally {
- unlockAfterRead();
- }
- return result;
+ });
}
private boolean isFoiling(BravoFix fix) {
@@ -136,44 +193,47 @@ public class BravoFixTrackImpl extends S
@Override
public Duration getTimeSpentFoiling(TimePoint from, TimePoint to) {
- Duration result = Duration.NULL;
- lockForRead();
- try {
- TimePoint last = from;
- boolean isFoiling = false;
- for (final BravoFix fix : getFixes(from, true, to, true)) {
- final boolean fixFoils = isFoiling(fix);
- if (isFoiling && fixFoils) {
- result = result.plus(last.until(fix.getTimePoint()));
+ return getValueSum(from, to, /* nullElement */ Duration.NULL, Duration::plus, foilingTimeCache,
+ /* valueCalculator */ new Track.TimeRangeValueCalculator() {
+ @Override
+ public Duration calculate(TimePoint from, TimePoint to) {
+ Duration result = Duration.NULL;
+ TimePoint last = from;
+ boolean isFoiling = false;
+ for (final BravoFix fix : getFixes(from, true, to, true)) {
+ final boolean fixFoils = isFoiling(fix);
+ if (isFoiling && fixFoils) {
+ result = result.plus(last.until(fix.getTimePoint()));
+ }
+ last = fix.getTimePoint();
+ isFoiling = fixFoils;
}
- last = fix.getTimePoint();
- isFoiling = fixFoils;
+ return result;
}
- } finally {
- unlockAfterRead();
- }
- return result;
+ });
}
@Override
- public Distance getDistanceSpentFoiling(GPSFixTrack gpsFixTrack, TimePoint from, TimePoint to) {
- Distance result = Distance.NULL;
- lockForRead();
- try {
- TimePoint last = from;
- boolean isFoiling = false;
- for (final BravoFix fix : getFixes(from, true, to, true)) {
- final boolean fixFoils = isFoiling(fix);
- if (isFoiling && fixFoils) {
- result = result.add(gpsFixTrack.getDistanceTraveled(last, fix.getTimePoint()));
+ public Distance getDistanceSpentFoiling(TimePoint from, TimePoint to) {
+ assert gpsTrack != null;
+ return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
+ /* valueCalculator */ new Track.TimeRangeValueCalculator() {
+ @Override
+ public Distance calculate(TimePoint from, TimePoint to) {
+ Distance result = Distance.NULL;
+ TimePoint last = from;
+ boolean isFoiling = false;
+ for (final BravoFix fix : getFixes(from, true, to, true)) {
+ final boolean fixFoils = isFoiling(fix);
+ if (isFoiling && fixFoils) {
+ result = result.add(gpsTrack.getDistanceTraveled(last, fix.getTimePoint()));
+ }
+ last = fix.getTimePoint();
+ isFoiling = fixFoils;
}
- last = fix.getTimePoint();
- isFoiling = fixFoils;
+ return result;
}
- } finally {
- unlockAfterRead();
- }
- return result;
+ });
}
@Override
@@ -185,18 +245,31 @@ public class BravoFixTrackImpl extends S
* Generic implementation to get values from extended fixes. The implementation ensured that in case of simple
* {@link BravoFix} instances, just {@code null} is returned. If valid {@link BravoExtendedFix BravoExtendedFixes}
* are found, the provided getter is used to extract the value from the identified fix.
+ *
*
* In case of a mix of {@link BravoFix} and {@link BravoExtendedFix} instances, this method may return null values
* for specific {@link TimePoint TimePoints}.
+ *
+ *
+ * If the value extracted by the {@code getter} is {@code null}, the next fix will be probed, until no more fix
+ * exists in that direction or a fix is found that delivers a non-{@code null} value for the {@code getter} result.
+ * This way it is possible to skip fixes that don't make a statement with regard to the attribute extracted by the
+ * {@code getter}.
*/
- private T getValueFromExtendedFix(final TimePoint timePoint, final Function getter,
+ private T getValueFromExtendedFixSkippingNullValues(
+ final TimePoint timePoint, final Function getter,
Function> converterToScalableValue) {
- if (!hasExtendedFixes) {
- return null;
- }
final com.sap.sse.common.Util.Function> converter =
- fix -> converterToScalableValue.apply(getter.apply((BravoExtendedFix) fix));
- return getInterpolatedValue(timePoint, converter);
+ fix -> {
+ @SuppressWarnings("unchecked")
+ final BravoFixType castFix = (BravoFixType) fix;
+ return converterToScalableValue.apply(getter.apply(castFix));
+ };
+ return getInterpolatedValue(timePoint, converter, fix->{
+ @SuppressWarnings("unchecked")
+ final BravoFixType castFix = (BravoFixType) fix;
+ return getter.apply(castFix) != null;
+ });
}
public BravoExtendedFix getFirstFixAtOrAfterIfExtended(TimePoint timePoint) {
@@ -211,34 +284,112 @@ public class BravoFixTrackImpl extends S
@Override
public Double getPortDaggerboardRakeIfAvailable(TimePoint timePoint) {
- return getValueFromExtendedFix(timePoint, BravoExtendedFix::getPortDaggerboardRake,
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getPortDaggerboardRake,
ScalableDouble::new);
}
@Override
public Double getStbdDaggerboardRakeStbdIfAvailable(TimePoint timePoint) {
- return getValueFromExtendedFix(timePoint, BravoExtendedFix::getStbdDaggerboardRake,
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getStbdDaggerboardRake,
ScalableDouble::new);
}
@Override
public Double getPortRudderRakeIfAvailable(TimePoint timePoint) {
- return getValueFromExtendedFix(timePoint, BravoExtendedFix::getPortRudderRake,
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getPortRudderRake,
ScalableDouble::new);
}
@Override
public Double getStbdRudderRakeIfAvailable(TimePoint timePoint) {
- return getValueFromExtendedFix(timePoint, BravoExtendedFix::getStbdRudderRake,
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getStbdRudderRake,
ScalableDouble::new);
}
@Override
public Bearing getMastRotationIfAvailable(TimePoint timePoint) {
- return getValueFromExtendedFix(timePoint, BravoExtendedFix::getMastRotation,
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getMastRotation,
NaivelyScalableBearing::new);
}
+ @Override
+ public Bearing getLeewayIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getLeeway,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Double getSetIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getSet,
+ ScalableDouble::new);
+ }
+
+ @Override
+ public Bearing getDriftIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getDrift,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Distance getDepthIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getDepth,
+ ScalableDistance::new);
+ }
+
+ @Override
+ public Bearing getRudderIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getRudder,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Double getForestayLoadIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getForestayLoad,
+ ScalableDouble::new);
+ }
+
+ @Override
+ public Double getForestayPressureIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getForestayPressure,
+ ScalableDouble::new);
+ }
+
+ @Override
+ public Bearing getTackAngleIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getTackAngle,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Bearing getRakeIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getRake,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Double getDeflectorPercentageIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getDeflectorPercentage,
+ ScalableDouble::new);
+ }
+
+ @Override
+ public Bearing getTargetHeelIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getTargetHeel,
+ NaivelyScalableBearing::new);
+ }
+
+ @Override
+ public Distance getDeflectorIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getDeflector,
+ ScalableDistance::new);
+ }
+
+ @Override
+ public Double getTargetBoatspeedPIfAvailable(TimePoint timePoint) {
+ return getValueFromExtendedFixSkippingNullValues(timePoint, BravoExtendedFix::getTargetBoatspeedP,
+ ScalableDouble::new);
+ }
+
private static class NaivelyScalableBearing implements ScalableValue {
private final double deg;
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DistanceCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DistanceCache.java
deleted file mode 100755
index e6e7d847632..00000000000
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DistanceCache.java
+++ /dev/null
@@ -1,237 +0,0 @@
-package com.sap.sailing.domain.tracking.impl;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map.Entry;
-import java.util.NavigableSet;
-
-import com.sap.sailing.domain.common.Distance;
-import com.sap.sailing.domain.common.tracking.GPSFix;
-import com.sap.sse.common.TimePoint;
-import com.sap.sse.common.Util;
-import com.sap.sse.common.Util.Pair;
-import com.sap.sse.concurrent.LockUtil;
-import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
-import com.sap.sse.util.impl.ArrayListNavigableSet;
-
-/**
- * Computing {@link #getDistanceTraveled(TimePoint, TimePoint)} is more expensive the longer the track is and the
- * higher the tracking frequency. It is called regularly, usually for the same start time. Keeping to re-evaluate
- * all distances between all GPS fixes, even if they remain constant, is a waste of resources, particularly when we
- * assume that usually GPS fixes are appended to a track and not usually randomly inserted, even though this can
- * happen.
- *
- *
- * This cache looks "backwards." It contains pairs whose first component represents a to parameter used
- * in {@link #getDistanceTraveled(TimePoint, TimePoint)}. It is ordered by this component. The second component is a
- * navigable, ordered set of pairs where the first pair component represents a from parameter used in
- * {@link #getDistanceTraveled(TimePoint, TimePoint)} and the second pair component represents the result of
- * {@link #getDistanceTraveled(TimePoint, TimePoint)} for this parameter combination. Note that the cache does only
- * store distances for from time points actually passed to
- * {@link #getDistanceTraveled(TimePoint, TimePoint)} and does not cache all interim combinations collected while
- * computing a result. As such, it differs from a typical "dynamic programming" algorithm which would store all
- * in-between values too. Assuming that from values are sparse (leg start, race start, ...), there
- * doesn't seem to be much use in storing all the other in-between values too.
- *
- *
- * For implementation efficiency in combination with using a {@link ArrayListNavigableSet} for the values and in
- * order to be able to efficiently extend a cache entry for a single to fix, the navigable sets
- * containing the from fixes and distances are ordered such that earlier fixes come later in the set.
- * This way, extending the cache entry for a to fix to an earlier from fix only requires
- * appending to the set.
- *
- *
- * Invalidation: When a new fix is added to this track, all distance cache entries for fixes at or later than
- * the new fix's time point are removed from this cache. Additionally, the fix insertion may have an impact on the
- * {@link #getEarlierFixesWhoseValidityMayBeAffected(GPSFix) previous fix's} validity (track smoothing) and
- * therefore on its selection for distance aggregation. Therefore, if fix addition turned the previous fix invalid,
- * the cache entries for the time points at or after the previous fix also need to be removed.
- *
- *
- * Cache use: When {@link #getDistanceTraveled(TimePoint, TimePoint)} is called, it first looks for a cache
- * entry for the to parameter. If one is found, the earliest entry in the navigable set for the
- * navigable set of from and distance values that is at or after the requested from time
- * point is determined. If such an entry exists, the distance is remembered and the algorithm is repeated
- * recursively, using the from value found in the cache as the new to value, and the
- * from value originally passed to {@link #getDistanceTraveled(TimePoint, TimePoint)} as
- * from again. If no entry is found in the cache entry for to that is at or after the
- * requested from time, the distance is computed by iterating the smoothened fixes.
- *
- *
- * If a cache entry for to is not found, the latest cache entry before it is looked up. If one is
- * found, the distance between the to time point requested and the to time point found in
- * the cache is computed by iterating the smoothened fixes for this interval. If none is found, the distance is
- * computed by iterating backwards all the way to from.
- *
- *
- * Once the {@link #getDistanceTraveled(TimePoint, TimePoint)} has computed its value, it adds the result to the
- * cache.
- *
- * @author Axel Uhl (D043530)
- */
-public class DistanceCache {
- public static final int MAX_SIZE = 100;
-
- private final NavigableSet>>> distanceCache;
-
- /**
- * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is
- * defined as having been returned by {@link #getEarliestFromAndDistanceAtOrAfterFrom(TimePoint, TimePoint)} or
- * having been added by {@link #cache(TimePoint, TimePoint, Distance)}.
- *
- *
- * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements,
- * the expunging will be admitted, and the entry is removed from the {@link #distanceCache} core structure. Reading
- * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash
- * map that counts access as "use" has a modifying effect on its internal structures.
- *
- *
- * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the
- * alignment of the {@link #distanceCache} structure which has as its outer keys the "to" time point.
- *
- * Read access is to be synchronized using this field's mutex; write access only happens under the
- * {@link #lock write lock} and therefore will have no contenders.
- */
- private final LinkedHashMap, Void> lruCache;
-
- private final NamedReentrantReadWriteLock lock;
-
- private static final Comparator> timePointInPairComparator = new Comparator>() {
- @Override
- public int compare(Util.Pair o1, Util.Pair o2) {
- return o1.getA().compareTo(o2.getA());
- }
- };
-
- public DistanceCache(String nameForLockLogging) {
- lock = new NamedReentrantReadWriteLock("lock for DistanceCache for "+nameForLockLogging, /* fair */ true);
- this.distanceCache = new ArrayListNavigableSet>>>(timePointInPairComparator);
- this.lruCache = new LinkedHashMap, Void>(/* initial capacity */ 10, /* load factor */ 0.75f,
- /* access-based ordering */ true) {
- private static final long serialVersionUID = -6568235517111733193L;
-
- @Override
- protected boolean removeEldestEntry(Entry, Void> eldest) {
- final boolean expunge = size() > MAX_SIZE;
- if (expunge) {
- removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB());
- }
- return expunge;
- }
- };
- }
-
- public int size() {
- return lruCache.size();
- }
-
- private void removeCacheEntry(TimePoint from, TimePoint to) {
- assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock
- Util.Pair>> entryForTo = distanceCache.floor(createDummy(to));
- if (entryForTo.getA().equals(to)) {
- Pair entryForFrom = entryForTo.getB().ceiling(new Util.Pair(from, null));
- if (entryForFrom.getA().equals(from)) {
- entryForTo.getB().remove(entryForFrom);
- if (entryForTo.getB().isEmpty()) {
- distanceCache.remove(entryForTo);
- }
- }
- }
- }
-
- /**
- * Looks up the entry closest to but no later than to. If not found, null is returned. If
- * found, the earliest pair of from/distance that is at or after from will be returned, together with
- * the to value of the entry. If there is no entry that is at or after from,
- * null is returned.
- */
- public Util.Pair> getEarliestFromAndDistanceAtOrAfterFrom(TimePoint from, TimePoint to) {
- LockUtil.lockForRead(lock);
- try {
- Util.Pair> result = null;
- Util.Pair>> entryForTo = distanceCache.floor(createDummy(to));
- if (entryForTo != null) {
- final Util.Pair fromCeiling = entryForTo.getB().ceiling(new Util.Pair(from, null));
- if (fromCeiling != null) {
- result = new Util.Pair>(entryForTo.getA(), fromCeiling);
- }
- }
- // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using
- // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry
- // modifies the internal parts of the data structure which is not thread safe.
- synchronized (lruCache) {
- lruCache.get(new Util.Pair(from, to));
- }
- return result;
- } finally {
- LockUtil.unlockAfterRead(lock);
- }
- }
-
- /**
- * Removes all cache entries that have a to time point that is at or after timePoint.
- */
- public void invalidateAllAtOrLaterThan(TimePoint timePoint) {
- LockUtil.lockForWrite(lock);
- try {
- Util.Pair>> dummy = createDummy(timePoint);
- NavigableSet>>> toRemove = distanceCache.tailSet(dummy, /* inclusive */ true);
- for (Iterator>>> i=toRemove.iterator(); i.hasNext(); ) {
- Util.Pair>> entryToRemove = i.next();
- assert entryToRemove.getA().compareTo(timePoint) >= 0;
- for (Pair fromAndDistance : entryToRemove.getB()) {
- lruCache.remove(new Util.Pair<>(fromAndDistance.getA(), entryToRemove.getA()));
- }
- i.remove();
- }
- } finally {
- LockUtil.unlockAfterWrite(lock);
- }
- }
-
- private NavigableSet> getEntryForTo(TimePoint to) {
- NavigableSet> result = null;
- Util.Pair>> dummyForTo = createDummy(to);
- Util.Pair>> entryForTo = distanceCache.floor(dummyForTo);
- if (entryForTo != null && entryForTo.getA().equals(to)) {
- result = entryForTo.getB();
- }
- return result;
- }
-
- public void cache(TimePoint from, TimePoint to, Distance distance) {
- LockUtil.lockForWrite(lock);
- try {
- NavigableSet> entryForTo = getEntryForTo(to);
- if (entryForTo == null) {
- entryForTo = new ArrayListNavigableSet>(timePointInPairComparator);
- Util.Pair>> pairForTo = new Util.Pair>>(
- to, entryForTo);
- distanceCache.add(pairForTo);
- }
- entryForTo.add(new Util.Pair(from, distance));
- lruCache.put(new Util.Pair(from, to), null);
- } finally {
- LockUtil.unlockAfterWrite(lock);
- }
- }
-
- private Util.Pair>> createDummy(TimePoint to) {
- return new Util.Pair>>(to, null);
- }
-
- /**
- * Removes all contents from this cache
- */
- public void clear() {
- LockUtil.lockForWrite(lock);
- try {
- distanceCache.clear();
- lruCache.clear();
- } finally {
- LockUtil.unlockAfterWrite(lock);
- }
-
- }
-}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
index 815eccfdbc4..b1b9077ef62 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.tracking.WithValidityCache;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.GPSTrackListener;
+import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -116,7 +117,7 @@ public abstract class GPSFixTrackImpl extends
* Once the {@link #getDistanceTraveled(TimePoint, TimePoint)} has computed its value, it adds the result to the
* cache.
*/
- private transient DistanceCache distanceCache;
+ private transient TimeRangeCache distanceCache;
private transient MaxSpeedCache maxSpeedCache;
@@ -155,7 +156,7 @@ public abstract class GPSFixTrackImpl extends
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
this.maxSpeedForSmoothing = maxSpeedForSmoothening;
this.listeners = new TrackListenerCollection>();
- this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString());
+ this.distanceCache = new TimeRangeCache<>(trackedItem==null?"null":trackedItem.toString());
this.maxSpeedCache = createMaxSpeedCache();
this.validityCachingSuspended = false;
this.losslessCompaction = losslessCompaction;
@@ -190,7 +191,7 @@ public abstract class GPSFixTrackImpl extends
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
- distanceCache = new DistanceCache(getTrackedItem().toString());
+ distanceCache = new TimeRangeCache<>(getTrackedItem().toString());
maxSpeedCache = createMaxSpeedCache();
}
@@ -506,76 +507,33 @@ public abstract class GPSFixTrackImpl extends
@Override
public Distance getDistanceTraveled(TimePoint from, TimePoint to) {
- return getDistanceTraveledRecursively(from, to, 0);
- }
-
- private Distance getDistanceTraveledRecursively(TimePoint from, TimePoint to, int recursionDepth) {
- Distance result;
- if (!from.before(to)) {
- result = Distance.NULL;
- } else {
- boolean perfectCacheHit = false;
- lockForRead();
- try {
- Util.Pair> bestCacheEntry = getDistanceCache()
- .getEarliestFromAndDistanceAtOrAfterFrom(from, to);
- if (bestCacheEntry != null) {
- perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below
- // compute the missing stretches between best cache entry's "from" and our "from" and the cache
- // entry's "to" and our "to"
- Distance distanceFromFromToBeginningOfCacheEntry = Distance.NULL;
- Distance distanceFromEndOfCacheEntryToTo = Distance.NULL;
- if (!bestCacheEntry.getB().getA().equals(from)) {
- assert bestCacheEntry.getB().getA().after(from);
- perfectCacheHit = false;
- distanceFromFromToBeginningOfCacheEntry = getDistanceTraveledRecursively(from, bestCacheEntry
- .getB().getA(), recursionDepth + 1);
- }
- if (!bestCacheEntry.getA().equals(to)) {
- assert bestCacheEntry.getA().before(to);
- perfectCacheHit = false;
- distanceFromEndOfCacheEntryToTo = getDistanceTraveledRecursively(bestCacheEntry.getA(), to,
- recursionDepth + 1);
- }
- result = distanceFromFromToBeginningOfCacheEntry.add(bestCacheEntry.getB().getB()).add(
- distanceFromEndOfCacheEntryToTo);
- } else {
- double distanceInNauticalMiles = 0;
- if (from.compareTo(to) < 0) {
+ return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, getDistanceCache(),
+ /* valueCalculator */ new Track.TimeRangeValueCalculator() {
+ @Override
+ public Distance calculate(TimePoint from, TimePoint to) {
// getEstimatedPosition's current implementation returns a position equal to that of a fix at
// "from" if there is one with exactly that time stamp
+ Distance distance = Distance.NULL;
Position fromPos = getEstimatedPosition(from, /* extrapolate */false);
- if (fromPos == null) {
- result = Distance.NULL;
- } else {
+ if (fromPos != null) {
// TODO bug 4122: idea: use the subset to determine fromPos and toPos in one go, eliminating two expensive getEstimatedPosition calls
NavigableSet subset = getGPSFixes().subSet(new DummyGPSFix(from),
/* fromInclusive */false, new DummyGPSFix(to),
/* toInclusive */false);
for (GPSFix fix : subset) {
- double distanceBetweenAdjacentFixesInNauticalMiles = fromPos.getDistance(
- fix.getPosition()).getNauticalMiles();
- distanceInNauticalMiles += distanceBetweenAdjacentFixesInNauticalMiles;
+ Distance distanceBetweenAdjacentFixes = fromPos.getDistance(
+ fix.getPosition());
+ distance = distance.add(distanceBetweenAdjacentFixes);
fromPos = fix.getPosition();
}
Position toPos = getEstimatedPosition(to, false);
- distanceInNauticalMiles += fromPos.getDistance(toPos).getNauticalMiles();
- result = new NauticalMileDistance(distanceInNauticalMiles);
+ distance = distance.add(fromPos.getDistance(toPos));
}
- } else {
- result = Distance.NULL;
+ return distance;
}
- }
- } finally {
- unlockAfterRead();
- }
- if (!perfectCacheHit && recursionDepth == 0) {
- getDistanceCache().cache(from, to, result);
- }
- }
- return result;
+ });
}
-
+
@Override
public Distance getRawDistanceTraveled(TimePoint from, TimePoint to) {
lockForRead();
@@ -1065,7 +1023,7 @@ public abstract class GPSFixTrackImpl extends
}
}
- protected DistanceCache getDistanceCache() {
+ protected TimeRangeCache getDistanceCache() {
return distanceCache;
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java
index b5172980ecb..6bcbaf8e926 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java
@@ -44,7 +44,6 @@ import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
-import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
@@ -98,7 +97,7 @@ public class TrackedLegImpl implements TrackedLeg {
}
@Override
- public TrackedRace getTrackedRace() {
+ public TrackedRaceImpl getTrackedRace() {
return trackedRace;
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java
index c55005f065d..f2e94464680 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java
@@ -29,7 +29,6 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
-import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sse.common.Duration;
@@ -68,7 +67,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return trackedLeg.getLeg();
}
- private TrackedRace getTrackedRace() {
+ private TrackedRaceImpl getTrackedRace() {
return getTrackedLeg().getTrackedRace();
}
@@ -268,29 +267,28 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
/**
* Projects speed onto the wind direction for upwind/downwind legs to see how fast a boat travels
- * "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto
- * the leg's direction.
- * @param windPositionMode see {@link #getWind(Position, TimePoint, Set)}
+ * "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto the
+ * leg's direction.
*
- * @throws NoWindException in case the wind direction is not known
+ * @param speed
+ * if {@code null} then {@code null} will be returned
+ * @param windPositionMode
+ * see {@link #getWind(Position, TimePoint, Set)}
+ *
+ * @throws NoWindException
+ * in case the wind direction is not known
*/
private SpeedWithBearing getWindwardSpeed(SpeedWithBearing speed, final TimePoint at, WindPositionMode windPositionMode,
WindLegTypeAndLegBearingCache cache) {
- SpeedWithBearing result = null;
+ final SpeedWithBearing result;
if (speed != null) {
Bearing projectToBearing;
try {
if (cache.getLegType(getTrackedLeg(), at) != LegType.REACHING) {
- final Wind wind;
- if (windPositionMode == WindPositionMode.EXACT) {
- wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
- } else {
- wind = getTrackedRace().getWind(
- getTrackedLeg().getEffectiveWindPosition(
- () -> getTrackedRace().getTrack(getCompetitor())
- .getEstimatedPosition(at, false), at, windPositionMode), at);
- }
+ final Wind wind = getTrackedRace().getWind(windPositionMode, getTrackedLeg(), getCompetitor(), at, cache);
if (wind == null) {
+ // This is not really likely to happen because wind==null would have let the call
+ // to cache.getLegType(...) fail with a NoWindException
throw new NoWindException("Need at least wind direction to determine windward speed");
}
projectToBearing = wind.getBearing();
@@ -307,7 +305,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
projectToBearing = projectToBearing.reverse();
}
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
+ } else {
+ result = null;
}
+ } else {
+ result = null;
}
return result;
}
@@ -645,7 +647,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
@Override
- public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
+ public SpeedWithBearing getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
if (hasStartedLeg(at)) {
TimePoint timePoint;
if (hasFinishedLeg(at)) {
@@ -709,7 +711,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
public Distance getRideHeight(TimePoint at) {
final Distance result;
if (hasStartedLeg(at)) {
- TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
+ TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
BravoFixTrack track = getTrackedRace()
.> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
result = track == null ? null : track.getRideHeight(timePoint);
@@ -719,6 +721,37 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return result;
}
+ @Override
+ public Distance getDistanceFoiled(TimePoint at) {
+ final Distance result;
+ if (hasStartedLeg(at)) {
+ TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
+ BravoFixTrack track = getTrackedRace()
+ .> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
+ result = track == null ? null
+ : track.getDistanceSpentFoiling(getMarkPassingForLegStart().getTimePoint(),
+ timePoint);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ @Override
+ public Duration getDurationFoiled(TimePoint at) {
+ final Duration result;
+ if (hasStartedLeg(at)) {
+ TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
+ BravoFixTrack track = getTrackedRace()
+ .> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
+ result = track == null ? null
+ : track.getTimeSpentFoiling(getMarkPassingForLegStart().getTimePoint(), timePoint);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
@Override
public Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode) {
return getEstimatedTimeToNextMark(timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint));
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
index dcf53b30598..3767441eff5 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
@@ -1212,7 +1212,50 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
private Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint, boolean considerGateStart) {
- final Distance result;
+ return getValueFromStartToTimePointOrEnd(competitor, timePoint,
+ (from, to)->{
+ final Distance result;
+ final Distance preResult = getTrack(competitor).getDistanceTraveled(from, to);
+ if (considerGateStart && preResult != null) {
+ result = preResult.add(getAdditionalGateStartDistance(competitor, timePoint));
+ } else {
+ result = preResult;
+ }
+ return result;
+ });
+ }
+
+ @Override
+ public Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint) {
+ return getBravoValue(competitor, timePoint, BravoFixTrack::getDistanceSpentFoiling);
+ }
+
+ @Override
+ public Duration getDurationFoiled(Competitor competitor, TimePoint timePoint) {
+ return getBravoValue(competitor, timePoint, BravoFixTrack::getTimeSpentFoiling);
+ }
+
+ @FunctionalInterface
+ private static interface BravoFromToValueCalculator {
+ T getValue(BravoFixTrack bravoFixTrack, TimePoint from, TimePoint to);
+ }
+
+ private T getBravoValue(Competitor competitor, TimePoint timePoint, BravoFromToValueCalculator bravoValueCalculator) {
+ return getValueFromStartToTimePointOrEnd(competitor, timePoint,
+ (from, to)->{
+ final T result;
+ final BravoFixTrack bravoFixTrack = getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
+ if (bravoFixTrack != null) {
+ result = bravoValueCalculator.getValue(bravoFixTrack, from, to);
+ } else {
+ result = null;
+ }
+ return result;
+ });
+ }
+
+ private T getValueFromStartToTimePointOrEnd(Competitor competitor, TimePoint timePoint, Track.TimeRangeValueCalculator valueCalculator) {
+ final T result;
NavigableSet markPassings = getMarkPassings(competitor);
try {
lockForRead(markPassings);
@@ -1239,12 +1282,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
if (end == null) {
result = null;
} else {
- final Distance preResult = getTrack(competitor).getDistanceTraveled(markPassings.first().getTimePoint(), end);
- if (considerGateStart && preResult != null) {
- result = preResult.add(getAdditionalGateStartDistance(competitor, timePoint));
- } else {
- result = preResult;
- }
+ result = valueCalculator.calculate(markPassings.first().getTimePoint(), end);
}
}
return result;
@@ -3570,12 +3608,14 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
@Override
public Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart) {
+ final Speed result;
if (getStartOfRace() == null) {
- return null;
+ result = null;
+ } else {
+ TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
+ result = getTrack(competitor).getEstimatedSpeed(beforeStart);
}
-
- TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
- return getTrack(competitor).getEstimatedSpeed(beforeStart);
+ return result;
}
@Override
@@ -4189,4 +4229,76 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
public Iterable getAttachedRaceLogs() {
return new HashSet<>(attachedRaceLogs.values());
}
+
+ @Override
+ public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
+ Speed result = null;
+ Duration totalTimeSailedInRace = Duration.NULL;
+ Distance totalDistanceSailedInRace = Distance.NULL;
+ for (TrackedLeg legGeneral : getTrackedLegs()) {
+ TrackedLegOfCompetitor leg = legGeneral.getTrackedLeg(competitor);
+ if (leg != null && leg.hasStartedLeg(timePoint)) {
+ totalDistanceSailedInRace = totalDistanceSailedInRace.add(leg.getDistanceTraveled(timePoint));
+ totalTimeSailedInRace = totalTimeSailedInRace.plus(leg.getTime(timePoint));
+ }
+ }
+ if (!totalTimeSailedInRace.equals(Duration.NULL) && !totalDistanceSailedInRace.equals(Distance.NULL)) {
+ result = totalDistanceSailedInRace.inTime(totalTimeSailedInRace);
+ }
+ return result;
+ }
+
+ @Override
+ public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
+ WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
+ TrackedLegOfCompetitor trackedLeg = getTrackedLeg(competitor, timePoint);
+ final SpeedWithBearing result;
+ if (trackedLeg != null) {
+ result = trackedLeg.getVelocityMadeGood(timePoint, windPositionMode, cache);
+ } else {
+ // check if wind information is available; if so, compute a VMG only based on wind data:
+ if (windPositionMode == WindPositionMode.LEG_MIDDLE) {
+ result = null;
+ } else {
+ final Wind wind = getWind(windPositionMode, /* trackedLeg */ null, competitor, timePoint, cache);
+ result = projectOnto(getTrack(competitor).getEstimatedSpeed(timePoint), wind.getBearing());
+ }
+ }
+ return result;
+ }
+
+ SpeedWithBearing projectOnto(SpeedWithBearing speed, Bearing projectToBearing) {
+ final SpeedWithBearing result;
+ if (speed != null && speed.getBearing() != null && projectToBearing != null) {
+ double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
+ if (cos < 0) {
+ projectToBearing = projectToBearing.reverse();
+ }
+ result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ /**
+ * @param trackedLeg
+ * The caller is expected to obtain the {@link #getTrackedLeg(Competitor, TimePoint) tracked leg} the
+ * {@code competitor} is sailing in at time point {@code at}. If {@code null}, any
+ * non-{@link WindPositionMode#EXACT exact} wind position mode will use {@code null} for the wind
+ * position, defaulting to the "COMBINED" wind source at the middle of the course. In particular, it then
+ * obviously makes no real sense to request {@link WindPositionMode#LEG_MIDDLE} because no leg is known.
+ */
+ Wind getWind(WindPositionMode windPositionMode, TrackedLegImpl trackedLeg, Competitor competitor, TimePoint at, WindLegTypeAndLegBearingCache cache) {
+ final Wind wind;
+ if (windPositionMode == WindPositionMode.EXACT) {
+ wind = cache.getWind(this, competitor, at);
+ } else {
+ wind = getWind(trackedLeg == null ? null :
+ trackedLeg.getEffectiveWindPosition(
+ () -> getTrack(competitor)
+ .getEstimatedPosition(at, false), at, windPositionMode), at);
+ }
+ return wind;
+ }
}
diff --git a/java/com.sap.sailing.expeditionconnector.common/.classpath b/java/com.sap.sailing.expeditionconnector.common/.classpath
new file mode 100755
index 00000000000..afb7df28d9e
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.common/.project b/java/com.sap.sailing.expeditionconnector.common/.project
new file mode 100755
index 00000000000..96ef73399d7
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/.project
@@ -0,0 +1,39 @@
+
+
+ com.sap.sailing.expeditionconnector.common
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ com.google.gdt.eclipse.core.webAppProjectValidator
+
+
+
+
+ com.google.gwt.eclipse.core.gwtProjectValidator
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+ com.google.gwt.eclipse.core.gwtNature
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 00000000000..d17b6724d17
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 00000000000..f29e940a005
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/java/com.sap.sailing.expeditionconnector.common/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector.common/META-INF/MANIFEST.MF
new file mode 100755
index 00000000000..d41efa2965a
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Common
+Bundle-SymbolicName: com.sap.sailing.expeditionconnector.common
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: SAP
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Bundle-ActivationPolicy: lazy
+Export-Package: com.sap.sailing.expeditionconnector
+Require-Bundle: com.sap.sse.gwt,
+ com.sap.sailing.domain.common
diff --git a/java/com.sap.sailing.expeditionconnector.common/build.properties b/java/com.sap.sailing.expeditionconnector.common/build.properties
new file mode 100755
index 00000000000..34d2e4d2dad
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/java/com.sap.sailing.expeditionconnector.common/pom.xml b/java/com.sap.sailing.expeditionconnector.common/pom.xml
new file mode 100755
index 00000000000..c641b232220
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/pom.xml
@@ -0,0 +1,39 @@
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sailing.expeditionconnector.common
+ eclipse-plugin
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-plugin
+ ${tycho-version}
+
+ 1.7
+ 1.7
+
+
+
+ org.eclipse.tycho
+ tycho-source-plugin
+ ${tycho-version}
+
+
+ plugin-source
+ generate-sources
+
+ plugin-source
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/ExpeditionConnectorCommon.gwt.xml b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/ExpeditionConnectorCommon.gwt.xml
new file mode 100755
index 00000000000..78b5c462b1b
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/ExpeditionConnectorCommon.gwt.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration.java b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration.java
new file mode 100755
index 00000000000..730784d7ccf
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration.java
@@ -0,0 +1,75 @@
+package com.sap.sailing.expeditionconnector;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+import com.sap.sse.common.impl.NamedImpl;
+
+public class ExpeditionDeviceConfiguration extends NamedImpl implements Serializable {
+ private static final long serialVersionUID = -7819154195403387909L;
+
+ private final UUID deviceUuid;
+
+ /**
+ * The ID listed as first element in a UDP message coming from Expedition, prefixed by a '#'
+ * character. If {@code null}, no mapping currently exists for the device.
+ */
+ private final Integer expeditionBoatId;
+
+ public ExpeditionDeviceConfiguration(String name, UUID deviceUuid, Integer expeditionBoatId) {
+ super(name);
+ this.deviceUuid = deviceUuid;
+ this.expeditionBoatId = expeditionBoatId;
+ }
+
+ public UUID getDeviceUuid() {
+ return deviceUuid;
+ }
+
+ public Integer getExpeditionBoatId() {
+ return expeditionBoatId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
+ result = prime * result + ((deviceUuid == null) ? 0 : deviceUuid.hashCode());
+ result = prime * result + ((expeditionBoatId == null) ? 0 : expeditionBoatId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ExpeditionDeviceConfiguration other = (ExpeditionDeviceConfiguration) obj;
+ if (getName() == null) {
+ if (other.getName() != null)
+ return false;
+ } else if (!getName().equals(other.getName()))
+ return false;
+ if (deviceUuid == null) {
+ if (other.deviceUuid != null)
+ return false;
+ } else if (!deviceUuid.equals(other.deviceUuid))
+ return false;
+ if (expeditionBoatId == null) {
+ if (other.expeditionBoatId != null)
+ return false;
+ } else if (!expeditionBoatId.equals(other.expeditionBoatId))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpeditionDeviceConfiguration [deviceUuid=" + deviceUuid + ", expeditionBoatId=" + expeditionBoatId
+ + ", getName()=" + getName() + "]";
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceIdentifier.java b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceIdentifier.java
new file mode 100755
index 00000000000..dd44b6d6f39
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionDeviceIdentifier.java
@@ -0,0 +1,12 @@
+package com.sap.sailing.expeditionconnector;
+
+import java.util.UUID;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+
+public interface ExpeditionDeviceIdentifier extends DeviceIdentifier {
+ /**
+ * Derived from an {@link ExpeditionDeviceConfiguration#getDeviceUuid()}.
+ */
+ UUID getId();
+}
diff --git a/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionSensorDeviceIdentifier.java b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionSensorDeviceIdentifier.java
new file mode 100755
index 00000000000..ee56e5f788f
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.common/src/com/sap/sailing/expeditionconnector/ExpeditionSensorDeviceIdentifier.java
@@ -0,0 +1,12 @@
+package com.sap.sailing.expeditionconnector;
+
+/**
+ * A device identifier used to identify an installation of the regatta tool "Expedition"
+ * regarding its sensor data beyond the basic GPS and wind information.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public interface ExpeditionSensorDeviceIdentifier extends ExpeditionDeviceIdentifier {
+ public static final String TYPE = "EXPEDITION_SENSOR";
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/.classpath b/java/com.sap.sailing.expeditionconnector.persistence/.classpath
new file mode 100755
index 00000000000..eca7bdba8f0
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/.project b/java/com.sap.sailing.expeditionconnector.persistence/.project
new file mode 100755
index 00000000000..c6034e7c533
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/.project
@@ -0,0 +1,28 @@
+
+
+ com.sap.sailing.expeditionconnector.persistence
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 00000000000..f29e940a005
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF
new file mode 100755
index 00000000000..2d660db1059
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Persistence
+Bundle-SymbolicName: com.sap.sailing.expeditionconnector.persistence
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: com.sap.sailing.expeditionconnector.persistence.impl.Activator
+Bundle-Vendor: SAP
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.osgi.framework;version="1.3.0"
+Require-Bundle: com.sap.sailing.domain.persistence,
+ com.sap.sailing.domain.shared.android,
+ com.sap.sailing.domain.common,
+ com.sap.sse.common,
+ com.sap.sse.mongodb,
+ org.mongodb.mongo-java-driver;bundle-version="2.13.0",
+ com.sap.sailing.expeditionconnector.common,
+ com.sap.sailing.server.gateway.serialization.shared.android
+Export-Package: com.sap.sailing.expeditionconnector.persistence
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/build.properties b/java/com.sap.sailing.expeditionconnector.persistence/build.properties
new file mode 100755
index 00000000000..34d2e4d2dad
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/pom.xml b/java/com.sap.sailing.expeditionconnector.persistence/pom.xml
new file mode 100755
index 00000000000..d8d0564b320
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/pom.xml
@@ -0,0 +1,12 @@
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sailing.expeditionconnector.persistence
+ eclipse-plugin
+
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/DomainObjectFactory.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/DomainObjectFactory.java
new file mode 100755
index 00000000000..3e03a1249b1
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/DomainObjectFactory.java
@@ -0,0 +1,7 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
+
+public interface DomainObjectFactory {
+ Iterable getExpeditionDeviceConfigurations();
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifier.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifier.java
new file mode 100755
index 00000000000..7c59f1aeae0
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifier.java
@@ -0,0 +1,14 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceIdentifier;
+
+/**
+ * A device identifier used to identify an installation of the regatta tool "Expedition"
+ * regarding its basic GPS information.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public interface ExpeditionGpsDeviceIdentifier extends ExpeditionDeviceIdentifier {
+ public static final String TYPE = "EXPEDITION_GPS";
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierImpl.java
new file mode 100755
index 00000000000..3f9712f87aa
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierImpl.java
@@ -0,0 +1,18 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import java.util.UUID;
+
+import com.sap.sailing.expeditionconnector.persistence.impl.AbstractExpeditionDeviceIdentifierImpl;
+
+public class ExpeditionGpsDeviceIdentifierImpl extends AbstractExpeditionDeviceIdentifierImpl implements ExpeditionGpsDeviceIdentifier {
+ private static final long serialVersionUID = -4049961972156611640L;
+
+ public ExpeditionGpsDeviceIdentifierImpl(UUID id) {
+ super(id);
+ }
+
+ @Override
+ public String getIdentifierType() {
+ return ExpeditionGpsDeviceIdentifier.TYPE;
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierJsonHandler.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierJsonHandler.java
new file mode 100755
index 00000000000..7139cac90c9
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierJsonHandler.java
@@ -0,0 +1,16 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
+
+public class ExpeditionGpsDeviceIdentifierJsonHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
+implements DeviceIdentifierJsonHandler {
+
+ @Override
+ public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
+ throws TransformationException {
+ return deserialize((String) serialized, type, stringRepresentation);
+ }
+
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierSerializationHandler.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierSerializationHandler.java
new file mode 100755
index 00000000000..5965eb87869
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionGpsDeviceIdentifierSerializationHandler.java
@@ -0,0 +1,27 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import java.util.UUID;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sse.common.Util;
+
+public class ExpeditionGpsDeviceIdentifierSerializationHandler {
+ private ExpeditionGpsDeviceIdentifier castIdentifier(DeviceIdentifier identifier) throws TransformationException {
+ if (!(identifier instanceof ExpeditionGpsDeviceIdentifier))
+ throw new TransformationException("Expected a ExpeditionGpsDeviceIdentifier, got instead: " + identifier);
+ return (ExpeditionGpsDeviceIdentifier) identifier;
+ }
+
+ public Util.Pair serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
+ return new Util.Pair(ExpeditionGpsDeviceIdentifier.TYPE, castIdentifier(deviceIdentifier).getId().toString());
+ }
+
+ public DeviceIdentifier deserialize(String input, String type, String stringRep) throws TransformationException {
+ try {
+ return new ExpeditionGpsDeviceIdentifierImpl(UUID.fromString(input));
+ } catch (IllegalArgumentException e) {
+ throw new TransformationException("Invalid string representation of smartphone UUID", e);
+ }
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierImpl.java
new file mode 100755
index 00000000000..4ad233a9d82
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierImpl.java
@@ -0,0 +1,19 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import java.util.UUID;
+
+import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.persistence.impl.AbstractExpeditionDeviceIdentifierImpl;
+
+public class ExpeditionSensorDeviceIdentifierImpl extends AbstractExpeditionDeviceIdentifierImpl implements ExpeditionSensorDeviceIdentifier {
+ private static final long serialVersionUID = -4049961972156611640L;
+
+ public ExpeditionSensorDeviceIdentifierImpl(UUID id) {
+ super(id);
+ }
+
+ @Override
+ public String getIdentifierType() {
+ return ExpeditionSensorDeviceIdentifier.TYPE;
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierSerializationHandler.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierSerializationHandler.java
new file mode 100755
index 00000000000..3872e9255f9
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/ExpeditionSensorDeviceIdentifierSerializationHandler.java
@@ -0,0 +1,28 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import java.util.UUID;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
+import com.sap.sse.common.Util;
+
+public class ExpeditionSensorDeviceIdentifierSerializationHandler {
+ private ExpeditionSensorDeviceIdentifier castIdentifier(DeviceIdentifier identifier) throws TransformationException {
+ if (!(identifier instanceof ExpeditionSensorDeviceIdentifier))
+ throw new TransformationException("Expected a ExpeditionSensorDeviceIdentifier, got instead: " + identifier);
+ return (ExpeditionSensorDeviceIdentifier) identifier;
+ }
+
+ public Util.Pair serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
+ return new Util.Pair(ExpeditionSensorDeviceIdentifier.TYPE, castIdentifier(deviceIdentifier).getId().toString());
+ }
+
+ public DeviceIdentifier deserialize(String input, String type, String stringRep) throws TransformationException {
+ try {
+ return new ExpeditionSensorDeviceIdentifierImpl(UUID.fromString(input));
+ } catch (IllegalArgumentException e) {
+ throw new TransformationException("Invalid string representation of smartphone UUID", e);
+ }
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/MongoObjectFactory.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/MongoObjectFactory.java
new file mode 100755
index 00000000000..e4cfecac400
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/MongoObjectFactory.java
@@ -0,0 +1,9 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
+
+public interface MongoObjectFactory {
+ void storeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration);
+
+ void removeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration);
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/PersistenceFactory.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/PersistenceFactory.java
new file mode 100755
index 00000000000..d955241fbae
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/PersistenceFactory.java
@@ -0,0 +1,23 @@
+package com.sap.sailing.expeditionconnector.persistence;
+
+import com.sap.sailing.expeditionconnector.persistence.impl.PersistenceFactoryImpl;
+import com.sap.sse.mongodb.MongoDBConfiguration;
+import com.sap.sse.mongodb.MongoDBService;
+
+public interface PersistenceFactory {
+ PersistenceFactory INSTANCE = new PersistenceFactoryImpl();
+
+ /**
+ * Obtains the domain object factory using the default persistence settings from {@link MongoDBConfiguration#getDefaultConfiguration()}.
+ */
+ DomainObjectFactory getDefaultDomainObjectFactory();
+
+ DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService);
+
+ /**
+ * Obtains the Mongo object factory using the default persistence settings from {@link MongoDBConfiguration#getDefaultConfiguration()}.
+ */
+ MongoObjectFactory getDefaultMongoObjectFactory();
+
+ MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService);
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/AbstractExpeditionDeviceIdentifierImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/AbstractExpeditionDeviceIdentifierImpl.java
new file mode 100755
index 00000000000..21ff23cd0a6
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/AbstractExpeditionDeviceIdentifierImpl.java
@@ -0,0 +1,37 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import java.util.UUID;
+
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceIdentifier;
+
+public abstract class AbstractExpeditionDeviceIdentifierImpl implements ExpeditionDeviceIdentifier {
+ private static final long serialVersionUID = -6605059302775505785L;
+ private final UUID id;
+
+ public AbstractExpeditionDeviceIdentifierImpl(UUID id) {
+ super();
+ this.id = id;
+ }
+
+ @Override
+ public String getStringRepresentation() {
+ return id.toString();
+ }
+
+ @Override
+ public UUID getId() {
+ return id;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ExpeditionDeviceIdentifier) &&
+ ((ExpeditionDeviceIdentifier) obj).getIdentifierType().equals(getIdentifierType()) &&
+ ((ExpeditionDeviceIdentifier) obj).getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return 9478 ^ getIdentifierType().hashCode() ^ getId().hashCode();
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java
new file mode 100755
index 00000000000..4e988439924
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java
@@ -0,0 +1,63 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
+import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
+import com.sap.sse.common.TypeBasedServiceFinder;
+import com.sap.sse.mongodb.MongoDBService;
+
+public class Activator implements BundleActivator {
+
+ private static BundleContext context;
+
+ /**
+ * Registrations of OSGi services to be de-registered when the bundle shuts down
+ */
+ private Set> registrations = new HashSet<>();
+
+ static BundleContext getContext() {
+ return context;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bundleContext) throws Exception {
+ Activator.context = bundleContext;
+ registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionGpsDeviceIdentifierMongoHandler(), getDict(ExpeditionGpsDeviceIdentifier.TYPE)));
+ registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionSensorDeviceIdentifierMongoHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
+ for (CollectionNames name : CollectionNames.values()) {
+ MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bundleContext) throws Exception {
+ Activator.context = null;
+ for (ServiceRegistration> reg : registrations) {
+ reg.unregister();
+ }
+ registrations.clear();
+ }
+
+ private Dictionary getDict(String type) {
+ Dictionary properties = new Hashtable();
+ properties.put(TypeBasedServiceFinder.TYPE, type);
+ return properties;
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/CollectionNames.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/CollectionNames.java
new file mode 100755
index 00000000000..d3abbcdfcda
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/CollectionNames.java
@@ -0,0 +1,5 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+public enum CollectionNames {
+ EXPEDITION_DEVICE_CONFIGURATIONS;
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/DomainObjectFactoryImpl.java
new file mode 100755
index 00000000000..d02b63d2639
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/DomainObjectFactoryImpl.java
@@ -0,0 +1,35 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
+import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
+
+public class DomainObjectFactoryImpl implements DomainObjectFactory {
+ private final DB db;
+
+ public DomainObjectFactoryImpl(DB db) {
+ this.db = db;
+ }
+
+ @Override
+ public Iterable getExpeditionDeviceConfigurations() {
+ final List result = new ArrayList<>();
+ final DBCollection expeditionDeviceConfigurationsCollection = db.getCollection(CollectionNames.EXPEDITION_DEVICE_CONFIGURATIONS.name());
+ for (final Object o : expeditionDeviceConfigurationsCollection.find()) {
+ final DBObject dbo = (DBObject) o;
+ final UUID uuid = (UUID) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name());
+ final String name = (String) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_NAME.name());
+ final Number boatIdAsNumber = (Number) dbo.get(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID.name());
+ final Integer boatId = boatIdAsNumber == null ? null : boatIdAsNumber.intValue();
+ result.add(new ExpeditionDeviceConfiguration(name, uuid, boatId));
+ }
+ return result;
+ }
+
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionGpsDeviceIdentifierMongoHandler.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionGpsDeviceIdentifierMongoHandler.java
new file mode 100755
index 00000000000..16387b809d9
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionGpsDeviceIdentifierMongoHandler.java
@@ -0,0 +1,15 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierSerializationHandler;
+
+public class ExpeditionGpsDeviceIdentifierMongoHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
+implements DeviceIdentifierMongoHandler {
+ @Override
+ public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
+ throws TransformationException {
+ return deserialize((String) serialized, type, stringRepresentation);
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionSensorDeviceIdentifierMongoHandler.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionSensorDeviceIdentifierMongoHandler.java
new file mode 100755
index 00000000000..a549dab7d6c
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/ExpeditionSensorDeviceIdentifierMongoHandler.java
@@ -0,0 +1,15 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import com.sap.sailing.domain.common.DeviceIdentifier;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierSerializationHandler;
+
+public class ExpeditionSensorDeviceIdentifierMongoHandler extends ExpeditionSensorDeviceIdentifierSerializationHandler
+implements DeviceIdentifierMongoHandler {
+ @Override
+ public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
+ throws TransformationException {
+ return deserialize((String) serialized, type, stringRepresentation);
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/FieldNames.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/FieldNames.java
new file mode 100755
index 00000000000..26e383ab254
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/FieldNames.java
@@ -0,0 +1,5 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+public enum FieldNames {
+ EXPEDITION_DEVICE_CONFIGURATION_NAME, EXPEDITION_DEVICE_CONFIGURATION_UUID, EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID;
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MongoObjectFactoryImpl.java
new file mode 100755
index 00000000000..b7a67de00f0
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MongoObjectFactoryImpl.java
@@ -0,0 +1,59 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.WriteConcern;
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
+import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
+
+public class MongoObjectFactoryImpl implements MongoObjectFactory {
+ private static final Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
+ private final DBCollection expeditionDeviceConfigurationsCollection;
+
+ public MongoObjectFactoryImpl(DB db) {
+ this.expeditionDeviceConfigurationsCollection = db.getCollection(CollectionNames.EXPEDITION_DEVICE_CONFIGURATIONS.name());
+ DBObject index = new BasicDBObject();
+ index.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), 1);
+ expeditionDeviceConfigurationsCollection.createIndex(index, new BasicDBObject("unique", true));
+ }
+
+ private BasicDBObject getExpeditionDeviceConfigurationDBKey(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
+ final BasicDBObject basicDBObject = new BasicDBObject(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), expeditionDeviceConfiguration.getDeviceUuid());
+ return basicDBObject;
+ }
+
+ @Override
+ public void storeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
+ final BasicDBObject key = getExpeditionDeviceConfigurationDBKey(expeditionDeviceConfiguration);
+ final DBObject expeditionDeviceConfigurationDBObject = new BasicDBObject();
+ expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_UUID.name(), expeditionDeviceConfiguration.getDeviceUuid());
+ expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_NAME.name(), expeditionDeviceConfiguration.getName());
+ expeditionDeviceConfigurationDBObject.put(FieldNames.EXPEDITION_DEVICE_CONFIGURATION_BOAT_ID.name(), expeditionDeviceConfiguration.getExpeditionBoatId());
+ boolean success = false;
+ int attempt = 0;
+ Exception lastException = null;
+ while (attempt < 5 && !success) {
+ try {
+ expeditionDeviceConfigurationsCollection.update(key, expeditionDeviceConfigurationDBObject, /* upsert */ true, /* multi */ false, WriteConcern.SAFE);
+ success = true;
+ attempt++;
+ } catch (Exception e) {
+ lastException = e;
+ logger.log(Level.WARNING, "Exception trying to write Expedition device configuration. Trying again", e);
+ }
+ }
+ if (!success) {
+ throw new RuntimeException("Couldn't store Expedition device configuration "+expeditionDeviceConfiguration, lastException);
+ }
+ }
+
+ @Override
+ public void removeExpeditionDeviceConfiguration(ExpeditionDeviceConfiguration expeditionDeviceConfiguration) {
+ expeditionDeviceConfigurationsCollection.remove(getExpeditionDeviceConfigurationDBKey(expeditionDeviceConfiguration), WriteConcern.SAFE);
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/PersistenceFactoryImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/PersistenceFactoryImpl.java
new file mode 100755
index 00000000000..3a70e37b2b2
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/PersistenceFactoryImpl.java
@@ -0,0 +1,31 @@
+package com.sap.sailing.expeditionconnector.persistence.impl;
+
+import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory;
+import com.sap.sse.mongodb.MongoDBConfiguration;
+import com.sap.sse.mongodb.MongoDBService;
+
+public class PersistenceFactoryImpl implements PersistenceFactory {
+
+ @Override
+ public DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService) {
+ return new DomainObjectFactoryImpl(mongoDbService.getDB());
+ }
+
+ @Override
+ public MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService) {
+ return new MongoObjectFactoryImpl(mongoDbService.getDB());
+ }
+
+ @Override
+ public DomainObjectFactory getDefaultDomainObjectFactory() {
+ return getDomainObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService());
+ }
+
+ @Override
+ public MongoObjectFactory getDefaultMongoObjectFactory() {
+ return getMongoObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService());
+ }
+
+}
diff --git a/java/com.sap.sailing.expeditionconnector.test/.classpath b/java/com.sap.sailing.expeditionconnector.test/.classpath
index 4f83b2397ec..03c4ce9fdd8 100644
--- a/java/com.sap.sailing.expeditionconnector.test/.classpath
+++ b/java/com.sap.sailing.expeditionconnector.test/.classpath
@@ -2,6 +2,7 @@
+
diff --git a/java/com.sap.sailing.expeditionconnector.test/build.properties b/java/com.sap.sailing.expeditionconnector.test/build.properties
index 34d2e4d2dad..f11210d3f2c 100755
--- a/java/com.sap.sailing.expeditionconnector.test/build.properties
+++ b/java/com.sap.sailing.expeditionconnector.test/build.properties
@@ -1,4 +1,5 @@
-source.. = src/
+source.. = src/,\
+ resources/
output.. = bin/
bin.includes = META-INF/,\
.
diff --git a/java/com.sap.sailing.expeditionconnector.test/resources/2017Nov04VeryShort.csv b/java/com.sap.sailing.expeditionconnector.test/resources/2017Nov04VeryShort.csv
new file mode 100644
index 00000000000..87bbb0618b3
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.test/resources/2017Nov04VeryShort.csv
@@ -0,0 +1,64 @@
+Boat,Utc,Bsp,Awa,Aws,Twa,Tws,Twd,RudderFwd,Leeway,Set,Drift,Hdg,AirTemp,SeaTemp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FStayLen,MastButt,Load S,Load P,Rake,Volts,ROT,GpQual,PDOP,GpsNum,GpsAge,Altitude,GeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,RunnerS,RunnerP,Vang,Trav,Main,KeelAng,KeelHt,Board,Oil P,RPM 1,RPM 2,Board P,Board S,DistToLn,RchTmToLn,RchDtToLn,GPS time,Twd+90,Twd-90,Downhaul2,Mk Lat,Mk Lon,Port lat,Port lon,Stbd lat,Stbd lon,HPE,RH,Lead P,Lead S,BackStay,RakeDeg,DflectrPP,TG Heell,ForestayPres,DflectrMM,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion,Mwa,Mws,Boom,Twist,TackLossT,TackLossD,TrimRate,HeelRate,DeflectorP,RudderP,RudderS,RudderToe,BspTr,FStayInner,DeflectorS,Bobstay,Outhaul,D0 P,D0 S,D1 P,D1 S,V0 P,V0 S,V1 P,V1 S
+0,43043.622986,0,105.2,9.21,106.1,8.95,259.9,,-0,152.9,0.06,151.6,,,,2.8,0.92,0.01,-7.19,,1.036,,,,,,,67,,,0,,0,0,30.92,0,,-33.9085207,18.4197313,152.9,0.063,,,,,,,,,,,,,,,,,,,0.622989,180,0,,-33.8723241,18.4068248,-33.8998667,18.44635,-33.9048333,18.4433333,,,,,,1.255319,78.54628,17.899,0.964532,484.2771,-0.000027,,,,,,,,,,,,,,,,,,,,,,,,,,,0.005822,,,0.941392,,,,,,,,,,,0
+1,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+2,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+3,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+4,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+5,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+6,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+7,43043.622986,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0
+.52,155.6,5.4,333.2,,0,58.4,0.06,179,,,,1.4,0.7,-0.05,7.17,,0.543,,,,,,,104,,,0,,0,0,35.33,0,,-33.908531,18.4197168,58.4,0.064,,,,,,,,,,,,,,,,4494.746494,,,0.305674,63.2,243.2,,,,,,,,,,,,,2.367484,98.865089,9.160931,0.494149,579.191406,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.141281,,4369.841626
+0,43043.305710,0,153.2,5.33,154.2,5.36,332.9,,0,58.4,0.1,179.1,,,,1.4,0.68,0.01,7.16,,0.545,,,,,,,104,,,0,,0,0,35.3,0,,-33.9085312,18.4197166,58.4,0.103,,,,,,,,,,,,,,,,4494.745257,,,0.305686,62.9,242.9,,39.5759514,2.5748167,,,,,,,,,,2.367534,98.865723,9.031358,0.493427,579.195068,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.384381,,4369.841628
+0,43043.305723,0,151.7,5.12,152.9,5.31,332.6,,0,58.4,0.11,178.9,,,,1.2,0.65,-0.07,7.18,,0.538,,,,,,,104,,,0,,0,0,35.32,0,,-33.9085314,18.4197169,58.4,0.11,,,,,,,,,,,,,,,,4494.744879,,,0.3057,62.6,242.6,,,,,,,,,,,,,2.367484,98.866142,8.846865,0.493583,579.195679,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.692521,,4369.841645
+0,43043.305734,0,150.5,4.97,151.9,5.24,332.3,,0,58.4,0.09,178.8,,,,1.2,0.63,-0.04,7.17,,0.545,,,,,,,104,,,0,,0,0,35.29,0,,-33.9085313,18.419717,58.4,0.089,,,,,,,,,,,,,,,,4494.745473,,,0.305712,62.3,242.3,,,,,,,,,,,,,2.367501,98.86599,8.596081,0.494632,579.193542,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.160375,,4369.841654
+0,43043.305745,0,150.1,4.89,151.4,5.18,332,,0,58.4,0.05,178.8,,,,1.2,0.68,-0.05,7.17,,0.542,,,,,,,104,,,0,,0,0,35.24,0,,-33.9085312,18.4197168,58.4,0.047,,,,,,,,,,,,,,,,4494.746847,,,0.305723,62.1,242.1,,,,,,,,,,,,,2.367514,98.865654,8.345397,0.494219,579.191162,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.48573,,4369.841648
+0,43043.305758,0,149.1,4.85,150.4,5.14,331.7,,0,58.4,0.05,178.9,,,,1.4,0.79,-0,7.16,,0.546,,,,,,,104,,,0,,0,0,35.17,0,,-33.9085311,18.4197165,58.4,0.046,,,,,,,,,,,,,,,,4494.747044,,,0.305735,61.7,241.7,,,,,,,,,,,,,2.367511,98.865479,8.207174,0.494884,579.190796,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.398528,,4369.841627
+0,43043.305769,0,148.1,4.77,149.6,5.12,331.3,,0,58.4,0.05,179.2,,,,1.4,0.85,-0.03,7.17,,0.545,,,,,,,104,,,0,,0,0,35.09,0,,-33.9085309,18.4197168,58.4,0.051,,,,,,,,,,,,,,,,4494.746833,,,0.305747,61.4,241.4,,,,,,,,,,,,,2.367501,98.865295,8.123696,0.493687,579.192505,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.170342,,4369.841614
+0,43043.305781,0,148.1,4.72,149.1,5.1,331.1,,0,58.4,0.06,179.6,,,,1.4,0.83,-0,7.16,,0.544,,,,,,,104,,,0,,0,0,35.02,0,,-33.9085306,18.4197173,58.4,0.062,,,,,,,,,,,,,,,,4494.746565,,,0.305758,61.1,241.1,,,,,,,,,,,,,2.367521,98.865692,8.040314,0.494814,579.191406,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.82654,,4369.841601
+0,43043.305793,0,148.4,4.71,149.3,5.07,331,,0,58.4,0.07,179.6,,,,1.4,0.84,-0.04,7.16,,0.547,,,,,,,104,,,0,,0,0,34.92,0,,-33.9085303,18.4197174,58.4,0.066,,,,,,,,,,,,,,,,4494.746314,,,0.30577,61.1,241.1,,,,,,,,,,,,,2.367481,98.865128,7.936176,0.494846,579.18927,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.81312,,4369.841594
+0,43043.305804,0,147.6,4.65,148.5,5.02,330.7,,0,58.4,0.07,179.5,,,,1.4,0.89,0.01,7.17,,0.545,,,,,,,104,,,0,,0,0,34.89,0,,-33.9085302,18.419717,58.4,0.067,,,,,,,,,,,,,,,,4494.746283,,,0.305781,60.7,240.7,,,,,,,,,,,,,2.367517,98.865089,7.759246,0.494822,579.192688,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.646684,,4369.84158
+0,43043.305816,0,147.2,4.62,148.1,4.98,330.5,,0,58.4,0.07,179.5,,,,1.4,0.93,0,7.16,,0.547,,,,,,,104,,,0,,0,0,34.95,0,,-33.9085303,18.4197172,58.4,0.065,,,,,,,,,,,,,,,,4494.746274,,,0.305793,60.5,240.5,,,,,,,,,,,,,2.367514,98.865921,7.594308,0.493407,579.193115,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.572412,,4369.841589
+0,43043.305827,0,148.9,4.69,148.9,4.98,330.6,,0,58.4,0.05,179.3,,,,1.4,0.91,0.05,7.18,,0.548,,,,,,,104,,,0,,0,0,34.99,0,,-33.9085306,18.4197173,58.4,0.051,,,,,,,,,,,,,,,,4494.746742,,,0.305804,60.5,240.5,,,,,,,,,,,,,2.367514,98.865173,7.596838,0.49495,579.186035,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.766499,,4369.841615
+0,43043.305839,0,152.8,4.78,151.5,4.99,331.2,,0,58.4,0.05,179.1,,,,1.4,0.85,0.04,7.2,,0.551,,,,,,,104,,,0,,0,0,34.92,0,,-33.9085307,18.4197173,58.4,0.051,,,,,,,,,,,,,,,,4494.746863,,,0.305816,61.1,241.1,,,,,,,,,,,,,2.367504,98.864769,7.629427,0.494454,579.188171,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.057669,,4369.841627
+0,43043.305851,0,153.5,4.8,152.5,4.98,331.4,,0,58.4,0.05,179.2,,,,1.4,0.84,0.07,7.16,,0.557,,,,,,,104,,,0,,0,0,34.87,0,,-33.9085305,18.4197175,58.4,0.053,,,,,,,,,,,,,,,,4494.746728,,,0.305828,61.4,241.4,,,,,,,,,,,,,2.367504,98.864746,7.608146,0.494083,579.190308,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.21491,,4369.84162
+0,43043.305862,0,154.6,4.75,153.2,4.95,331.6,,0,58.4,0.05,179.3,,,,1.4,0.86,-0.05,7.17,,0.542,,,,,,,104,,,0,,0,0,34.79,0,,-33.9085304,18.4197177,58.4,0.054,,,,,,,,,,,,,,,,4494.746707,,,0.305839,61.6,241.6,,,,,,,,,,,,,2.367477,98.864372,7.457287,0.495428,579.183655,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.620092,,4369.841617
+0,43043.305874,0,152.5,4.68,152.4,4.9,331.4,,0,58.4,0.05,179.2,,,,1.4,0.82,-0.06,7.17,,0.542,,,,,,,104,,,0,,0,0,34.74,0,,-33.9085302,18.4197176,58.4,0.049,,,,,,,,,,,,,,,,4494.746823,,,0.305851,61.4,241.4,,,,,,,,,,,,,2.367517,98.864601,7.258937,0.49545,579.187744,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.152083,,4369.841609
+0,43043.305886,0,151.2,4.7,151.4,4.89,331.1,,0,58.4,0.04,179.3,,,,1.4,0.71,-0.05,7.17,,0.543,,,,,,,104,,,0,,0,0,34.72,0,,-33.90853,18.4197174,58.4,0.041,,,,,,,,,,,,,,,,4494.747111,,,0.305862,61.2,241.2,,,,,,,,,,,,,2.367527,98.864799,7.251778,0.493887,579.18689,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.082603,,4369.841593
+0,43043.305897,0,149.2,4.78,150,4.91,330.7,,0,58.4,0.04,179.3,,,,1.4,0.58,-0.05,7.17,,0.543,,,,,,,104,,,0,,0,0,34.75,0,,-33.90853,18.4197174,58.4,0.044,,,,,,,,,,,,,,,,4494.747004,,,0.305874,60.8,240.8,,,,,,,,,,,,,2.367501,98.864571,7.301067,0.494079,579.18927,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.699507,,4369.841591
+0,43043.305909,0,148.2,4.8,149.2,4.89,330.4,,0,58.4,0.04,179.4,,,,1.4,0.51,-0.05,7.17,,0.542,,,,,,,104,,,0,,0,0,34.87,0,,-33.9085299,18.4197176,58.4,0.043,,,,,,,,,,,,,,,,4494.747033,,,0.305885,60.5,240.5,,,,,,,,,,,,,2.367507,98.864716,7.254144,0.493431,579.189026,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.528905,,4369.841588
+0,43043.305921,0,145.2,4.74,147.1,4.86,329.7,,0,58.4,0.05,179.6,,,,1.4,0.51,-0.04,7.17,,0.543,,,,,,,104,,,0,,0,0,34.93,0,,-33.9085297,18.4197177,58.4,0.049,,,,,,,,,,,,,,,,4494.74687,,,0.305897,59.9,239.9,,,,,,,,,,,,,2.367527,98.864639,7.141332,0.494522,579.183044,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.369035,,4369.841577
+0,43043.305933,0,142.7,4.65,144.8,4.82,329,,0,58.4,0.05,179.6,,,,1.4,0.53,-0.04,7.17,,0.544,,,,,,,104,,,0,,0,0,34.96,0,,-33.9085296,18.4197175,58.4,0.05,,,,,,,,,,,,,,,,4494.746803,,,0.305909,59.1,239.1,,,,,,,,,,,,,2.367504,98.864037,6.976142,0.494604,579.186462,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.286149,,4369.841565
+0,43043.305944,0,142.3,4.56,144.3,4.77,328.7,,0,58.4,0.05,179.6,,,,1.4,0.53,-0.04,7.17,,0.545,,,,,,,104,,,0,,0,0,34.89,0,,-33.9085292,18.4197173,58.4,0.048,,,,,,,,,,,,,,,,4494.746825,,,0.30592,58.7,238.7,,,,,,,,,,,,,2.367541,98.864273,6.768263,0.495198,579.184937,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.566006,,4369.84154
+0,43043.305957,0,141.1,4.46,143,4.68,328.1,,0,58.4,0.04,179.6,,,,1.4,0.49,-0.06,7.18,,0.547,,,,,,,104,,,0,,0,0,34.87,0,,-33.9085289,18.419717,58.4,0.039,,,,,,,,,,,,,,,,4494.747067,,,0.305932,58.2,238.2,,,,,,,,,,,,,2.367497,98.864983,6.428645,0.495544,579.194458,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.945739,,4369.841514
+0,43043.305968,0,141.2,4.39,142.7,4.61,327.7,,0,58.4,0.06,179.6,,,,1.4,0.44,-0.05,7.17,,0.544,,,,,,,104,,,0,,0,0,34.86,0,,-33.9085286,18.4197169,58.4,0.061,,,,,,,,,,,,,,,,4494.746729,,,0.305943,57.8,237.8,,,,,,,,,,,,,2.367521,98.865654,6.165024,0.494762,579.188416,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.349829,,4369.841498
+0,43043.305979,0,141.1,4.29,142.4,4.57,327.4,,0,58.4,0.08,179.4,,,,1.4,0.41,-0.03,7.16,,0.544,,,,,,,104,,,0,,0,0,34.91,0,,-33.9085285,18.4197166,58.4,0.082,,,,,,,,,,,,,,,,4494.745714,,,0.305955,57.5,237.5,,,,,,,,,,,,,2.367497,98.8647,6.012874,0.495066,579.189026,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.652994,,4369.84149
+0,43043.305991,0,139.7,4.16,141.2,4.53,326.8,,0,58.4,0.09,179.4,,,,1.4,0.43,-0.04,7.17,,0.544,,,,,,,104,,,0,,0,0,34.94,0,,-33.9085286,18.4197167,58.4,0.095,,,,,,,,,,,,,,,,4494.74531,,,0.305966,56.8,236.8,,,,,,,,,,,,,2.367514,98.864342,5.854272,0.494269,579.185791,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.640193,,4369.8415
+0,43043.306002,0,139.5,4.07,140.9,4.49,326.4,,0,58.4,0.07,179.4,,,,1.4,0.47,-0,7.17,,0.546,,,,,,,104,,,0,,0,0,34.94,0,,-33.9085288,18.4197167,58.4,0.073,,,,,,,,,,,,,,,,4494.745619,,,0.305978,56.5,236.5,,,,,,,,,,,,,2.367487,98.864784,5.71282,0.495704,579.18927,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.73358,,4369.841509
+0,43043.306015,0,139.6,4.04,140.5,4.48,326,,0,58.4,0.05,179.4,,,,1.4,0.5,-0.01,7.17,,0.548,,,,,,,104,,,0,,0,0,34.9,0,,-33.908529,18.4197167,58.4,0.051,,,,,,,,,,,,,,,,4494.746677,,,0.305992,56,236,,,,,,,,,,,,,2.367487,98.865372,5.657006,0.494508,579.193787,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.586456,,4369.841519
+0,43043.306026,0,139.8,4.08,140.6,4.49,325.7,,0,58.4,0.04,179.4,,,,1.4,0.51,0.13,7.17,,0.553,,,,,,,104,,,0,,0,0,34.83,0,,-33.9085289,18.4197168,58.4,0.04,,,,,,,,,,,,,,,,4494.747024,,,0.306001,55.7,235.7,,,,,,,,,,,,,2.367544,98.865784,5.693069,0.494978,579.190979,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.369375,,4369.841518
+0,43043.306038,0,140.1,4.18,140.4,4.51,325.4,,0,58.4,0.04,179.5,,,,1.4,0.58,0.04,7.17,,0.549,,,,,,,104,,,0,,0,0,34.85,0,,-33.9085289,18.4197173,58.4,0.043,,,,,,,,,,,,,,,,4494.747023,,,0.306013,55.4,235.4,,,,,,,,,,,,,2.367471,98.864349,5.787839,0.495452,579.183228,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.834913,,4369.84153
+0,43043.306049,0,143,4.26,142.4,4.5,325.7,,0,58.4,0.06,179.5,,,,1.4,0.72,0.06,7.17,,0.55,,,,,,,104,,,0,,0,0,34.93,0,,-33.9085285,18.4197174,58.4,0.06,,,,,,,,,,,,,,,,4494.746438,,,0.306024,55.7,235.7,,,,,,,,,,,,,2.367527,98.864777,5.744619,0.495404,579.191406,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.092266,,4369.841516
+0,43043.306061,0,144.1,4.31,143.2,4.48,325.7,,0,58.4,0.06,179.3,,,,1.4,0.85,0.06,7.17,,0.551,,,,,,,104,,,0,,0,0,34.99,0,,-33.9085286,18.4197174,58.4,0.061,,,,,,,,,,,,,,,,4494.746376,,,0.306039,55.7,235.7,,39.5759514,2.5748167,,,,,,,,,,2.367501,98.863998,5.652483,0.494522,579.182373,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.450637,,4369.841528
+0,43043.306073,0,146.5,4.37,145,4.49,326.1,,0,58.4,0.06,179.3,,,,1.4,0.89,0.05,7.17,,0.542,,,,,,,104,,,0,,0,0,35.04,0,,-33.9085288,18.4197175,58.4,0.063,,,,,,,,,,,,,,,,4494.746299,,,0.30605,56,236,,,,,,,,,,,,,2.367501,98.863808,5.684719,0.494808,579.183899,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.620995,,4369.84154
+0,43043.306085,0,151.8,4.45,148.6,4.5,327,,0,58.4,0.06,179.3,,,,1.3,0.84,0.07,7.17,,0.553,,,,,,,104,,,0,,0,0,34.99,0,,-33.908529,18.4197178,58.4,0.059,,,,,,,,,,,,,,,,4494.746646,,,0.306062,56.9,236.9,,,,,,,,,,,,,2.367507,98.863884,5.737547,0.496847,579.185791,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,39.067871,,4369.841558
+0,43043.306096,0,154.6,4.56,151.3,4.54,327.7,,0,58.4,0.1,179,,,,1.4,0.8,0.07,7.18,,0.549,,,,,,,104,,,0,,0,0,34.97,0,,-33.9085291,18.419718,58.4,0.1,,,,,,,,,,,,,,,,4494.74513,,,0.306071,57.6,237.6,,,,,,,,,,,,,2.367514,98.864517,5.866133,0.495148,579.187744,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,39.49511,,4369.841581
+0,43043.306108,0,154.9,4.75,152.4,4.63,328.1,,0,58.4,0.1,179.4,,,,1.4,0.79,0.07,7.19,,0.553,,,,,,,104,,,0,,0,0,34.98,0,,-33.9085292,18.4197179,58.4,0.099,,,,,,,,,,,,,,,,4494.745193,,,0.306085,58.1,238.1,,,,,,,,,,,,,2.367507,98.864769,6.238055,0.495054,579.182373,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,38.827474,,4369.841569
+0,43043.306120,0,151.6,4.95,150.9,4.76,327.8,,0,58.4,0.11,179.3,,,,1.4,0.81,0.02,7.18,,0.542,,,,,,,104,,,0,,0,0,34.92,0,,-33.9085294,18.4197178,58.4,0.115,,,,,,,,,,,,,,,,4494.74457,,,0.306097,57.9,237.9,,,,,,,,,,,,,2.367464,98.863533,6.728293,0.495422,579.184082,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,37.529799,,4369.841579
+0,43043.306131,0,145.5,5.09,146.9,4.85,326.8,,0,58.4,0.1,179.3,,,,1.4,0.8,-0.03,7.18,,0.568,,,,,,,104,,,0,,0,0,34.87,0,,-33.9085293,18.4197182,58.4,0.104,,,,,,,,,,,,,,,,4494.74531,,,0.306108,56.9,236.9,,,,,,,,,,,,,2.367484,98.863914,7.083571,0.495694,579.183655,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,36.000165,,4369.841585
+0,43043.306143,0,138.5,5.2,141.9,4.88,325.5,,0,58.4,0.14,179.6,,,,1.4,0.69,0.03,7.18,,0.54,,,,,,,104,,,0,,0,0,34.78,0,,-33.9085293,18.4197192,58.4,0.14,,,,,,,,,,,,,,,,4494.743829,,,0.30612,55.6,235.6,,,,,,,,,,,,,2.367554,98.863335,7.188333,0.49929,579.176147,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,34.716986,,4369.841608
+0,43043.306155,0,135.4,5.26,139.1,4.85,324.5,,0,58.4,0.13,180.1,,,,1.4,0.56,0.01,7.18,,0.538,,,,,,,104,,,0,,0,0,34.7,0,,-33.9085294,18.4197202,58.4,0.128,,,,,,,,,,,,,,,,4494.744186,,,0.306131,54.8,234.8,,,,,,,,,,,,,2.367584,98.860855,7.099799,0.496363,579.170105,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,34.056772,,4369.84163
+0,43043.306166,0,130.5,5.22,134.8,4.82,323.2,,0,57.5,0.13,180.1,,,,1.4,0.55,0.01,7.18,,0.545,,,,,,,104,,,0,,0,0,34.63,0,,-33.908529,18.4197211,57.3,0.121,,,,,,,,,,,,,,,,4494.744047,,,0.306143,53.5,233.5,,,,,,,,,,,,,2.367578,98.860954,6.947227,0.497447,579.166443,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.579793,,4369.841636
+0,43043.306178,0,130.6,5.17,134.2,4.77,322.6,,0,56.2,0.11,179.7,,,,1.3,0.58,0.01,7.18,,0.538,,,,,,,104,,,0,,0,0,34.67,0,,-33.9085293,18.4197207,56.2,0.109,,,,,,,,,,,,,,,,4494.744776,,,0.306155,52.7,232.7,,,,,,,,,,,,,2.367564,98.860756,6.770802,0.496485,579.175049,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.788713,,4369.841651
+0,43043.306190,0,129.7,5.08,132.6,4.74,321.8,,0,55.8,0.12,179.8,,,,1.4,0.5,0,7.17,,0.535,,,,,,,104,,,0,,0,0,34.74,0,,-33.9085298,18.41972,55.8,0.117,,,,,,,,,,,,,,,,4494.744618,,,0.306166,51.9,231.9,,,,,,,,,,,,,2.367571,99.007469,6.664012,0.497367,581.100708,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.518448,,4369.841646
+0,43043.306202,0,130.1,4.96,132.6,4.75,321.3,,0,55.8,0.1,179.9,,,,1.4,0.34,-0.03,7.17,,0.481,,,,,,,104,,,0,,0,0,34.66,0,,-33.9085299,18.4197202,55.8,0.1,,,,,,,,,,,,,,,,4494.744771,,,0.306178,51.4,231.4,,,,,,,,,,,,,2.365773,99.772903,6.686917,0.471604,585.662476,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.219946,,4369.841653
+0,43043.306213,0,132.3,4.74,133.2,4.73,321.1,,0,55.8,0.08,179.8,,,,1.6,0.21,-0.01,7.18,,0.465,,,,,,,104,,,0,,0,0,34.57,0,,-33.9085301,18.4197204,55.8,0.077,,,,,,,,,,,,,,,,4494.745837,,,0.306189,51,231,,,,,,,,,,,,,2.364245,100.821457,6.619851,0.424921,591.220581,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.117383,,4369.841672
+0,43043.306225,0,138.2,4.57,137.1,4.71,321.8,,0,55.8,0.06,179.5,,,,1.4,0.19,-0.04,7.17,,0.446,,,,,,,104,,,0,,0,0,34.52,0,,-33.9085308,18.4197203,55.8,0.061,,,,,,,,,,,,,,,,4494.7464,,,0.306201,51.7,231.7,,,,,,,,,,,,,2.363921,101.887444,6.54773,0.417969,595.222595,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.840911,,4369.841714
+0,43043.306237,0,141,4.6,139.4,4.77,322.1,,0,55.8,0.07,179.2,,,,1.4,0.29,-0,7.19,,0.439,,,,,,,104,,,0,,0,0,34.5,0,,-33.9085314,18.4197198,55.8,0.068,,,,,,,,,,,,,,,,4494.74639,,,0.306215,52,232,,,,,,,,,,,,,2.362825,102.492744,6.754865,0.394745,597.877625,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.863526,,4369.841735
+0,43043.306249,0,143.7,4.78,141.4,4.86,322.5,,0,55.8,0.09,179.3,,,,1.4,0.35,-0,7.18,,0.441,,,,,,,104,,,0,,0,0,34.5,0,,-33.9085316,18.41972,55.8,0.092,,,,,,,,,,,,,,,,4494.745476,,,0.306227,52.4,232.4,,,,,,,,,,,,,2.362845,102.847641,7.134893,0.394569,598.614563,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,33.25136,,4369.84175
+0,43043.306260,0,146.5,5.02,143.8,4.97,323,,0,55.8,0.09,179.3,,,,1.4,0.3,-0.02,7.17,,0.441,,,,,,,104,,,0,,0,0,34.51,0,,-33.9085316,18.4197203,55.8,0.087,,,,,,,,,,,,,,,,4494.745482,,,0.306238,52.9,232.9,,,,,,,,,,,,,2.362792,102.896881,7.545109,0.398081,598.711548,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,32.74152,,4369.841757
+0,43043.306272,0,147.8,5.33,145.2,5.1,323.4,,0,55.8,0.07,179.2,,,,1.4,0.22,-0.01,7.18,,0.445,,,,,,,104,,,0,,0,0,34.51,0,,-33.9085316,18.4197205,55.8,0.069,,,,,,,,,,,,,,,,4494.746137,,,0.306247,53.4,233.4,,,,,,,,,,,,,2.362818,102.911133,8.060515,0.397374,598.774536,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,32.456471,,4369.841769
+0,43043.306284,0,148,5.79,146,5.33,323.6,,0,55.8,0.05,179.2,,,,1.4,0.22,-0.02,7.18,,0.458,,,,,,,104,,,0,,0,0,34.48,0,,-33.9085317,18.4197209,55.8,0.056,,,,,,,,,,,,,,,,4494.746909,,,0.306262,53.6,233.6,,,,,,,,,,,,,2.362845,102.932083,8.930959,0.396582,598.878845,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,31.621008,,4369.841779
+0,43043.306295,0,146.6,6.18,145.5,5.54,323.5,,0,55.8,0.08,179.3,,,,1.4,0.31,-0.01,7.16,,0.599,,,,,,,104,,,0,,0,0,34.43,0,,-33.9085315,18.4197211,55.8,0.084,,,,,,,,,,,,,,,,4494.745723,,,0.306273,53.6,233.6,,,,,,,,,,,,,2.366752,102.944511,9.646354,0.446602,598.913452,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,29.906062,,4369.841785
+0,43043.306307,0,145.3,6.61,144.7,5.72,323.4,,0,55.8,0.08,179.5,,,,1.4,0.38,-0.02,7.19,,0.657,,,,,,,104,,,0,,0,0,34.44,0,,-33.9085314,18.4197215,55.8,0.082,,,,,,,,,,,,,,,,4494.745765,,,0.306285,53.4,233.4,,,,,,,,,,,,,2.370409,102.947891,10.227253,0.558491,598.919434,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,28.078976,,4369.841781
+0,43043.306319,0,144.3,7.06,144.2,5.91,323.3,,0,55.8,0.07,179.5,,,,1.4,0.37,-0.02,7.19,,0.672,,,,,,,104,,,0,,0,0,34.41,0,,-33.9085312,18.4197216,55.8,0.062,,,,,,,,,,,,,,,,4494.745845,,,0.306293,53.3,233.3,,,,,,,,,,,,,2.371047,102.947685,10.765502,0.580573,598.915161,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,26.559608,,4369.841778
+0,43043.306331,0,142.9,7.36,143.2,6.06,323.1,,0,55.8,0.04,179.5,,,,1.3,0.34,-0.02,7.17,,0.84,,,,,,,105,,,0,,0,0,34.44,0,,-33.9085311,18.419722,55.8,0.042,,,,,,,,,,,,,,,,4494.747071,,,0.306308,53.1,233.1,,,,,,,,,,,,,2.377057,102.942726,11.129307,0.648761,598.823792,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,25.354303,,4369.841785
diff --git a/java/com.sap.sailing.expeditionconnector.test/resources/Expedition_28Oct17_0820.txt b/java/com.sap.sailing.expeditionconnector.test/resources/Expedition_28Oct17_0820.txt
new file mode 100755
index 00000000000..38a4c37b4d2
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.test/resources/Expedition_28Oct17_0820.txt
@@ -0,0 +1,159 @@
+ #0,1,0.000,2,-129.8,3,2.64,4,-125.4,5,2.54,6,42.8,9,153.4,10,0.00,11,0.0,12,0.12,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.126,146,0.347763,152,109.8,172,1.872,173,2.0,175,1.185,176,294.957,238,3.256*0E
+ #0,1,0.000,2,-130.2,3,2.65,4,-125.7,5,2.54,6,42.7,9,153.4,10,0.00,11,0.0,12,0.13,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.136,146,0.347766,152,109.2,172,1.872,173,2.0,175,1.185,176,294.959,238,3.256*03
+ #0,1,0.000,2,-130.7,3,2.65,4,-126.0,5,2.54,6,42.5,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.146,146,0.347766,152,108.6,172,1.872,173,2.0,175,1.185,176,294.960,238,3.256*0F
+ #0,9,153.4,11,0.0,12,0.15,152,107.9,238,3.256*32
+ #0,1,0.000,2,-131.2,3,2.65,4,-126.4,5,2.54,6,42.3,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.155,146,0.347769,152,107.1,172,1.872,173,2.0,175,1.184,176,294.959,238,3.256*04
+ #0,1,0.000,2,-132.4,3,2.65,4,-127.4,5,2.54,6,41.9,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.176,146,0.347769,152,105.3,172,1.872,173,2.0,175,1.184,176,294.954,238,3.255*08
+ #0,1,0.000,2,-133.1,3,2.65,4,-127.9,5,2.54,6,41.7,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.176,146,0.347772,152,104.3,172,1.872,173,2.0,175,1.183,176,294.956,238,3.254*01
+ #0,1,0.000,2,-133.8,3,2.66,4,-128.4,5,2.54,6,41.5,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.173,146,0.347772,152,103.2,172,1.872,173,2.0,175,1.184,176,294.956,238,3.254*00
+ #0,1,0.000,2,-134.5,3,2.66,4,-128.9,5,2.54,6,41.3,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.167,146,0.347775,172,1.872,173,2.0,175,1.184,176,294.954*0F
+ #0,1,0.000,2,-135.1,3,2.66,4,-129.5,5,2.54,6,41.1,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.159,146,0.347775,152,102.1,172,1.872,173,2.0,175,1.184,176,294.956,238,3.253*00
+ #0,1,0.000,2,-135.8,3,2.66,4,-130.0,5,2.54,6,40.9,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.50,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.152,146,0.347775,152,101.0,172,1.872,173,2.0,175,1.184,176,294.957,238,3.252*07
+ #0,1,0.000,2,-136.3,3,2.66,4,-130.6,5,2.54,6,40.6,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.145,146,0.347778,152,99.9,172,1.872,173,2.0,175,1.184,176,294.958,238,3.252*3A
+ #0,1,0.000,2,-136.8,3,2.66,4,-131.0,5,2.54,6,40.4,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.138,146,0.347778,152,98.9,172,1.872,173,2.0,175,1.184,176,294.956,238,3.251*33
+ #0,1,0.000,2,-137.2,3,2.66,4,-131.5,5,2.54,6,40.3,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.131,146,0.347781,152,97.9,172,1.872,173,2.0,175,1.183,176,294.957,238,3.251*3C
+ #0,9,153.4,11,0.0,12,0.13,152,97.0,238,3.250*03
+ #0,1,0.000,2,-137.8,3,2.66,4,-132.2,5,2.54,6,39.9,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.160,146,0.347781,152,95.6,172,1.872,173,2.0,175,1.184,176,294.956,238,3.250*38
+ #0,1,0.000,2,-137.8,3,2.66,4,-132.2,5,2.54,6,39.9,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.160,146,0.347781,152,95.6,172,1.872,173,2.0,175,1.184,176,294.956,238,3.249*30
+ #0,1,0.000,2,-138.0,3,2.67,4,-132.6,5,2.54,6,39.6,9,153.4,10,0.00,11,0.0,12,0.19,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.193,146,0.347784,152,94.7,172,1.872,173,2.0,175,1.184,176,294.954,238,3.249*39
+ #0,1,0.000,2,-138.0,3,2.67,4,-132.8,5,2.54,6,39.5,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.209,146,0.347786,152,94.4,172,1.872,173,2.0,175,1.184,176,294.954,238,3.250*36
+ #0,1,0.000,2,-138.0,3,2.67,4,-132.9,5,2.54,6,39.4,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.223,146,0.347786,152,94.2,172,1.872,173,2.0,175,1.184,176,294.955,238,3.251*3B
+ #0,1,0.000,2,-138.0,3,2.67,4,-133.0,5,2.54,6,39.3,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.233,146,0.347786,152,94.2,172,1.872,173,2.0,175,1.184,176,294.954,238,3.252*05
+ #0,1,0.000,2,-138.0,3,2.67,4,-133.1,5,2.54,6,39.1,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.233,146,0.347789,152,94.0,172,1.872,173,2.0,175,1.184,176,294.951,238,3.254*3A
+ #0,1,0.000,2,-138.0,3,2.67,4,-133.2,5,2.54,6,39.0,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.230,146,0.347789,152,93.8,172,1.872,173,2.0,175,1.184,176,294.951,238,3.254*35
+ #0,1,0.000,2,-138.1,3,2.67,4,-133.3,5,2.55,6,38.9,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.223,146,0.347792,152,93.6,172,1.872,173,2.0,175,1.185,176,294.951,238,3.255*3B
+ #0,1,0.000,2,-138.2,3,2.68,4,-133.5,5,2.55,6,38.7,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.217,146,0.347792,152,93.3,172,1.872,173,2.0,175,1.184,176,294.952,238,3.257*3C
+ #0,1,0.000,2,-138.4,3,2.68,4,-133.7,5,2.55,6,38.6,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.210,146,0.347792,152,93.0,172,1.872,173,2.0,175,1.184,176,294.954,238,3.259*34
+ #0,1,0.000,2,-138.9,3,2.68,4,-134.1,5,2.55,6,38.2,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.210,146,0.347795,152,92.7,172,1.872,173,2.0,175,1.184,176,294.954,238,3.261*35
+ #0,9,153.4,11,0.0,12,0.21,152,91.7,238,3.264*04
+ #0,1,0.000,2,-139.1,3,2.69,4,-134.4,5,2.56,6,38.1,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.210,146,0.347798,152,91.2,172,1.872,173,2.0,175,1.185,176,294.958,238,3.270*3F
+ #0,1,0.000,2,-139.4,3,2.69,4,-134.7,5,2.56,6,37.9,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419951,50,0.0,51,0.207,146,0.347798,152,90.7,172,1.872,173,2.0,175,1.185,176,294.958,238,3.274*3B
+ #0,1,0.000,2,-139.6,3,2.69,4,-134.9,5,2.56,6,37.8,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.207,146,0.347798,152,90.2,172,1.872,173,2.0,175,1.185,176,294.960,238,3.278*35
+ #0,1,0.000,2,-139.8,3,2.70,4,-135.1,5,2.57,6,37.6,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.207,146,0.347801,152,89.7,172,1.872,173,2.0,175,1.185,176,294.961,238,3.282*30
+ #0,1,0.000,2,-139.9,3,2.70,4,-135.4,5,2.57,6,37.5,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419951,50,0.0,51,0.207,146,0.347801,152,89.3,172,1.872,173,2.0,175,1.185,176,294.960,238,3.287*34
+ #0,1,0.000,2,-140.0,3,2.70,4,-135.5,5,2.58,6,37.3,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347804,152,89.0,172,1.872,173,2.0,175,1.185,176,294.962,238,3.292*39
+ #0,1,0.000,2,-140.0,3,2.71,4,-135.7,5,2.58,6,37.2,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347804,152,88.7,172,1.872,173,2.0,175,1.185,176,294.958,238,3.297*30
+ #0,1,0.000,2,-140.0,3,2.71,4,-135.8,5,2.58,6,37.1,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347804,152,88.4,172,1.872,173,2.0,175,1.185,176,294.956,238,3.302*3F
+ #0,1,0.000,2,-140.0,3,2.72,4,-135.9,5,2.59,6,36.9,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347807,152,88.3,172,1.872,173,2.0,175,1.185,176,294.956,238,3.308*3A
+ #0,1,0.000,2,-140.0,3,2.72,4,-136.0,5,2.59,6,36.8,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347807,152,88.1,172,1.872,173,2.0,175,1.185,176,294.956,238,3.313*39
+ #0,1,0.000,2,-140.2,3,2.73,4,-136.2,5,2.60,6,36.5,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347810,172,1.872,173,2.0,175,1.184,176,294.951*33
+ #0,9,153.4,11,0.0,12,0.21,152,87.6,238,3.318*08
+ #0,1,0.000,2,-140.4,3,2.73,4,-136.3,5,2.60,6,36.4,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347810,152,87.3,172,1.872,173,2.0,175,1.184,176,294.949,238,3.328*35
+ #0,1,0.000,2,-140.6,3,2.73,4,-136.5,5,2.61,6,36.2,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.214,146,0.347813,152,87.3,172,1.872,173,2.0,175,1.184,176,294.950,238,3.333*05
+ #0,1,0.000,2,-141.0,3,2.74,4,-136.8,5,2.61,6,36.0,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.222,146,0.347813,152,86.9,172,1.872,173,2.0,175,1.184,176,294.951,238,3.337*31
+ #0,1,0.000,2,-141.4,3,2.74,4,-137.1,5,2.61,6,35.9,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.229,146,0.347815,152,86.4,172,1.872,173,2.0,175,1.184,176,294.951,238,3.337*36
+ #0,1,0.000,2,-141.8,3,2.74,4,-137.4,5,2.62,6,35.7,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.212,146,0.347815,152,85.8,172,1.872,173,2.0,175,1.184,176,294.953,238,3.341*35
+ #0,1,0.000,2,-142.3,3,2.75,4,-137.8,5,2.62,6,35.5,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.195,146,0.347815,152,85.1,172,1.872,173,2.0,175,1.185,176,294.954,238,3.345*35
+ #0,1,0.000,2,-142.8,3,2.75,4,-138.2,5,2.62,6,35.3,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.178,146,0.347818,152,84.4,172,1.872,173,2.0,175,1.185,176,294.954,238,3.349*3B
+ #0,1,0.000,2,-143.3,3,2.75,4,-138.6,5,2.63,6,35.0,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.161,146,0.347818,152,83.6,172,1.872,173,2.0,175,1.185,176,294.954,238,3.353*3B
+ #0,1,0.000,2,-143.8,3,2.75,4,-139.1,5,2.63,6,34.8,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.144,146,0.347821,152,82.7,172,1.872,173,2.0,175,1.186,176,294.955,238,3.357*3B
+ #0,1,0.000,2,-144.6,3,2.76,4,-139.9,5,2.63,6,34.4,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.158,146,0.347821,152,81.9,172,1.872,173,2.0,175,1.186,176,294.962,238,3.360*37
+ #0,1,0.000,2,-144.6,3,2.76,4,-139.9,5,2.63,6,34.4,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.158,146,0.347821,152,80.3,172,1.872,173,2.0,175,1.186,176,294.962,238,3.364*3A
+ #0,9,153.4,11,0.0,12,0.16,152,80.3,238,3.370*00
+ #0,1,0.000,2,-145.3,3,2.76,4,-140.5,5,2.64,6,34.1,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.146,146,0.347824,152,78.9,172,1.872,173,2.0,175,1.185,176,294.964,238,3.370*3B
+ #0,1,0.000,2,-145.5,3,2.76,4,-140.8,5,2.64,6,33.9,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419950,50,0.0,51,0.146,146,0.347827,152,78.4,172,1.872,173,2.0,175,1.185,176,294.962,238,3.375*32
+ #0,1,0.000,2,-145.6,3,2.76,4,-141.0,5,2.64,6,33.7,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.145,146,0.347827,152,77.9,172,1.872,173,2.0,175,1.185,176,294.965,238,3.377*30
+ #0,1,0.000,2,-145.7,3,2.76,4,-141.2,5,2.64,6,33.6,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.144,146,0.347827,152,77.5,172,1.872,173,2.0,175,1.184,176,294.964,238,3.379*33
+ #0,1,0.000,2,-145.7,3,2.76,4,-141.4,5,2.64,6,33.5,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.168,146,0.347830,152,77.3,172,1.872,173,2.0,175,1.184,176,294.962,238,3.381*3A
+ #0,1,0.000,2,-145.7,3,2.76,4,-141.5,5,2.64,6,33.3,9,153.4,10,0.00,11,0.0,12,0.19,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.188,146,0.347830,152,77.0,172,1.872,173,2.0,175,1.183,176,294.963,238,3.383*3A
+ #0,1,0.000,2,-145.7,3,2.76,4,-141.6,5,2.65,6,33.2,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.205,146,0.347833,152,76.9,172,1.872,173,2.0,175,1.183,176,294.963,238,3.385*3A
+ #0,1,0.000,2,-145.6,3,2.76,4,-141.6,5,2.65,6,33.1,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.212,146,0.347833,152,76.8,172,1.872,173,2.0,175,1.183,176,294.963,238,3.387*3D
+ #0,1,0.000,2,-145.6,3,2.76,4,-141.7,5,2.65,6,32.9,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.218,146,0.347833,152,76.8,172,1.872,173,2.0,175,1.183,176,294.959,238,3.388*09
+ #0,1,0.000,2,-145.5,3,2.76,4,-141.7,5,2.65,6,32.8,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.222,146,0.347836,152,76.7,172,1.872,173,2.0,175,1.184,176,294.958,238,3.390*37
+ #0,1,0.000,2,-145.4,3,2.77,4,-141.8,5,2.65,6,32.6,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.218,146,0.347839,152,76.6,172,1.872,173,2.0,175,1.184,176,294.961,238,3.390*3B
+ #0,9,153.4,11,0.0,12,0.22,152,76.4,238,3.392*05
+ #0,1,0.000,2,-145.3,3,2.77,4,-141.8,5,2.65,6,32.4,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.208,146,0.347839,152,76.3,172,1.872,173,2.0,175,1.184,176,294.959,238,3.395*34
+ #0,1,0.000,2,-145.3,3,2.77,4,-141.9,5,2.65,6,32.3,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.197,146,0.347839,152,76.2,172,1.872,173,2.0,175,1.184,176,294.960,238,3.396*3E
+ #0,1,0.000,2,-145.2,3,2.77,4,-141.9,5,2.66,6,32.2,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.197,146,0.347841,152,76.2,172,1.872,173,2.0,175,1.184,176,294.959,238,3.398*37
+ #0,1,0.000,2,-145.1,3,2.77,4,-141.9,5,2.66,6,32.1,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.201,146,0.347841,152,76.2,172,1.872,173,2.0,175,1.184,176,294.959,238,3.400*3D
+ #0,1,0.000,2,-144.9,3,2.77,4,-141.9,5,2.66,6,32.0,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347844,172,1.872,173,2.0,175,1.184,176,294.958*3D
+ #0,1,0.000,2,-144.8,3,2.78,4,-141.9,5,2.66,6,31.9,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347844,152,76.2,172,1.872,173,2.0,175,1.184,176,294.958,238,3.402*30
+ #0,1,0.000,2,-144.6,3,2.78,4,-141.8,5,2.66,6,31.8,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.207,146,0.347844,152,76.3,172,1.872,173,2.0,175,1.184,176,294.958,238,3.404*38
+ #0,1,0.000,2,-144.4,3,2.78,4,-141.8,5,2.66,6,31.7,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.214,146,0.347847,152,76.4,172,1.872,173,2.0,175,1.184,176,294.957,238,3.406*3E
+ #0,1,0.000,2,-144.2,3,2.78,4,-141.7,5,2.67,6,31.6,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.220,146,0.347847,152,76.5,172,1.872,173,2.0,175,1.184,176,294.958,238,3.408*33
+ #0,1,0.000,2,-144.0,3,2.79,4,-141.6,5,2.67,6,31.5,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.226,146,0.347850,152,76.6,172,1.872,173,2.0,175,1.185,176,294.958,238,3.410*3A
+ #0,1,0.000,2,-143.8,3,2.79,4,-141.5,5,2.67,6,31.4,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419950,50,0.0,51,0.244,146,0.347850,152,76.8,172,1.872,173,2.0,175,1.184,176,294.957,238,3.413*36
+ #0,1,0.000,2,-143.7,3,2.79,4,-141.5,5,2.67,6,31.3,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.244,146,0.347853,152,77.0,172,1.872,173,2.0,175,1.185,176,294.959,238,3.413*33
+ #0,9,153.4,11,0.0,12,0.24,152,77.1,238,3.416*0C
+ #0,1,0.000,2,-143.6,3,2.79,4,-141.5,5,2.67,6,31.1,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.244,146,0.347853,152,77.1,172,1.872,173,2.0,175,1.185,176,294.960,238,3.417*3E
+ #0,1,0.000,2,-143.6,3,2.80,4,-141.5,5,2.67,6,31.0,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.244,146,0.347856,152,77.0,172,1.872,173,2.0,175,1.184,176,294.959,238,3.418*38
+ #0,1,0.000,2,-143.7,3,2.80,4,-141.5,5,2.67,6,30.9,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.237,146,0.347856,152,77.0,172,1.872,173,2.0,175,1.184,176,294.959,238,3.419*01
+ #0,1,0.000,2,-143.8,3,2.80,4,-141.6,5,2.67,6,30.8,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.231,146,0.347856,152,77.0,172,1.872,173,2.0,175,1.184,176,294.959,238,3.420*34
+ #0,1,0.000,2,-143.9,3,2.80,4,-141.7,5,2.67,6,30.7,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.231,146,0.347859,152,76.8,172,1.872,173,2.0,175,1.185,176,294.961,238,3.420*30
+ #0,1,0.000,2,-144.0,3,2.80,4,-141.8,5,2.67,6,30.5,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.231,146,0.347859,152,76.6,172,1.872,173,2.0,175,1.185,176,294.961,238,3.421*3C
+ #0,1,0.000,2,-144.1,3,2.81,4,-141.9,5,2.67,6,30.4,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.231,146,0.347862,152,76.4,172,1.872,173,2.0,175,1.184,176,294.959,238,3.421*3C
+ #0,1,0.000,2,-144.3,3,2.81,4,-142.1,5,2.67,6,30.2,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.251,146,0.347862,152,76.1,172,1.872,173,2.0,175,1.185,176,294.962,238,3.422*3A
+ #0,1,0.000,2,-144.4,3,2.81,4,-142.2,5,2.67,6,30.1,9,153.4,10,0.00,11,0.0,12,0.25,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.271,146,0.347862,152,75.9,172,1.872,173,2.0,175,1.185,176,294.963,238,3.422*33
+ #0,1,0.000,2,-144.7,3,2.82,4,-142.5,5,2.67,6,29.8,9,153.4,10,0.00,11,0.0,12,0.27,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.258,146,0.347865,152,75.6,172,1.872,173,2.0,175,1.185,176,294.961,238,3.422*37
+ #0,1,0.000,2,-144.8,3,2.82,4,-142.6,5,2.67,6,29.7,9,153.4,10,0.00,11,0.0,12,0.26,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.252,146,0.347867,152,75.1,172,1.872,173,2.0,175,1.185,176,294.962,238,3.423*39
+ #0,9,153.4,11,0.0,12,0.25,152,74.8,238,3.423*01
+ #0,1,0.000,2,-144.8,3,2.82,4,-142.7,5,2.67,6,29.6,9,153.4,10,0.00,11,0.0,12,0.25,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.245,146,0.347867,152,74.6,172,1.872,173,2.0,175,1.185,176,294.962,238,3.422*3B
+ #0,1,0.000,2,-144.9,3,2.82,4,-142.8,5,2.67,6,29.5,9,153.4,10,0.00,11,0.0,12,0.24,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.238,146,0.347867,152,74.4,172,1.872,173,2.0,175,1.185,176,294.964,238,3.422*39
+ #0,1,0.000,2,-145.0,3,2.83,4,-142.9,5,2.67,6,29.3,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.232,146,0.347870,152,74.2,172,1.872,173,2.0,175,1.184,176,294.963,238,3.422*3D
+ #0,1,0.000,2,-145.0,3,2.83,4,-143.0,5,2.67,6,29.2,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.225,146,0.347870,152,74.1,172,1.872,173,2.0,175,1.185,176,294.962,238,3.421*32
+ #0,1,0.000,2,-145.0,3,2.83,4,-143.0,5,2.67,6,29.1,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.219,146,0.347873,152,73.9,172,1.872,173,2.0,175,1.185,176,294.964,238,3.421*35
+ #0,1,0.000,2,-145.0,3,2.83,4,-143.1,5,2.67,6,29.0,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.219,146,0.347873,152,73.8,172,1.872,173,2.0,175,1.185,176,294.961,238,3.420*32
+ #0,1,0.000,2,-145.0,3,2.83,4,-143.1,5,2.67,6,28.9,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.219,146,0.347873,152,73.8,172,1.872,173,2.0,175,1.185,176,294.962,238,3.418*30
+ #0,1,0.000,2,-145.0,3,2.83,4,-143.1,5,2.67,6,28.8,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.219,146,0.347876,152,73.7,172,1.872,173,2.0,175,1.185,176,294.961,238,3.417*37
+ #0,1,0.000,2,-144.9,3,2.83,4,-143.2,5,2.67,6,28.7,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.219,146,0.347876,152,73.7,172,1.872,173,2.0,175,1.184,176,294.961,238,3.416*03
+ #0,1,0.000,2,-144.8,3,2.82,4,-143.2,5,2.66,6,28.5,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.226,146,0.347879,172,1.872,173,2.0,175,1.185,176,294.962*07
+ #0,1,0.000,2,-144.7,3,2.82,4,-143.2,5,2.66,6,28.4,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.233,146,0.347879,152,73.6,172,1.872,173,2.0,175,1.185,176,294.960,238,3.414*30
+ #0,9,153.4,11,0.0,12,0.23,152,73.7,238,3.411*0E
+ #0,1,0.000,2,-144.7,3,2.82,4,-143.1,5,2.66,6,28.3,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.227,146,0.347882,152,73.7,172,1.872,173,2.0,175,1.185,176,294.960,238,3.409*38
+ #0,1,0.000,2,-144.6,3,2.81,4,-143.1,5,2.66,6,28.2,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.220,146,0.347882,152,73.7,172,1.872,173,2.0,175,1.185,176,294.958,238,3.406*39
+ #0,1,0.000,2,-144.5,3,2.81,4,-143.1,5,2.66,6,28.1,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.214,146,0.347885,152,73.7,172,1.872,173,2.0,175,1.185,176,294.955,238,3.406*34
+ #0,1,0.000,2,-144.4,3,2.81,4,-143.1,5,2.66,6,28.0,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.207,146,0.347885,152,73.9,172,1.872,173,2.0,175,1.185,176,294.955,238,3.402*3C
+ #0,1,0.000,2,-144.3,3,2.80,4,-143.0,5,2.65,6,28.0,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.201,146,0.347885,152,73.9,172,1.872,173,2.0,175,1.185,176,294.955,238,3.402*3E
+ #0,1,0.000,2,-144.2,3,2.80,4,-143.0,5,2.65,6,27.9,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.201,146,0.347888,152,73.9,172,1.872,173,2.0,175,1.185,176,294.955,238,3.399*30
+ #0,1,0.000,2,-144.0,3,2.79,4,-142.9,5,2.65,6,27.8,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.202,146,0.347888,152,74.0,172,1.872,173,2.0,175,1.185,176,294.957,238,3.397*3C
+ #0,1,0.000,2,-143.9,3,2.78,4,-142.9,5,2.65,6,27.7,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.202,146,0.347891,152,74.1,172,1.872,173,2.0,175,1.185,176,294.955,238,3.394*34
+ #0,1,0.000,2,-143.5,3,2.77,4,-142.7,5,2.64,6,27.6,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907349,49,18.419949,50,0.0,51,0.178,146,0.347891,152,74.2,172,1.872,173,2.0,175,1.185,176,294.957,238,3.391*32
+ #0,1,0.000,2,-143.3,3,2.76,4,-142.6,5,2.64,6,27.5,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.185,146,0.347894,152,74.6,172,1.872,173,2.0,175,1.185,176,294.958,238,3.388*39
+ #0,1,0.000,2,-143.0,3,2.75,4,-142.5,5,2.64,6,27.5,9,153.4,10,0.00,11,0.0,12,0.19,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907349,49,18.419949,50,0.0,51,0.193,146,0.347894,152,74.8,172,1.872,173,2.0,175,1.185,176,294.960,238,3.382*32
+ #0,9,153.4,11,0.0,12,0.19,152,75.1,238,3.379*0E
+ #0,1,0.000,2,-142.8,3,2.74,4,-142.3,5,2.63,6,27.4,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.200,146,0.347896,152,75.4,172,1.872,173,2.0,175,1.185,176,294.961,238,3.375*3F
+ #0,1,0.000,2,-142.4,3,2.73,4,-142.1,5,2.63,6,27.4,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.180,146,0.347896,152,75.7,172,1.872,173,2.0,175,1.185,176,294.960,238,3.372*32
+ #0,1,0.000,2,-142.1,3,2.72,4,-141.9,5,2.63,6,27.4,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.159,146,0.347896,152,76.1,172,1.872,173,2.0,175,1.185,176,294.961,238,3.368*38
+ #0,1,0.000,2,-141.7,3,2.71,4,-141.7,5,2.63,6,27.4,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.156,146,0.347899,152,76.6,172,1.872,173,2.0,175,1.184,176,294.962,238,3.365*38
+ #0,1,0.000,2,-141.3,3,2.70,4,-141.5,5,2.62,6,27.4,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907349,49,18.419949,50,0.0,51,0.156,146,0.347899,152,77.1,172,1.872,173,2.0,175,1.184,176,294.960,238,3.361*3E
+ #0,1,0.000,2,-141.0,3,2.69,4,-141.2,5,2.62,6,27.3,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.159,146,0.347902,152,77.6,172,1.872,173,2.0,175,1.184,176,294.960,238,3.358*3C
+ #0,1,0.000,2,-140.6,3,2.68,4,-141.0,5,2.62,6,27.3,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.149,146,0.347902,152,78.1,172,1.872,173,2.0,175,1.185,176,294.961,238,3.355*3F
+ #0,1,0.000,2,-140.2,3,2.67,4,-140.7,5,2.62,6,27.3,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419949,50,0.0,51,0.138,146,0.347902,152,78.1,172,1.872,173,2.0,175,1.184,176,294.959,238,3.351*0C
+ #0,1,0.000,2,-139.4,3,2.65,4,-140.2,5,2.61,6,27.3,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419949,50,0.0,51,0.155,146,0.347905,172,1.872,173,2.0,175,1.184,176,294.957*01
+ #0,1,0.000,2,-139.1,3,2.64,4,-139.9,5,2.61,6,27.3,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.168,146,0.347908,152,79.7,172,1.872,173,2.0,175,1.185,176,294.959,238,3.341*3E
+ #0,9,153.4,11,0.0,12,0.17,152,80.2,238,3.341*02
+ #0,1,0.000,2,-138.7,3,2.63,4,-139.7,5,2.61,6,27.3,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.168,146,0.347908,152,80.2,172,1.872,173,2.0,175,1.184,176,294.959,238,3.338*3D
+ #0,1,0.000,2,-138.4,3,2.62,4,-139.4,5,2.60,6,27.3,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.168,146,0.347908,152,80.7,172,1.872,173,2.0,175,1.185,176,294.959,238,3.335*34
+ #0,1,0.000,2,-138.1,3,2.62,4,-139.2,5,2.60,6,27.3,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.161,146,0.347911,152,81.6,172,1.872,173,2.0,175,1.185,176,294.957,238,3.335*39
+ #0,1,0.000,2,-137.9,3,2.61,4,-139.0,5,2.60,6,27.2,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.153,146,0.347911,152,81.6,172,1.872,173,2.0,175,1.185,176,294.956,238,3.328*05
+ #0,1,0.000,2,-137.6,3,2.60,4,-138.8,5,2.60,6,27.2,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.146,146,0.347914,152,82.0,172,1.872,173,2.0,175,1.185,176,294.956,238,3.325*3F
+ #0,1,0.000,2,-137.4,3,2.59,4,-138.6,5,2.59,6,27.2,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.159,146,0.347914,152,82.4,172,1.872,173,2.0,175,1.185,176,294.955,238,3.325*3A
+ #0,1,0.000,2,-137.2,3,2.58,4,-138.5,5,2.59,6,27.2,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.169,146,0.347914,152,82.7,172,1.872,173,2.0,175,1.185,176,294.955,238,3.322*3A
+ #0,1,0.000,2,-137.0,3,2.58,4,-138.3,5,2.59,6,27.1,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.179,146,0.347917,152,83.1,172,1.872,173,2.0,175,1.185,176,294.957,238,3.319*32
+ #0,1,0.000,2,-136.8,3,2.56,4,-138.1,5,2.58,6,27.1,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.199,146,0.347920,152,83.4,172,1.872,173,2.0,175,1.184,176,294.953,238,3.316*3C
+ #0,1,0.000,2,-136.7,3,2.55,4,-138.0,5,2.58,6,27.0,9,153.4,10,0.00,11,0.0,12,0.20,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.208,146,0.347920,152,83.9,172,1.872,173,2.0,175,1.184,176,294.953,238,3.312*39
+ #0,1,0.000,2,-136.7,3,2.55,4,-137.9,5,2.58,6,27.0,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.43,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.218,146,0.347920,152,84.1,172,1.872,173,2.0,175,1.184,176,294.953,238,3.306*35
+ #0,1,0.000,2,-136.7,3,2.54,4,-137.9,5,2.57,6,26.9,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.225,146,0.347922,152,84.2,172,1.872,173,2.0,175,1.185,176,294.956,238,3.303*3F
+ #0,9,153.4,11,0.0,12,0.22,152,84.3,238,3.299*05
+ #0,1,0.000,2,-136.8,3,2.53,4,-137.9,5,2.57,6,26.9,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.228,146,0.347922,152,84.3,172,1.872,173,2.0,175,1.184,176,294.958,238,3.296*39
+ #0,1,0.000,2,-137.0,3,2.52,4,-137.9,5,2.57,6,26.8,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.228,146,0.347925,152,84.2,172,1.872,173,2.0,175,1.185,176,294.957,238,3.292*3E
+ #0,1,0.000,2,-137.1,3,2.52,4,-138.0,5,2.57,6,26.7,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.229,146,0.347925,152,84.1,172,1.872,173,2.0,175,1.185,176,294.957,238,3.288*3E
+ #0,1,0.000,2,-137.4,3,2.51,4,-138.1,5,2.56,6,26.6,9,153.4,10,0.00,11,0.0,12,0.23,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.229,146,0.347925,152,83.9,172,1.872,173,2.0,175,1.184,176,294.957,238,3.284*3A
+ #0,1,0.000,2,-137.7,3,2.50,4,-138.2,5,2.56,6,26.5,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.223,146,0.347928,152,83.6,172,1.872,173,2.0,175,1.185,176,294.958,238,3.280*3B
+ #0,1,0.000,2,-138.0,3,2.49,4,-138.4,5,2.56,6,26.4,9,153.4,10,0.00,11,0.0,12,0.22,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.216,146,0.347928,152,83.3,172,1.872,173,2.0,175,1.185,176,294.958,238,3.276*36
+ #0,1,0.000,2,-138.3,3,2.48,4,-138.6,5,2.55,6,26.3,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.210,146,0.347931,152,83.3,172,1.872,173,2.0,175,1.185,176,294.959,238,3.272*0A
+ #0,1,0.000,2,-138.6,3,2.47,4,-138.8,5,2.55,6,26.1,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.210,146,0.347931,152,82.5,172,1.872,173,2.0,175,1.184,176,294.959,238,3.267*3E
+ #0,1,0.000,2,-138.9,3,2.46,4,-139.0,5,2.55,6,26.0,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.211,146,0.347931,152,82.5,172,1.872,173,2.0,175,1.184,176,294.957,238,3.263*03
+ #0,1,0.000,2,-139.6,3,2.45,4,-139.4,5,2.54,6,25.8,9,153.4,10,0.00,11,0.0,12,0.21,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.185,146,0.347934,152,82.1,172,1.872,173,2.0,175,1.184,176,294.955,238,3.258*34
+ #0,1,0.000,2,-139.8,3,2.44,4,-139.6,5,2.53,6,25.7,9,153.4,10,0.00,11,0.0,12,0.18,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.172,146,0.347937,152,81.2,172,1.872,173,2.0,175,1.184,176,294.955,238,3.258*30
+ #0,9,153.4,11,0.0,12,0.17,152,80.8,238,3.249*01
+ #0,1,0.000,2,-140.1,3,2.43,4,-139.8,5,2.53,6,25.6,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.193,48,-33.907350,49,18.419948,50,0.0,51,0.172,146,0.347937,152,80.5,172,1.872,173,2.0,175,1.184,176,294.956,238,3.244*39
+ #0,1,0.000,2,-140.3,3,2.42,4,-139.9,5,2.53,6,25.5,9,153.4,10,0.00,11,0.0,12,0.17,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.40,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.173,146,0.347937,152,80.1,172,1.872,173,2.0,175,1.184,176,294.955,238,3.240*3B
+ #0,1,0.000,2,-140.5,3,2.41,4,-140.1,5,2.52,6,25.4,9,153.4,10,0.00,11,0.0,12,0.16,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.160,146,0.347940,152,79.8,172,1.872,173,2.0,175,1.184,176,294.956,238,3.235*34
+ #0,1,0.000,2,-140.7,3,2.40,4,-140.3,5,2.52,6,25.3,9,153.4,10,0.00,11,0.0,12,0.15,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.147,146,0.347940,152,79.5,172,1.872,173,2.0,175,1.184,176,294.959,238,3.231*32
+ #0,1,0.000,2,-140.9,3,2.39,4,-140.4,5,2.52,6,25.2,9,153.4,10,0.00,11,0.0,12,0.13,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.134,146,0.347943,152,79.2,172,1.872,173,2.0,175,1.185,176,294.960,238,3.227*3E
+ #0,1,0.000,2,-141.1,3,2.38,4,-140.5,5,2.51,6,25.1,9,153.4,10,0.00,11,0.0,12,0.13,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.135,146,0.347943,152,78.9,172,1.872,173,2.0,175,1.185,176,294.962,238,3.222*3B
+ #0,1,0.000,2,-141.3,3,2.37,4,-140.7,5,2.51,6,25.0,9,153.4,10,0.00,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.41,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.135,146,0.347943,172,1.872,173,2.0,175,1.185,176,294.964*3A
+ #0,1,0.000,2,-141.5,3,2.36,4,-140.8,5,2.51,6,24.9,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.136,146,0.347946,152,78.7,172,1.872,173,2.0,175,1.185,176,294.963,238,3.218*31
+ #0,1,0.000,2,-141.8,3,2.35,4,-141.0,5,2.50,6,24.7,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.137,146,0.347948,152,78.4,172,1.872,173,2.0,175,1.185,176,294.969,238,3.214*33
+ #0,1,0.000,2,-141.9,3,2.34,4,-141.2,5,2.50,6,24.6,9,153.4,10,0.00,11,0.0,12,0.14,13,153.4,17,-3.00,18,0.51,19,0.28,20,18.42,22,1.192,48,-33.907350,49,18.419948,50,0.0,51,0.117,146,0.347948,152,77.9,172,1.872,173,2.0,175,1.185,176,294.969,238,3.209*3C
diff --git a/java/com.sap.sailing.expeditionconnector.test/resources/UsrChannels.h b/java/com.sap.sailing.expeditionconnector.test/resources/UsrChannels.h
new file mode 100755
index 00000000000..8d3a133bb5e
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector.test/resources/UsrChannels.h
@@ -0,0 +1,393 @@
+#pragma once
+
+// All directions in magnetic
+
+// multiboat channels
+
+enum ExChannels
+{
+ ExUtc, // Microsoft DATE type,utc system time
+ ExBsp,
+ ExAwa,
+ ExAws,
+ ExTwa, // #4
+ ExTws, // #5
+ ExTwd, // #6
+
+ ExRudderFwd,
+ ExDeltaTargBsp,
+ ExCourse,
+
+ ExLwy, // #10
+ ExSet,
+ ExDrift,
+ ExHdg, // #13
+ ExAirTemp,
+ ExSeaTemp,
+ ExBaro,
+ ExDepth, // metres
+ ExRoll,
+ ExPitch,
+ ExRudder, // #20
+ ExTab,
+ ExForestayLoad,
+ ExDownhaulLoad,
+ ExMastAngle,
+ ExForestayLen,
+ ExMast,
+ ExStbdLoadCell,
+ ExPortLoadCell,
+ ExRake,
+ ExVolts, // #30
+ ExVmg,
+ ExROT,
+ ExLayDistOnStrb,
+ ExLayTimeOnStrb,
+ ExLayPortBear,
+ ExLayDistOnPort,
+ ExLayTimeOnPort,
+ ExLayStrbBear,
+ ExGpsQuality, // 0 Bad, 1 Autonomous, 2 Differential, 3 p-code, 4,5 Rtk, 6 dr, if change this, need to change CPort::NmeaAPB()
+ ExGpsHDOP, // #40
+ ExGpsPDOP,
+ ExGpsVDOP,
+ ExGpsNumber, // Number of satellites in active constellation
+ ExGpsAge, // Age of differential data
+ ExGpsAltitude, // antenna height
+ ExGpsGeoidSeparation,
+ ExGpsMode, // 0 = 1D, 1 = 2D, 2 = 3D, 3 = Auto, 6 = error
+
+ ExLat, // #48 // if add GPS vars, extend CCore::IsGPSvar()
+ ExLon, // #49
+ ExCog, // #50
+ ExSog, // #51
+
+ ExDiffRefStn,
+
+ ExTargTwaN,
+ ExTargBspN,
+ ExTargVmg,
+ ExTargRoll,
+ ExPolarBsp,
+ ExPolarBspPercent,
+ ExPolarRoll,
+
+ ExErrorCode, // #60
+
+ ExStrbRunner,
+ ExPortRunner,
+
+ ExPolarBspN,
+ ExPolarBspPercentN,
+
+ ExTargTwaLwy, // target twa without leeway
+
+ ExVmgPercent,
+
+ ExVang,
+ ExTraveller,
+ ExMainSheet,
+
+ ExPolVmcToMark, // #70 // vmc if headed at mark
+
+ ExKeelAngle,
+ ExKeelHeight,
+ ExBoard,
+ ExOilPressure,
+ ExRPM1,
+ ExRPM2,
+
+ ExBoardP,
+ ExBoardS,
+
+ ExOppTrack,
+ ExDistFinish, // #80
+
+ ExStartTimeToPort,
+ ExStartTimeToStrb,
+ ExLineSquareWind,
+ ExStartDistToLine,
+ ExStartRchTimeToLine, // time to reach into line
+ ExStartRchDistToLine,
+ ExStartRchBspToLine,
+
+ ExMarkTime,
+ ExNextMarkTimeOnPort,
+ ExNextMarkTimeOnStrb, // #90
+
+ ExXte,
+ ExVmc,
+
+ ExMagVar,
+
+ ExGwd, // #94
+ ExGws, // #95
+
+ ExLayDist, // distance to layline we are heading to
+ ExLayTime, // time to layline
+ ExLayBear, // bearing of that layline
+
+ ExVmcPercent,
+ ExPolVmc, // #100
+ ExOptVmc,
+ ExOptVmcHdg,
+ ExOptVmcTwa,
+ ExDeltaTargTwa,
+
+ ExMarkRng,
+ ExMarkBrg,
+ ExMarkGpsTime,
+ ExMarkTwa,
+
+ ExPredSet,
+ ExPredDrift, // #110
+
+ ExNextMarkRng,
+ ExNextMarkBrg,
+ ExNextMarkTwa,
+
+ ExRadarRng,
+ ExRadarBrg,
+ ExStartDistBelowLineStern,
+
+ ExAlt0, // alternating number channels must be consecutive
+ ExAlt1,
+ ExAlt2,
+ ExAlt3, // #120
+ ExAlt4,
+ ExAlt5,
+ ExAlt6,
+ ExAlt7,
+ ExAlt8,
+ ExAlt9,
+// ExAltNum = ExAlt9 - ExAlt0 + 1 // defined in CoreMem.h
+ ExAltMaxId = ExAlt9, // alternating channels must be consecutive
+
+ ExNextMarkPolTime,
+
+ ExStartLineBiasDeg,
+ ExStartLineBiasLen,
+
+ ExStartLayPortBear, // #130 // laylines for start line
+ ExStartLayStrbBear,
+
+ ExNextMarkAwa,
+ ExNextMarkAws,
+
+ ExStartRSTime, // turning to right, ending up on starboard
+ ExStartRPTime, // turning to right, ending up on port
+ ExStartLSTime, // turning to left, ending up on starboard
+ ExStartLPTime, // turning to left, ending up on port
+
+ ExGpsDistToRaceNote,
+ ExGpsTimeToRaceNote,
+
+ ExLogBsp, // #140
+ ExLogSog,
+ ExStartGpsTimeToLine,
+ ExStartGpsTimeToBurn,
+ ExTargTwaS, // Start
+ ExTargBspS, // Start
+
+ ExGpsTime, // #146
+
+ ExTwdPlus90, // Twd + 90
+ ExTwdLess90, // Twd - 90
+
+ ExShadow,
+ ExShadowOppTack, // #150
+
+ ExDownhaulLoad2,
+
+ ExTackAngle,
+ ExTackAnglePolar,
+
+ ExTargAwa,
+
+ ExStartTimeBurnStrbX, // time to burn when tack onto starboard end starboard layline and sail to 20s from line
+ ExStartTimeBurnPortX, // offset for the start stbd layline to the pin
+ ExStartLayTimeP,
+ ExStartLayTimeS,
+
+ ExMarkSet,
+ ExMarkDrift, // #160
+
+ ExMarkLat,
+ ExMarkLon,
+ ExStartPortEndLat, // ends of line
+ ExStartPortEndLon,
+ ExStartStrbEndLat,
+ ExStartStrbEndLon,
+
+ ExGpsHPE,
+ ExHumidity,
+ ExLeadPort,
+ ExLeadStbd, // #170
+ ExBackstay,
+
+ ExUser0, // #172: RakeDeg (Rake in degrees) // user channels must be consecutive
+ ExUser1, // #173: DflectrPP (Deflector percentage)
+ ExUser2, // #174: TG Heel (Target Heel)
+ ExUser3, // #175: ForestayPres (Forestay Pressure)
+ ExUser4, // #176: DflectrMM (Deflector in mm)
+ ExUser5,
+ ExUser6,
+ ExUser7,
+ ExUser8, // #180
+ ExUser9,
+ ExUser10,
+ ExUser11,
+ ExUser12,
+ ExUser13,
+ ExUser14,
+ ExUser15,
+ ExUser16,
+ ExUser17,
+ ExUser18, // #190
+ ExUser19,
+ ExUser20,
+ ExUser21,
+ ExUser22,
+ ExUser23,
+ ExUser24,
+ ExUser25,
+ ExUser26,
+ ExUser27,
+ ExUser28, // #200
+ ExUser29,
+ ExUser30,
+ ExUser31,
+ ExUserMax = ExUser31, // user channels must be consecutive
+
+ ExStartTimeToGun,
+ ExStartTimeToLine,
+ ExStartTimeToBurn,
+ ExStartDistBelowLine,
+ ExStartDistBelowLineGun,
+
+ ExGateTimeOnPort, // this is to the gate mark
+ ExGateDistOnStrb, // #210
+ ExGateTimeOnStrb,
+ ExGateDistOnPort,
+
+ ExGateSpotTimeOnStrb,
+ ExGateSpotTimeOnPort,
+
+ ExLayPortBearUp,
+ ExLayStrbBearUp,
+ ExLayPortBearDn,
+ ExLayStrbBearDn,
+
+ ExTideLayPortTimeOnPort,
+ ExTideLayPortTimeOnStbd, // #220
+ ExTideLayStbdTimeOnPort,
+ ExTideLayStbdTimeOnStbd,
+ ExTideLayPortTime,
+ ExTideLayStbdTime,
+
+ ExMaxLayPortBear,
+ ExMinLayPortBear,
+ ExMaxLayStrbBear,
+ ExMinLayStrbBear,
+
+ ExTwdLayMark,
+ ExTwdLayMarkOpp, // #230 // lay on other board
+
+ ExDeltaBspSog,
+ ExDeltaHdgCog,
+
+ ExLayPortRatio,
+ ExLayStrbRatio,
+
+ ExFourierTwd,
+ ExFourierTws,
+
+ ExTargTwaP,
+ ExTargBspP,
+
+ ExTwdTarg,
+
+ ExPolCustom1, // #240 // these need to be in this order : see CCore::DerivedPolarNumbers()
+ ExPolCustom2,
+ ExPolCustom3,
+ ExPolCustom4,
+ ExPolCustom1PC,
+ ExPolCustom2PC,
+ ExPolCustom3PC,
+ ExPolCustom4PC,
+ ExPolCustom1Targ,
+ ExPolCustom2Targ,
+ ExPolCustom3Targ, // #250
+ ExPolCustom4Targ,
+
+ ExWaveSigHeight, // XDR from Volvo wave sensor
+ ExWaveSigPeriod,
+ ExWaveMaxHeight,
+ ExWaveMaxPeriod,
+
+ ExSlam,
+ ExMotion,
+
+ ExMwa,
+ ExMws,
+ ExBoom, // #260
+
+ ExTargBspPercent,
+ ExHeadingToSteer,
+ ExHeadingToSteerPol,
+
+ ExStartBspToPort,
+ ExStartBspToStrb,
+ ExStartBspOnPort,
+ ExStartBspOnStrb,
+
+ ExTwdTwist,
+
+ ExSailNow,
+ ExSailMark, // #270
+ ExSailNext,
+
+ ExTackLossVMGSec,
+ ExTackLossVMGMetres,
+
+ ExNearestTide,
+
+ ExTripLog,
+ ExTurnToMark, // delta of cog and bearing to mark
+
+ ExPitchRate,
+ ExRollRate,
+
+ ExDeltaPolBsp,
+ ExDeltaTargRoll, // #280
+
+ ExDeflectorP,
+ ExRudderP,
+ ExRudderS,
+ ExRudderToe,
+ ExBspTransverse,
+ ExForestayInner,
+ ExGateTime, // this is to the gate mark
+
+ ExZeroAhead,
+ ExBrgFromBoat0,
+ ExRngFromBoat0, // #290
+
+ ExDeflectorS,
+ ExBobstay,
+ ExOuthaul,
+
+ ExD0port,
+ ExD0starboard,
+ ExD1port,
+ ExD1starbboard,
+ ExV0port,
+ ExV0starbboard,
+ ExV1port, // #300
+ ExV1starbboard,
+
+ ExStartTimeToPortSimple,
+ ExStartTimeToStrbSimple,
+
+ ExNumChannels
+};
diff --git a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java
index 19bda80b374..709e5294686 100755
--- a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java
+++ b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java
@@ -5,7 +5,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
@@ -15,12 +17,19 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
+import java.util.UUID;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
import org.junit.After;
import org.junit.Before;
@@ -30,18 +39,34 @@ import org.junit.rules.Timeout;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.DeclinationService;
+import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.DegreePosition;
+import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
+import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.GPSFix;
+import com.sap.sailing.domain.common.tracking.GPSFixMoving;
+import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl;
+import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
+import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.test.mock.MockedTrackedRace;
+import com.sap.sailing.expeditionconnector.DeviceRegistry;
import com.sap.sailing.expeditionconnector.ExpeditionListener;
import com.sap.sailing.expeditionconnector.ExpeditionMessage;
+import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.ExpeditionTrackerFactory;
import com.sap.sailing.expeditionconnector.ExpeditionWindTracker;
-import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory;
import com.sap.sailing.expeditionconnector.UDPExpeditionReceiver;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierImpl;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierImpl;
+import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.TimeRange;
+import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -58,9 +83,132 @@ public class UDPExpeditionReceiverTest {
private UDPExpeditionReceiver receiver;
private ExpeditionListener listener;
private Thread receiverThread;
+ private DynamicDeviceRegistry deviceRegistry;
+ private TestSensorFixStore sensorFixStore;
+ private class DynamicDeviceRegistry implements DeviceRegistry {
+ private final Map gpsDeviceIdentifiers;
+ private final Map sensorDeviceIdentifiers;
+
+ public DynamicDeviceRegistry() {
+ gpsDeviceIdentifiers = new HashMap<>();
+ sensorDeviceIdentifiers = new HashMap<>();
+ }
+
+ @Override
+ public ExpeditionGpsDeviceIdentifier getGpsDeviceIdentifier(int boatId) {
+ ExpeditionGpsDeviceIdentifier result = gpsDeviceIdentifiers.get(boatId);
+ if (result == null) {
+ result = new ExpeditionGpsDeviceIdentifierImpl(UUID.randomUUID());
+ gpsDeviceIdentifiers.put(boatId, result);
+ }
+ return result;
+ }
+
+ @Override
+ public ExpeditionSensorDeviceIdentifier getSensorDeviceIdentifier(int boatId) {
+ ExpeditionSensorDeviceIdentifier result = sensorDeviceIdentifiers.get(boatId);
+ if (result == null) {
+ result = new ExpeditionSensorDeviceIdentifierImpl(UUID.randomUUID());
+ sensorDeviceIdentifiers.put(boatId, result);
+ }
+ return result;
+ }
+
+ @Override
+ public SensorFixStore getSensorFixStore() {
+ return sensorFixStore;
+ }
+ }
+
+ private class TestSensorFixStore implements SensorFixStore {
+ private Map> fixesReceived;
+
+ public TestSensorFixStore() {
+ fixesReceived = new HashMap<>();
+ }
+
+ @Override
+ public void loadFixes(Consumer consumer, DeviceIdentifier deviceIdentifier,
+ TimePoint start, TimePoint end, boolean toIsInclusive)
+ throws NoCorrespondingServiceRegisteredException, TransformationException {
+ }
+
+ @Override
+ public void loadFixes(Consumer consumer, DeviceIdentifier deviceIdentifier,
+ TimePoint start, TimePoint end, boolean inclusive, BooleanSupplier isPreemptiveStopped,
+ Consumer progressReporter)
+ throws NoCorrespondingServiceRegisteredException, TransformationException {
+ }
+
+ @Override
+ public void storeFix(DeviceIdentifier device, FixT fix) {
+ List fixesReceivedFromDevice = fixesReceived.get(device);
+ if (fixesReceivedFromDevice == null) {
+ fixesReceivedFromDevice = new ArrayList<>();
+ fixesReceived.put(device, fixesReceivedFromDevice);
+ }
+ fixesReceivedFromDevice.add(fix);
+ }
+
+ @Override
+ public void storeFixes(DeviceIdentifier device, Iterable fixes) {
+ for (final FixT fix : fixes) {
+ storeFix(device, fix);
+ }
+ }
+
+ @Override
+ public void addListener(FixReceivedListener extends Timed> listener, DeviceIdentifier device) {
+ }
+
+ @Override
+ public void removeListener(FixReceivedListener extends Timed> listener) {
+ }
+
+ @Override
+ public void removeListener(FixReceivedListener extends Timed> listener, DeviceIdentifier device) {
+ }
+
+ @Override
+ public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
+ throws TransformationException, NoCorrespondingServiceRegisteredException {
+ return null;
+ }
+
+ @Override
+ public long getNumberOfFixes(DeviceIdentifier device)
+ throws TransformationException, NoCorrespondingServiceRegisteredException {
+ return fixesReceived.get(device) == null ? 0 : fixesReceived.get(device).size();
+ }
+
+ @Override
+ public Map getLastFix(Iterable forDevices)
+ throws TransformationException, NoCorrespondingServiceRegisteredException {
+ final Map result = new HashMap<>();
+ for (final Entry> fixes : fixesReceived.entrySet()) {
+ @SuppressWarnings("unchecked")
+ final List fixList = (List) fixes.getValue();
+ result.put(fixes.getKey(), fixList.get(fixList.size()-1));
+ }
+ return result;
+ }
+
+ @Override
+ public boolean loadOldestFix(Consumer consumer, DeviceIdentifier device,
+ TimeRange timeRangetoLoad) throws NoCorrespondingServiceRegisteredException, TransformationException {
+ return false;
+ }
+
+ @Override
+ public boolean loadYoungestFix(Consumer consumer, DeviceIdentifier device,
+ TimeRange timeRangetoLoad) throws NoCorrespondingServiceRegisteredException, TransformationException {
+ return false;
+ }
+ }
+
@Before
- public void setUp() throws UnknownHostException, SocketException {
+ public void setUp() throws UnknownHostException, SocketException, InterruptedException {
validLines = new String[] {
"#0,1,7.700,2,-39.0,3,23.00,9,319.0,12,1.17,146,40348.390035*37",
"#0,4,-54.9,5,17.69,6,263.1,9,318.0*0D",
@@ -114,9 +262,12 @@ public class UDPExpeditionReceiverTest {
buf = new byte[512];
packet = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), PORT);
socket = new DatagramSocket();
- receiver = new UDPExpeditionReceiver(PORT);
+ sensorFixStore = new TestSensorFixStore();
+ deviceRegistry = new DynamicDeviceRegistry();
+ receiver = new UDPExpeditionReceiver(PORT, deviceRegistry);
receiverThread = new Thread(receiver, "Expedition Receiver");
receiverThread.start();
+ Thread.sleep(10); // to give receiver enough time to start listening on UDP socket
listener = new ExpeditionListener() {
@Override
public void received(ExpeditionMessage message) {
@@ -175,6 +326,32 @@ public class UDPExpeditionReceiverTest {
assertEquals(windFixes.get(0).getPosition(), windFixes.get(2).getPosition());
}
+ @Test
+ public void testBasicPhoenixUDPProperties() throws IOException, InterruptedException {
+ receiver.addListener(listener, /* validMessagesOnly */ true);
+ final InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/Expedition_28Oct17_0820.txt"));
+ final BufferedReader br = new BufferedReader(reader);
+ final List lines = new ArrayList<>();
+ String line;
+ while ((line=br.readLine()) != null) {
+ if (!line.trim().isEmpty()) {
+ lines.add(line.trim());
+ }
+ }
+ br.close();
+ sendAndWaitABit(lines.toArray(new String[0]));
+ assertEquals(159, lines.size());
+ assertEquals(159, messages.size());
+ final ExpeditionGpsDeviceIdentifier gpsDevice = deviceRegistry.getGpsDeviceIdentifier(0);
+ final ExpeditionSensorDeviceIdentifier sensorDevice = deviceRegistry.getSensorDeviceIdentifier(0);
+ assertTrue(sensorFixStore.getNumberOfFixes(gpsDevice) > 0);
+ assertEquals(-33.907350, ((GPSFixMoving) sensorFixStore.getLastFix(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLatDeg(), 0.0001);
+ assertEquals(18.419951, ((GPSFixMoving) sensorFixStore.getLastFix(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLngDeg(), 0.0001);
+ assertTrue(sensorFixStore.getNumberOfFixes(sensorDevice) > 0);
+ final BravoExtendedFix sensorFix = new BravoExtendedFixImpl((DoubleVectorFix) sensorFixStore.getLastFix(Collections.singleton(sensorDevice)).get(sensorDevice));
+ assertEquals(1.872, sensorFix.getRake().getDegrees(), 0.000001);
+ assertEquals(18.4, sensorFix.getRudder().getDegrees(), 0.05);
+ }
@Test
public void testTimeStampConversion() throws IOException, InterruptedException {
@@ -261,7 +438,7 @@ public class UDPExpeditionReceiverTest {
MockedTrackedRace race = new MockedTrackedRace();
DeclinationService declinationService = DeclinationService.INSTANCE;
ExpeditionWindTracker windTracker = new ExpeditionWindTracker(race, declinationService, receiver,
- (ExpeditionWindTrackerFactory) ExpeditionWindTrackerFactory.getInstance());
+ (ExpeditionTrackerFactory) ExpeditionTrackerFactory.getInstance());
receiver.addListener(listener, /* validMessagesOnly */ true);
receiver.addListener(windTracker, /* validMessagesOnly */ true);
String[] lines = new String[validLines.length+1];
diff --git a/java/com.sap.sailing.expeditionconnector/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector/META-INF/MANIFEST.MF
index 07ea47da229..137a3878953 100644
--- a/java/com.sap.sailing.expeditionconnector/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.expeditionconnector/META-INF/MANIFEST.MF
@@ -6,13 +6,18 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: com.sap.sailing.expeditionconnector,
- com.sap.sailing.expeditionconnector.impl;x-friends:="com.sap.sailing.server.test"
+ com.sap.sailing.expeditionconnector.impl;x-friends:="com.sap.sailing.server.test,com.sap.sailing.expeditionconnector.persistence"
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.declination,
com.sap.sailing.udpconnector,
com.sap.sailing.domain.common,
com.sap.sse.common,
- com.sap.sse
+ com.sap.sse,
+ com.sap.sailing.domain.shared.android,
+ com.sap.sailing.server.gateway.serialization.shared.android,
+ com.sap.sailing.expeditionconnector.common,
+ com.sap.sailing.expeditionconnector.persistence
Bundle-ActivationPolicy: lazy
-Import-Package: org.osgi.framework
+Import-Package: org.osgi.framework,
+ org.osgi.util.tracker;version="1.5.1"
Bundle-Activator: com.sap.sailing.expeditionconnector.impl.Activator
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/DeviceRegistry.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/DeviceRegistry.java
new file mode 100755
index 00000000000..9f918719a8c
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/DeviceRegistry.java
@@ -0,0 +1,27 @@
+package com.sap.sailing.expeditionconnector;
+
+import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
+
+/**
+ * Makes accessible a mapping from "Expedition" boat IDs to device UUIDs.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ */
+public interface DeviceRegistry {
+ /**
+ * If a device is registered for the boat ID {@code boatId}, a non-{@code null} device identifier is returned that
+ * represents a virtual device for the GPS data received from that boat ID. Otherwise, {@code null} is returned.
+ */
+ ExpeditionGpsDeviceIdentifier getGpsDeviceIdentifier(int boatId);
+
+ /**
+ * If a device is registered for the boat ID {@code boatId}, a non-{@code null} device identifier is returned that
+ * represents a virtual device for the additional non-GPS and non-wind sensor data received from that boat ID.
+ * Otherwise, {@code null} is returned.
+ */
+ ExpeditionSensorDeviceIdentifier getSensorDeviceIdentifier(int boatId);
+
+ SensorFixStore getSensorFixStore();
+}
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionMessage.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionMessage.java
index 4793bd5de57..5d024f16490 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionMessage.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionMessage.java
@@ -49,6 +49,89 @@ public interface ExpeditionMessage extends UDPMessage {
#0,6,344.2,40,2.10,94,350.8,95,2.35*28
#0,4,135.0,5,2.06,6,345.5,94,350.8,95,2.35*05
*/
+ /**
+ * variable ID for boat speed
+ */
+ final int ID_BSP = 1;
+
+ /**
+ * variable ID for apparent wind angle (probably in decimal degrees)
+ */
+ final int ID_AWA = 2;
+
+ /**
+ * variable ID for apparent wind speed (probably in decimal knots)
+ */
+ final int ID_AWS = 3;
+
+ /**
+ * variable ID for true wind angle, relative to keel, in decimal degrees
+ */
+ final int ID_TWA = 4;
+
+ /**
+ * variable ID for true wind speed in decimal knots
+ */
+ final int ID_TWS = 5;
+
+ /**
+ * Variable ID for what Expedition thinks is the true wind direction ("from"), in decimal degrees. Some
+ * users prefer to not enter the current declination for a time / place into their Expedition
+ * client. In this case, the values presented for this key are the magnetic wind direction instead.
+ * They should then be corrected by adding the current declination.
+ */
+ final int ID_TWD = 6;
+
+ /**
+ * variable ID for course
+ */
+ final int ID_COURSE = 9;
+
+ /**
+ * variable ID for leeway
+ */
+ final int ID_LWY = 10;
+
+ /**
+ * variable ID for "set" (?)
+ */
+ final int ID_SET = 11;
+
+ /**
+ * variable ID for drift
+ */
+ final int ID_DRIFT = 12;
+
+ /**
+ * variable ID for magnetic heading, meaning the keel's direction, in decimal degrees
+ */
+ final int ID_HEADING = 13;
+
+ /**
+ * variable ID for depth in meters
+ */
+ final int ID_DEPTH = 17;
+
+ /**
+ * variable ID for roll (heel), probably in degrees
+ */
+ final int ID_ROLL = 18;
+
+ /**
+ * variable ID for pitch, probably in degrees
+ */
+ final int ID_PITCH = 19;
+
+ /**
+ * variable ID for rudder, probably in degrees
+ */
+ final int ID_RUDDER = 20;
+
+ /**
+ * variable ID for forestay load (unit unknown yet)
+ */
+ final int ID_FORESTAY_LOAD = 22;
+
/**
* variable ID for the GPS-measured latitude, in decimal degrees
*/
@@ -67,35 +150,7 @@ public interface ExpeditionMessage extends UDPMessage {
/**
* variable ID for the GPS-measured speed over ground (SoG)
*/
- final int ID_GPS_SOG = 50;
-
- /**
- * variable ID for the GPS-measured time as days since 31.12.1899 UTC, meaning 1.0 is 1.1.1900 0:00:00 UTC
- */
- final int ID_GPS_TIME = 146;
-
- /**
- * variable ID for magnetic heading, meaning the keel's direction, in decimal degrees
- */
- final int ID_HEADING = 13;
-
- /**
- * variable ID for true wind angle, relative to keel, in decimal degrees
- */
- final int ID_TWA = 4;
-
- /**
- * Variable ID for what Expedition thinks is the true wind direction ("from"), in decimal degrees. Some
- * users prefer to not enter the current declination for a time / place into their Expedition
- * client. In this case, the values presented for this key are the magnetic wind direction instead.
- * They should then be corrected by adding the current declination.
- */
- final int ID_TWD = 6;
-
- /**
- * variable ID for true wind speed in decimal knots
- */
- final int ID_TWS = 5;
+ final int ID_GPS_SOG = 51;
/**
* True wind direction over ground ("from") in decimal degrees, cleansed using the GPS device
@@ -106,7 +161,47 @@ public interface ExpeditionMessage extends UDPMessage {
* True wind speed over ground, cleansed using the GPS device
*/
final int ID_GWS = 95;
-
+
+ /**
+ * variable ID for the GPS-measured time as days since 31.12.1899 UTC, meaning 1.0 is 1.1.1900 0:00:00 UTC
+ */
+ final int ID_GPS_TIME = 146;
+
+ /**
+ * variable ID for tack angle, probably in degrees
+ */
+ final int ID_TACK_ANGLE = 152;
+
+ /**
+ * variable ID for rake in degrees
+ */
+ final int ID_RAKE_DEG = 172;
+
+ /**
+ * variable ID for deflector percentage
+ */
+ final int ID_DFLCTR_PP = 173;
+
+ /**
+ * variable ID for target heel angle
+ */
+ final int ID_TG_HEEL = 174;
+
+ /**
+ * variable ID for forestay pressure
+ */
+ final int ID_FORESTAY_PRES = 175;
+
+ /**
+ * variable ID for deflector in millimeters
+ */
+ final int ID_DFLECTR_MM = 176;
+
+ /**
+ * variable ID for target boat speed (P?), presumably in knots
+ */
+ final int ID_TARG_BSP_P = 238;
+
/**
* A message's checksum determines whether the package is to be considered valid.
*/
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionTrackerFactory.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionTrackerFactory.java
new file mode 100755
index 00000000000..630d19832ef
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionTrackerFactory.java
@@ -0,0 +1,264 @@
+package com.sap.sailing.expeditionconnector;
+
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import com.sap.sailing.declination.DeclinationService;
+import com.sap.sailing.domain.base.RaceDefinition;
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+import com.sap.sailing.domain.common.tracking.GPSFixMoving;
+import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
+import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier;
+import com.sap.sailing.domain.tracking.DynamicTrackedRace;
+import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
+import com.sap.sailing.domain.tracking.WindTracker;
+import com.sap.sailing.domain.tracking.WindTrackerFactory;
+import com.sap.sailing.expeditionconnector.impl.Activator;
+import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierImpl;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierImpl;
+import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory;
+
+public class ExpeditionTrackerFactory implements WindTrackerFactory, DeviceRegistry {
+ private static Logger logger = Logger.getLogger(ExpeditionTrackerFactory.class.getName());
+
+ private static ExpeditionTrackerFactory defaultInstance;
+
+ /**
+ * Remembers the wind tracker and the port on which the UDP receiver with which the wind tracker is
+ * registers is listening for incoming Expedition messages.
+ */
+ private final Map windTrackers;
+
+ private final Map windReceivers;
+
+ /**
+ * When one or more device configurations exist then each {@link UDPExpeditionReceiver} created by this factory will
+ * also produce {@link GPSFixMoving} and {@link DoubleVectorFix} fixes and send them to the {@link SensorFixStore},
+ * using device identifiers of type {@link ExpeditionGpsDeviceIdentifier} and
+ * {@link ExpeditionSensorDeviceIdentifier}, respectively, whose inner ID is the
+ * {@link ExpeditionDeviceConfiguration#getDeviceUuid() UUID} of the device configuration.
+ */
+ private final ConcurrentHashMap deviceConfigurations;
+
+ /**
+ * Holds the mappings from Expedition boat IDs (default being 0, counting upwards) to the
+ * {@link ExpeditionDeviceConfiguration} that {@link ExpeditionDeviceConfiguration#getExpeditionBoatId() has this
+ * boat ID}.
+ */
+ private final ConcurrentHashMap devicesPerBoatId;
+
+ private final int defaultPort;
+
+ /**
+ * Cached result from calling {@link SensorFixStoreSupplier#getSensorFixStore()} on any supplier discovered
+ * in the OSGi registry. Refreshed when the sensor fix store supplier changes in the OSGi registry.
+ */
+ private SensorFixStore sensorFixStore;
+
+ private ServiceTracker sensorFixServiceTracker;
+
+ private final MongoObjectFactory mongoObjectFactory;
+
+ public ExpeditionTrackerFactory(SensorFixStore sensorFixStore, DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory) {
+ this.windTrackers = new HashMap();
+ this.sensorFixStore = sensorFixStore;
+ windReceivers = new HashMap();
+ final Activator activator = Activator.getInstance();
+ defaultPort = activator.getExpeditionUDPPort();
+ deviceConfigurations = new ConcurrentHashMap<>();
+ devicesPerBoatId = new ConcurrentHashMap<>();
+ final BundleContext context = activator.getContext();
+ this.mongoObjectFactory = mongoObjectFactory;
+ for (final ExpeditionDeviceConfiguration expeditionDeviceConfigurationLoadedFromDB : domainObjectFactory.getExpeditionDeviceConfigurations()) {
+ addOrReplaceDeviceConfigurationNoPersistence(expeditionDeviceConfigurationLoadedFromDB);
+ }
+ if (context != null) {
+ sensorFixServiceTracker = startListeningForSensorFixStoreSuppliers(context);
+ } else {
+ sensorFixServiceTracker = null;
+ }
+ logger.info("Created "+getClass().getName()+" with default UDP port "+defaultPort);
+ }
+
+ private ServiceTracker startListeningForSensorFixStoreSuppliers(BundleContext context) {
+ final ServiceTracker result = new ServiceTracker<>(context, SensorFixStoreSupplier.class,
+ new ServiceTrackerCustomizer() {
+ @Override
+ public SensorFixStoreSupplier addingService(ServiceReference reference) {
+ final SensorFixStoreSupplier service = Activator.getInstance().getContext().getService(reference);
+ sensorFixStore = service.getSensorFixStore();
+ return service;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference reference,
+ SensorFixStoreSupplier service) {
+ }
+
+ @Override
+ public void removedService(ServiceReference reference,
+ SensorFixStoreSupplier service) {
+ // check if there is still a supplier left:
+ final SensorFixStoreSupplier nextService = sensorFixServiceTracker.getService();
+ if (nextService != null) {
+ sensorFixStore = nextService.getSensorFixStore();
+ }
+ }
+ });
+ result.open();
+ return result;
+ }
+
+ public synchronized static ExpeditionTrackerFactory getInstance() {
+ return getInstance(/* sensorFixStore */ null, PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(),
+ PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory());
+ }
+
+ /**
+ * Use this constructor in non-OSGi scenarios such as plain JUnit test environments where the
+ * {@link SensorFixStoreSupplier} cannot be discovered through the registry.
+ */
+ public static ExpeditionTrackerFactory getInstance(SensorFixStore sensorFixStore, DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory) {
+ if (defaultInstance == null) {
+ defaultInstance = new ExpeditionTrackerFactory(sensorFixStore, domainObjectFactory, mongoObjectFactory);
+ }
+ return defaultInstance;
+ }
+
+ @Override
+ public WindTracker createWindTracker(DynamicTrackedRegatta trackedRegatta, RaceDefinition race,
+ boolean correctByDeclination) throws SocketException {
+ WindTracker result = getExistingWindTracker(race);
+ if (result == null) {
+ DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(race);
+ UDPExpeditionReceiver receiver = getOrCreateWindReceiverOnDefaultPort();
+ result = new ExpeditionWindTracker(trackedRace,
+ correctByDeclination ? DeclinationService.INSTANCE : null, receiver, this);
+ windTrackers.put(race, result);
+ }
+ return result;
+ }
+
+ @Override
+ public WindTracker getExistingWindTracker(RaceDefinition race) {
+ return windTrackers.get(race);
+ }
+
+ public UDPExpeditionReceiver getOrCreateWindReceiverOnDefaultPort() throws SocketException {
+ return getOrCreateWindReceiverForPort(defaultPort);
+ }
+
+ private synchronized UDPExpeditionReceiver getOrCreateWindReceiverForPort(int port) throws SocketException {
+ UDPExpeditionReceiver receiver = windReceivers.get(port);
+ if (receiver == null) {
+ receiver = new UDPExpeditionReceiver(port, this);
+ windReceivers.put(port, receiver);
+ Thread t = new Thread(receiver, "Expedition Wind Receiver on port "+port);
+ t.setDaemon(true);
+ t.start();
+ }
+ return receiver;
+ }
+
+ /**
+ * Notifies the factory that the wind tracker has stopped tracking wind for race. This
+ * will remove the tracker from the respective caches.
+ */
+ synchronized void trackerStopped(RaceDefinition race, ExpeditionWindTracker windTracker) {
+ if (windTrackers.get(race) != windTracker) {
+ throw new IllegalArgumentException("Intenral error: expected to remove wind tracker "+windTracker+
+ ", but another wind tracker "+windTrackers.get(race)+" was registered.");
+ }
+ windTrackers.remove(race);
+ if (windTracker.getReceiver().isStopped()) {
+ UDPExpeditionReceiver receiver = windReceivers.get(windTracker.getReceiver().getPort());
+ if (receiver != windTracker.getReceiver()) {
+ throw new IllegalArgumentException("Internal error: expected to remove wind receiver "+
+ windTracker.getReceiver()+" but found receiver "+receiver);
+ }
+ windReceivers.remove(windTracker.getReceiver().getPort());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ExpeditionWindTrackerFactory [defaultPort=" + defaultPort + "]";
+ }
+
+ public Iterable extends ExpeditionDeviceConfiguration> getDeviceConfigurations() {
+ return Collections.unmodifiableCollection(deviceConfigurations.values());
+ }
+
+ public void addOrReplaceDeviceConfiguration(ExpeditionDeviceConfiguration deviceConfiguration) {
+ addOrReplaceDeviceConfigurationNoPersistence(deviceConfiguration);
+ mongoObjectFactory.storeExpeditionDeviceConfiguration(deviceConfiguration);
+ }
+
+ private void addOrReplaceDeviceConfigurationNoPersistence(ExpeditionDeviceConfiguration deviceConfiguration) {
+ if (deviceConfiguration.getExpeditionBoatId() != null &&
+ devicesPerBoatId.containsKey(deviceConfiguration.getExpeditionBoatId()) &&
+ !devicesPerBoatId.get(deviceConfiguration.getExpeditionBoatId()).equals(deviceConfiguration)) {
+ throw new IllegalStateException("Trying to create an ambiguous Expedition Boat ID mapping: established is "+
+ devicesPerBoatId.get(deviceConfiguration.getExpeditionBoatId())+
+ " and boat ID #"+deviceConfiguration.getExpeditionBoatId()+" therefore cannot be mapped to "+deviceConfiguration+
+ " at the same time.");
+ }
+ final ExpeditionDeviceConfiguration old = deviceConfigurations.put(deviceConfiguration.getDeviceUuid(), deviceConfiguration);
+ if (old != null && old.getExpeditionBoatId() != null) {
+ devicesPerBoatId.remove(old.getExpeditionBoatId());
+ }
+ if (deviceConfiguration.getExpeditionBoatId() != null) {
+ devicesPerBoatId.put(deviceConfiguration.getExpeditionBoatId(), deviceConfiguration);
+ }
+ }
+
+ public void removeDeviceConfiguration(ExpeditionDeviceConfiguration deviceConfiguration) {
+ deviceConfigurations.remove(deviceConfiguration.getDeviceUuid());
+ if (deviceConfiguration.getExpeditionBoatId() != null) {
+ devicesPerBoatId.remove(deviceConfiguration.getExpeditionBoatId());
+ }
+ mongoObjectFactory.removeExpeditionDeviceConfiguration(deviceConfiguration);
+ }
+
+ @Override
+ public ExpeditionGpsDeviceIdentifier getGpsDeviceIdentifier(int boatId) {
+ final ExpeditionDeviceConfiguration deviceConfig = devicesPerBoatId.get(boatId);
+ final ExpeditionGpsDeviceIdentifier result;
+ if (deviceConfig == null) {
+ result = null;
+ } else {
+ result = new ExpeditionGpsDeviceIdentifierImpl(deviceConfig.getDeviceUuid());
+ }
+ return result;
+ }
+
+ @Override
+ public ExpeditionSensorDeviceIdentifier getSensorDeviceIdentifier(int boatId) {
+ final ExpeditionDeviceConfiguration deviceConfig = devicesPerBoatId.get(boatId);
+ final ExpeditionSensorDeviceIdentifier result;
+ if (deviceConfig == null) {
+ result = null;
+ } else {
+ result = new ExpeditionSensorDeviceIdentifierImpl(deviceConfig.getDeviceUuid());
+ }
+ return result;
+ }
+
+ @Override
+ public SensorFixStore getSensorFixStore() {
+ return sensorFixStore;
+ }
+}
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTracker.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTracker.java
index e4b9c2b898d..843a9e4c798 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTracker.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTracker.java
@@ -20,7 +20,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.WindTracker;
/**
- * Can be subscribed to a {@link UDPExpeditionReceiver} and forwards the wind information
+ * Can be subscribed to a {@link UDPExpeditionReceiver} and forwards wind information
* received to the {@link DynamicTrackedRace} passed to the constructor.
*
* @author Axel Uhl (d043530)
@@ -35,7 +35,7 @@ public class ExpeditionWindTracker extends AbstractWindTracker implements Expedi
private final UDPExpeditionReceiver receiver;
- private final ExpeditionWindTrackerFactory factory;
+ private final ExpeditionTrackerFactory factory;
/**
* @param declinationService
@@ -47,7 +47,7 @@ public class ExpeditionWindTracker extends AbstractWindTracker implements Expedi
* calling {@link #stop}, this subscription will be removed again.
*/
public ExpeditionWindTracker(DynamicTrackedRace race, DeclinationService declinationService,
- UDPExpeditionReceiver receiver, ExpeditionWindTrackerFactory factory) {
+ UDPExpeditionReceiver receiver, ExpeditionTrackerFactory factory) {
super(race);
this.lastKnownPositionPerBoatID = new HashMap();
this.declinationService = declinationService;
@@ -60,7 +60,7 @@ public class ExpeditionWindTracker extends AbstractWindTracker implements Expedi
public void stop() {
synchronized (factory) {
receiver.removeListener(this);
- factory.windTrackerStopped(getTrackedRace().getRace(), this);
+ factory.trackerStopped(getTrackedRace().getRace(), this);
}
}
@@ -87,7 +87,7 @@ public class ExpeditionWindTracker extends AbstractWindTracker implements Expedi
try {
Declination declination = declinationService.getDeclination(message.getTimePoint(),
lastKnownPositionPerBoatID.get(message.getBoatID()),
- /* timeoutForOnlineFetchInMilliseconds */5000);
+ /* timeoutForOnlineFetchInMilliseconds */500);
if (declination != null) {
windSpeed = new KnotSpeedWithBearingImpl(windSpeed.getKnots(), new DegreeBearingImpl(
windSpeed.getBearing().getDegrees()).add(declination.getBearingCorrectedTo(message.getTimePoint())));
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTrackerFactory.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTrackerFactory.java
deleted file mode 100755
index b5bc9246921..00000000000
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/ExpeditionWindTrackerFactory.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.sap.sailing.expeditionconnector;
-
-import java.net.SocketException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import com.sap.sailing.declination.DeclinationService;
-import com.sap.sailing.domain.base.RaceDefinition;
-import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
-import com.sap.sailing.domain.tracking.DynamicTrackedRace;
-import com.sap.sailing.domain.tracking.WindTracker;
-import com.sap.sailing.domain.tracking.WindTrackerFactory;
-import com.sap.sailing.expeditionconnector.impl.Activator;
-
-public class ExpeditionWindTrackerFactory implements WindTrackerFactory {
- private static Logger logger = Logger.getLogger(ExpeditionWindTrackerFactory.class.getName());
-
- private static ExpeditionWindTrackerFactory defaultInstance;
-
- /**
- * Remembers the wind tracker and the port on which the UDP receiver with which the wind tracker is
- * registers is listening for incoming Expedition messages.
- */
- private final Map windTrackers;
-
- private final Map windReceivers;
-
- private final int defaultPort;
-
- public ExpeditionWindTrackerFactory() {
- this.windTrackers = new HashMap();
- windReceivers = new HashMap();
- defaultPort = Activator.getInstance().getExpeditionUDPPort();
- logger.info("Created "+getClass().getName()+" with default UDP port "+defaultPort);
- }
-
- public synchronized static ExpeditionWindTrackerFactory getInstance() {
- if (defaultInstance == null) {
- defaultInstance = new ExpeditionWindTrackerFactory();
- }
- return defaultInstance;
- }
-
- @Override
- public WindTracker createWindTracker(DynamicTrackedRegatta trackedRegatta, RaceDefinition race,
- boolean correctByDeclination) throws SocketException {
- WindTracker result = getExistingWindTracker(race);
- if (result == null) {
- DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(race);
- UDPExpeditionReceiver receiver = getOrCreateWindReceiverOnDefaultPort();
- result = new ExpeditionWindTracker(trackedRace,
- correctByDeclination ? DeclinationService.INSTANCE : null, receiver, this);
- windTrackers.put(race, result);
- }
- return result;
- }
-
- @Override
- public WindTracker getExistingWindTracker(RaceDefinition race) {
- return windTrackers.get(race);
- }
-
- public UDPExpeditionReceiver getOrCreateWindReceiverOnDefaultPort() throws SocketException {
- return getOrCreateWindReceiverForPort(defaultPort);
- }
-
- private synchronized UDPExpeditionReceiver getOrCreateWindReceiverForPort(int port) throws SocketException {
- UDPExpeditionReceiver receiver = windReceivers.get(port);
- if (receiver == null) {
- receiver = new UDPExpeditionReceiver(port);
- windReceivers.put(port, receiver);
- Thread t = new Thread(receiver, "Expedition Wind Receiver on port "+port);
- t.setDaemon(true);
- t.start();
- }
- return receiver;
- }
-
- /**
- * Notifies the factory that the wind tracker has stopped tracking wind for race. This
- * will remove the tracker from the respective caches.
- */
- synchronized void windTrackerStopped(RaceDefinition race, ExpeditionWindTracker windTracker) {
- if (windTrackers.get(race) != windTracker) {
- throw new IllegalArgumentException("Intenral error: expected to remove wind tracker "+windTracker+
- ", but another wind tracker "+windTrackers.get(race)+" was registered.");
- }
- windTrackers.remove(race);
- if (windTracker.getReceiver().isStopped()) {
- UDPExpeditionReceiver receiver = windReceivers.get(windTracker.getReceiver().getPort());
- if (receiver != windTracker.getReceiver()) {
- throw new IllegalArgumentException("Internal error: expected to remove wind receiver "+
- windTracker.getReceiver()+" but found receiver "+receiver);
- }
- windReceivers.remove(windTracker.getReceiver().getPort());
- }
- }
-
- @Override
- public String toString() {
- return "ExpeditionWindTrackerFactory [defaultPort=" + defaultPort + "]";
- }
-}
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/UDPExpeditionReceiver.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/UDPExpeditionReceiver.java
index 8edf05e4cd8..1b6be99d92a 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/UDPExpeditionReceiver.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/UDPExpeditionReceiver.java
@@ -4,8 +4,15 @@ import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
+import com.sap.sailing.domain.common.sensordata.ExpeditionExtendedSensorDataMetadata;
+import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
+import com.sap.sailing.domain.common.tracking.GPSFixMoving;
+import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
+import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.expeditionconnector.impl.ExpeditionMessageParser;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
import com.sap.sailing.udpconnector.UDPReceiver;
/**
@@ -20,10 +27,18 @@ public class UDPExpeditionReceiver extends UDPReceiver timeStampOfLastMessageReceived;
+ private final Map delayBetweenMessageTimestampAndTimepointReceived;
private final ExpeditionMessageParser parser;
+ /**
+ * An optional lookup facility for device identifiers for a {@link ExpeditionMessage#getBoatID() boat ID} as
+ * received in an Expedition UDP stream; if device identifiers are returned by a non-{@code null} registry, GPS /
+ * sensor fixes will be assembled upon receiving them, and they will be submitted to the {@link SensorFixStore}.
+ * Otherwise, only wind data will be forwarded.
+ */
+ private final DeviceRegistry deviceRegistry;
+
/**
* Launches a listener and dumps messages received to the console
* @param args 0: port to listen on
@@ -46,13 +61,93 @@ public class UDPExpeditionReceiver extends UDPReceiver();
- parser = new ExpeditionMessageParser(this);
+ this(listeningOnPort, /* DeviceRegistry */ null);
}
- public Map getTimeStampOfLastMessageReceived() {
- return timeStampOfLastMessageReceived;
+ /**
+ * You need call {@link #run} to actually start receiving events. To do this asynchronously, start this object in a
+ * new thread.
+ *
+ * @param deviceRegistry
+ * if not {@code null}, a look-up for each message's {@link ExpeditionMessage#getBoatID() boat ID} will
+ * be performed; if valid device identifiers for GPS and sensor tracks are returned, this receiver will
+ * produce GPS and sensor fixes, respectively, with the device identifiers produced by the
+ * {@link DeviceRegistry} and submit them to the {@link SensorFixStore}.
+ */
+ public UDPExpeditionReceiver(int listeningOnPort, DeviceRegistry deviceRegistry) throws SocketException {
+ super(listeningOnPort);
+ this.deviceRegistry = deviceRegistry;
+ this.delayBetweenMessageTimestampAndTimepointReceived = new HashMap();
+ parser = new ExpeditionMessageParser(this);
+ addListener(msg->produceAndStoreOptionalFixes(msg), /* validMessagesOnly */ true);
+ }
+
+ private void produceAndStoreOptionalFixes(ExpeditionMessage msg) {
+ if (deviceRegistry != null) {
+ final ExpeditionGpsDeviceIdentifier gpsDeviceIdentifier = deviceRegistry.getGpsDeviceIdentifier(msg.getBoatID());
+ if (gpsDeviceIdentifier != null) {
+ tryToProduceAndStoreGpsFix(msg, gpsDeviceIdentifier);
+ }
+ final ExpeditionSensorDeviceIdentifier sensorDeviceIdentifier = deviceRegistry.getSensorDeviceIdentifier(msg.getBoatID());
+ if (sensorDeviceIdentifier != null) {
+ tryToProduceAndStoreSensorFix(msg, sensorDeviceIdentifier);
+ }
+ }
+ }
+
+ /**
+ * If this message completes the set of data required to produce a sensor fix, do so and
+ * store in the {@link DeviceRegistry#getSensorFixStore() sensor fix store}.
+ */
+ private void tryToProduceAndStoreSensorFix(ExpeditionMessage msg, ExpeditionSensorDeviceIdentifier sensorDeviceIdentifier) {
+ final Map mapExpeditionMessageFieldIdToMetadata = new HashMap<>();
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_ROLL, ExpeditionExtendedSensorDataMetadata.HEEL);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_PITCH, ExpeditionExtendedSensorDataMetadata.TRIM);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_RUDDER, ExpeditionExtendedSensorDataMetadata.RUDDER);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_TACK_ANGLE, ExpeditionExtendedSensorDataMetadata.TACK_ANGLE);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_RAKE_DEG, ExpeditionExtendedSensorDataMetadata.RAKE_DEG);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_DFLCTR_PP, ExpeditionExtendedSensorDataMetadata.DEFLECTOR_PERCENTAGE);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_TG_HEEL, ExpeditionExtendedSensorDataMetadata.TARGET_HEEL);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_FORESTAY_LOAD, ExpeditionExtendedSensorDataMetadata.FORESTAY_LOAD);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_FORESTAY_PRES, ExpeditionExtendedSensorDataMetadata.FORESTAY_PRESSURE);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_DFLECTR_MM, ExpeditionExtendedSensorDataMetadata.DEFLECTOR_MILLIMETERS);
+ mapExpeditionMessageFieldIdToMetadata.put(ExpeditionMessage.ID_TARG_BSP_P, ExpeditionExtendedSensorDataMetadata.TARGET_BOATSPEED_P);
+
+ int maxColumnIndex = -1;
+ final Map valuesPerDoubleVectorFixColumnIndices = new HashMap<>();
+ for (final Entry mapEntry : mapExpeditionMessageFieldIdToMetadata.entrySet()) {
+ if (msg.hasValue(mapEntry.getKey())) {
+ valuesPerDoubleVectorFixColumnIndices.put(mapEntry.getValue().getColumnIndex(), msg.getValue(mapEntry.getKey()));
+ maxColumnIndex = Math.max(maxColumnIndex, mapEntry.getValue().getColumnIndex());
+ }
+ }
+ final Double[] vector = new Double[maxColumnIndex+1];
+ for (final Entry valuesPerDoubleVectorFixColumnIndex : valuesPerDoubleVectorFixColumnIndices.entrySet()) {
+ vector[valuesPerDoubleVectorFixColumnIndex.getKey()] = valuesPerDoubleVectorFixColumnIndex.getValue();
+ }
+ final DoubleVectorFix fix = new DoubleVectorFixImpl(msg.getTimePoint(), vector);
+ if (fix != null && fix.hasValidData()) {
+ deviceRegistry.getSensorFixStore().storeFix(sensorDeviceIdentifier, fix);
+ }
+ }
+
+ /**
+ * If this message completes the set of data required to produce a GPS fix, do so and
+ * store in the {@link DeviceRegistry#getSensorFixStore() sensor fix store}.
+ */
+ private void tryToProduceAndStoreGpsFix(ExpeditionMessage msg, ExpeditionGpsDeviceIdentifier gpsDeviceIdentifier) {
+ final GPSFixMoving fix = msg.getGPSFixMoving();
+ if (fix != null) {
+ deviceRegistry.getSensorFixStore().storeFix(gpsDeviceIdentifier, fix);
+ }
+ }
+
+ public Long getLastKnownMessageDelayInMillis(int boatID) {
+ return delayBetweenMessageTimestampAndTimepointReceived.get(boatID);
+ }
+
+ public void updateLastKnownMessageDelay(int boatID, long timestampAsMillis) {
+ delayBetweenMessageTimestampAndTimepointReceived.put(boatID, timestampAsMillis);
}
protected ExpeditionMessageParser getParser() {
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java
index 79e02938b9a..31074365dd0 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java
@@ -1,13 +1,30 @@
package com.sap.sailing.expeditionconnector.impl;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.domain.tracking.WindTrackerFactory;
-import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory;
+import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.ExpeditionTrackerFactory;
+import com.sap.sailing.expeditionconnector.persistence.DomainObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierJsonHandler;
+import com.sap.sailing.expeditionconnector.persistence.MongoObjectFactory;
+import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory;
+import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
+import com.sap.sse.common.TypeBasedServiceFinder;
+import com.sap.sse.util.impl.ThreadFactoryWithPriority;
public class Activator implements BundleActivator {
private static Logger logger = Logger.getLogger(Activator.class.getName());
@@ -16,17 +33,33 @@ public class Activator implements BundleActivator {
private static Activator instance;
+ /**
+ * Registrations of OSGi services to be de-registered when the bundle shuts down
+ */
+ private Set> registrations = new HashSet<>();
+
private static final int DEFAULT_PORT = 2013;
private int port;
+
+ private BundleContext context;
+ private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true));
+
public Activator() {
port = Integer.valueOf(System.getProperty(EXPEDITION_UDP_PORT_PROPERTY_NAME, ""+DEFAULT_PORT));
logger.log(Level.INFO, "setting default for "+EXPEDITION_UDP_PORT_PROPERTY_NAME+" to "+port);
}
+ private Dictionary getDict(String type) {
+ Dictionary properties = new Hashtable();
+ properties.put(TypeBasedServiceFinder.TYPE, type);
+ return properties;
+ }
+
@Override
public void start(BundleContext context) throws Exception {
+ this.context = context;
if (instance == null) {
instance = this;
}
@@ -35,8 +68,20 @@ public class Activator implements BundleActivator {
logger.log(Level.INFO, "found "+EXPEDITION_UDP_PORT_PROPERTY_NAME+"="+port+" in OSGi context");
}
// register the Expedition wind tracker factory as an OSGi service
- context.registerService(ExpeditionWindTrackerFactory.class, ExpeditionWindTrackerFactory.getInstance(), /* properties */null);
- context.registerService(WindTrackerFactory.class, ExpeditionWindTrackerFactory.getInstance(), /* properties */null);
+ final DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory();
+ final MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory();
+ executor.execute(()->{
+ logger.info("Creating ExpeditionTrackerFactory");
+ final ExpeditionTrackerFactory expeditionTrackerFactory = new ExpeditionTrackerFactory(
+ /* sensorFixStore will be discovered by tracker factory through OSGi */ null, domainObjectFactory, mongoObjectFactory);
+ registrations.add(context.registerService(ExpeditionTrackerFactory.class, expeditionTrackerFactory, /* properties */null));
+ registrations.add(context.registerService(WindTrackerFactory.class, expeditionTrackerFactory, /* properties */null));
+
+ registrations.add(context.registerService(DeviceIdentifierJsonHandler.class, new ExpeditionGpsDeviceIdentifierJsonHandler(), getDict(ExpeditionGpsDeviceIdentifier.TYPE)));
+ registrations.add(context.registerService(DeviceIdentifierStringSerializationHandler.class, new ExpeditionGpsStringSerializationHandler(), getDict(ExpeditionGpsDeviceIdentifier.TYPE)));
+ registrations.add(context.registerService(DeviceIdentifierJsonHandler.class, new ExpeditionSensorDeviceIdentifierJsonHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
+ registrations.add(context.registerService(DeviceIdentifierStringSerializationHandler.class, new ExpeditionSensorStringSerializationHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
+ });
}
public static Activator getInstance() {
@@ -46,8 +91,21 @@ public class Activator implements BundleActivator {
return instance;
}
+ public BundleContext getContext() {
+ return context;
+ }
+
+ public void setContext(BundleContext context) {
+ this.context = context;
+ }
+
@Override
public void stop(BundleContext context) throws Exception {
+ for (ServiceRegistration> reg : registrations) {
+ reg.unregister();
+ }
+ registrations.clear();
+ this.context = null;
}
public int getExpeditionUDPPort() {
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionGpsStringSerializationHandler.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionGpsStringSerializationHandler.java
new file mode 100755
index 00000000000..3714e70be85
--- /dev/null
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionGpsStringSerializationHandler.java
@@ -0,0 +1,9 @@
+package com.sap.sailing.expeditionconnector.impl;
+
+import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
+import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifierSerializationHandler;
+
+public class ExpeditionGpsStringSerializationHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
+implements DeviceIdentifierStringSerializationHandler {
+
+}
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java
index d9b999ee772..f0508c90773 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java
@@ -1,5 +1,6 @@
package com.sap.sailing.expeditionconnector.impl;
+import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
@@ -42,19 +43,7 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
* {@link #getTimePoint() time point}.
*/
public ExpeditionMessageImpl(int boatID, Map values, boolean valid, String originalMessage) {
- this.boatID = boatID;
- // ensure that nobody can manipulate the map used by this message object from outside
- this.values = new HashMap(values);
- this.valid = valid;
- this.originalMessage = originalMessage;
- this.createdAtMillis = System.currentTimeMillis();
- if (hasValue(ID_GPS_TIME)) {
- timePoint = new MillisecondsTimePoint((long)
- (getValue(ID_GPS_TIME)*24*3600*1000) + // this is the milliseconds since 31.12.1899 0:00:00 UTC
- cal.getTimeInMillis());
- } else {
- timePoint = new MillisecondsTimePoint(createdAtMillis);
- }
+ this(boatID, values, valid, /* defaultTimePoint */ null, originalMessage, /* unused */ true);
}
/**
@@ -63,9 +52,13 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
* stamp
*/
public ExpeditionMessageImpl(int boatID, Map values, boolean valid, TimePoint defaultTimePoint, String originalMessage) {
+ this(boatID, values, valid, defaultTimePoint, originalMessage, /* unused */ true);
if (defaultTimePoint == null) {
- throw new IllegalArgumentException("defaultTimePoint for ExpeditionMessageImpl constructor must not be null");
+ throw new IllegalArgumentException("defaultTimePoint for this ExpeditionMessageImpl constructor must not be null");
}
+ }
+
+ private ExpeditionMessageImpl(int boatID, Map values, boolean valid, TimePoint defaultTimePoint, String originalMessage, boolean unused) {
this.boatID = boatID;
// ensure that nobody can manipulate the map used by this message object from outside
this.values = new HashMap(values);
@@ -73,9 +66,25 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
this.originalMessage = originalMessage;
this.createdAtMillis = System.currentTimeMillis();
if (hasValue(ID_GPS_TIME)) {
+ final double gpsTimeValue = getValue(ID_GPS_TIME);
+ final long referenceTime;
+ if (gpsTimeValue <= 1.0) { // "Phoenix" law: if between 0 and 1 then it's likely a day-relative time point
+ // so add the millis of today midnight UTC
+ Calendar todayMidnightUTC = new GregorianCalendar();
+ todayMidnightUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
+ todayMidnightUTC.set(Calendar.HOUR_OF_DAY, 0);
+ todayMidnightUTC.set(Calendar.MINUTE, 0);
+ todayMidnightUTC.set(Calendar.SECOND, 0);
+ todayMidnightUTC.set(Calendar.MILLISECOND, 0);
+ referenceTime = todayMidnightUTC.getTimeInMillis();
+ } else {
+ referenceTime = cal.getTimeInMillis();
+ }
timePoint = new MillisecondsTimePoint((long)
- (getValue(ID_GPS_TIME)*24*3600*1000) + // this is the milliseconds since 31.12.1899 0:00:00 UTC
- cal.getTimeInMillis());
+ (gpsTimeValue*24*3600*1000) + // this is the milliseconds since 31.12.1899 0:00:00 UTC
+ referenceTime);
+ } else if (defaultTimePoint == null) {
+ timePoint = new MillisecondsTimePoint(createdAtMillis);
} else {
timePoint = defaultTimePoint;
}
diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageParser.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageParser.java
index c21d19f5d48..88a13bbefae 100755
--- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageParser.java
+++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageParser.java
@@ -36,7 +36,7 @@ public class ExpeditionMessageParser implements UDPMessageParser values = new HashMap();
String[] variablesAndValuesInterleaved = variableValuePairs.split(",");
long now = System.currentTimeMillis();
- Long diff = receiver.getTimeStampOfLastMessageReceived().get(boatID);
+ Long diff = receiver.getLastKnownMessageDelayInMillis(boatID);
TimePoint defaultForMessageTimePoint;
if (diff != null) {
// compute a reasonable default for a time stamp in case message doesn't provide one
@@ -60,7 +60,7 @@ public class ExpeditionMessageParser implements UDPMessageParser
+
+
@@ -46,6 +48,7 @@
+
@@ -90,4 +93,11 @@
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml
index 728e3f5ab9a..4ec77eb88b3 100755
--- a/java/com.sap.sailing.feature/feature.xml
+++ b/java/com.sap.sailing.feature/feature.xml
@@ -395,4 +395,32 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
+
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.gwt.ui.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui.test/META-INF/MANIFEST.MF
index b6912d31c74..7fc34e485e1 100644
--- a/java/com.sap.sailing.gwt.ui.test/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.gwt.ui.test/META-INF/MANIFEST.MF
@@ -15,7 +15,7 @@ Require-Bundle: com.sap.sailing.server,
com.sap.sailing.domain.racelogtrackingadapter.testsupport,
com.rabbitmq.client;bundle-version="2.8.4",
com.sap.sse.filestorage,
- com.google.gwt.user;bundle-version="2.8.1";resolution:=optional,
+ com.google.gwt.user;bundle-version="2.8.2";resolution:=optional,
org.moxieapps.gwt.highcharts;bundle-version="1.5.0"
Bundle-ClassPath: .
Import-Package: com.sap.sailing.domain.racelogtracking.impl,
diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java
index 58af534d869..a6398d346fd 100644
--- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java
+++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java
@@ -70,6 +70,7 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -406,6 +407,16 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null;
}
+ @Override
+ public Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint) {
+ return null;
+ }
+
+ @Override
+ public Duration getDurationFoiled(Competitor competitor, TimePoint timePoint) {
+ return null;
+ }
+
@Override
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
return null;
@@ -720,7 +731,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override
public boolean isUsingMarkPassingCalculator() {
- // TODO Auto-generated method stub
return false;
}
@@ -881,4 +891,15 @@ public class MockedTrackedRace implements DynamicTrackedRace {
String trackName) {
return Collections.emptySet();
}
+
+ @Override
+ public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
+ return null;
+ }
+
+ @Override
+ public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
+ WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
+ return null;
+ }
}
diff --git a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs
index 872f8cf77fc..176dead2344 100755
--- a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs
+++ b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs
@@ -3,6 +3,7 @@ encoding//src/main/java/com/sap/sailing/gwt/home/mobile/partials/sectionHeader/S
encoding//src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java=utf-8
encoding//src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java=utf-8
encoding//src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java=utf-8
+encoding//src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionDeviceConfigurationsPanel.java=utf-8
encoding//src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java=utf-8
encoding//src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java=utf-8
encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties=UTF-8
diff --git a/java/com.sap.sailing.gwt.ui/AdminConsole.css b/java/com.sap.sailing.gwt.ui/AdminConsole.css
index 15c8f269e5a..9e65fc5bcdc 100644
--- a/java/com.sap.sailing.gwt.ui/AdminConsole.css
+++ b/java/com.sap.sailing.gwt.ui/AdminConsole.css
@@ -80,10 +80,6 @@ fieldset legend {
color: #FF0000;
}
-.linkNoBorder {
- border: none;
-}
-
.refreshLink {
border: none;
position: relative;
@@ -95,11 +91,6 @@ fieldset legend {
vertical-align: middle;
}
-.boldLabel {
- font-size: 14px;
- font-weight: bold;
-}
-
.boldAnchor {
font-weight: bold;
vertical-align: middle;
diff --git a/java/com.sap.sailing.gwt.ui/DataMining.css b/java/com.sap.sailing.gwt.ui/DataMining.css
index 54a9607fc06..d6f22a64cb9 100644
--- a/java/com.sap.sailing.gwt.ui/DataMining.css
+++ b/java/com.sap.sailing.gwt.ui/DataMining.css
@@ -109,23 +109,6 @@ table {
margin: 0 12px 18px 12px;
}
-.sapLogo {
- float: left;
- margin: 13px 0 0 12px;
-}
-
-.sailingAnalyticsLabelPanel {
- margin: 10px 0 0 0;
-}
-
-.sailingAnalyticsLabel {
- float: left;
- color: #fff;
- font-size: 14px;
- font-weight: bold;
- margin: 15px 0 0 0;
-}
-
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
@@ -257,4 +240,4 @@ input.magnifier {
.queryDefinitionViewerContent {
background-color: white;
padding: 5px;
-}
\ No newline at end of file
+}
diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF
index 2f95c07d959..3d652f0ac0f 100644
--- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF
@@ -24,8 +24,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.freg.resultimport,
com.sap.sailing.domain.swisstimingreplayadapter,
com.sap.sailing.simulator,
- com.google.gwt.user;bundle-version="2.8.1";resolution:=optional,
- com.google.gwt.servlet;bundle-version="2.8.1",
+ com.google.gwt.user;bundle-version="2.8.2";resolution:=optional,
+ com.google.gwt.servlet;bundle-version="2.8.2",
com.sap.sailing.domain.igtimiadapter,
com.sap.sailing.xrr.resultimport,
com.sap.sailing.datamining.shared,
@@ -53,7 +53,9 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.polars.datamining,
com.sap.sse.datamining.annotations,
com.sap.sse.shared.android,
- com.sap.sailing.competitorimport
+ com.sap.sailing.competitorimport,
+ com.sap.sailing.expeditionconnector,
+ com.sap.sailing.expeditionconnector.common
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: javax.servlet;version="3.1.0",
diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch
index 54e1704423a..44a3fb497e9 100755
--- a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch
+++ b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch
@@ -55,6 +55,7 @@
+
diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch
index 3ec300f1256..482871f0a61 100644
--- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch
+++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch
@@ -53,6 +53,8 @@
+
+
diff --git a/java/com.sap.sailing.gwt.ui/Simulator.css b/java/com.sap.sailing.gwt.ui/Simulator.css
index 0f78295f104..ba0f5ccc33e 100644
--- a/java/com.sap.sailing.gwt.ui/Simulator.css
+++ b/java/com.sap.sailing.gwt.ui/Simulator.css
@@ -27,22 +27,6 @@ html {
font-size: 15px;
}
-.titleLabelWrapper {
- position: relative;
- margin-left: auto;
- margin-right: auto;
- width: 50%;
-}
-
-.titleLabel {
- text-align: center;
- font-family: 'Open Sans', Arial, Verdana, sans-serif;
- font-weight: 600
- font-size: 18px;
- color: #fff;
- margin: 23px 0 0 0;
-}
-
body {
background: #ebebeb;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
@@ -81,31 +65,11 @@ table {
border-collapse: separate;
}
-/*.RaceBoardHeaderPanel {
- margin: 0 12px 14px 12px;
-}*/
-.sapLogo {
- float: left;
- margin: 13px 0 0 12px;
-}
-
.pioLogo {
float: right;
margin: 11px 20px 0 12px;
}
-.sailingAnalyticsLabelPanel {
- margin: 10px 0 0 0;
-}
-
-.sailingAnalyticsLabel {
- float: left;
- color: #fff;
- font-size: 14px;
- font-weight: bold;
- margin: 15px 0 0 0;
-}
-
.timePanelSettings {
display: none;
}
diff --git a/java/com.sap.sailing.gwt.ui/Spectator.css b/java/com.sap.sailing.gwt.ui/Spectator.css
index 6f55fe4310c..277654681e4 100644
--- a/java/com.sap.sailing.gwt.ui/Spectator.css
+++ b/java/com.sap.sailing.gwt.ui/Spectator.css
@@ -96,23 +96,6 @@ table {
margin: 0 12px 18px 12px;
}
-.sapLogo {
- float: left;
- margin: 13px 0 0 12px;
-}
-
-.sailingAnalyticsLabelPanel {
- margin: 10px 0 0 0;
-}
-
-.sailingAnalyticsLabel {
- float: left;
- color: #fff;
- font-size: 14px;
- font-weight: bold;
- margin: 15px 0 0 0;
-}
-
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
diff --git a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
index 2dd07f89431..8538c7b0804 100755
--- a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
+++ b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
@@ -53,6 +53,15 @@
/*
+
+ LocaleInjectionFilter
+ com.sap.sse.security.ui.server.LocaleInjectionFilter
+
+
+ LocaleInjectionFilter
+ *.html
+
+
Sailing ServiceSailingService
diff --git a/java/com.sap.sailing.gwt.ui/images/sap_66_transparent.png b/java/com.sap.sailing.gwt.ui/images/sap_66_transparent.png
deleted file mode 100644
index 7e76e7fcf00..00000000000
Binary files a/java/com.sap.sailing.gwt.ui/images/sap_66_transparent.png and /dev/null differ
diff --git a/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png b/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png
deleted file mode 100644
index 04c78e98c3c..00000000000
Binary files a/java/com.sap.sailing.gwt.ui/images/titleLabelInner.png and /dev/null differ
diff --git a/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png b/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png
deleted file mode 100644
index 3881f73df40..00000000000
Binary files a/java/com.sap.sailing.gwt.ui/images/titleLabelWrapper.png and /dev/null differ
diff --git a/java/com.sap.sailing.gwt.ui/pom.xml b/java/com.sap.sailing.gwt.ui/pom.xml
index 820d5ab5f18..101dc398344 100755
--- a/java/com.sap.sailing.gwt.ui/pom.xml
+++ b/java/com.sap.sailing.gwt.ui/pom.xml
@@ -50,6 +50,12 @@
1.6.0-SNAPSHOTsources
+
+ com.sap.sailing
+ com.sap.sailing.expeditionconnector.common
+ ${project.version}
+ sources
+ com.sap.sailingcom.sap.sailing.domain.common
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration_CustomFieldSerializer.java
new file mode 100755
index 00000000000..9a17f3263e8
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/expeditionconnector/ExpeditionDeviceConfiguration_CustomFieldSerializer.java
@@ -0,0 +1,52 @@
+package com.google.gwt.user.client.rpc.core.com.sap.sailing.expeditionconnector;
+
+import java.util.UUID;
+
+import com.google.gwt.user.client.rpc.CustomFieldSerializer;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
+
+public class ExpeditionDeviceConfiguration_CustomFieldSerializer extends CustomFieldSerializer {
+
+ @Override
+ public void serializeInstance(SerializationStreamWriter streamWriter, ExpeditionDeviceConfiguration instance)
+ throws SerializationException {
+ serialize(streamWriter, instance);
+ }
+
+ public static void serialize(SerializationStreamWriter streamWriter, ExpeditionDeviceConfiguration instance)
+ throws SerializationException {
+ streamWriter.writeString(instance.getName());
+ streamWriter.writeObject(instance.getDeviceUuid());
+ streamWriter.writeObject(instance.getExpeditionBoatId());
+ }
+
+ @Override
+ public boolean hasCustomInstantiateInstance() {
+ return true;
+ }
+
+ @Override
+ public ExpeditionDeviceConfiguration instantiateInstance(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return instantiate(streamReader);
+ }
+
+ public static ExpeditionDeviceConfiguration instantiate(SerializationStreamReader streamReader)
+ throws SerializationException {
+ return new ExpeditionDeviceConfiguration(streamReader.readString(), (UUID) streamReader.readObject(), (Integer) streamReader.readObject());
+ }
+
+ @Override
+ public void deserializeInstance(SerializationStreamReader streamReader, ExpeditionDeviceConfiguration instance)
+ throws SerializationException {
+ deserialize(streamReader, instance);
+ }
+
+ public static void deserialize(SerializationStreamReader streamReader, ExpeditionDeviceConfiguration instance) {
+ // Done by instantiateInstance
+ }
+
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml
index b79c9c282d7..970d476cb47 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml
@@ -22,4 +22,5 @@
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
index c88495e58d6..0fe92e2e587 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
@@ -123,6 +123,7 @@ public class BoatClassImageResolver {
boatClassIconsMap.put(BoatClassMasterdata.TEENY.getDisplayName(), imageResources.TeenyIcon());
boatClassIconsMap.put(BoatClassMasterdata.TORNADO.getDisplayName(), imageResources.TornadoIcon());
boatClassIconsMap.put(BoatClassMasterdata.TOM_28_MAX.getDisplayName(), imageResources.Tom28MaxIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.TP52.getDisplayName(), imageResources.TP52Icon());
boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon());
boatClassIconsMap.put(BoatClassMasterdata.VIPER_640.getDisplayName(), imageResources.Viper640Icon());
boatClassIconsMap.put(BoatClassMasterdata.VX_ONE.getDisplayName(), imageResources.VXOneIcon());
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
index b249b1829a9..ee8b1e880fa 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
@@ -313,6 +313,9 @@ public interface BoatClassImageResources extends ClientBundle {
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/TOM_28_MAX.png")
ImageResource Tom28MaxIcon();
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TP52.png")
+ ImageResource TP52Icon();
+
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/RUNNING.png")
ImageResource RunningIcon();
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/HomeBase.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/HomeBase.gwt.xml
index 8bdd972094f..63efcd24539 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/HomeBase.gwt.xml
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/HomeBase.gwt.xml
@@ -42,6 +42,7 @@
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetEventViewAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetEventViewAction.java
index 0dd41c1365d..921ce88f966 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetEventViewAction.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetEventViewAction.java
@@ -67,6 +67,13 @@ public class GetEventViewAction implements SailingAction, IsClient
dto.setState(HomeServiceUtil.calculateEventState(event));
// bug2982: always show leaderboard and competitor analytics
dto.setHasAnalytics(true);
+
+ String description = event.getDescription();
+ if (description == null || description.trim().isEmpty() || event.getName().equalsIgnoreCase(description)) {
+ // If a description isn't useful, it should not be shown in the UI
+ description = null;
+ }
+ dto.setDescription(description);
final EventType eventType = EventUtil.getEventType(event);
dto.setType(eventType);
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/EventViewDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/EventViewDTO.java
index d2b159a8e85..20ee063f1a4 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/EventViewDTO.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/EventViewDTO.java
@@ -23,6 +23,7 @@ public class EventViewDTO extends EventMetadataDTO implements Result, HasLogo {
private ImageDTO logoImage;
private String officialWebsiteURL;
private String sailorsInfoWebsiteURL;
+ private String description;
public EventType getType() {
return type;
@@ -116,4 +117,12 @@ public class EventViewDTO extends EventMetadataDTO implements Result, HasLogo {
}
return venue;
}
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/DesktopActivityMapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/DesktopActivityMapper.java
index cd607d79b82..abc7a8f86d2 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/DesktopActivityMapper.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/DesktopActivityMapper.java
@@ -3,7 +3,6 @@ package com.sap.sailing.gwt.home.desktop.app;
import com.google.gwt.activity.shared.Activity;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.place.shared.Place;
-import com.sap.sailing.gwt.common.client.formfactor.DeviceDetector;
import com.sap.sailing.gwt.home.desktop.places.aboutus.AboutUsActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.aboutus.AboutUsPlace;
import com.sap.sailing.gwt.home.desktop.places.contact.ContactActivityProxy;
@@ -11,7 +10,7 @@ import com.sap.sailing.gwt.home.desktop.places.contact.ContactPlace;
import com.sap.sailing.gwt.home.desktop.places.error.ErrorActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.event.EventActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.events.EventsActivityProxy;
-import com.sap.sailing.gwt.home.desktop.places.morelogininformation.MoreLoginInformationActivity;
+import com.sap.sailing.gwt.home.desktop.places.morelogininformation.MoreLoginInformationActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.solutions.SolutionsActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.sponsoring.SponsoringActivityProxy;
import com.sap.sailing.gwt.home.desktop.places.sponsoring.SponsoringPlace;
@@ -37,6 +36,7 @@ import com.sap.sailing.gwt.home.shared.places.user.confirmation.ConfirmationPlac
import com.sap.sailing.gwt.home.shared.places.user.passwordreset.PasswordResetActivityProxy;
import com.sap.sailing.gwt.home.shared.places.user.passwordreset.PasswordResetPlace;
import com.sap.sailing.gwt.home.shared.places.user.profile.AbstractUserProfilePlace;
+import com.sap.sse.gwt.client.formfactor.DeviceDetector;
public class DesktopActivityMapper implements ActivityMapper {
private final DesktopClientFactory clientFactory;
@@ -84,11 +84,11 @@ public class DesktopActivityMapper implements ActivityMapper {
} else if (place instanceof ConfirmationPlace) {
return new ConfirmationActivityProxy((ConfirmationPlace) place, clientFactory);
} else if (place instanceof PasswordResetPlace) {
- return new PasswordResetActivityProxy((PasswordResetPlace) place, clientFactory);
+ return new PasswordResetActivityProxy((PasswordResetPlace) place, clientFactory);
} else if (place instanceof ImprintPlace) {
return new ImprintActivityProxy((ImprintPlace) place);
} else if (place instanceof MoreLoginInformationPlace) {
- return new MoreLoginInformationActivity(place);
+ return new MoreLoginInformationActivityProxy((MoreLoginInformationPlace) place, clientFactory);
} else {
return null;
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java
index 88c9af0a6dc..40b4f95cf4b 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java
@@ -35,7 +35,6 @@ import com.sap.sse.security.ui.authentication.AuthenticationClientFactoryImpl;
import com.sap.sse.security.ui.authentication.AuthenticationManager;
import com.sap.sse.security.ui.authentication.AuthenticationManagerImpl;
import com.sap.sse.security.ui.authentication.AuthenticationPlaceManagementController;
-import com.sap.sse.security.ui.authentication.WrappedPlaceManagementController;
import com.sap.sse.security.ui.authentication.info.LoggedInUserInfoPlace;
import com.sap.sse.security.ui.authentication.view.FlyoutAuthenticationPresenter;
import com.sap.sse.security.ui.client.i18n.StringMessages;
@@ -43,7 +42,7 @@ import com.sap.sse.security.ui.client.i18n.StringMessages;
public class TabletAndDesktopApplicationClientFactory extends AbstractApplicationClientFactory implements DesktopClientFactory {
private final SailingDispatchSystem dispatch = new SailingDispatchSystemImpl();
- private final WrappedPlaceManagementController userManagementWizardController;
+ private final AuthenticationPlaceManagementController userManagementWizardController;
private final AuthenticationManager authenticationManager;
public TabletAndDesktopApplicationClientFactory(boolean isStandaloneServer) {
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.gss
new file mode 100644
index 00000000000..e876110e092
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.gss
@@ -0,0 +1,18 @@
+.box {
+ font-size: 1rem;
+ border: 1px solid #dedede;
+ border-radius: 0.333333333333333em;
+ overflow: hidden;
+}
+.box_header {
+ color: #333;
+ font-size: 1.2em;
+ font-weight: 700;
+ background: #f2f2f2;
+ padding: 1.111111111111111em;
+ border-bottom: 1px solid #dedede;
+}
+.box_content {
+ margin: 0;
+ padding: 1em;
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.java
new file mode 100644
index 00000000000..79f1d13a5eb
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.java
@@ -0,0 +1,24 @@
+package com.sap.sailing.gwt.home.desktop.partials.eventdescription;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+public class EventDescription extends Composite {
+
+ private static StatisticsBoxUiBinder uiBinder = GWT.create(StatisticsBoxUiBinder.class);
+
+ interface StatisticsBoxUiBinder extends UiBinder {
+ }
+
+ @UiField DivElement descriptionUi;
+
+ public EventDescription(String description) {
+ EventDescriptionResources.INSTANCE.css().ensureInjected();
+ initWidget(uiBinder.createAndBindUi(this));
+ descriptionUi.setInnerText(description);
+ }
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.ui.xml
new file mode 100644
index 00000000000..6215d693745
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventdescription/EventDescription.ui.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+