Merge branch 'master' into bug4312

This commit is contained in:
Benjamin Barth
2017-11-06 14:57:27 +01:00
155 changed files with 3165 additions and 461 deletions
@@ -39,6 +39,8 @@
<inherits name='com.google.gwt.maps.Maps' />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<inherits name="com.sap.sailing.ExpeditionConnectorCommon" />
<!-- module containing locale configuration -->
<inherits name='com.sap.sailing.gwt.common.SailingLocalesAllPermutations' />
<inherits name="com.sap.sailing.gwt.ui.NoUI" />
@@ -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<Double, Distance> {
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<Double, Distance> 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());
}
}
@@ -14,6 +14,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,
@@ -42,6 +42,9 @@ public enum BravoExtendedSensorDataMetadata implements ColumnMetadata {
return columnName;
}
/**
* The index in the {@link DoubleVectorFix} where this data item will be stored
*/
public int getColumnIndex() {
return this.ordinal();
}
@@ -40,6 +40,9 @@ public enum ExpeditionExtendedSensorDataMetadata {
return columnName;
}
/**
* The index in the {@link DoubleVectorFix} where this data item will be stored
*/
public int getColumnIndex() {
return mappedToBravoField.getColumnIndex();
}
@@ -6,9 +6,9 @@ import com.sap.sailing.domain.common.Bearing;
* 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();
}
@@ -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();
}
@@ -24,28 +24,29 @@ public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedF
}
@Override
public double getPortDaggerboardRake() {
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);
}
}
@@ -58,11 +58,13 @@ public class BravoFixImpl extends SensorFixImpl 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);
}
}
@@ -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<fixData.length; i++) {
if (fixData[i] != null) {
this.validComponents.set(i);
this.fixData[i] = fixData[i];
}
}
}
@Override
@@ -26,14 +45,22 @@ public class DoubleVectorFixImpl implements DoubleVectorFix {
}
@Override
public double get(int index) {
return fixData[index];
public Double get(int index) {
return index<fixData.length && validComponents.get(index) ? fixData[index] : null;
}
@Override
public double[] get() {
// TODO defensive copy?
return fixData;
public Double[] get() {
final Double[] result = new Double[fixData.length];
for (int i=0; i<fixData.length; i++) {
result[i] = validComponents.get(i) ? fixData[i] : null;
}
return result;
}
@Override
public boolean hasValidData() {
return !validComponents.isEmpty();
}
@Override
@@ -42,6 +69,7 @@ public class DoubleVectorFixImpl implements DoubleVectorFix {
int result = 1;
result = prime * result + Arrays.hashCode(fixData);
result = prime * result + ((timePoint == null) ? 0 : timePoint.hashCode());
result = prime * result + ((validComponents == null) ? 0 : validComponents.hashCode());
return result;
}
@@ -61,6 +89,11 @@ public class DoubleVectorFixImpl implements DoubleVectorFix {
return false;
} else if (!timePoint.equals(other.timePoint))
return false;
if (validComponents == null) {
if (other.validComponents != null)
return false;
} else if (!validComponents.equals(other.validComponents))
return false;
return true;
}
}
@@ -1,7 +0,0 @@
package com.sap.sailing.domain.expeditionadapter.impl;
public enum FieldType {
date, race_name, leg_name, leg_score, boat_speed, heel, leeway, rudder, heading, course, depth,
tws, twa, twd, aws, awa, latitude, longitude, sog, cog, abstwa, absawa, absheel, absleeway, absrudder,
foresail, staysail, mainsail, forestay, rake, vmg, status, starboard_port, rudder2, keelang, absrudder2, abskeelang;
}
@@ -38,20 +38,20 @@ public class DoubleVectorFixMongoHandlerImpl implements FixMongoHandler<DoubleVe
return new DoubleVectorFixImpl(timePoint, fromDBObject((DBObject) dbObject.get(FieldNames.FIX.name())));
}
private DBObject toDBObject(double[] data) {
private DBObject toDBObject(Double[] data) {
BasicDBList result = new BasicDBList();
for (double value : data) {
for (Double value : data) {
result.add(value);
}
return result;
}
private double[] fromDBObject(DBObject dbObject) {
private Double[] fromDBObject(DBObject dbObject) {
@SuppressWarnings("unchecked")
List<Number> dbValues = (List<Number>) 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;
}
@@ -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}));
}
}
@@ -542,7 +542,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 +554,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);
}
@@ -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);
@@ -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() {
@@ -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 <FixType>
*/
public interface FixAcceptancePredicate<FixType> {
boolean isAcceptFix(FixType fix);
}
@@ -6,6 +6,7 @@ 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;
@@ -148,14 +149,25 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
@Override
public FixType getLastFixAtOrBefore(TimePoint timePoint) {
return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null);
}
private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) {
lockForRead();
try {
return (FixType) getInternalFixes().floor(getDummyFix(timePoint));
final NavigableSet<FixType> headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true);
for (final Iterator<FixType> 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 +200,20 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
@Override
public FixType getFirstFixAtOrAfter(TimePoint timePoint) {
return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null);
}
private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) {
lockForRead();
try {
return (FixType) getInternalFixes().ceiling(getDummyFix(timePoint));
final NavigableSet<FixType> tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true);
for (final Iterator<FixType> i=tailSet.iterator(); i.hasNext(); ) {
final FixType next = i.next();
if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) {
return next;
}
}
return null;
} finally {
unlockAfterRead();
}
@@ -254,9 +277,16 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
}
}
private Pair<FixType, FixType> 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<FixType, FixType> getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) {
FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate);
FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate);
com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right);
return result;
}
@@ -274,9 +304,15 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
}
@Override
public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, Function<FixType, ScalableValue<InternalType, ValueType>> converter) {
public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint,
Function<FixType, ScalableValue<InternalType, ValueType>> converter) {
return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null);
}
protected <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint,
Function<FixType, ScalableValue<InternalType, ValueType>> converter, FixAcceptancePredicate<FixType> fixAcceptancePredicate) {
final ValueType result;
Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint);
Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate);
if (fixPair.getA() == null) {
if (fixPair.getB() == null) {
result = null;
@@ -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<CourseArea> 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<CourseArea> 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));
}
}
@@ -67,7 +67,7 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark
time = System.currentTimeMillis() - time;
result.put("FinderPerformance", time);
System.out.println(time);
Assert.assertTrue("Time expected to be less than 5000ms but was "+time+"ms", time < 5000);
Assert.assertTrue("Time expected to be less than 7000ms but was "+time+"ms", time < 7000);
}
@Test
@@ -116,7 +116,7 @@ public class BravoFixTrackSerializationTest {
}
private void addOrReplaceBravoFixToTrack(boolean replace) {
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;
@@ -0,0 +1,13 @@
package com.sap.sailing.domain.racelog.tracking;
/**
* Some object that can supply a {@link SensorFixStore} can be registered with the OSGi
* registry using this interface, so it can be discovered by others, and the sensor fix
* store can then be obtained.
*
* @author Axel Uhl (d043530)
*
*/
public interface SensorFixStoreSupplier {
SensorFixStore getSensorFixStore();
}
@@ -8,6 +8,8 @@ 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.ScalableBearing;
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;
@@ -44,62 +46,20 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
@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,
ScalableBearing::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,
ScalableBearing::new);
}
@Override
@@ -185,18 +145,31 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> 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.
* <p>
*
* In case of a mix of {@link BravoFix} and {@link BravoExtendedFix} instances, this method may return null values
* for specific {@link TimePoint TimePoints}.
* <p>
*
* 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, I> T getValueFromExtendedFix(final TimePoint timePoint, final Function<BravoExtendedFix, T> getter,
private <T, I, BravoFixType extends BravoFix> T getValueFromExtendedFixSkippingNullValues(
final TimePoint timePoint, final Function<BravoFixType, T> getter,
Function<T, ScalableValue<I, T>> converterToScalableValue) {
if (!hasExtendedFixes) {
return null;
}
final com.sap.sse.common.Util.Function<BravoFix, ScalableValue<I, T>> 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,31 +184,31 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> 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);
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.expeditionconnector.common</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.google.gdt.eclipse.core.webAppProjectValidator</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.google.gwt.eclipse.core.gwtProjectValidator</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>com.google.gwt.eclipse.core.gwtNature</nature>
</natures>
</projectDescription>
@@ -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
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,10 @@
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
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.expeditionconnector.common</artifactId>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-plugin</artifactId>
<version>${tycho-version}</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-source-plugin</artifactId>
<version>${tycho-version}</version>
<executions>
<execution>
<id>plugin-source</id>
<phase>generate-sources</phase>
<goals>
<goal>plugin-source</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd">
<module>
<inherits name="com.sap.sse.SSECommon" />
<source path='expeditionconnector'/>
</module>
@@ -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() + "]";
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.expeditionconnector.persistence</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -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
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -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
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.expeditionconnector.persistence</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,7 @@
package com.sap.sailing.expeditionconnector.persistence;
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
public interface DomainObjectFactory {
Iterable<ExpeditionDeviceConfiguration> getExpeditionDeviceConfigurations();
}
@@ -0,0 +1,13 @@
package com.sap.sailing.expeditionconnector.persistence;
import java.util.UUID;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
public interface ExpeditionDeviceIdentifier extends DeviceIdentifier {
/**
* Derived from an {@link ExpeditionDeviceConfiguration#getDeviceUuid()}.
*/
UUID getId();
}
@@ -0,0 +1,12 @@
package com.sap.sailing.expeditionconnector.persistence;
/**
* 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";
}
@@ -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;
}
}
@@ -0,0 +1,16 @@
package com.sap.sailing.expeditionconnector.persistence;
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 ExpeditionGpsDeviceIdentifierJsonHandler extends ExpeditionGpsDeviceIdentifierSerializationHandler
implements DeviceIdentifierJsonHandler {
@Override
public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
throws TransformationException {
return deserialize((String) serialized, type, stringRepresentation);
}
}
@@ -0,0 +1,27 @@
package com.sap.sailing.expeditionconnector.persistence;
import java.util.UUID;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
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<String, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
return new Util.Pair<String, String>(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);
}
}
}
@@ -0,0 +1,12 @@
package com.sap.sailing.expeditionconnector.persistence;
/**
* 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";
}
@@ -0,0 +1,18 @@
package com.sap.sailing.expeditionconnector.persistence;
import java.util.UUID;
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;
}
}
@@ -0,0 +1,27 @@
package com.sap.sailing.expeditionconnector.persistence;
import java.util.UUID;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
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<String, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
return new Util.Pair<String, String>(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);
}
}
}
@@ -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);
}
@@ -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);
}
@@ -0,0 +1,37 @@
package com.sap.sailing.expeditionconnector.persistence.impl;
import java.util.UUID;
import com.sap.sailing.expeditionconnector.persistence.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();
}
}
@@ -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.persistence.ExpeditionGpsDeviceIdentifier;
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifier;
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<ServiceRegistration<?>> 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<String, String> getDict(String type) {
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(TypeBasedServiceFinder.TYPE, type);
return properties;
}
}
@@ -0,0 +1,5 @@
package com.sap.sailing.expeditionconnector.persistence.impl;
public enum CollectionNames {
EXPEDITION_DEVICE_CONFIGURATIONS;
}
@@ -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<ExpeditionDeviceConfiguration> getExpeditionDeviceConfigurations() {
final List<ExpeditionDeviceConfiguration> 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;
}
}
@@ -0,0 +1,15 @@
package com.sap.sailing.expeditionconnector.persistence.impl;
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.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);
}
}
@@ -0,0 +1,15 @@
package com.sap.sailing.expeditionconnector.persistence.impl;
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.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);
}
}
@@ -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;
}
@@ -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);
}
}
@@ -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());
}
}
@@ -2,6 +2,7 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -1,4 +1,5 @@
source.. = src/
source.. = src/,\
resources/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -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
@@ -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, // user channels must be consecutive
ExUser1,
ExUser2,
ExUser3,
ExUser4,
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
};
@@ -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;
@@ -17,16 +19,19 @@ import java.util.ArrayList;
import java.util.Calendar;
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.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;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import com.sap.sailing.declination.Declination;
import com.sap.sailing.declination.DeclinationService;
@@ -34,19 +39,31 @@ 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.GPSFix;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
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.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.ExpeditionSensorDeviceIdentifier;
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;
public class UDPExpeditionReceiverTest {
@Rule public Timeout TestTimeout = new Timeout(60 * 1000);
// @Rule public Timeout TestTimeout = new Timeout(60 * 1000);
private String[] validLines;
private String[] someValidWithFourInvalidLines;
@@ -58,9 +75,126 @@ 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<Integer, ExpeditionGpsDeviceIdentifier> gpsDeviceIdentifiers;
private final Map<Integer, ExpeditionSensorDeviceIdentifier> 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<DeviceIdentifier, List<Timed>> fixesReceived;
public TestSensorFixStore() {
fixesReceived = new HashMap<>();
}
@Override
public <FixT extends Timed> void loadFixes(Consumer<FixT> consumer, DeviceIdentifier deviceIdentifier,
TimePoint start, TimePoint end, boolean toIsInclusive)
throws NoCorrespondingServiceRegisteredException, TransformationException {
}
@Override
public <FixT extends Timed> void loadFixes(Consumer<FixT> consumer, DeviceIdentifier deviceIdentifier,
TimePoint start, TimePoint end, boolean inclusive, BooleanSupplier isPreemptiveStopped,
Consumer<Double> progressReporter)
throws NoCorrespondingServiceRegisteredException, TransformationException {
}
@Override
public <FixT extends Timed> void storeFix(DeviceIdentifier device, FixT fix) {
List<Timed> fixesReceivedFromDevice = fixesReceived.get(device);
if (fixesReceivedFromDevice == null) {
fixesReceivedFromDevice = new ArrayList<>();
fixesReceived.put(device, fixesReceivedFromDevice);
}
fixesReceivedFromDevice.add(fix);
}
@Override
public <FixT extends Timed> void storeFixes(DeviceIdentifier device, Iterable<FixT> 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 <FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices)
throws TransformationException, NoCorrespondingServiceRegisteredException {
return null;
}
@Override
public <FixT extends Timed> boolean loadOldestFix(Consumer<FixT> consumer, DeviceIdentifier device,
TimeRange timeRangetoLoad) throws NoCorrespondingServiceRegisteredException, TransformationException {
return false;
}
@Override
public <FixT extends Timed> boolean loadYoungestFix(Consumer<FixT> 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 +248,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 +312,27 @@ 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<String> 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);
assertTrue(sensorFixStore.getNumberOfFixes(sensorDevice) > 0);
}
@Test
public void testTimeStampConversion() throws IOException, InterruptedException {
@@ -261,7 +419,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];
@@ -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
@@ -0,0 +1,28 @@
package com.sap.sailing.expeditionconnector;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifier;
/**
* 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();
}
@@ -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
*/
@@ -69,34 +152,6 @@ public interface ExpeditionMessage extends UDPMessage {
*/
final int ID_GPS_SOG = 51;
/**
* 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;
/**
* True wind direction over ground ("from") in decimal degrees, cleansed using the GPS device
*/
@@ -106,7 +161,22 @@ 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 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.
*/
@@ -0,0 +1,265 @@
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.ExpeditionSensorDeviceIdentifier;
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<RaceDefinition, WindTracker> windTrackers;
private final Map<Integer, UDPExpeditionReceiver> 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<UUID, ExpeditionDeviceConfiguration> 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<Integer, ExpeditionDeviceConfiguration> 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<SensorFixStoreSupplier, SensorFixStoreSupplier> sensorFixServiceTracker;
private final MongoObjectFactory mongoObjectFactory;
public ExpeditionTrackerFactory(SensorFixStore sensorFixStore, DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory) {
this.windTrackers = new HashMap<RaceDefinition, WindTracker>();
this.sensorFixStore = sensorFixStore;
windReceivers = new HashMap<Integer, UDPExpeditionReceiver>();
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<SensorFixStoreSupplier, SensorFixStoreSupplier> startListeningForSensorFixStoreSuppliers(BundleContext context) {
final ServiceTracker<SensorFixStoreSupplier, SensorFixStoreSupplier> result = new ServiceTracker<>(context, SensorFixStoreSupplier.class,
new ServiceTrackerCustomizer<SensorFixStoreSupplier, SensorFixStoreSupplier>() {
@Override
public SensorFixStoreSupplier addingService(ServiceReference<SensorFixStoreSupplier> reference) {
final SensorFixStoreSupplier service = Activator.getInstance().getContext().getService(reference);
sensorFixStore = service.getSensorFixStore();
return service;
}
@Override
public void modifiedService(ServiceReference<SensorFixStoreSupplier> reference,
SensorFixStoreSupplier service) {
}
@Override
public void removedService(ServiceReference<SensorFixStoreSupplier> 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 <code>race</code>. 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;
}
}
@@ -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<Integer, Position>();
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);
}
}
@@ -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<RaceDefinition, WindTracker> windTrackers;
private final Map<Integer, UDPExpeditionReceiver> windReceivers;
private final int defaultPort;
public ExpeditionWindTrackerFactory() {
this.windTrackers = new HashMap<RaceDefinition, WindTracker>();
windReceivers = new HashMap<Integer, UDPExpeditionReceiver>();
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 <code>race</code>. 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 + "]";
}
}
@@ -5,7 +5,14 @@ import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
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.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifier;
import com.sap.sailing.udpconnector.UDPReceiver;
/**
@@ -20,10 +27,18 @@ public class UDPExpeditionReceiver extends UDPReceiver<ExpeditionMessage, Expedi
* Remembers, per boat ID, the milliseconds difference between the time the message was received
* and the GPS time stamp provided by the message.
*/
private final Map<Integer, Long> timeStampOfLastMessageReceived;
private final Map<Integer, Long> 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,74 @@ public class UDPExpeditionReceiver extends UDPReceiver<ExpeditionMessage, Expedi
* start this object in a new thread.
*/
public UDPExpeditionReceiver(int listeningOnPort) throws SocketException {
super(listeningOnPort);
this.timeStampOfLastMessageReceived = new HashMap<Integer, Long>();
parser = new ExpeditionMessageParser(this);
this(listeningOnPort, /* DeviceRegistry */ null);
}
public Map<Integer, Long> 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<Integer, Long>();
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 Double heelInDegrees = msg.hasValue(ExpeditionMessage.ID_ROLL) ? msg.getValue(ExpeditionMessage.ID_ROLL) : null;
final Double trimInDegrees = msg.hasValue(ExpeditionMessage.ID_PITCH) ? msg.getValue(ExpeditionMessage.ID_PITCH) : null;
final Double[] vector = new Double[Math.max(ExpeditionExtendedSensorDataMetadata.HEEL.getColumnIndex(),
ExpeditionExtendedSensorDataMetadata.TRIM.getColumnIndex())+1];
vector[ExpeditionExtendedSensorDataMetadata.HEEL.getColumnIndex()] = heelInDegrees;
vector[ExpeditionExtendedSensorDataMetadata.TRIM.getColumnIndex()] = trimInDegrees;
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() {
@@ -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.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.ExpeditionSensorDeviceIdentifier;
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<ServiceRegistration<?>> 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<String, String> getDict(String type) {
Dictionary<String, String> properties = new Hashtable<String, String>();
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() {
@@ -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 {
}
@@ -42,19 +42,7 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
* {@link #getTimePoint() time point}.
*/
public ExpeditionMessageImpl(int boatID, Map<Integer, Double> 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<Integer, Double>(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 +51,13 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
* stamp
*/
public ExpeditionMessageImpl(int boatID, Map<Integer, Double> 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<Integer, Double> 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<Integer, Double>(values);
@@ -76,6 +68,8 @@ public class ExpeditionMessageImpl implements ExpeditionMessage {
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 if (defaultTimePoint == null) {
timePoint = new MillisecondsTimePoint(createdAtMillis);
} else {
timePoint = defaultTimePoint;
}
@@ -36,7 +36,7 @@ public class ExpeditionMessageParser implements UDPMessageParser<ExpeditionMessa
Map<Integer, Double> values = new HashMap<Integer, Double>();
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<ExpeditionMessa
}
if (result.hasValue(ExpeditionMessage.ID_GPS_TIME)) {
// an original GPS time stamp; then remember the difference between now and the time stamp
receiver.getTimeStampOfLastMessageReceived().put(boatID, now - result.getTimePoint().asMillis());
receiver.updateLastKnownMessageDelay(boatID, now - result.getTimePoint().asMillis());
}
return result;
} else {
@@ -0,0 +1,17 @@
package com.sap.sailing.expeditionconnector.impl;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierSerializationHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
public class ExpeditionSensorDeviceIdentifierJsonHandler extends ExpeditionSensorDeviceIdentifierSerializationHandler
implements DeviceIdentifierJsonHandler {
@Override
public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
throws TransformationException {
return deserialize((String) serialized, type, stringRepresentation);
}
}
@@ -0,0 +1,9 @@
package com.sap.sailing.expeditionconnector.impl;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.expeditionconnector.persistence.ExpeditionSensorDeviceIdentifierSerializationHandler;
public class ExpeditionSensorStringSerializationHandler extends ExpeditionSensorDeviceIdentifierSerializationHandler
implements DeviceIdentifierStringSerializationHandler {
}
@@ -47,6 +47,7 @@
<plugin id="com.sap.sailing.domain.tractracadapter" autoStart="false" startLevel="4" />
<plugin id="com.sap.sailing.domain.tractracadapter.persistence" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.ess40.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.expeditionconnector.persistence" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.freg.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.kiworesultimport" autoStart="true" startLevel="3" />
+14
View File
@@ -402,4 +402,18 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.expeditionconnector.persistence"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.expeditionconnector.common"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
@@ -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
@@ -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",
@@ -55,6 +55,7 @@
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.polars.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.annotations/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+6
View File
@@ -50,6 +50,12 @@
<version>1.6.0-SNAPSHOT</version>
<classifier>sources</classifier>
</dependency>
<dependency>
<groupId>com.sap.sailing</groupId>
<artifactId>com.sap.sailing.expeditionconnector.common</artifactId>
<version>${project.version}</version>
<classifier>sources</classifier>
</dependency>
<dependency>
<groupId>com.sap.sailing</groupId>
<artifactId>com.sap.sailing.domain.common</artifactId>
@@ -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<ExpeditionDeviceConfiguration> {
@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
}
}
@@ -67,6 +67,13 @@ public class GetEventViewAction implements SailingAction<EventViewDTO>, 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);
@@ -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;
}
}
@@ -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;
}
@@ -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<Widget, EventDescription> {
}
@UiField DivElement descriptionUi;
public EventDescription(String description) {
EventDescriptionResources.INSTANCE.css().ensureInjected();
initWidget(uiBinder.createAndBindUi(this));
descriptionUi.setInnerText(description);
}
}
@@ -0,0 +1,11 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.eventdescription.EventDescriptionResources" />
<g:HTMLPanel addStyleNames="{local_res.css.box}">
<div class="{local_res.css.box_header}" ui:field="titleUi"><ui:text from="{i18n.description}" /></div>
<div class="{local_res.css.box_content}" ui:field="descriptionUi"></div>
</g:HTMLPanel>
</ui:UiBinder>
@@ -0,0 +1,18 @@
package com.sap.sailing.gwt.home.desktop.partials.eventdescription;
import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
public interface EventDescriptionResources extends ClientBundle {
public static final EventDescriptionResources INSTANCE = GWT.create(EventDescriptionResources.class);
@Source("EventDescription.gss")
LocalCss css();
public interface LocalCss extends CssResource {
String box();
String box_header();
String box_content();
}
}
@@ -94,7 +94,12 @@ public class MultiRegattaListStepsBody extends UIObject implements RequiresResiz
}
private String caculateSeriesName(RegattaProgressSeriesDTO seriesProgress) {
return DEFAULT_SERIES_NAME.equals(seriesProgress.getName()) ? I18N.races() : seriesProgress.getName();
if (seriesProgress.getName() == null || seriesProgress.getName().isEmpty()
|| DEFAULT_SERIES_NAME.equals(seriesProgress.getName())) {
return I18N.races();
} else {
return seriesProgress.getName();
}
}
private String caculateSeriesNameMedium() {
@@ -1,6 +1,7 @@
package com.sap.sailing.gwt.home.desktop.partials.old.leaderboard;
import java.util.Date;
import java.util.Objects;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
@@ -10,8 +11,6 @@ import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.uibinder.client.UiBinder;
@@ -24,14 +23,11 @@ import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.gwt.home.desktop.partials.old.EventRegattaLeaderboardResources;
import com.sap.sailing.gwt.home.desktop.partials.old.LeaderboardDelegate;
import com.sap.sailing.gwt.settings.client.leaderboard.MultiRaceLeaderboardSettings;
import com.sap.sailing.gwt.ui.client.DebugIdHelper;
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.leaderboard.MultiRaceLeaderboardPanel;
@@ -81,28 +77,6 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
fullscreenAnchor.setTitle(StringMessages.INSTANCE.openFullscreenView());
this.delegate = delegate;
this.setupFullscreenDelegate();
// adding handler on page loading
this.addAttachHandler(new Handler() {
@Override
public void onAttachOrDetach(AttachEvent event) {
if (event.isAttached() && leaderboardPanel != null) {
// waiting while leaderboard is loaded
leaderboardPanel.addLeaderboardUpdateListener(new LeaderboardUpdateListener() {
@Override
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
// If race or regatta is live then check button by default
if (leaderboard.hasLiveRace(autoRefreshTimer.getLiveTimePointInMillis())) {
turnOnAutoPlay();
}
}
@Override
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) { }
});
}
}
});
}
private void setupFullscreenDelegate() {
@@ -142,14 +116,19 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
if (autoRefreshTimer.getPlayState() != PlayStates.Playing) {
autoRefreshTimer.setPlayMode(PlayModes.Live);
}
// Styles applied each time because of tabs switching. In this case play mode stays as Playing but styling is lost
autoRefreshAnchor.addStyleName(local_res.css().regattaleaderboard_meta_reload_live());
if (delegate != null) {
delegate.getAutoRefreshControl().addStyleName(local_res.css().regattaleaderboard_meta_reload_live());
}
// Styles applied each time because of tabs switching. In this case play mode stays as Playing but style is lost
this.updateAutoRefreshStylesDependingOnPlayState();
}
private void updateAutoRefreshStylesDependingOnPlayState() {
final boolean isPlaying = autoRefreshTimer != null && autoRefreshTimer.getPlayState() == PlayStates.Playing;
final String isPlayingStyleName = local_res.css().regattaleaderboard_meta_reload_live();
this.autoRefreshAnchor.setStyleName(isPlayingStyleName, isPlaying);
if (Objects.nonNull(delegate)) {
this.delegate.getAutoRefreshControl().setStyleName(isPlayingStyleName, isPlaying);
}
}
@UiHandler("autoRefreshAnchor")
void toogleAutoRefreshClicked(ClickEvent event) {
autoRefreshAnchor.removeStyleName(local_res.css().regattaleaderboard_meta_reload_live());
@@ -161,21 +140,10 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
if (autoRefreshTimer != null) {
if (autoRefreshTimer.getPlayState() == PlayStates.Playing) {
autoRefreshTimer.pause();
// autoRefreshAnchor.getElement().getStyle().setBackgroundColor("#8ab54e");
// autoRefreshAnchor.addStyleName(local_res.css().regattaleaderboard_meta_reload_playing());
// if (delegate != null) {
// delegate.getAutoRefreshControl().getElement().getStyle().setBackgroundColor("#8ab54e");
// delegate.getAutoRefreshControl().addStyleName(local_res.css().regattaleaderboard_meta_reload_playing());
// }
} else {
// playing the standalone leaderboard means putting it into live mode
autoRefreshTimer.setPlayMode(PlayModes.Live);
// autoRefreshAnchor.getElement().getStyle().setBackgroundColor("red");
autoRefreshAnchor.addStyleName(local_res.css().regattaleaderboard_meta_reload_live());
if (delegate != null) {
// delegate.getAutoRefreshControl().getElement().getStyle().setBackgroundColor("red");
delegate.getAutoRefreshControl().addStyleName(local_res.css().regattaleaderboard_meta_reload_live());
}
this.updateAutoRefreshStylesDependingOnPlayState();
}
}
}
@@ -233,12 +201,13 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
if (leaderboardPanel.getComponentContext() == null) {
throw new IllegalStateException("Leaderboard Component with null Context");
}
this.updateAutoRefreshStylesDependingOnPlayState();
oldLeaderboardPanel.add(leaderboardPanel);
leaderboardPanel.addBusyStateChangeListener(this);
}
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
boolean hasLiveRace = leaderboardPanel.hasLiveRace();
final boolean hasLiveRace = leaderboardPanel.hasLiveRace();
if (leaderboard != null) {
String comment = leaderboard.getComment() != null ? leaderboard.getComment() : "";
String scoringScheme = leaderboard.scoringScheme != null ? ScoringSchemeTypeFormatter.getDescription(leaderboard.scoringScheme, StringMessages.INSTANCE) : "";
@@ -276,6 +245,9 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
setVisible(delegate.getLastScoringUpdateTimeElement(), !hasLiveRace);
setVisible(delegate.getScoringSchemeElement(), true);
}
if (hasLiveRace) {
turnOnAutoPlay();
}
}
}
@@ -36,7 +36,7 @@ public class StandingsListCompetitor extends UIObject {
teamIdUi.setInnerText(competitor.getSailID());
teamNameUi.setInnerText(competitor.getName());
String pointsString = i18n.pointsValue(item.getNetPoints());
if(showRaceCounts) {
if (showRaceCounts) {
pointsString += " (" + i18n.racesCount(item.getRaceCount()) + ")";
}
pointsUi.setInnerText(pointsString);
@@ -8,11 +8,13 @@ import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.gwt.common.client.controls.tabbar.TabView;
import com.sap.sailing.gwt.home.communication.event.GetLiveRacesForEventAction;
import com.sap.sailing.gwt.home.communication.event.GetRegattaListViewAction;
import com.sap.sailing.gwt.home.communication.event.statistics.GetEventStatisticsAction;
import com.sap.sailing.gwt.home.communication.regatta.RegattaWithProgressDTO;
import com.sap.sailing.gwt.home.desktop.partials.eventdescription.EventDescription;
import com.sap.sailing.gwt.home.desktop.partials.eventstage.EventOverviewStage;
import com.sap.sailing.gwt.home.desktop.partials.liveraces.LiveRacesList;
import com.sap.sailing.gwt.home.desktop.partials.multiregattalist.MultiRegattaList;
@@ -41,6 +43,7 @@ public class MultiregattaOverviewTabView extends Composite implements Multiregat
private static MyBinder ourUiBinder = GWT.create(MyBinder.class);
@UiField(provided = true) EventOverviewStage stageUi;
@UiField SimplePanel descriptionUi;
@UiField(provided = true) LiveRacesList liveRacesListUi;
@UiField(provided = true) DropdownFilter<String> boatCategoryFilterUi;
@UiField(provided = true) MultiRegattaList regattaListUi;
@@ -71,7 +74,16 @@ public class MultiregattaOverviewTabView extends Composite implements Multiregat
boatCategoryFilterUi = new DropdownFilter<String>(StringMessages.INSTANCE.all(), regattaFilterList);
regattaListUi = new MultiRegattaList(currentPresenter, false);
statisticsBoxUi = new EventStatisticsBox(true, new DesktopStatisticsBoxView());
initWidget(ourUiBinder.createAndBindUi(this));
final String description = currentPresenter.getEventDTO().getDescription();
if (description != null) {
descriptionUi.add(new EventDescription(description));
} else {
descriptionUi.removeFromParent();
}
raceOfficeSectionUi.addLink(StringMessages.INSTANCE.racesOverview(), currentPresenter.getRegattaOverviewLink());
RefreshManager refreshManager = new RefreshManagerWithErrorAndBusy(this, contentArea, currentPresenter.getDispatch(), currentPresenter.getErrorAndBusyClientFactory());
@@ -1,17 +1,18 @@
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:dp='urn:import:com.sap.sailing.gwt.home.desktop.partials'
xmlns:sp='urn:import:com.sap.sailing.gwt.home.shared.partials'>
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<g:HTMLPanel>
<dp:eventstage.EventOverviewStage ui:field="stageUi" />
<div class="{res.mediaCss.grid}">
<div class="{res.mediaCss.small12} {res.mediaCss.columns} {res.mainCss.spacermarginbottommedium}">
<g:SimplePanel ui:field="descriptionUi" addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" />
<dp:liveraces.LiveRacesList ui:field="liveRacesListUi" addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" />
<dp:regattanavigation.DropdownFilter ui:field="boatCategoryFilterUi"
addStyleNames="{res.mainCss.spacermargintopmedium} {res.mainCss.spacermarginbottombig}"/>
<dp:multiregattalist.MultiRegattaList ui:field="regattaListUi" />
<sp:statistics.EventStatisticsBox addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" ui:field="statisticsBoxUi"/>
addStyleNames="{res.mainCss.spacermargintopmedium} {res.mainCss.spacermarginbottombig}"/>
<dp:multiregattalist.MultiRegattaList ui:field="regattaListUi" />
<sp:statistics.EventStatisticsBox addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" ui:field="statisticsBoxUi"/>
</div>
</div>
<dp:raceoffice.RaceOfficeSection ui:field="raceOfficeSectionUi" />
@@ -131,6 +131,7 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView<R
}
});
} else {
leaderboardPanel.loadCompleteLeaderboard(false);
leaderboardConsumer.consume(leaderboardPanel);
}
} else {
@@ -17,6 +17,7 @@ import com.sap.sailing.gwt.home.communication.event.statistics.GetEventStatistic
import com.sap.sailing.gwt.home.communication.eventview.HasRegattaMetadata;
import com.sap.sailing.gwt.home.communication.eventview.HasRegattaMetadata.RegattaState;
import com.sap.sailing.gwt.home.communication.regatta.RegattaWithProgressDTO;
import com.sap.sailing.gwt.home.desktop.partials.eventdescription.EventDescription;
import com.sap.sailing.gwt.home.desktop.partials.eventstage.EventOverviewStage;
import com.sap.sailing.gwt.home.desktop.partials.liveraces.LiveRacesList;
import com.sap.sailing.gwt.home.desktop.partials.multiregattalist.MultiRegattaListItem;
@@ -42,10 +43,11 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView<
private static MyBinder ourUiBinder = GWT.create(MyBinder.class);
private Presenter currentPresenter;
@UiField SimplePanel regattaInfoContainerUi;
@UiField(provided = true) LiveRacesList liveRacesListUi;
@UiField(provided = true) EventOverviewStage stageUi;
@UiField SimplePanel descriptionUi;
@UiField(provided = true) StandingsList standingsUi;
@UiField(provided = true) EventStatisticsBox statisticsBoxUi;
@UiField RaceOfficeSection raceOfficeSectionUi;
@@ -71,6 +73,16 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView<
final HasRegattaMetadata regattaMetadata = currentPresenter.getRegattaMetadata();
standingsUi = new StandingsList(regattaMetadata != null && regattaMetadata.getState() == RegattaState.FINISHED, currentPresenter.getRegattaLeaderboardNavigation(currentPresenter.getRegattaId()));
initWidget(ourUiBinder.createAndBindUi(this));
if (!currentPresenter.showRegattaMetadata()) {
final String description = currentPresenter.getEventDTO().getDescription();
if (description != null) {
descriptionUi.add(new EventDescription(description));
} else {
descriptionUi.removeFromParent();
}
}
raceOfficeSectionUi.addLink(StringMessages.INSTANCE.racesOverview(), currentPresenter.getRegattaOverviewLink());
RefreshManager refreshManager = new RefreshManagerWithErrorAndBusy(this, contentArea, currentPresenter.getDispatch(), currentPresenter.getErrorAndBusyClientFactory());
@@ -1,15 +1,16 @@
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:dp='urn:import:com.sap.sailing.gwt.home.desktop.partials'
xmlns:sp='urn:import:com.sap.sailing.gwt.home.shared.partials'>
xmlns:dp='urn:import:com.sap.sailing.gwt.home.desktop.partials'
xmlns:sp='urn:import:com.sap.sailing.gwt.home.shared.partials'>
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<g:HTMLPanel>
<div class="{res.mediaCss.grid}">
<g:SimplePanel addStyleNames="{res.mediaCss.small12} {res.mediaCss.columns}" ui:field="regattaInfoContainerUi"/>
<dp:liveraces.LiveRacesList ui:field="liveRacesListUi" addStyleNames="{res.mediaCss.small12} {res.mediaCss.columns} {res.mainCss.spacermargintopmedium}" />
<dp:liveraces.LiveRacesList ui:field="liveRacesListUi" addStyleNames="{res.mediaCss.small12} {res.mediaCss.columns} {res.mainCss.spacermargintopmedium}" />
</div>
<dp:eventstage.EventOverviewStage ui:field="stageUi" addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopsmall}" />
<div class="{res.mediaCss.grid}">
<div class="{res.mediaCss.small12} {res.mediaCss.columns}">
<g:SimplePanel ui:field="descriptionUi" addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" />
<dp:standings.StandingsList ui:field="standingsUi" addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium}" />
<sp:statistics.EventStatisticsBox addStyleNames="{res.mainCss.spacermargin} {res.mainCss.spacermargintopmedium} {res.mainCss.spacermarginbottommedium}" ui:field="statisticsBoxUi" />
</div>
@@ -22,6 +22,8 @@
state of the currently opened page to get lost.
After a sign out (and another page reload) the language preference will stop to take effect.
</li>
<li>If available, an event description is now shown on event overview pages.
</li>
</ul>
<h5 class="articleSubheadline">September 2017</h5>
@@ -0,0 +1,24 @@
package com.sap.sailing.gwt.home.mobile.partials.eventdescription;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.sap.sailing.gwt.home.mobile.partials.section.MobileSection;
import com.sap.sailing.gwt.home.mobile.partials.sectionHeader.SectionHeaderContent;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class EventDescription extends Composite {
public EventDescription(String description) {
final MobileSection section = new MobileSection();
SectionHeaderContent header = new SectionHeaderContent();
header.setSectionTitle(StringMessages.INSTANCE.description());
section.addHeader(header);
final Label content = new Label(description);
content.getElement().getStyle().setPaddingTop(0.5, Unit.EM);
content.getElement().getStyle().setPaddingBottom(0.75, Unit.EM);
section.addContent(content);
initWidget(section);
}
}

Some files were not shown because too many files have changed in this diff Show More