added ORC PCS explicit scratch boat selection support

Change-Id: Ie0bab7789a16476249760b80f3007e010aa48cc4
This commit is contained in:
Axel Uhl committed 2019-09-16 18:12:10 +02:00
1 parent 41c0f95d09
commit ce57e5a808
22 files changed
+345 -55

No files matched your search

@@ -46,9 +46,11 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RegattaLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCCertificateAssignmentEventImpl;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCLegDataEventImpl;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCScratchBoatEventImpl;
import com.sap.sailing.domain.abstractlog.orc.impl.RegattaLogORCCertificateAssignmentEventImpl;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
@@ -1632,12 +1634,20 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
resultEvent = loadRaceLogORCLegDataEvent(createdAt, author, logicalTimePoint, id, passId, dbObject);
} else if (eventClass.equals(RaceLogORCCertificateAssignmentEvent.class.getSimpleName())) {
resultEvent = loadRaceLogORCCertificateAssignmentEvent(createdAt, author, logicalTimePoint, id, passId, dbObject);
} else if (eventClass.equals(RaceLogORCScratchBoatEvent.class.getSimpleName())) {
resultEvent = loadRaceLogORCScratchBoatEvent(createdAt, author, logicalTimePoint, id, passId, dbObject);
} else {
throw new IllegalStateException(String.format("Unknown RaceLogEvent type %s", eventClass));
}
return new Pair<>(resultEvent, dbObjectForUpdate);
}
private RaceLogEvent loadRaceLogORCScratchBoatEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, Integer passId, Document dbObject) {
Serializable competitorId = (Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name());
return new RaceLogORCScratchBoatEventImpl(createdAt, logicalTimePoint, author, id, passId, competitorId);
}
private RaceLogEvent loadRaceLogUseCompetitorsFromRaceLogEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors,
Document dbObject) {
@@ -2038,8 +2048,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(certificateDbObject.toJson(writerSettings)));
final ORCCertificate certificate = new ORCCertificateJsonDeserializer().deserialize(json);
Serializable competitorId = (Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name());
return new RaceLogORCCertificateAssignmentEventImpl(createdAt, logicalTimePoint, author, id, passId, certificate, competitorId);
Serializable boatId = (Serializable) dbObject.get(FieldNames.RACE_LOG_BOAT_ID.name());
return new RaceLogORCCertificateAssignmentEventImpl(createdAt, logicalTimePoint, author, id, passId, certificate, boatId);
}
@Override
@@ -2240,8 +2250,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(certificateDbObject.toJson(writerSettings)));
final ORCCertificate certificate = new ORCCertificateJsonDeserializer().deserialize(json);
Serializable competitorId = (Serializable) dbObject.get(FieldNames.COMPETITOR_ID.name());
return new RegattaLogORCCertificateAssignmentEventImpl(createdAt, logicalTimePoint, author, id, certificate, competitorId);
Serializable boatId = (Serializable) dbObject.get(FieldNames.RACE_LOG_BOAT_ID.name());
return new RegattaLogORCCertificateAssignmentEventImpl(createdAt, logicalTimePoint, author, id, certificate, boatId);
}
/**
@@ -29,6 +29,7 @@ import com.mongodb.client.result.DeleteResult;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RegattaLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
@@ -1010,11 +1011,26 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
Document result = new Document();
storeRaceLogEventProperties(event, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogORCCertificateAssignmentEvent.class.getSimpleName());
result.append(FieldNames.COMPETITOR_ID.name(), event.getBoatId());
result.append(FieldNames.RACE_LOG_BOAT_ID.name(), event.getBoatId());
result.append(FieldNames.ORC_CERTIFICATE.name(), createORCCertificateObject(event.getCertificate()));
return result;
}
public Document storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogORCScratchBoatEvent event) {
Document result = new Document();
storeRaceLogIdentifier(raceLogIdentifier, result);
result.put(FieldNames.RACE_LOG_EVENT.name(), storeORCScratchBoatEvent(event));
return result;
}
private Document storeORCScratchBoatEvent(RaceLogORCScratchBoatEvent event) {
Document result = new Document();
storeRaceLogEventProperties(event, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogORCScratchBoatEvent.class.getSimpleName());
result.append(FieldNames.COMPETITOR_ID.name(), event.getCompetitorId());
return result;
}
private Document storeORCLegDataEvent(RaceLogORCLegDataEvent event) {
Document result = new Document();
storeRaceLogEventProperties(event, result);
@@ -6,6 +6,7 @@ import org.bson.Document;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -202,4 +203,10 @@ public class MongoRaceLogStoreVisitor implements RaceLogEventVisitor {
Document object = mongoObjectFactory.storeRaceLogEntry(raceLogIdentifier, event);
mongoObjectFactory.storeRaceLogEventEvent(object);
}
@Override
public void visit(RaceLogORCScratchBoatEvent event) {
Document object = mongoObjectFactory.storeRaceLogEntry(raceLogIdentifier, event);
mongoObjectFactory.storeRaceLogEventEvent(object);
}
}
@@ -5,6 +5,6 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.Revokable;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
public interface ORCScratchBoatEvent extends RaceLogEvent, Revokable {
public interface RaceLogORCScratchBoatEvent extends RaceLogEvent, Revokable {
Serializable getCompetitorId();
}
@@ -0,0 +1,40 @@
package com.sap.sailing.domain.abstractlog.orc;
import java.io.Serializable;
import java.util.Map;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.base.Competitor;
public class RaceLogORCScratchBoatFinder extends BaseLogAnalyzer<RaceLog, RaceLogEvent, RaceLogEventVisitor, Competitor> {
private static final Logger logger = Logger.getLogger(RaceLogORCScratchBoatFinder.class.getName());
private final Map<Serializable, Competitor> competitorsById;
public RaceLogORCScratchBoatFinder(RaceLog log, Map<Serializable, Competitor> competitorsById) {
super(log);
this.competitorsById = competitorsById;
}
@Override
protected Competitor performAnalysis() {
for (final RaceLogEvent o : getLog().getUnrevokedEventsDescending()) {
if (o instanceof RaceLogORCScratchBoatEvent) {
RaceLogORCScratchBoatEvent event = (RaceLogORCScratchBoatEvent) o;
final Serializable competitorId = event.getCompetitorId();
final Competitor competitor = competitorsById.get(competitorId);
if (competitor != null) {
return competitor;
} else {
logger.warning("Unable to find competitor with ID " + competitorId
+ " as an ORC Performance Curve scratch boat. Race log event is ignored.");
}
}
}
return null;
}
}
@@ -0,0 +1,52 @@
package com.sap.sailing.domain.abstractlog.orc.impl;
import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.AbstractLogEventImpl;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventData;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventDataImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.common.TimePoint;
public class RaceLogORCScratchBoatEventImpl extends AbstractLogEventImpl<RaceLogEventVisitor> implements RaceLogORCScratchBoatEvent {
private static final long serialVersionUID = 3506411337361892600L;
private final RaceLogEventData raceLogEventData;
private final Serializable competitorId;
public RaceLogORCScratchBoatEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, int passId, Competitor competitor) {
this(createdAt, logicalTimePoint, author, pId, passId, competitor.getId());
}
public RaceLogORCScratchBoatEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, int passId, Serializable competitorId) {
super(createdAt, logicalTimePoint, author, pId);
this.raceLogEventData = new RaceLogEventDataImpl(/* involvedBoats */ null, passId);
this.competitorId = competitorId;
}
@Override
public void accept(RaceLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public int getPassId() {
return raceLogEventData.getPassId();
}
@Override
public <T extends Competitor> List<T> getInvolvedCompetitors() {
return raceLogEventData.getInvolvedCompetitors();
}
@Override
public Serializable getCompetitorId() {
return competitorId;
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.abstractlog.race;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
@@ -60,4 +61,6 @@ public interface RaceLogEventVisitor {
void visit(RaceLogORCLegDataEvent event);
void visit(RaceLogORCCertificateAssignmentEvent event);
void visit(RaceLogORCScratchBoatEvent event);
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.abstractlog.race.impl;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -156,4 +157,9 @@ public abstract class AbstractRaceLogChangedVisitor implements RaceLogEventVisit
public void visit(RaceLogORCCertificateAssignmentEvent event) {
notifyListenerAboutEventAdded(event);
}
@Override
public void visit(RaceLogORCScratchBoatEvent event) {
notifyListenerAboutEventAdded(event);
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.abstractlog.race.impl;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -162,4 +163,9 @@ public abstract class BaseRaceLogEventVisitor implements RaceLogEventVisitor {
public void visit(RaceLogORCCertificateAssignmentEvent event) {
}
@Override
public void visit(RaceLogORCScratchBoatEvent event) {
}
}
@@ -154,7 +154,7 @@ public class TestORCRankingMetricCollaborationWithRaceLogAndRegattaLog {
assertTrue(leg instanceof ORCPerformanceCurveLegAdapter); // only adapters, no explicit leg specification anymore
}
}
@Test
public void testHigherPriorityLegDefinitionInRaceLogWins() throws NotRevokableException {
attachRaceLog();
@@ -4,6 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -180,4 +181,9 @@ public class RaceColumnRaceLogReplicator implements RaceLogEventVisitor, Seriali
public void visit(RaceLogORCCertificateAssignmentEvent event) {
notifyOnAdd(event);
}
@Override
public void visit(RaceLogORCScratchBoatEvent event) {
notifyOnAdd(event);
}
}
@@ -14,16 +14,16 @@ import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.MaxIterationsExceededException;
import com.sap.sailing.domain.abstractlog.orc.ORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.ORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentFinder;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataAnalyzer;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatFinder;
import com.sap.sailing.domain.abstractlog.orc.RegattaLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RegattaLogORCCertificateAssignmentFinder;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCLegDataEventImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
@@ -69,10 +69,18 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
private final Map<Serializable, Boat> boatsById;
private final Map<Serializable, Competitor> competitorsById;
private final RaceLogEventVisitor certificatesFromRaceLogUpdater;
private final RegattaLogEventVisitor certificatesFromRegattaLogUpdater;
/**
* Updated by an observer pattern that watches all {@link RaceLog}s {@link TrackedRace#getAttachedRaceLogs() attached} to the
* {@link #getTrackedRace() tracked race} for occurrence and revocations of {@link RaceLogORCScratchBoatEvent}s.
*/
private Competitor explicitScratchBoat;
/**
* TODO maybe it's a good idea to cache the {@link ORCPerformanceCurve} objects and implied wind speeds for all competitors involved in this object which serves as some sort of cache for ranking calculations for a single time point
*
@@ -86,14 +94,15 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
* Uses the {@link ORCPerformanceCurveRankingMetric#getScratchBoat(TimePoint) scratch boat} as the "boat
* farthest ahead." The default scratch boat is defined as such but may be overridden explicitly.
*/
public ORCPerformanceCurveRankingInfo(TimePoint timePoint, Map<Competitor, CompetitorRankingInfo> competitorRankingInfo) {
super(timePoint, competitorRankingInfo, getScratchBoat(timePoint));
public ORCPerformanceCurveRankingInfo(TimePoint timePoint, Map<Competitor, CompetitorRankingInfo> competitorRankingInfo, WindLegTypeAndLegBearingCache cache) {
super(timePoint, competitorRankingInfo, getScratchBoat(timePoint, cache));
}
}
public ORCPerformanceCurveRankingMetric(TrackedRace trackedRace) {
super(trackedRace);
boatsById = initBoatsById();
competitorsById = initCompetitorsById();
updateCertificatesFromLogs();
updateCourseFromRaceLogs();
certificatesFromRaceLogUpdater = createCertificatesFromRaceLogAndCourseUpdater();
@@ -110,12 +119,14 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
public void raceLogAttached(RaceLog raceLog) {
raceLog.addListener(certificatesFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
}
@Override
public void raceLogDetached(RaceLog raceLog) {
raceLog.removeListener(certificatesFromRaceLogUpdater);
updateCertificatesFromLogs();
updateScratchBoatFromLogs();
}
});
}
@@ -135,6 +146,16 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
return result;
}
private Map<Serializable, Competitor> initCompetitorsById() {
final Map<Serializable, Competitor> result = new HashMap<>();
if (getTrackedRace() != null) {
for (final Competitor competitor : getTrackedRace().getTrackedRegatta().getRegatta().getAllCompetitors()) {
result.put(competitor.getId(), competitor);
}
}
return result;
}
private RaceLogEventVisitor createCertificatesFromRaceLogAndCourseUpdater() {
return new BaseRaceLogEventVisitor() {
@Override
@@ -165,6 +186,21 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
};
}
/**
* For now, we assume that there is only one scratch boat defined for this race and that it's independent of the
* pass. We pick the last ("greatest") valid event of type {@link RaceLogORCScratchBoatEvent}.
*/
private void updateScratchBoatFromLogs() {
Competitor scratchBoatFromLog = null;
for (final RaceLog raceLog : getTrackedRace().getAttachedRaceLogs()) {
scratchBoatFromLog = new RaceLogORCScratchBoatFinder(raceLog, competitorsById).analyze();
if (scratchBoatFromLog != null) {
break;
}
}
explicitScratchBoat = scratchBoatFromLog;
}
/**
* To be called if the set of logs attached to the {@link #getTrackedRace() tracked race} changes or any of those
* logs sees a {@link ORCCertificateAssignmentEvent} being added. As a result, the {@link #certificates} map is
@@ -235,15 +271,19 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
* By default, we use the boat farthest ahead ("first ship home" when the race has finished) as the scratch boat,
* but other definitions are possible, such as greatest {@link ORCCertificate#getGPH() GPH value} or least
* {@link ORCCertificate#getCDL() CDL value}. An explicit selection can be made using the
* {@link ORCScratchBoatEvent} in the {@link RaceLog}. The event's {@link RaceLogEvent#getLogicalTimePoint() logical
* time point} determines starting when the {@link ORCScratchBoatEvent#getCompetitorId() scratch boat identified by
* the competitor ID in the event} shall be used.
*
* TODO really use the logical time point? Can the scratch boat change across the race duration?
* {@link RaceLogORCScratchBoatEvent} in the {@link RaceLog}. The last unrevoked event in the first {@link RaceLog}
* in which one is found is used. Only if none of the attached {@link RaceLog}s contains any unrevoked such event,
* the scratch boat is selected implicitly to be the boat farthest ahead (see
* {@link #getCompetitorFarthestAhead(TimePoint, WindLegTypeAndLegBearingCache)}).
*/
private Competitor getScratchBoat(TimePoint timePoint) {
// TODO Implement ORCPerformanceCurveRankingMetric.getScratchBoat(...)
return null;
private Competitor getScratchBoat(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
final Competitor result;
if (explicitScratchBoat != null) {
result = explicitScratchBoat;
} else {
result = getCompetitorFarthestAhead(timePoint, cache);
}
return result;
}
/**
@@ -365,7 +405,7 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
@Override
public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
Duration result = null;
final Competitor scratchBoat = getScratchBoat(timePoint);
final Competitor scratchBoat = getScratchBoat(timePoint, cache);
if (competitor == scratchBoat) {
// the scratch boat's corrected time is its own time sailed
result = getTrackedRace().getTimeSailedSinceRaceStart(competitor, timePoint);
@@ -439,7 +479,7 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
correctedTime));
}
}
return new ORCPerformanceCurveRankingInfo(timePoint, competitorRankingInfo);
return new ORCPerformanceCurveRankingInfo(timePoint, competitorRankingInfo, cache);
}
/**
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.ranking;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -9,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -23,6 +25,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sailing.domain.tracking.impl.AbstractRaceRankComparator;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
@@ -233,6 +236,14 @@ public abstract class AbstractRankingMetric implements RankingMetric {
return trackedRace;
}
protected Competitor getCompetitorFarthestAhead(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
Comparator<Competitor> oneDesignComparator = getWindwardDistanceTraveledComparator(timePoint, cache);
Competitor competitorFarthestAhead = StreamSupport
.stream(getCompetitors().spliterator(), /* parallel */true).
sorted(oneDesignComparator).findFirst().get();
return competitorFarthestAhead;
}
/**
* The time from the {@link TrackedRace#getStartOfRace() race start} until <code>timePoint</code> or until
* the point in time when <code>competitor</code> passed the finish mark, whichever comes first. If there is
@@ -391,6 +402,25 @@ public abstract class AbstractRankingMetric implements RankingMetric {
return getWindwardDistanceTraveled(competitor, getTrackedRace().getRace().getCourse().getFirstWaypoint(), timePoint, cache);
}
/**
* Constructs a comparator based on the results of
* {@link #getWindwardDistanceTraveled(Competitor, TimePoint, WindLegTypeAndLegBearingCache)} where competitors are
* "less" than other competitors ("better") if they are in a later leg or, if in the same leg, have a greater
* windward distance traveled. If both competitors have already finished the race, the finishing time is compared.
*/
private Comparator<Competitor> getWindwardDistanceTraveledComparator(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) {
final Map<Competitor, Distance> windwardDistanceTraveledPerCompetitor = new HashMap<>();
for (final Competitor competitor : getCompetitors()) {
windwardDistanceTraveledPerCompetitor.put(competitor, getWindwardDistanceTraveled(competitor, timePoint, cache));
}
return new AbstractRaceRankComparator<Distance>(getTrackedRace(), timePoint, /* lessIsBetter */ false) {
@Override
protected Distance getComparisonValueForSameLeg(Competitor competitor) {
return windwardDistanceTraveledPerCompetitor.get(competitor);
}
};
}
/**
* How far did <code>competitor</code> sail windwards/along-course since passing the <code>from</code> waypoint?
* For each leg the total windward distance sailed is limited to the leg's windward distance at its
@@ -4,7 +4,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.StreamSupport;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
@@ -13,7 +12,6 @@ import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.impl.AbstractRaceRankComparator;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -59,31 +57,9 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
super(trackedRace);
}
/**
* Constructs a comparator based on the results of
* {@link #getWindwardDistanceTraveled(Competitor, TimePoint, WindLegTypeAndLegBearingCache)} where competitors are
* "less" than other competitors ("better") if they are in a later leg or, if in the same leg, have a greater
* windward distance traveled. If both competitors have already finished the race, the finishing time is compared.
*/
private Comparator<Competitor> getWindwardDistanceTraveledComparator(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) {
final Map<Competitor, Distance> windwardDistanceTraveledPerCompetitor = new HashMap<>();
for (final Competitor competitor : getCompetitors()) {
windwardDistanceTraveledPerCompetitor.put(competitor, getWindwardDistanceTraveled(competitor, timePoint, cache));
}
return new AbstractRaceRankComparator<Distance>(getTrackedRace(), timePoint, /* lessIsBetter */ false) {
@Override
protected Distance getComparisonValueForSameLeg(Competitor competitor) {
return windwardDistanceTraveledPerCompetitor.get(competitor);
}
};
}
public RankingMetric.RankingInfo getRankingInfo(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
Map<Competitor, RankingMetric.CompetitorRankingInfo> result = new HashMap<>();
Comparator<Competitor> oneDesignComparator = getWindwardDistanceTraveledComparator(timePoint, cache);
Competitor competitorFarthestAhead = StreamSupport
.stream(getCompetitors().spliterator(), /* parallel */true).
sorted(oneDesignComparator).findFirst().get();
Competitor competitorFarthestAhead = getCompetitorFarthestAhead(timePoint, cache);
final Distance totalWindwardDistanceTraveled = getWindwardDistanceTraveled(competitorFarthestAhead, timePoint, cache);
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace != null) {
@@ -115,7 +91,7 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
}
return new NonPerformanceCurveRankingInfoImpl(timePoint, result, competitorFarthestAhead);
}
/**
* Predicts how long <code>who</code> will take to reach competitor <code>to</code>'s position at
* <code>timePoint</code>, starting at <code>who</code>'s position at <code>timePoint</code>, assuming a continued
@@ -89,7 +89,8 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
new RaceLogUseCompetitorsFromRaceLogEventDeserializer(competitorDeserializer),
new RaceLogEndOfTrackingEventDeserializer(competitorDeserializer),
new RaceLogTagEventDeserializer(competitorDeserializer),
new RaceLogORCLegDataEventDeserializer(competitorDeserializer));
new RaceLogORCLegDataEventDeserializer(competitorDeserializer),
new RaceLogORCScratchBoatEventDeserializer(competitorDeserializer));
}
protected final JsonDeserializer<RaceLogEvent> flagEventDeserializer;
@@ -117,6 +118,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
protected final JsonDeserializer<RaceLogEvent> endOfTrackingDeserializer;
protected final JsonDeserializer<RaceLogEvent> tagEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcLegDataEventDeserializer;
protected final JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer;
public RaceLogEventDeserializer(JsonDeserializer<RaceLogEvent> flagEventDeserializer,
JsonDeserializer<RaceLogEvent> startTimeEventDeserializer,
@@ -141,7 +143,9 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
JsonDeserializer<RaceLogEvent> startOfTrackingEventDeserializer,
JsonDeserializer<RaceLogEvent> useCompetitorsFromRaceLogDeserializer,
JsonDeserializer<RaceLogEvent> endOfTrackingEventDeserializer,
JsonDeserializer<RaceLogEvent> tagEventDeserializer, JsonDeserializer<RaceLogEvent> orcLegDataEventDeserializer) {
JsonDeserializer<RaceLogEvent> tagEventDeserializer,
JsonDeserializer<RaceLogEvent> orcLegDataEventDeserializer,
JsonDeserializer<RaceLogEvent> orcScratchBoatEventDeserializer) {
this.flagEventDeserializer = flagEventDeserializer;
this.startTimeEventDeserializer = startTimeEventDeserializer;
this.dependentStartTimeEventDeserializer = dependentStartTimeEventDeserializer;
@@ -167,6 +171,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
this.useCompetitorsFromRaceLogDeserializer = useCompetitorsFromRaceLogDeserializer;
this.tagEventDeserializer = tagEventDeserializer;
this.orcLegDataEventDeserializer = orcLegDataEventDeserializer;
this.orcScratchBoatEventDeserializer = orcScratchBoatEventDeserializer;
}
protected JsonDeserializer<RaceLogEvent> getDeserializer(JSONObject object) throws JsonDeserializationException {
@@ -15,6 +15,7 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializationExcepti
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogORCCertificateAssignmentEventSerializer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.util.impl.UUIDHelper;
/**
* Deserializer for {@link com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent ORCLegDataEvent}.
@@ -33,7 +34,8 @@ public class RaceLogORCCertificateAssignmentEventDeserializer extends BaseRaceLo
AbstractLogEventAuthor author, TimePoint timePoint, int passId, List<Competitor> competitors)
throws JsonDeserializationException {
final ORCCertificate certificate = new ORCCertificateJsonDeserializer().deserialize((JSONObject) object.get(RaceLogORCCertificateAssignmentEventSerializer.ORC_CERTIFICATE));
final Serializable boatId = (Serializable) object.get(RaceLogORCCertificateAssignmentEventSerializer.ORC_BOAT_ID);
Serializable boatId = (Serializable) object.get(RaceLogORCCertificateAssignmentEventSerializer.ORC_BOAT_ID);
boatId = UUIDHelper.tryUuidConversion(boatId);
return new RaceLogORCCertificateAssignmentEventImpl(createdAt, timePoint, author, id, passId, certificate, boatId);
}
@@ -0,0 +1,38 @@
package com.sap.sailing.server.gateway.deserialization.racelog.impl;
import java.io.Serializable;
import java.util.List;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.orc.impl.RaceLogORCScratchBoatEventImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogORCScratchBoatEventSerializer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.util.impl.UUIDHelper;
/**
* Deserializer for {@link com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent ORCLegDataEvent}.
*
* @author Daniel Lisunkin (I505543)
*/
public class RaceLogORCScratchBoatEventDeserializer extends BaseRaceLogEventDeserializer {
public RaceLogORCScratchBoatEventDeserializer(JsonDeserializer<DynamicCompetitor> competitorDeserializer) {
super(competitorDeserializer);
}
@Override
protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt,
AbstractLogEventAuthor author, TimePoint timePoint, int passId, List<Competitor> competitors)
throws JsonDeserializationException {
Serializable competitorId = (Serializable) object.get(RaceLogORCScratchBoatEventSerializer.ORC_COMPETITOR_ID);
competitorId = UUIDHelper.tryUuidConversion(competitorId);
return new RaceLogORCScratchBoatEventImpl(createdAt, timePoint, author, id, passId, competitorId);
}
}
@@ -4,6 +4,7 @@ import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -86,7 +87,8 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
new RaceLogEndOfTrackingEventSerializer(competitorSerializer),
new RaceLogTagEventSerializer(competitorSerializer),
new RaceLogORCLegDataEventSerializer(competitorSerializer),
new RaceLogORCCertificateAssignmentEventSerializer(competitorSerializer));
new RaceLogORCCertificateAssignmentEventSerializer(competitorSerializer),
new RaceLogORCScratchBoatEventSerializer(competitorSerializer));
}
private final JsonSerializer<RaceLogEvent> flagEventSerializer;
@@ -115,6 +117,7 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
private final JsonSerializer<RaceLogEvent> tagSerializer;
private final JsonSerializer<RaceLogEvent> orcLegDataEventSerializer;
private final JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer;
private final JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer;
private JsonSerializer<RaceLogEvent> chosenSerializer;
@@ -145,7 +148,8 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
JsonSerializer<RaceLogEvent> endOfTrackingEventSerializer,
JsonSerializer<RaceLogEvent> tagSerializer,
JsonSerializer<RaceLogEvent> orcLegDataEventSerializer,
JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer) {
JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer,
JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer) {
this.flagEventSerializer = flagEventSerializer;
this.startTimeSerializer = startTimeSerializer;
this.raceStatusSerializer = raceStatusSerializer;
@@ -172,6 +176,7 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
this.tagSerializer = tagSerializer;
this.orcLegDataEventSerializer = orcLegDataEventSerializer;
this.orcCertificateAssignmentEventSerializer = orcCertificateAssignmentEventSerializer;
this.orcScratchBoatEventSerializer = orcScratchBoatEventSerializer;
this.chosenSerializer = null;
}
@@ -318,4 +323,9 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
public void visit(RaceLogORCCertificateAssignmentEvent event) {
chosenSerializer = orcCertificateAssignmentEventSerializer;
}
@Override
public void visit(RaceLogORCScratchBoatEvent event) {
chosenSerializer = orcScratchBoatEventSerializer;
}
}
@@ -28,7 +28,7 @@ public class RaceLogORCCertificateAssignmentEventSerializer extends BaseRaceLogE
RaceLogORCCertificateAssignmentEvent certificateAssignmentEvent = (RaceLogORCCertificateAssignmentEvent) object;
JSONObject result = super.serialize(certificateAssignmentEvent);
result.put(ORC_CERTIFICATE, new ORCCertificateJsonSerializer().serialize(certificateAssignmentEvent.getCertificate()));
result.put(ORC_BOAT_ID, certificateAssignmentEvent.getBoatId());
result.put(ORC_BOAT_ID, certificateAssignmentEvent.getBoatId().toString());
return result;
}
@@ -0,0 +1,38 @@
package com.sap.sailing.server.gateway.serialization.racelog.impl;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
/**
* Serializer for {@link com.sap.sailing.domain.abstractlog.orc.RaceLogORCLegDataEvent ORCLegDataEvent}.
*
* @author Daniel Lisunkin (i505543)
* @author Axel Uhl (d043530)
*/
public class RaceLogORCScratchBoatEventSerializer extends BaseRaceLogEventSerializer {
public static final String VALUE_CLASS = RaceLogORCScratchBoatEvent.class.getSimpleName();
public static final String ORC_CERTIFICATE = "certificate";
public static final String ORC_COMPETITOR_ID = "competitorId";
public RaceLogORCScratchBoatEventSerializer(JsonSerializer<Competitor> competitorSerializer) {
super(competitorSerializer);
}
@Override
public JSONObject serialize(RaceLogEvent object) {
RaceLogORCScratchBoatEvent scratchBoatEvent = (RaceLogORCScratchBoatEvent) object;
JSONObject result = super.serialize(scratchBoatEvent);
result.put(ORC_COMPETITOR_ID, scratchBoatEvent.getCompetitorId().toString());
return result;
}
@Override
protected String getClassFieldValue() {
return VALUE_CLASS;
}
}
@@ -48,7 +48,9 @@ public class RaceLogEventDeserializerTest {
super(mockitoRaceLogFlagEventDeserializer, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null,
mockitoRaceLogUseCompetitorsFromRaceLogEventDeserializer,
mockitoRaceLogEndOfTrackingEventDeserializer, mockitoRaceLogTagEventDeserializer, new RaceLogORCLegDataEventDeserializer(competitorDeserializer));
mockitoRaceLogEndOfTrackingEventDeserializer, mockitoRaceLogTagEventDeserializer,
new RaceLogORCLegDataEventDeserializer(competitorDeserializer),
null);
}
}
@@ -73,6 +73,7 @@ public class RaceLogEventSerializerTest {
private JsonSerializer<RaceLogEvent> tagEventSerializer;
private JsonSerializer<RaceLogEvent> orcLegDataEventSerializer;
private JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer;
private JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer;
private AbstractLogEventAuthor author = new LogEventAuthorImpl("Test Author", 1);
@@ -106,6 +107,8 @@ public class RaceLogEventSerializerTest {
tagEventSerializer = mock(JsonSerializer.class);
orcLegDataEventSerializer = mock(JsonSerializer.class);
orcCertificateAssignmentEventSerializer = mock(JsonSerializer.class);
orcScratchBoatEventSerializer = mock(JsonSerializer.class);
serializer = new RaceLogEventSerializer(flagEventSerializer, startTimeSerializer, raceStatusSerializer,
passChangedSerializer, courseDesignChangedEventSerializer,
finishPositioningListChangedEventSerializer, finishPositioningConfirmedEventSerializer,
@@ -117,7 +120,7 @@ public class RaceLogEventSerializerTest {
additionalScoringInformationSerializer, dependentStartTimeEventSerializer,
startOfTrackingEventSerializer, useCompetitorsFromRaceLogEventSerializer,
endOfTrackingEventSerializer, tagEventSerializer, orcLegDataEventSerializer,
orcCertificateAssignmentEventSerializer);
orcCertificateAssignmentEventSerializer, orcScratchBoatEventSerializer);
}
@Test