Merge branch 'master' into bug4811-with-bug5288

This commit is contained in:
Axel Uhl committed 2021-01-15 14:47:00 +01:00
commit 8a87532483
33 files changed
+362 -48

No files matched your search

@@ -72,6 +72,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -95,6 +96,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPathfinderEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogProtestStartTimeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogResultsAreOfficialEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRevokeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartProcedureChangedEventImpl;
@@ -1661,6 +1663,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
resultEvent = loadRaceLogORCScratchBoatEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
} else if (eventClass.equals(RaceLogORCImpliedWindSourceEvent.class.getSimpleName())) {
resultEvent = loadRaceLogORCSetImpliedWindEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
} else if (eventClass.equals(RaceLogResultsAreOfficialEvent.class.getSimpleName())) {
resultEvent = loadRaceLogResultsAreOfficialEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
} else {
throw new IllegalStateException(String.format("Unknown RaceLogEvent type %s", eventClass));
}
@@ -1686,6 +1690,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return new RaceLogORCImpliedWindSourceEventImpl(createdAt, logicalTimePoint, author, id, passId, impliedWindSource);
}
private RaceLogEvent loadRaceLogResultsAreOfficialEvent(TimePoint createdAt,
AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId,
List<Competitor> competitors, Document dbObject) throws JsonDeserializationException, ParseException {
return new RaceLogResultsAreOfficialEventImpl(createdAt, logicalTimePoint, author, id, passId);
}
private RaceLogEvent loadRaceLogUseCompetitorsFromRaceLogEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors,
@@ -53,6 +53,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -940,6 +941,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogWindFix(event));
return result;
}
public Document storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogResultsAreOfficialEvent event) {
Document result = new Document();
storeRaceLogIdentifier(raceLogIdentifier, result);
result.put(FieldNames.RACE_LOG_EVENT.name(), storeRaceLogResultsAreOfficialEvent(event));
return result;
}
public Document storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogDenoteForTrackingEvent event) {
Document result = new Document();
@@ -1143,6 +1151,14 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeTimePoint(event.getToInclusive(), result, toField);
}
}
private Object storeRaceLogResultsAreOfficialEvent(RaceLogResultsAreOfficialEvent event) {
Document result = new Document();
storeRaceLogEventProperties(event, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogResultsAreOfficialEvent.class.getSimpleName());
// currently there are no further properties; the event speaks for itself
return result;
}
private Object storeRaceLogDenoteForTrackingEvent(RaceLogDenoteForTrackingEvent event) {
Document result = new Document();
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -216,4 +217,10 @@ public class MongoRaceLogStoreVisitor implements RaceLogEventVisitor {
Document object = mongoObjectFactory.storeRaceLogEntry(raceLogIdentifier, event);
mongoObjectFactory.storeRaceLogEventEvent(object);
}
@Override
public void visit(RaceLogResultsAreOfficialEvent event) {
Document object = mongoObjectFactory.storeRaceLogEntry(raceLogIdentifier, event);
mongoObjectFactory.storeRaceLogEventEvent(object);
}
}
@@ -0,0 +1,35 @@
package com.sap.sailing.domain.racelog.analyzing.test;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import org.junit.Test;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ResultsAreOfficialFinder;
public class ResultsAreOfficialFinderTest extends RaceLogAnalyzerTest<ResultsAreOfficialFinder> {
@Override
protected ResultsAreOfficialFinder createAnalyzer(RaceLog raceLog) {
return new ResultsAreOfficialFinder(raceLog);
}
@Test
public void testFalseForNoResultsAreOfficialEvent() {
RaceLogEvent event1 = createEvent(RaceLogEvent.class, 1);
raceLog.add(event1);
assertNull(analyzer.analyze());
}
@Test
public void testSingleLast() {
RaceLogResultsAreOfficialEvent event1 = createEvent(RaceLogResultsAreOfficialEvent.class, 1);
RaceLogResultsAreOfficialEvent event2 = createEvent(RaceLogResultsAreOfficialEvent.class, 2);
raceLog.add(event1);
raceLog.add(event2);
RaceLogResultsAreOfficialEvent result = analyzer.analyze();
assertSame(event2, result);
}
}
@@ -197,11 +197,15 @@ public class RaceStateTest {
@Test
public void testGetRacingProcedure() throws InterruptedException {
state.setRacingProcedure(nowMock, RacingProcedureType.RRS26);
GateStartRacingProcedure procedure = state.getTypedRacingProcedure(GateStartRacingProcedure.class);
assertNull(procedure);
}
@Test
public void testResultsAreOfficial() {
assertFalse(state.isResultsAreOfficial());
state.setResultsAreOfficial(TimePoint.now());
assertTrue(state.isResultsAreOfficial());
}
}
@@ -66,4 +66,6 @@ public interface RaceLogEventVisitor {
void visit(RaceLogORCScratchBoatEvent event);
void visit(RaceLogORCImpliedWindSourceEvent event);
void visit(RaceLogResultsAreOfficialEvent event);
}
@@ -0,0 +1,5 @@
package com.sap.sailing.domain.abstractlog.race;
public interface RaceLogResultsAreOfficialEvent extends RaceLogEvent {
}
@@ -0,0 +1,30 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
/**
* Analysis returns the most recent {@link RaceLogResultsAreOfficialEvent}.
* <p>
*
* If there is no {@link RaceLogResultsAreOfficialEvent} in log, <code>null</code> is returned. Otherwise a single
* {@link RaceLogResultsAreOfficialEvent} is returned.
*
*/
public class ResultsAreOfficialFinder extends RaceLogAnalyzer<RaceLogResultsAreOfficialEvent> {
public ResultsAreOfficialFinder(RaceLog raceLog) {
super(raceLog);
}
@Override
protected RaceLogResultsAreOfficialEvent performAnalysis() {
for (final RaceLogEvent e : getAllEventsDescending()) {
if (e instanceof RaceLogResultsAreOfficialEvent) {
return (RaceLogResultsAreOfficialEvent) e;
}
}
return null;
}
}
@@ -18,6 +18,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -168,4 +169,9 @@ public abstract class AbstractRaceLogChangedVisitor implements RaceLogEventVisit
public void visit(RaceLogORCImpliedWindSourceEvent event) {
notifyListenerAboutEventAdded(event);
}
@Override
public void visit(RaceLogResultsAreOfficialEvent event) {
notifyListenerAboutEventAdded(event);
}
}
@@ -17,6 +17,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -174,4 +175,9 @@ public abstract class BaseRaceLogEventVisitor implements RaceLogEventVisitor {
public void visit(RaceLogORCImpliedWindSourceEvent event) {
}
@Override
public void visit(RaceLogResultsAreOfficialEvent event) {
}
}
@@ -0,0 +1,32 @@
package com.sap.sailing.domain.abstractlog.race.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sse.common.TimePoint;
public class RaceLogResultsAreOfficialEventImpl extends RaceLogEventImpl implements RaceLogResultsAreOfficialEvent {
private static final long serialVersionUID = -1796278009919318553L;
public RaceLogResultsAreOfficialEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, int pPassId) {
super(createdAt, logicalTimePoint, author, pId, pPassId);
}
public RaceLogResultsAreOfficialEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author, int pPassId) {
this(now(), logicalTimePoint, author, randId(), pPassId);
}
@Override
public void accept(RaceLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public String getShortInfo() {
return "Indicates that the results of this race are now official";
}
}
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.scoring.AdditionalScoringInformationType;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
@@ -146,7 +147,6 @@ public interface RaceState extends ReadonlyRaceState {
/**
* Enters a new wind fix for this race.
* @param isMagnetic TODO
*/
void setWindFix(TimePoint now, Wind wind, boolean isMagnetic);
@@ -162,4 +162,14 @@ public interface RaceState extends ReadonlyRaceState {
* forces a RaceStateUpdate
*/
void forceUpdate();
/**
* Marks the results of the race to which this state belongs as official, using a
* {@link RaceLogResultsAreOfficialEvent}.
*
* @param timePoint
* the time point at which the results were declared official
* @see #isResultsAreOfficial()
*/
void setResultsAreOfficial(TimePoint timePoint);
}
@@ -18,5 +18,5 @@ public interface RaceStateChangedListener {
void onCourseDesignChanged(ReadonlyRaceState state);
void onWindFixChanged(ReadonlyRaceState state);
void onTagEventsChanged(ReadonlyRaceState state);
void onResultsAreOfficialChanged(ReadonlyRaceState state);
}
@@ -102,6 +102,11 @@ public interface ReadonlyRaceState extends RaceStateEventProcessor {
* Gets the races overall status.
*/
RaceLogRaceStatus getStatus();
/**
* Tells if the results found for this race have been confirmed officially.
*/
boolean isResultsAreOfficial();
/**
* If there is a start time set for the current pass, returns it. Otherwise <code>null</code>.
@@ -68,4 +68,8 @@ public abstract class BaseRaceStateChangedListener implements RaceStateChangedLi
}
@Override
public void onResultsAreOfficialChanged(ReadonlyRaceState state) {
}
}
@@ -118,4 +118,11 @@ public class RaceStateChangedListeners extends HashSet<RaceStateChangedListener>
listener.onTagEventsChanged(state);
}
}
@Override
public void onResultsAreOfficialChanged(ReadonlyRaceState state) {
for (RaceStateChangedListener listener : getWorkingCopyOfListeners()) {
listener.onResultsAreOfficialChanged(state);
}
}
}
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFlagEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogProtestStartTimeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogResultsAreOfficialEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartProcedureChangedEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogWindFixEventImpl;
@@ -135,6 +136,11 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
// ensure caches are synched
forceUpdate();
}
@Override
public void setResultsAreOfficial(TimePoint timePoint) {
raceLog.add(new RaceLogResultsAreOfficialEventImpl(timePoint, author, raceLog.getCurrentPassId()));
}
@Override
public void setProtestTime(TimePoint now, TimeRange protestTime) {
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogChangedListener;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogTagEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
@@ -27,6 +28,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer.Clock;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RacingProcedureTypeAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ResultsAreOfficialFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TagFinder;
@@ -165,6 +167,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
private final FinishingTimeFinder finishingTimeAnalyzer;
private final FinishedTimeFinder finishedTimeAnalyzer;
private final ProtestTimeFinder protestTimeAnalyzer;
private final ResultsAreOfficialFinder resultsAreOfficialAnalyzer;
private final FinishPositioningListFinder finishPositioningListAnalyzer;
private final ConfirmedFinishPositioningListFinder confirmedFinishPositioningListAnalyzer;
@@ -205,6 +208,8 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
private Iterable<RaceLogTagEvent> cachedTagEvents;
private boolean cachedResultsAreOfficial;
private int cachedPassId;
private StartTimeFinderResult cachedStartTimeFinderResult;
private TimePoint cachedFinishingTime;
@@ -250,19 +255,18 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
this.finishingTimeAnalyzer = new FinishingTimeFinder(raceLog);
this.finishedTimeAnalyzer = new FinishedTimeFinder(raceLog);
this.protestTimeAnalyzer = new ProtestTimeFinder(raceLog);
this.resultsAreOfficialAnalyzer = new ResultsAreOfficialFinder(raceLog);
this.finishPositioningListAnalyzer = new FinishPositioningListFinder(raceLog);
this.confirmedFinishPositioningListAnalyzer = new ConfirmedFinishPositioningListFinder(raceLog);
this.courseDesignerAnalyzer = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ false);
this.lastWindFixAnalyzer = new LastWindFixFinder(raceLog);
this.tagAnalyzer = new TagFinder(raceLog);
this.raceStateToObserveListener = new BaseRaceStateChangedListener() {
@Override
public void onStartTimeChanged(ReadonlyRaceState state) {
update();
}
};
this.cachedRacingProcedureTypeNoFallback = determineInitialProcedureType();
if (this.cachedRacingProcedureTypeNoFallback == null
|| this.cachedRacingProcedureTypeNoFallback == RacingProcedureType.UNKNOWN) {
@@ -368,6 +372,11 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
return cachedRaceStatus;
}
@Override
public boolean isResultsAreOfficial() {
return cachedResultsAreOfficial;
}
/**
* Finds out whether any of the time points in {@link #timePointsWhenRaceStatusMayChange} falls into the time range
* provided. In this case we assume that a race status update may have happened between the time point when the
@@ -581,6 +590,11 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
cachedTagEvents = tagEvents;
changedListeners.onTagEventsChanged(this);
}
final RaceLogResultsAreOfficialEvent resultsAreOfficialEvent = resultsAreOfficialAnalyzer.analyze();
if ((resultsAreOfficialEvent != null) != cachedResultsAreOfficial) {
cachedResultsAreOfficial = resultsAreOfficialEvent != null;
changedListeners.onResultsAreOfficialChanged(this);
}
}
/**
@@ -15,8 +15,8 @@ import com.sap.sailing.domain.tractracadapter.impl.TracTracRaceTrackerImpl;
* @author Frederik Petersen
*
*/
@FunctionalInterface
public interface LoadingQueueDoneCallBack {
/**
* Callback method
* See {@link AbstractReceiverWithQueue} and {@link TracTracRaceTrackerImpl} for example usage.
@@ -24,5 +24,4 @@ public interface LoadingQueueDoneCallBack {
* @param receiver Receiver that handled the marked event.
*/
void loadingQueueDone(Receiver receiver);
}
@@ -32,16 +32,19 @@ import com.tractrac.subscription.lib.api.race.IRaceCompetitorListener;
* @author Axel Uhl (d043530)
*
*/
public class CompetitorChangeReceiver extends AbstractReceiverWithQueue<IRaceCompetitor, Void, Void> {
public class CompetitorChangeReceiver extends AbstractReceiverWithQueue<IRaceCompetitor, Void, Void>
implements OfficialCompetitorUpdateProvider {
private static final Logger logger = Logger.getLogger(CompetitorChangeReceiver.class.getName());
private final IRaceCompetitorListener listener;
private final RaceAndCompetitorStatusWithRaceLogReconciler reconciler;
public CompetitorChangeReceiver(DynamicTrackedRegatta trackedRegatta, IEvent tractracEvent,
IRace tractracRace, Simulator simulator, DomainFactory domainFactory,
IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, long timeoutInMilliseconds, RaceAndCompetitorStatusWithRaceLogReconciler raceAndCompetitorStatusWithRaceLogReconciler) {
IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, long timeoutInMilliseconds,
RaceAndCompetitorStatusWithRaceLogReconciler raceAndCompetitorStatusWithRaceLogReconciler) {
super(domainFactory, tractracEvent, trackedRegatta, simulator, eventSubscriber, raceSubscriber, timeoutInMilliseconds);
reconciler = raceAndCompetitorStatusWithRaceLogReconciler;
reconciler.setOfficialCompetitorUpdateProvider(this);
listener = new IRaceCompetitorListener() {
@Override
public void addRaceCompetitor(IRaceCompetitor raceCompetitor) {
@@ -99,4 +102,9 @@ public class CompetitorChangeReceiver extends AbstractReceiverWithQueue<IRaceCom
+ ". Dropping competitor change event " + event);
}
}
@Override
public void runWhenNoMoreOfficialCompetitorUpdatesPending(Runnable runnable) {
callBackWhenLoadingQueueIsDone(receiver->runnable.run());
}
}
@@ -583,7 +583,7 @@ public class DomainFactoryImpl implements DomainFactory {
tracTracUsername, tracTracPassword, eventSubscriber, raceSubscriber,
useInternalMarkPassingAlgorithm, timeoutInMilliseconds, raceTrackingHandler, raceAndCompetitorStatusWithRaceLogReconciler,
ReceiverType.RACECOURSE, ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH,
ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA);
ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA, ReceiverType.COMPETITOR);
}
@Override
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.tractracadapter.impl;
public interface OfficialCompetitorUpdateProvider {
/**
* Runs the {@link Runnable} when all competitor update requests enqueued so far have been
* processed.
*/
void runWhenNoMoreOfficialCompetitorUpdatesPending(Runnable runnable);
}
@@ -28,7 +28,9 @@ import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultsImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFinishPositioningConfirmedEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFlagEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
import com.sap.sailing.domain.abstractlog.race.state.impl.ReadonlyRaceStateImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
@@ -65,6 +67,7 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
private final LogEventAuthorImpl raceLogEventAuthor;
private final IRace tractracRace;
private final Map<Pair<TrackedRace, RaceLog>, RaceLogListener> raceLogListeners;
private OfficialCompetitorUpdateProvider officialCompetitorUpdateProvider;
private final static Map<RaceStatusType, Flags> flagForRaceStatus;
static {
@@ -209,7 +212,7 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
*/
public void reconcileRaceStatus(IRace tractracRace, TrackedRace trackedRace) {
final RaceStatusType raceStatus = tractracRace.getStatus();
final MillisecondsTimePoint raceStatusUpdateTime = new MillisecondsTimePoint(tractracRace.getStatusTime());
final TimePoint raceStatusUpdateTime = TimePoint.of(tractracRace.getStatusTime());
RaceLogFlagEvent abortingFlagEvent = null;
for (final RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
if (abortingFlagEvent == null) {
@@ -218,6 +221,14 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
}
}
final RaceLog defaultRaceLog = getDefaultRaceLog(trackedRace);
if (raceStatus == RaceStatusType.OFFICIAL && !ReadonlyRaceStateImpl.getOrCreate(raceLogResolver, defaultRaceLog).isResultsAreOfficial()) {
final Runnable setResultsAreOfficial = ()->RaceStateImpl.create(raceLogResolver, defaultRaceLog, raceLogEventAuthor).setResultsAreOfficial(raceStatusUpdateTime);
if (officialCompetitorUpdateProvider != null) {
officialCompetitorUpdateProvider.runWhenNoMoreOfficialCompetitorUpdatesPending(setResultsAreOfficial);
} else {
setResultsAreOfficial.run();
}
}
if (abortingFlagEvent != null && !isAbortedState(raceStatus) && raceStatusUpdateTime.after(abortingFlagEvent.getLogicalTimePoint())) {
startNewPass(raceStatusUpdateTime, defaultRaceLog);
} else if (isAbortedState(raceStatus) &&
@@ -229,7 +240,7 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
}
}
protected void startNewPass(final MillisecondsTimePoint timePointForStartOfNewPass, final RaceLog raceLog) {
protected void startNewPass(final TimePoint timePointForStartOfNewPass, final RaceLog raceLog) {
raceLog.add(new RaceLogPassChangeEventImpl(timePointForStartOfNewPass, raceLogEventAuthor, raceLog.getCurrentPassId() + 1));
}
@@ -397,4 +408,15 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
}
return resultFromRaceLogAndItsCreationTimePoint;
}
/**
* To be called by the object later calling {@link #reconcileCompetitorStatus(IRaceCompetitor, TrackedRace)}. This
* way, this object can tell when the race is to be {@link RaceState#setResultsAreOfficial(TimePoint) moved to
* OFFICIAL state} whether any updates are pending and defer the race state transition to that moment.
*
* @see OfficialCompetitorUpdateProvider#runWhenNoMoreOfficialCompetitorUpdatesPending(Runnable)
*/
public void setOfficialCompetitorUpdateProvider(OfficialCompetitorUpdateProvider officialCompetitorUpdateProvider) {
this.officialCompetitorUpdateProvider = officialCompetitorUpdateProvider;
}
}
@@ -58,19 +58,19 @@ import com.sap.sse.common.Util;
import com.sap.sse.util.impl.ThreadFactoryWithPriority;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.model.lib.api.event.DataSource;
import com.tractrac.model.lib.api.event.ICompetitor;
import com.tractrac.model.lib.api.event.IEvent;
import com.tractrac.model.lib.api.event.IRace;
import com.tractrac.model.lib.api.event.IRaceCompetitor;
import com.tractrac.model.lib.api.route.IControl;
import com.tractrac.subscription.lib.api.IEventSubscriber;
import com.tractrac.subscription.lib.api.IRaceSubscriber;
import com.tractrac.subscription.lib.api.ISubscriberFactory;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
import com.tractrac.subscription.lib.api.SubscriptionLocator;
import com.tractrac.subscription.lib.api.competitor.ICompetitorsListener;
import com.tractrac.subscription.lib.api.event.IConnectionStatusListener;
import com.tractrac.subscription.lib.api.event.ILiveDataEvent;
import com.tractrac.subscription.lib.api.event.IStoredDataEvent;
import com.tractrac.subscription.lib.api.race.IRaceCompetitorListener;
import com.tractrac.subscription.lib.api.race.IRacesListener;
import com.tractrac.util.lib.api.exceptions.TimeOutException;
@@ -199,7 +199,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
private final IEventSubscriber eventSubscriber;
private final IRaceSubscriber raceSubscriber;
private final IRacesListener racesListener;
private final ICompetitorsListener competitorsListener;
private final IRaceCompetitorListener competitorsListener;
private final Set<Receiver> receivers;
private final DomainFactory domainFactory;
private final WindStore windStore;
@@ -351,33 +351,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
// Initialize data controller using live and stored data sources
ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory();
eventSubscriber = subscriberFactory.createEventSubscriber(tractracEvent, liveURI, effectiveStoredURI);
competitorsListener = new ICompetitorsListener() {
@Override
public void updateCompetitor(ICompetitor competitor) {
if (!competitor.isNonCompeting()) {
TracTracRaceTrackerImpl.this.domainFactory.updateCompetitor(competitor, raceTrackingHandler);
}
}
@Override
public void deleteCompetitor(UUID competitorId) {
try {
trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void addCompetitor(ICompetitor competitor) {
try {
trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
eventSubscriber.subscribeCompetitors(competitorsListener);
if (useOfficialEventsToUpdateRaceLog) {
reconciler = new RaceAndCompetitorStatusWithRaceLogReconciler(domainFactory, raceLogResolver, tractracRace);
} else {
@@ -456,6 +429,37 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
useInternalMarkPassingAlgorithm, timeoutInMilliseconds, raceTrackingHandler, reconciler)) {
receivers.add(receiver);
}
competitorsListener = new IRaceCompetitorListener() {
@Override
public void addRaceCompetitor(IRaceCompetitor raceCompetitor) {
try {
trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void updateRaceCompetitor(IRaceCompetitor raceCompetitor) {
if (!raceCompetitor.getCompetitor().isNonCompeting()) {
TracTracRaceTrackerImpl.this.domainFactory.updateCompetitor(raceCompetitor.getCompetitor(), raceTrackingHandler);
}
}
@Override
public void deleteRaceCompetitor(UUID competitorId) {
try {
trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void removeOffsetPositions(UUID competitorId, int offset) {
}
};
raceSubscriber.subscribeRaceCompetitor(competitorsListener);
addListenersForStoredDataAndStartController(receivers);
}
@@ -598,7 +602,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
if (!stopped) {
stopped = true;
eventSubscriber.unsubscribeRaces(racesListener);
eventSubscriber.unsubscribeCompetitors(competitorsListener);
raceSubscriber.unsubscribeRaceCompetitor(competitorsListener);
raceSubscriber.stop();
eventSubscriber.stop();
raceSubscriber.unsubscribeConnectionStatus(this);
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -192,4 +193,9 @@ public class RaceColumnRaceLogReplicator implements RaceLogEventVisitor, Seriali
public void visit(RaceLogORCImpliedWindSourceEvent event) {
notifyOnAdd(event);
}
@Override
public void visit(RaceLogResultsAreOfficialEvent event) {
notifyOnAdd(event);
}
}
@@ -25,11 +25,13 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogFlagEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogResultsAreOfficialEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRevokeEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTrackingEvent;
@@ -121,6 +123,15 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest {
assertEquals(lowerFlag, actualEvent.getLowerFlag());
assertEquals(isDisplayed, actualEvent.isDisplayed());
}
@Test
public void testStoreAndLoadResultsAreOfficialEvent() {
RaceLogResultsAreOfficialEvent expectedEvent = new RaceLogResultsAreOfficialEventImpl(MillisecondsTimePoint.now(), expectedEventTime,
author, expectedId, expectedPassId);
Document dbObject = mongoFactory.storeRaceLogEntry(logIdentifier, expectedEvent);
RaceLogResultsAreOfficialEvent actualEvent = loadEvent(dbObject);
assertBaseFields(expectedEvent, actualEvent);
}
@Test
public void testStoreAndLoadPassChangeEvent() {
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPathfinderEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogProtestStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartProcedureChangedEvent;
@@ -90,7 +91,8 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
new RaceLogORCLegDataEventSerializer(competitorSerializer),
new RaceLogORCCertificateAssignmentEventSerializer(competitorSerializer),
new RaceLogORCScratchBoatEventSerializer(competitorSerializer),
new RaceLogORCImpliedWindSourceEventSerializer(competitorSerializer));
new RaceLogORCImpliedWindSourceEventSerializer(competitorSerializer),
new RaceLogResultsAreOfficialEventSerializer(competitorSerializer));
}
private final JsonSerializer<RaceLogEvent> flagEventSerializer;
@@ -121,6 +123,7 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
private final JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer;
private final JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer;
private final JsonSerializer<RaceLogEvent> orcImpliedWindSourceEventSerializer;
private final JsonSerializer<RaceLogEvent> resultsAreOfficialEventSerializer;
private JsonSerializer<RaceLogEvent> chosenSerializer;
@@ -153,7 +156,8 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
JsonSerializer<RaceLogEvent> orcLegDataEventSerializer,
JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer,
JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer,
JsonSerializer<RaceLogEvent> orcImpliedWindSourceEventSerializer) {
JsonSerializer<RaceLogEvent> orcImpliedWindSourceEventSerializer,
JsonSerializer<RaceLogEvent> resultsAreOfficialEventSerializer) {
this.flagEventSerializer = flagEventSerializer;
this.startTimeSerializer = startTimeSerializer;
this.raceStatusSerializer = raceStatusSerializer;
@@ -182,6 +186,7 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
this.orcCertificateAssignmentEventSerializer = orcCertificateAssignmentEventSerializer;
this.orcScratchBoatEventSerializer = orcScratchBoatEventSerializer;
this.orcImpliedWindSourceEventSerializer = orcImpliedWindSourceEventSerializer;
this.resultsAreOfficialEventSerializer = resultsAreOfficialEventSerializer;
this.chosenSerializer = null;
}
@@ -338,4 +343,9 @@ public class RaceLogEventSerializer implements JsonSerializer<RaceLogEvent>, Rac
public void visit(RaceLogORCImpliedWindSourceEvent event) {
chosenSerializer = orcImpliedWindSourceEventSerializer;
}
@Override
public void visit(RaceLogResultsAreOfficialEvent event) {
chosenSerializer = resultsAreOfficialEventSerializer;
}
}
@@ -0,0 +1,33 @@
package com.sap.sailing.server.gateway.serialization.racelog.impl;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogResultsAreOfficialEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
/**
* Serializer for {@link RaceLogResultsAreOfficialEvent}.
*
* @author Axel Uhl (d043530)
*/
public class RaceLogResultsAreOfficialEventSerializer extends BaseRaceLogEventSerializer {
public static final String VALUE_CLASS = RaceLogResultsAreOfficialEvent.class.getSimpleName();
public RaceLogResultsAreOfficialEventSerializer(JsonSerializer<Competitor> competitorSerializer) {
super(competitorSerializer);
}
@Override
public JSONObject serialize(RaceLogEvent object) {
final RaceLogResultsAreOfficialEvent resultsAreOfficialEvent = (RaceLogResultsAreOfficialEvent) object;
final JSONObject result = super.serialize(resultsAreOfficialEvent);
return result;
}
@Override
protected String getClassFieldValue() {
return VALUE_CLASS;
}
}
@@ -76,6 +76,7 @@ public class RaceLogEventSerializerTest {
private JsonSerializer<RaceLogEvent> orcCertificateAssignmentEventSerializer;
private JsonSerializer<RaceLogEvent> orcScratchBoatEventSerializer;
private JsonSerializer<RaceLogEvent> orcSetImpliedWindEventSerializer;
private JsonSerializer<RaceLogEvent> resultsAreOfficialEventSerializer;
private AbstractLogEventAuthor author = new LogEventAuthorImpl("Test Author", 1);
@@ -111,6 +112,7 @@ public class RaceLogEventSerializerTest {
orcCertificateAssignmentEventSerializer = mock(JsonSerializer.class);
orcScratchBoatEventSerializer = mock(JsonSerializer.class);
orcSetImpliedWindEventSerializer = mock(JsonSerializer.class);
resultsAreOfficialEventSerializer = mock(JsonSerializer.class);
serializer = new RaceLogEventSerializer(flagEventSerializer, startTimeSerializer, raceStatusSerializer,
passChangedSerializer, courseDesignChangedEventSerializer,
@@ -124,7 +126,7 @@ public class RaceLogEventSerializerTest {
startOfTrackingEventSerializer, useCompetitorsFromRaceLogEventSerializer,
endOfTrackingEventSerializer, tagEventSerializer, orcLegDataEventSerializer,
orcCertificateAssignmentEventSerializer, orcScratchBoatEventSerializer,
orcSetImpliedWindEventSerializer);
orcSetImpliedWindEventSerializer, resultsAreOfficialEventSerializer);
}
@Test
@@ -10,6 +10,8 @@ import org.apache.shiro.SecurityUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ResultsAreOfficialFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.Fleet;
@@ -178,6 +180,7 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer
final JSONObject fleetJson = new JSONObject();
fleetsJson.add(fleetJson);
fleetJson.put("name", fleet.getName());
fleetJson.put("resultsAreOfficial", isResultsAreOfficial(raceColumn, fleet));
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
final JSONObject trackedRaceInfo;
if (trackedRace == null) {
@@ -203,6 +206,11 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer
}
}
private boolean isResultsAreOfficial(RaceColumn raceColumn, Fleet fleet) {
final RaceLog raceLog = raceColumn.getRaceLog(fleet);
return new ResultsAreOfficialFinder(raceLog).analyze() != null;
}
protected TimePoint calculateTimePointForResultState(Leaderboard leaderboard, ResultStates resultState) {
TimePoint result = null;
switch (resultState) {
View File
File mode changed.
File mode changed.
@@ -279,7 +279,10 @@ public class RaceSummaryFragment extends BaseFragment {
@Override
public void onTagEventsChanged(ReadonlyRaceState state) {
// TODO: add android support for tags
}
@Override
public void onResultsAreOfficialChanged(ReadonlyRaceState state) {
}
}