Merge branch 'master' into bug5085

This commit is contained in:
Steffen Schaefer
2019-10-18 08:20:45 +02:00
24 changed files with 344 additions and 95 deletions
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.common.orc;
import java.io.Serializable;
import com.sap.sse.common.Distance;
/**
@@ -12,7 +14,7 @@ import com.sap.sse.common.Distance;
* @author Daniel Lisunkin (i505543)
*
*/
public interface ORCPerformanceCurveCourse {
public interface ORCPerformanceCurveCourse extends Serializable {
/**
* @return {@link Iterable} object containing all {@link ORCPerformanceCurveLeg}s of an implementing instance of
@@ -1,9 +1,11 @@
package com.sap.sailing.domain.common.orc;
import java.io.Serializable;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
public interface ORCPerformanceCurveLeg {
public interface ORCPerformanceCurveLeg extends Serializable {
Distance getLength();
@@ -1,5 +1,26 @@
package com.sap.sailing.domain.common.orc;
import com.sap.sailing.domain.common.LegType;
public enum ORCPerformanceCurveLegTypes {
TWA, WINDWARD_LEEWARD, LONG_DISTANCE, NON_SPINNAKER, CIRCULAR_RANDOM
TWA, WINDWARD_LEEWARD, LONG_DISTANCE, NON_SPINNAKER, CIRCULAR_RANDOM;
/**
* Determines a {@link LegType} to use for, e.g., determining a leg's distance. If no ORC leg type is provided
* ({@code null}, or the type is {@link #TWA} or {@link #WINDWARD_LEEWARD}, the result will be {@code null}, telling
* the system to compute the real leg type by itself, leading to a windward projection for upwind and downwind legs,
* and rhumb-line projection for reaching legs.
* <p>
*
* For all other ORC leg types, rhumb-line projection is forced by returning {@link LegType#REACHING}.
*/
static public LegType getLegType(ORCPerformanceCurveLegTypes orcLegType) {
final LegType result;
if (orcLegType == null || orcLegType == TWA || orcLegType == WINDWARD_LEEWARD) {
result = null;
} else {
result = LegType.REACHING;
}
return result;
}
}
@@ -8,6 +8,7 @@ import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLeg;
import com.sap.sse.common.Util;
public class ORCPerformanceCurveCourseImpl implements ORCPerformanceCurveCourse {
private static final long serialVersionUID = -8975425405727779768L;
private final Iterable<ORCPerformanceCurveLeg> legs;
public ORCPerformanceCurveCourseImpl(Iterable<ORCPerformanceCurveLeg> legs) {
@@ -1,13 +1,11 @@
package com.sap.sailing.domain.common.orc.impl;
import java.io.Serializable;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLeg;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
public class ORCPerformanceCurveLegImpl implements Serializable, ORCPerformanceCurveLeg {
public class ORCPerformanceCurveLegImpl implements ORCPerformanceCurveLeg {
private static final long serialVersionUID = -1402717786643975976L;
private final Distance length;
private final Bearing twa;
@@ -37,7 +37,7 @@ extends Track<EventT>, WithID {
boolean add(EventT event);
/**
* Add a {@link VisitorT} as a listener for additions.
* Add a {@link VisitorT} as a listener for additions. Listeners won't be serialized together with this log.
*/
void addListener(VisitorT listener);
@@ -5,9 +5,11 @@ import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.mockito.Matchers;
import org.junit.Test;
import org.mockito.Mockito;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveCourse;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLeg;
@@ -16,7 +18,9 @@ import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveCourseImpl;
import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveLegImpl;
import com.sap.sailing.domain.orc.impl.ORCPerformanceCurveLegAdapter;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.DegreeBearingImpl;
@@ -146,20 +150,36 @@ public class TestORCPerformanceCurveCourse {
double accuracy = 0.000000001;
List<ORCPerformanceCurveLeg> legs = new ArrayList<>();
final TrackedLeg trackedLeg1 = Mockito.mock(TrackedLeg.class);
Mockito.when(trackedLeg1.getWindwardDistance()).thenReturn(new NauticalMileDistance(1));
Mockito.when(trackedLeg1.getWindwardDistance(Matchers.any(LegType.class), Matchers.any(TimePoint.class),
Matchers.any(WindLegTypeAndLegBearingAndORCPerformanceCurveCache.class))).thenReturn(new NauticalMileDistance(1));
final TrackedLeg trackedLeg2 = Mockito.mock(TrackedLeg.class);
Mockito.when(trackedLeg2.getWindwardDistance()).thenReturn(new NauticalMileDistance(2));
Mockito.when(trackedLeg2.getWindwardDistance(Matchers.any(LegType.class), Matchers.any(TimePoint.class),
Matchers.any(WindLegTypeAndLegBearingAndORCPerformanceCurveCache.class))).thenReturn(new NauticalMileDistance(2));
legs.add(new ORCPerformanceCurveLegAdapter(trackedLeg1) {
private static final long serialVersionUID = 2173629869089646863L;
@Override
public Bearing getTwa() {
return new DegreeBearingImpl(0);
}
@Override
public ORCPerformanceCurveLegTypes getType() {
return ORCPerformanceCurveLegTypes.TWA;
}
});
legs.add(new ORCPerformanceCurveLegAdapter(trackedLeg2) {
private static final long serialVersionUID = 5651091430433706403L;
@Override
public Bearing getTwa() {
return new DegreeBearingImpl(0);
}
@Override
public ORCPerformanceCurveLegTypes getType() {
return ORCPerformanceCurveLegTypes.TWA;
}
});
ORCPerformanceCurveCourse course = new ORCPerformanceCurveCourseImpl(legs);
// case 0: no leg finished, 40.0% of current leg
@@ -236,6 +236,10 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
TrackedRace trackedRace = getTrackedRace(fleet);
if (trackedRace != null) {
trackedRace.attachRaceLog(raceLog);
RegattaLog regattaLog = getRegattaLog();
if (regattaLog != null) {
trackedRace.attachRegattaLog(regattaLog);
}
}
}, /* prio */0);
// because this will add the race log to the tracked race's attachedRaceLogs collection again, and
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.orc.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
@@ -82,9 +84,9 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
private final Map<Serializable, Competitor> competitorsById;
private final RaceLogEventVisitor certificatesFromRaceLogUpdater;
private transient RaceLogEventVisitor certificatesAndCourseAndScratchBoatFromRaceLogUpdater;
private final RegattaLogEventVisitor certificatesFromRegattaLogUpdater;
private transient RegattaLogEventVisitor certificatesFromRegattaLogUpdater;
/**
* Updated by an observer pattern that watches all {@link RaceLog}s {@link TrackedRace#getAttachedRaceLogs() attached} to the
@@ -108,49 +110,67 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
super(trackedRace);
boatsById = initBoatsById();
competitorsById = initCompetitorsById();
certificatesFromRaceLogUpdater = createCertificatesFromRaceLogAndCourseUpdater();
initializeListeners();
updateCertificatesFromLogs();
updateCourseFromRaceLogs();
}
private void initializeListeners() {
certificatesAndCourseAndScratchBoatFromRaceLogUpdater = createCertificatesFromRaceLogAndCourseAndScratchBoatUpdater();
certificatesFromRegattaLogUpdater = createCertificatesFromRegattaLogUpdater();
if (trackedRace != null) {
trackedRace.addListener(new AbstractRaceChangeListener() {
@Override
public void regattaLogAttached(RegattaLog regattaLog) {
regattaLog.addListener(certificatesFromRegattaLogUpdater);
updateCertificatesFromLogs();
}
if (getTrackedRace() != null) {
addTrackedRaceListener(getTrackedRace());
for (final RegattaLog regattaLog : getTrackedRace().getAttachedRegattaLogs()) {
regattaLog.addListener(certificatesFromRegattaLogUpdater);
}
for (final RaceLog raceLog : getTrackedRace().getAttachedRaceLogs()) {
raceLog.addListener(certificatesAndCourseAndScratchBoatFromRaceLogUpdater);
}
}
}
@Override
public void raceLogAttached(RaceLog raceLog) {
raceLog.addListener(certificatesFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
updateCourseFromRaceLogs();
}
@Override
public void raceLogDetached(RaceLog raceLog) {
raceLog.removeListener(certificatesFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
updateCourseFromRaceLogs();
}
});
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
ois.registerValidation(()->initializeListeners(), /* prio */ -1);
}
private void addTrackedRaceListener(TrackedRace trackedRace) {
trackedRace.addListener(new AbstractRaceChangeListener() {
@Override
public void regattaLogAttached(RegattaLog regattaLog) {
regattaLog.addListener(certificatesFromRegattaLogUpdater);
updateCertificatesFromLogs();
}
@Override
public void raceLogAttached(RaceLog raceLog) {
raceLog.addListener(certificatesAndCourseAndScratchBoatFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
updateCourseFromRaceLogs();
}
@Override
public void raceLogDetached(RaceLog raceLog) {
raceLog.removeListener(certificatesAndCourseAndScratchBoatFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
updateCourseFromRaceLogs();
}
// see bug 5130: don't add as a course change listener on the course but on the race because
// only this way will the TrackedRace have aligned its TrackedLeg objects before triggering
// these hooks.
trackedRace.addListener(new AbstractRaceChangeListener() {
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
updateCourseFromRaceLogs();
}
@Override
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
updateCourseFromRaceLogs();
}
});
}
updateCertificatesFromLogs();
updateCourseFromRaceLogs();
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
updateCourseFromRaceLogs();
}
@Override
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
updateCourseFromRaceLogs();
}
});
}
public ORCCertificate getCertificate(Boat boat) {
@@ -181,7 +201,7 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
return result;
}
private RaceLogEventVisitor createCertificatesFromRaceLogAndCourseUpdater() {
private RaceLogEventVisitor createCertificatesFromRaceLogAndCourseAndScratchBoatUpdater() {
return new BaseRaceLogEventVisitor() {
@Override
public void visit(RaceLogORCLegDataEvent orcLegDataEventImpl) {
@@ -239,30 +259,30 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
* replaced by a new one that has the updated mapping of boats to their certificates.
*/
private void updateCertificatesFromLogs() {
final Map<Boat, ORCCertificate> newCertificates = new HashMap<>();
if (getTrackedRace() != null) {
final Map<Boat, ORCCertificate> newCertificates = new HashMap<>();
for (final RegattaLog regattaLog : getTrackedRace().getAttachedRegattaLogs()) {
newCertificates.putAll(new RegattaLogORCCertificateAssignmentFinder(regattaLog, boatsById).analyze());
}
for (final RaceLog raceLog : getTrackedRace().getAttachedRaceLogs()) {
newCertificates.putAll(new RaceLogORCCertificateAssignmentFinder(raceLog, boatsById).analyze());
}
}
certificates = newCertificates;
Duration minGPH = new MillisecondsDurationImpl(Long.MAX_VALUE);
Boat boatWithMinGPH = null;
for (final Entry<Boat, ORCCertificate> e : certificates.entrySet()) {
if (e.getValue().getGPH().compareTo(minGPH) < 0) {
boatWithMinGPH = e.getKey();
minGPH = e.getValue().getGPH();
certificates = newCertificates;
Duration minGPH = new MillisecondsDurationImpl(Long.MAX_VALUE);
Boat boatWithMinGPH = null;
for (final Entry<Boat, ORCCertificate> e : certificates.entrySet()) {
if (e.getValue().getGPH().compareTo(minGPH) < 0) {
boatWithMinGPH = e.getKey();
minGPH = e.getValue().getGPH();
}
}
boatWithLeastGPH = boatWithMinGPH;
}
boatWithLeastGPH = boatWithMinGPH;
}
private void updateCourseFromRaceLogs() {
final Map<Integer, ORCPerformanceCurveLeg> legsWithDefinitions = new HashMap<>();
if (getTrackedRace() != null) {
final Map<Integer, ORCPerformanceCurveLeg> legsWithDefinitions = new HashMap<>();
for (final RaceLog raceLog : getTrackedRace().getAttachedRaceLogs()) {
legsWithDefinitions.putAll(new RaceLogORCLegDataAnalyzer(raceLog).analyze());
}
@@ -321,7 +341,7 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
// use windward projection in case we deem the current leg an upwind or downwind leg
shareOfCurrentLeg = 1.0
- trackedLegOfCompetitor.getWindwardDistanceToGo(legType, timePoint, WindPositionMode.LEG_MIDDLE, cache).divide(
trackedLegOfCompetitor.getTrackedLeg().getWindwardDistance(timePoint, cache));
trackedLegOfCompetitor.getTrackedLeg().getWindwardDistance(legType, timePoint, cache));
result = totalCourse.subcourse(zeroBasedIndexOfCurrentLeg, shareOfCurrentLeg);
}
}
@@ -682,4 +702,11 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki
}
return timeForImpliedWindCalculation;
}
@Override
protected LegType getLegTypeForRanking(TrackedLeg trackedLeg) {
final int zeroBasedLegIndex = trackedLeg.getLeg().getZeroBasedIndexOfStartWaypoint();
final ORCPerformanceCurveLeg orcLeg = Util.get(getTotalCourse().getLegs(), zeroBasedLegIndex);
return ORCPerformanceCurveLegTypes.getLegType(orcLeg.getType());
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.orc.impl;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLeg;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
@@ -21,6 +22,7 @@ import com.sap.sse.common.TimePoint;
* be of type {@link ORCPerformanceCurveLegTypes#LONG_DISTANCE}, and {@link #getTwa()} will return {@code null}.
*/
public class ORCPerformanceCurveLegAdapter implements ORCPerformanceCurveLeg {
private static final long serialVersionUID = -6432064480098807397L;
private final TrackedLeg trackedLeg;
public ORCPerformanceCurveLegAdapter(TrackedLeg trackedLeg) {
@@ -29,11 +31,13 @@ public class ORCPerformanceCurveLegAdapter implements ORCPerformanceCurveLeg {
@Override
public Distance getLength() {
return trackedLeg.getWindwardDistance();
final TimePoint referenceTimePoint = trackedLeg.getReferenceTimePoint();
return trackedLeg.getWindwardDistance(ORCPerformanceCurveLegTypes.getLegType(getType()), referenceTimePoint,
new LeaderboardDTOCalculationReuseCache(referenceTimePoint));
}
public Distance getLength(WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return trackedLeg.getWindwardDistance(cache);
return trackedLeg.getWindwardDistance(ORCPerformanceCurveLegTypes.getLegType(getType()), trackedLeg.getReferenceTimePoint(), cache);
}
private Wind getWind() {
@@ -68,6 +72,8 @@ public class ORCPerformanceCurveLegAdapter implements ORCPerformanceCurveLeg {
@Override
public ORCPerformanceCurveLeg scale(final double share) {
return new ORCPerformanceCurveLegAdapter(trackedLeg) {
private static final long serialVersionUID = -6724721873285438431L;
@Override
public Distance getLength() {
return ORCPerformanceCurveLegAdapter.this.getLength().scale(share);
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.tracking.MarkPassing;
@@ -435,19 +436,21 @@ public abstract class AbstractRankingMetric implements RankingMetric {
for (final TrackedLeg trackedLeg : getTrackedRace().getTrackedLegs()) {
count = count || trackedLeg.getLeg().getFrom() == from;
if (count) {
final LegType legTypeForRanking = getLegTypeForRanking(trackedLeg);
final TrackedLegOfCompetitor trackedLegOfCompetitor = trackedLeg.getTrackedLeg(competitor);
if (isAssumedToHaveStartedLeg(timePoint, trackedLegOfCompetitor)) {
if (!isAssumedToHaveFinishedLeg(timePoint, trackedLegOfCompetitor)) {
// partial distance sailed:
final Position estimatedPosition = getTrackedRace().getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */ true);
if (estimatedPosition != null) {
final Distance windwardDistanceFromLegStart = trackedLeg.getWindwardDistanceFromLegStart(estimatedPosition, cache);
final Distance windwardDistanceFromLegStart = trackedLeg.getWindwardDistanceFromLegStart(legTypeForRanking,
estimatedPosition, cache);
if (windwardDistanceFromLegStart == null) {
// probably the leg start position is not known; therefore, distance cannot be determined; return null:
d = null;
break;
}
final Distance legWindwardDistance = trackedLeg.getWindwardDistance(cache);
final Distance legWindwardDistance = trackedLeg.getWindwardDistance(legTypeForRanking, cache);
if (legWindwardDistance != null && legWindwardDistance.compareTo(windwardDistanceFromLegStart) < 0) {
d = d.add(legWindwardDistance);
} else {
@@ -460,7 +463,7 @@ public abstract class AbstractRankingMetric implements RankingMetric {
}
break;
} else {
final Distance legWindwardDistance = trackedLeg.getWindwardDistance(cache);
final Distance legWindwardDistance = trackedLeg.getWindwardDistance(legTypeForRanking, cache);
if (legWindwardDistance != null) {
d = d.add(legWindwardDistance);
}
@@ -476,6 +479,17 @@ public abstract class AbstractRankingMetric implements RankingMetric {
return result;
}
/**
* Defines the leg type to use for ranking competitors in {@code trackedLeg}. This default implementation returns
* {@code null}, meaning to infer the leg type based on the wind direction on that leg. Specializations may override
* this, e.g., to use a leg type matching any ranking specifications made with the ranking metric for that leg, such
* as projecting to the rhumb line instead of to the wind in certain circumstances, even if the leg may be classified
* as an upwind or downwind leg based on the wind detected on it.
*/
protected LegType getLegTypeForRanking(TrackedLeg trackedLeg) {
return null;
}
/**
* The {@link Competitor} of the {@code trackedLegOfCompetitor} is assumed to have started the leg specified by
* {@code trackedLegOfCompetitor} if the competitor has a mark passing for the leg's end waypoint or any waypoint
@@ -138,6 +138,14 @@ public interface TrackedLeg extends Serializable {
*/
Distance getWindwardDistance(TimePoint at, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
* Like {@link #getWindwardDistance(TimePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache)}, only that
* the {@code legType} can optionally be specified explicitly; if not {@code null}, instead of inferring the leg type
* from the wind direction, the leg type provided is assumed. This way it is possible to explicitly evaluate the distance
* based on rhumb line, namely by providing {@link LegType#REACHING} as {@code legType}.
*/
Distance getWindwardDistance(LegType legType, TimePoint middle, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
Distance getAbsoluteWindwardDistance(TimePoint at, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
@@ -155,6 +163,14 @@ public interface TrackedLeg extends Serializable {
*/
Distance getWindwardDistance(WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
* Same as {@link #getWindwardDistance(WindLegTypeAndLegBearingAndORCPerformanceCurveCache)}, only that
* the {@code legType} can optionally be specified explicitly; if not {@code null}, instead of inferring the leg type
* from the wind direction, the leg type provided is assumed. This way it is possible to explicitly evaluate the distance
* based on rhumb line, namely by providing {@link LegType#REACHING} as {@code legType}.
*/
Distance getWindwardDistance(LegType legType, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
Distance getAbsoluteWindwardDistance(WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
@@ -187,6 +203,14 @@ public interface TrackedLeg extends Serializable {
Distance getWindwardDistanceFromLegStart(Position pos, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
* Like {@link #getWindwardDistanceFromLegStart(Position, WindLegTypeAndLegBearingAndORCPerformanceCurveCache)}, only that
* the {@code legType} can optionally be specified explicitly; if not {@code null}, instead of inferring the leg type
* from the wind direction, the leg type provided is assumed. This way it is possible to explicitly evaluate the distance
* based on rhumb line, namely by providing {@link LegType#REACHING} as {@code legType}.
*/
Distance getWindwardDistanceFromLegStart(LegType legType, Position pos, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/**
* Determines an average true wind direction for this leg; it does so by querying the tracked race for
* wind data at the leg's middle around a reference time point which is defined by the mark passings
@@ -404,12 +404,18 @@ public class TrackedLegImpl implements TrackedLeg {
@Override
public Distance getWindwardDistanceFromLegStart(Position pos, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final TimePoint referenceTimePoint = getReferenceTimePoint();
return getWindwardDistanceFromLegStart(pos, referenceTimePoint, cache);
return getWindwardDistanceFromLegStart(/* legType==null means infer leg type from wind */ null, pos, cache);
}
private Distance getWindwardDistanceFromLegStart(Position pos, final TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getWindwardDistance(getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint),
@Override
public Distance getWindwardDistanceFromLegStart(LegType legType, Position pos, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final TimePoint referenceTimePoint = getReferenceTimePoint();
return getWindwardDistanceFromLegStart(legType, pos, referenceTimePoint, cache);
}
private Distance getWindwardDistanceFromLegStart(final LegType legType, final Position pos,
final TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getWindwardDistance(legType, getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint),
pos, timePoint, WindPositionMode.LEG_MIDDLE, cache);
}
@@ -440,8 +446,13 @@ public class TrackedLegImpl implements TrackedLeg {
@Override
public Distance getWindwardDistance(WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getWindwardDistance(/* legType==null means infer from wind */ (LegType) null, cache);
}
@Override
public Distance getWindwardDistance(LegType legType, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final TimePoint middle = getReferenceTimePoint();
return getWindwardDistance(middle, cache);
return getWindwardDistance(legType, middle, cache);
}
@Override
@@ -484,9 +495,14 @@ public class TrackedLegImpl implements TrackedLeg {
@Override
public Distance getWindwardDistance(final TimePoint middle, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getWindwardDistance(/* legType==null means infer leg type */ null, middle, cache);
}
@Override
public Distance getWindwardDistance(final LegType legType, final TimePoint middle, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final Position fromPos = getTrackedRace().getApproximatePosition(getLeg().getFrom(), middle);
final Position toPos = getTrackedRace().getApproximatePosition(getLeg().getTo(), middle);
return getWindwardDistance(fromPos, toPos, middle, WindPositionMode.LEG_MIDDLE, cache);
return getWindwardDistance(legType, fromPos, toPos, middle, WindPositionMode.LEG_MIDDLE, cache);
}
@Override
@@ -83,7 +83,15 @@ public abstract class CourseManagementWidget implements IsWidget {
*
*/
public static interface LegGeometrySupplier {
void getLegGeometry(int[] zeroBasedLegNumbers, AsyncCallback<ORCPerformanceCurveLegImpl[]> callback);
/**
* The lengths of the {@code zeroBasedLegNumbers} and {@code orcPerformanceCurveLegTypes} arrays must be equal,
* and the indices bind the values together. The leg geometries will be determined based on the leg type selected.
* If the leg type is {@code null} for a leg, the server-defined leg type will be determined and applied, so that
* windward / leeward legs will have their length computed by projecting onto the wind direction, and reaching legs
* will be judged based on rhumb line distance; the same applies for {@link ORCPerformanceCurveLegTypes#WINDWARD_LEEWARD}
* and {@link ORCPerformanceCurveLegTypes#TWA}-typed legs. For all other leg types, rhumb-line distance is to be computed.
*/
void getLegGeometry(int[] zeroBasedLegNumbers, ORCPerformanceCurveLegTypes[] orcPerformanceCurveLegTypes, AsyncCallback<ORCPerformanceCurveLegImpl[]> callback);
}
public static class SingleLegValidator implements Validator<ORCPerformanceCurveLegImpl> {
@@ -156,7 +156,7 @@ public class ORCPerformanceCurveAllLegsDialog extends DataEntryDialog<ORCPerform
}
private void fetchTrackingBasedDistanceAndTwa() {
legGeometrySupplier.getLegGeometry(IntStream.range(0, waypointList.getList().size()-1).toArray(),
legGeometrySupplier.getLegGeometry(IntStream.range(0, waypointList.getList().size()-1).toArray(), getSelectedLegTypes(),
new AsyncCallback<ORCPerformanceCurveLegImpl[]>() {
@Override
public void onFailure(Throwable caught) {
@@ -190,6 +190,14 @@ public class ORCPerformanceCurveAllLegsDialog extends DataEntryDialog<ORCPerform
return selectedValue == null || selectedValue.equals("null") ? null : ORCPerformanceCurveLegTypes.valueOf(selectedValue);
}
private ORCPerformanceCurveLegTypes[] getSelectedLegTypes() {
final ORCPerformanceCurveLegTypes[] result = new ORCPerformanceCurveLegTypes[legTypeBoxes.length];
for (int i=0; i<legTypeBoxes.length; i++) {
result[i] = getSelectedLegType(i);
}
return result;
}
@Override
protected Widget getAdditionalWidget() {
final Grid result = new Grid(waypointList.getList().size()+1, 7);
@@ -80,7 +80,7 @@ public class ORCPerformanceCurveLegDialog extends DataEntryDialog<ORCPerformance
private void fetchTrackingBasedDistanceAndTwa() {
final int zeroBasedLegIndex = waypointList.getList().indexOf(forLegEndingAt)-1;
legGeometrySupplier.getLegGeometry(new int[] { zeroBasedLegIndex },
legGeometrySupplier.getLegGeometry(new int[] { zeroBasedLegIndex }, new ORCPerformanceCurveLegTypes[] { getSelectedLegType() },
new AsyncCallback<ORCPerformanceCurveLegImpl[]>() {
@Override
public void onFailure(Throwable caught) {
@@ -64,7 +64,8 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel {
@Override
protected LegGeometrySupplier getLegGeometrySupplier() {
return (zeroBasedLegIndices, callback)->sailingService.getLegGeometry(singleSelectedRace, zeroBasedLegIndices, callback);
return (zeroBasedLegIndices, legTypes, callback)->
sailingService.getLegGeometry(singleSelectedRace, zeroBasedLegIndices, legTypes, callback);
}
@Override
@@ -202,6 +202,7 @@ public class RaceLogCourseManagementWidget extends CourseManagementWidget {
@Override
protected LegGeometrySupplier getLegGeometrySupplier() {
return (zeroBasedLegIndices, callback)->sailingService.getLegGeometry(leaderboardName, raceColumnName, fleetName, zeroBasedLegIndices, callback);
return (zeroBasedLegIndices, legTypes, callback)->
sailingService.getLegGeometry(leaderboardName, raceColumnName, fleetName, zeroBasedLegIndices, legTypes, callback);
}
}
@@ -59,6 +59,7 @@ import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.TagDTO;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveLegImpl;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException;
@@ -1195,9 +1196,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
boolean getTrackedRaceIsUsingMarkPassingCalculator(RegattaAndRaceIdentifier regattaNameAndRaceName);
ORCPerformanceCurveLegImpl[] getLegGeometry(String leaderboardName, String raceColumnName, String fleetName,
int[] zeroBasedLegIndices);
int[] zeroBasedLegIndices, ORCPerformanceCurveLegTypes[] legTypes);
ORCPerformanceCurveLegImpl[] getLegGeometry(RegattaAndRaceIdentifier singleSelectedRace, int[] zeroBasedLegIndices);
ORCPerformanceCurveLegImpl[] getLegGeometry(RegattaAndRaceIdentifier singleSelectedRace, int[] zeroBasedLegIndices,
ORCPerformanceCurveLegTypes[] legTypes);
/**
* @throws NotFoundException
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.TagDTO;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveLegImpl;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl.PreciseCompactPosition;
@@ -1131,9 +1132,9 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
void getLegGeometry(String leaderboardName, String raceColumnName, String fleetName, int[] zeroBasedLegIndices,
AsyncCallback<ORCPerformanceCurveLegImpl[]> callback);
ORCPerformanceCurveLegTypes[] legTypes, AsyncCallback<ORCPerformanceCurveLegImpl[]> callback);
void getLegGeometry(RegattaAndRaceIdentifier singleSelectedRace, int[] zeroBasedLegIndices,
void getLegGeometry(RegattaAndRaceIdentifier singleSelectedRace, int[] zeroBasedLegIndices, ORCPerformanceCurveLegTypes[] legTypes,
AsyncCallback<ORCPerformanceCurveLegImpl[]> callback);
void getORCPerformanceCurveLegInfo(String leaderboardName, String raceColumnName, String fleetName,
@@ -3539,15 +3539,15 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
break;
case LEG_GAP_TO_LEADER_IN_SECONDS:
if (trackedLeg != null) {
final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint);
final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint, cache); // TODO use cache
final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getGapToLeaderInOwnTime(rankingInfo, competitor, cache);
result = gapToLeaderInOwnTime == null ? null : gapToLeaderInOwnTime.asSeconds();
}
break;
case CHART_WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD:
if (trackedLeg != null) {
final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint);
Distance distanceToLeader = trackedLeg.getWindwardDistanceToCompetitorFarthestAhead(timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo);
final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint, cache);
Distance distanceToLeader = trackedLeg.getWindwardDistanceToCompetitorFarthestAhead(timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo, cache);
result = (distanceToLeader == null) ? null : distanceToLeader.getMeters();
}
break;
@@ -9379,7 +9379,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
@Override
public ORCPerformanceCurveLegImpl[] getLegGeometry(String leaderboardName, String raceColumnName, String fleetName, int[] zeroBasedLegIndices) {
public ORCPerformanceCurveLegImpl[] getLegGeometry(String leaderboardName, String raceColumnName, String fleetName,
int[] zeroBasedLegIndices, ORCPerformanceCurveLegTypes[] legTypes) {
assert zeroBasedLegIndices.length == legTypes.length;
ORCPerformanceCurveLegImpl[] result = null;
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard != null) {
@@ -9390,8 +9392,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
result = new ORCPerformanceCurveLegImpl[zeroBasedLegIndices.length];
final LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(MillisecondsTimePoint.now());
for (int i=0; i<zeroBasedLegIndices.length; i++) {
result[i] = getLegGeometry(zeroBasedLegIndices[i], trackedRace);
result[i] = getLegGeometry(zeroBasedLegIndices[i], legTypes[i], trackedRace, cache);
}
}
}
@@ -9401,21 +9404,25 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
@Override
public ORCPerformanceCurveLegImpl[] getLegGeometry(RegattaAndRaceIdentifier regattaNameAndRaceName, int[] zeroBasedLegIndices) {
public ORCPerformanceCurveLegImpl[] getLegGeometry(RegattaAndRaceIdentifier regattaNameAndRaceName, int[] zeroBasedLegIndices,
ORCPerformanceCurveLegTypes[] legTypes) {
final LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(MillisecondsTimePoint.now());
final TrackedRace trackedRace = getExistingTrackedRace(regattaNameAndRaceName);
final ORCPerformanceCurveLegImpl[] result = new ORCPerformanceCurveLegImpl[zeroBasedLegIndices.length];
for (int i=0; i<zeroBasedLegIndices.length; i++) {
result[i] = getLegGeometry(zeroBasedLegIndices[i], trackedRace);
result[i] = getLegGeometry(zeroBasedLegIndices[i], legTypes[i], trackedRace, cache);
}
return result;
}
private ORCPerformanceCurveLegImpl getLegGeometry(int zeroBasedLegIndex, final TrackedRace trackedRace) {
private ORCPerformanceCurveLegImpl getLegGeometry(int zeroBasedLegIndex, ORCPerformanceCurveLegTypes legType,
final TrackedRace trackedRace, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final ORCPerformanceCurveLegImpl result;
if (trackedRace != null) {
final Leg leg = trackedRace.getRace().getCourse().getLeg(zeroBasedLegIndex);
final TrackedLeg trackedLeg = trackedRace.getTrackedLeg(leg);
final Distance distance = trackedLeg.getWindwardDistance();
final Distance distance = trackedLeg.getWindwardDistance(ORCPerformanceCurveLegTypes.getLegType(legType),
trackedLeg.getReferenceTimePoint(), cache);
Bearing twa;
try {
twa = trackedLeg.getTWA(trackedLeg.getReferenceTimePoint());
@@ -30,6 +30,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogFinishPo
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogFixedMarkPassingEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogFlagEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogGateLineOpeningTimeEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogORCCertificateAssignmentEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogORCLegDataEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogPassChangeEventSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogPathfinderEventSerializer;
@@ -90,7 +91,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
new RaceLogEndOfTrackingEventDeserializer(competitorDeserializer),
new RaceLogTagEventDeserializer(competitorDeserializer),
new RaceLogORCLegDataEventDeserializer(competitorDeserializer),
new RaceLogORCScratchBoatEventDeserializer(competitorDeserializer));
new RaceLogORCScratchBoatEventDeserializer(competitorDeserializer),
new RaceLogORCCertificateAssignmentEventDeserializer(competitorDeserializer));
}
protected final JsonDeserializer<RaceLogEvent> flagEventDeserializer;
@@ -119,6 +121,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
protected final JsonDeserializer<RaceLogEvent> tagEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcLegDataEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcCertificateAssignmentEventDeserializer;
public RaceLogEventDeserializer(JsonDeserializer<RaceLogEvent> flagEventDeserializer,
JsonDeserializer<RaceLogEvent> startTimeEventDeserializer,
@@ -145,7 +148,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
JsonDeserializer<RaceLogEvent> endOfTrackingEventDeserializer,
JsonDeserializer<RaceLogEvent> tagEventDeserializer,
JsonDeserializer<RaceLogEvent> orcLegDataEventDeserializer,
JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer) {
JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer,
JsonDeserializer<RaceLogEvent> orcCertificateAssignmentEventDeserializer) {
this.flagEventDeserializer = flagEventDeserializer;
this.startTimeEventDeserializer = startTimeEventDeserializer;
this.dependentStartTimeEventDeserializer = dependentStartTimeEventDeserializer;
@@ -172,6 +176,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
this.tagEventDeserializer = tagEventDeserializer;
this.orcLegDataEventDeserializer = orcLegDataEventDeserializer;
this.orcScratchBoatEventDeserializer = orcScratchBoatEventDeserializer;
this.orcCertificateAssignmentEventDeserializer = orcCertificateAssignmentEventDeserializer;
}
protected JsonDeserializer<RaceLogEvent> getDeserializer(JSONObject object) throws JsonDeserializationException {
@@ -227,6 +232,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
return tagEventDeserializer;
} else if (type.equals(RaceLogORCLegDataEventSerializer.VALUE_CLASS)){
return orcLegDataEventDeserializer;
} else if (type.equals(RaceLogORCCertificateAssignmentEventSerializer.VALUE_CLASS)) {
return orcCertificateAssignmentEventDeserializer;
}
throw new JsonDeserializationException(String.format("There is no deserializer defined for event type %s.",
type));
@@ -2,30 +2,41 @@ package com.sap.sailing.server.gateway.deserialization.test.racelog;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.StringReader;
import java.util.UUID;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.junit.Test;
import org.mockito.Mockito;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCCertificateAssignmentEventImpl;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCLegDataEventImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFlagEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogTagEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sailing.domain.common.orc.ORCPerformanceCurveLegTypes;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.orc.ORCCertificatesImporter;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEndOfTrackingEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogFlagEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogORCCertificateAssignmentEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogORCLegDataEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogTagEventDeserializer;
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogUseCompetitorsFromRaceLogEventDeserializer;
@@ -50,7 +61,7 @@ public class RaceLogEventDeserializerTest {
mockitoRaceLogUseCompetitorsFromRaceLogEventDeserializer,
mockitoRaceLogEndOfTrackingEventDeserializer, mockitoRaceLogTagEventDeserializer,
new RaceLogORCLegDataEventDeserializer(competitorDeserializer),
null);
null, new RaceLogORCCertificateAssignmentEventDeserializer(competitorDeserializer));
}
}
@@ -59,7 +70,6 @@ public class RaceLogEventDeserializerTest {
private TimePoint timePoint = TimePoint.BeginningOfTime;
private TimePoint timePoint2 = TimePoint.EndOfTime;
private InnerRaceLogEventDeserializer deserializer = new InnerRaceLogEventDeserializer();
@Test
public void testSerializationAndDeserializationForRaceLogFlagEvent() throws Exception{
@@ -109,6 +119,26 @@ public class RaceLogEventDeserializerTest {
assertEquals(originalEvent.getLength(), newEvent.getLength());
}
@Test
public void testSerializationAndDeserializationForRaceLogORCCertificateAssignmentEvent() throws IOException, ParseException {
final Boat boat = new BoatImpl(UUID.randomUUID(), "Testboot", new BoatClassImpl("Testklasse", BoatClassMasterdata.ORC), "GER 007");
final ORCCertificate certificate = ORCCertificatesImporter.INSTANCE.read(new StringReader("NATCERTN.FILE_ID SAILNUMB NAME TYPE BUILDER DESIGNER YEAR CLUB OWNER ADRS1 ADRS2 C_Type D CREW DD_MM_yyYY HH:MM:SS LOA IMSL DRAFT BMAX DSPL INDEX DA GPH TMF ILCGA PLT-O PLD-O WL6 WL8 WL10 WL12 WL14 WL16 WL20 OL6 OL8 OL10 OL12 OL14 OL16 OL20 CR6 CR8 CR10 CR12 CR14 CR16 CR20 NSP6 NSP8 NSP10 NSP12 NSP14 NSP16 NSP20 OC6 OC8 OC10 OC12 OC14 OC16 OC20 UA6 UA8 UA10 UA12 UA14 UA16 UA20 DA6 DA8 DA10 DA12 DA14 DA16 DA20 UP6 UP8 UP10 UP12 UP14 UP16 UP20 R526 R528 R5210 R5212 R5214 R5216 R5220 R606 R608 R6010 R6012 R6014 R6016 R6020 R756 R758 R7510 R7512 R7514 R7516 R7520 R906 R908 R9010 R9012 R9014 R9016 R9020 R1106 R1108 R11010 R11012 R11014 R11016 R11020 R1206 R1208 R12010 R12012 R12014 R12016 R12020 R1356 R1358 R13510 R13512 R13514 R13516 R13520 R1506 R1508 R15010 R15012 R15014 R15016 R15020 D6 D8 D10 D12 D14 D16 D20 OTNLOW OTNMED OTNHIG ITNLOW ITNMED ITNHIG DH_TOD DH_TOT PLT-I PLD-I TMF-OF PLT2H PLD2H OSN ReferenceNo CDL DSPS WSS MAIN GENOA SYM ASYM OTDLOW OTDMED OTDHIG ITDLOW ITDMED ITDHIG NS_TOD NS_TOT GNSTOD GNSTOT GDHTOD GDHTOT\r\n" +
"AUT017/19noi AUT NOI Sunbeam 40.1 Sunbeam Yachts J&J 2014 Austria CLUB C 789 12 03 2019 15:54:33 11.990 10.960 2.067 3.99 8500. 122.3 0.33 655.6 0.9532 708.2 0.000 0.0 1148.6 906.3 768.6 681.6 629.2 597.0 570.9 1081.1 863.0 740.1 664.4 621.3 598.6 579.4 930.4 739.6 634.3 571.5 532.6 508.0 480.5 930.4 739.6 634.3 571.5 532.6 508.0 480.5 1153.3 850.5 688.3 589.6 538.7 504.5 456.8 44.5 42.6 42.1 41.2 40.5 40.0 40.5 154.1 160.1 167.4 171.2 174.1 175.7 178.3 1101.4 902.4 790.1 719.8 688.5 678.5 681.3 706.8 592.3 524.6 490.4 475.6 470.2 468.3 661.7 559.6 502.6 476.1 462.8 456.1 452.9 630.6 534.7 487.5 465.4 451.0 440.1 428.5 638.5 537.7 487.2 464.0 448.6 433.5 410.8 711.0 581.4 506.6 472.4 453.4 437.2 404.7 754.8 616.9 533.9 486.1 462.0 444.9 411.8 883.0 695.9 592.5 522.9 482.7 460.6 427.5 1036.8 794.8 661.5 576.5 516.0 480.1 442.8 1195.8 910.2 747.1 643.3 569.7 515.5 460.4 0.8624 1.1782 1.3607 0.6570 0.9425 1.1340 0.0 0.0000 0.000 0.0 0.9470 0.000 0.0 633.6 AUT00002508 9.784 9570 36.0 45.7 39.3 0.0 0.0 782.7 572.9 496.1 1027.4 716.2 595.2 633.6 0.9470 655.6 0.9152 0.0 0.0000")).getCertificates().iterator().next();
final RaceLogORCCertificateAssignmentEventImpl originalEvent = new RaceLogORCCertificateAssignmentEventImpl(timePoint, timePoint, author, UUID.randomUUID(), 0, certificate, boat);
RaceLogEventSerializer serializer = (RaceLogEventSerializer) RaceLogEventSerializer.create(CompetitorJsonSerializer.create());
JSONObject object = serializer.serialize(originalEvent);
RaceLogEvent raceLogEvent = deserializer.deserialize(object);
RaceLogORCCertificateAssignmentEventImpl newEvent = (RaceLogORCCertificateAssignmentEventImpl) raceLogEvent;
//assert raceLogEvent has correct values
assertEquals(originalEvent.getTimePoint().toString(), newEvent.getTimePoint().toString());
assertEquals(originalEvent.getAuthor().toString(), newEvent.getAuthor().toString());
assertEquals(originalEvent.getPassId(), newEvent.getPassId());
assertEquals(originalEvent.getClass(), newEvent.getClass());
assertEquals(originalEvent.getId(), newEvent.getId());
assertEquals(originalEvent.getBoatId(), newEvent.getBoatId());
assertEquals(originalEvent.getCertificate().getGPH().asSeconds(), newEvent.getCertificate().getGPH().asSeconds(), 0.00001);
}
@Test
public void testSerializationAndDeserializationForRaceLogEndOfTrackingEvent() throws Exception{
RaceLogEndOfTrackingEventImpl originalEvent = new RaceLogEndOfTrackingEventImpl(timePoint, timePoint2, author, UUID.randomUUID(), 3);
+49
View File
@@ -0,0 +1,49 @@
package com.sap.sse.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.SecondsDurationImpl;
/**
* Provide an ISO timestamp first, then a duration in hh:mm:ss format. The time point resulting from
* adding the latter to the former will be sent to {@code stdout}.<p>
*
* Sample usage:
* <pre>
* $ java -jar TimeAdder.jar 2019-10-13T16:43:00+0200 24:18:57
* Race start Sun Oct 13 16:43:00 CEST 2019 plus elapsed time 24:18:57.000 gives finishing time Mon Oct 14 17:01:57 CEST 2019
* </pre>
* @author Axel Uhl (D043530)
*
*/
public class TimeAdder {
public static void main(String[] args) throws ParseException {
if (args.length != 2) {
usage();
} else {
final String startOfRaceAsString = args[0];
final String elapsedTimeAsString = args[1];
final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
final TimePoint startOfRace = new MillisecondsTimePoint(formatter.parse(startOfRaceAsString));
final String[] elapsedTimeAsHhmmss = elapsedTimeAsString.split(":");
final Duration elapsedTime = new SecondsDurationImpl(3600*Integer.valueOf(elapsedTimeAsHhmmss[0]) +
60*Integer.valueOf(elapsedTimeAsHhmmss[1]) + Integer.valueOf(elapsedTimeAsHhmmss[2]));
System.out.println("Race start "+startOfRace+" plus elapsed time "+elapsedTime+" gives finishing time "+
startOfRace.plus(elapsedTime));
}
}
private static void usage() {
System.err.println("Provide an ISO timestamp first, then a duration in hh:mm:ss format. The time point resulting from\r\n" +
"adding the latter to the former will be sent to {@code stdout}.<p>\r\n" +
"\r\n" +
"Sample usage:\r\n" +
"\r\n" +
" $ java -jar TimeAdder.jar 2019-10-13T16:43:00+0200 24:18:57\r\n" +
" Race start Sun Oct 13 16:43:00 CEST 2019 plus elapsed time 24:18:57.000 gives finishing time Mon Oct 14 17:01:57 CEST 2019\r\n");
}
}