diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index 721b992d996..09c01d9210d 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -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 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 competitors, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index 29ae0712349..49d32dddc92 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -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(); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRaceLogStoreVisitor.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRaceLogStoreVisitor.java index a6065185571..684e4a7be04 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRaceLogStoreVisitor.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoRaceLogStoreVisitor.java @@ -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); + } } diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ResultsAreOfficialFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ResultsAreOfficialFinderTest.java new file mode 100755 index 00000000000..d13970cfbe9 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/ResultsAreOfficialFinderTest.java @@ -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 { + @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); + } +} diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/state/test/RaceStateTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/state/test/RaceStateTest.java index 43652c07516..2d88711f5b5 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/state/test/RaceStateTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/state/test/RaceStateTest.java @@ -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()); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java index 287d30bd4dc..efa80b58559 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEventVisitor.java @@ -66,4 +66,6 @@ public interface RaceLogEventVisitor { void visit(RaceLogORCScratchBoatEvent event); void visit(RaceLogORCImpliedWindSourceEvent event); + + void visit(RaceLogResultsAreOfficialEvent event); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogResultsAreOfficialEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogResultsAreOfficialEvent.java new file mode 100755 index 00000000000..6914cef5719 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogResultsAreOfficialEvent.java @@ -0,0 +1,5 @@ +package com.sap.sailing.domain.abstractlog.race; + +public interface RaceLogResultsAreOfficialEvent extends RaceLogEvent { + +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ResultsAreOfficialFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ResultsAreOfficialFinder.java new file mode 100755 index 00000000000..ae62f0db26c --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ResultsAreOfficialFinder.java @@ -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}. + *

+ * + * If there is no {@link RaceLogResultsAreOfficialEvent} in log, null is returned. Otherwise a single + * {@link RaceLogResultsAreOfficialEvent} is returned. + * + */ +public class ResultsAreOfficialFinder extends RaceLogAnalyzer { + + public ResultsAreOfficialFinder(RaceLog raceLog) { + super(raceLog); + } + + @Override + protected RaceLogResultsAreOfficialEvent performAnalysis() { + for (final RaceLogEvent e : getAllEventsDescending()) { + if (e instanceof RaceLogResultsAreOfficialEvent) { + return (RaceLogResultsAreOfficialEvent) e; + } + } + return null; + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java index 3335971c022..29cc30666e5 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/AbstractRaceLogChangedVisitor.java @@ -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); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java index 733d2542567..2f1fbe543d5 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/BaseRaceLogEventVisitor.java @@ -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) { + + } } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogResultsAreOfficialEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogResultsAreOfficialEventImpl.java new file mode 100755 index 00000000000..886ceaea083 --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogResultsAreOfficialEventImpl.java @@ -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"; + } +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java index d38448c1b17..6d1d16676ad 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java @@ -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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java index 8a9551a0fd0..0c2272dd3b8 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java @@ -18,5 +18,5 @@ public interface RaceStateChangedListener { void onCourseDesignChanged(ReadonlyRaceState state); void onWindFixChanged(ReadonlyRaceState state); void onTagEventsChanged(ReadonlyRaceState state); - + void onResultsAreOfficialChanged(ReadonlyRaceState state); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java index 9cd979abd0a..ad052497d8a 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java @@ -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 null. diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/BaseRaceStateChangedListener.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/BaseRaceStateChangedListener.java index 3dc7a0fc842..462dba13ff5 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/BaseRaceStateChangedListener.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/BaseRaceStateChangedListener.java @@ -68,4 +68,8 @@ public abstract class BaseRaceStateChangedListener implements RaceStateChangedLi } + @Override + public void onResultsAreOfficialChanged(ReadonlyRaceState state) { + + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateChangedListeners.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateChangedListeners.java index c9bbd7dcc7c..0bd67ff6ee5 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateChangedListeners.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateChangedListeners.java @@ -118,4 +118,11 @@ public class RaceStateChangedListeners extends HashSet listener.onTagEventsChanged(state); } } + + @Override + public void onResultsAreOfficialChanged(ReadonlyRaceState state) { + for (RaceStateChangedListener listener : getWorkingCopyOfListeners()) { + listener.onResultsAreOfficialChanged(state); + } + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java index 1fab6dffd8c..3145fbaa783 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java @@ -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) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java index aa85034cc1d..1ef7657569a 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java @@ -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 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); + } } /** diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/LoadingQueueDoneCallBack.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/LoadingQueueDoneCallBack.java index 502d6e9f955..ad80d2f5812 100644 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/LoadingQueueDoneCallBack.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/LoadingQueueDoneCallBack.java @@ -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); - } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/CompetitorChangeReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/CompetitorChangeReceiver.java index 71e82c14b10..24d5be69842 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/CompetitorChangeReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/CompetitorChangeReceiver.java @@ -32,16 +32,19 @@ import com.tractrac.subscription.lib.api.race.IRaceCompetitorListener; * @author Axel Uhl (d043530) * */ -public class CompetitorChangeReceiver extends AbstractReceiverWithQueue { +public class CompetitorChangeReceiver extends AbstractReceiverWithQueue +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 AbstractReceiverWithQueuerunnable.run()); + } } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 0c0b43e5683..a27823f7a4c 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -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 diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/OfficialCompetitorUpdateProvider.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/OfficialCompetitorUpdateProvider.java new file mode 100644 index 00000000000..f1d432dd76c --- /dev/null +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/OfficialCompetitorUpdateProvider.java @@ -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); +} diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceAndCompetitorStatusWithRaceLogReconciler.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceAndCompetitorStatusWithRaceLogReconciler.java index 3f8b8b0b0de..21b88c4a948 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceAndCompetitorStatusWithRaceLogReconciler.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceAndCompetitorStatusWithRaceLogReconciler.java @@ -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, RaceLogListener> raceLogListeners; + private OfficialCompetitorUpdateProvider officialCompetitorUpdateProvider; private final static Map 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; + } } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java index 11d7f234250..791cd6e7260 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java @@ -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 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); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnRaceLogReplicator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnRaceLogReplicator.java index 5aa49dd6095..7e593ce50a0 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnRaceLogReplicator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnRaceLogReplicator.java @@ -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); + } } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java index 1a636c2da3e..92f9de63378 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java @@ -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() { diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogEventSerializer.java index ba4e88fd819..ab44fb2cc5b 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogEventSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogEventSerializer.java @@ -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, Rac new RaceLogORCLegDataEventSerializer(competitorSerializer), new RaceLogORCCertificateAssignmentEventSerializer(competitorSerializer), new RaceLogORCScratchBoatEventSerializer(competitorSerializer), - new RaceLogORCImpliedWindSourceEventSerializer(competitorSerializer)); + new RaceLogORCImpliedWindSourceEventSerializer(competitorSerializer), + new RaceLogResultsAreOfficialEventSerializer(competitorSerializer)); } private final JsonSerializer flagEventSerializer; @@ -121,6 +123,7 @@ public class RaceLogEventSerializer implements JsonSerializer, Rac private final JsonSerializer orcCertificateAssignmentEventSerializer; private final JsonSerializer orcScratchBoatEventSerializer; private final JsonSerializer orcImpliedWindSourceEventSerializer; + private final JsonSerializer resultsAreOfficialEventSerializer; private JsonSerializer chosenSerializer; @@ -153,7 +156,8 @@ public class RaceLogEventSerializer implements JsonSerializer, Rac JsonSerializer orcLegDataEventSerializer, JsonSerializer orcCertificateAssignmentEventSerializer, JsonSerializer orcScratchBoatEventSerializer, - JsonSerializer orcImpliedWindSourceEventSerializer) { + JsonSerializer orcImpliedWindSourceEventSerializer, + JsonSerializer resultsAreOfficialEventSerializer) { this.flagEventSerializer = flagEventSerializer; this.startTimeSerializer = startTimeSerializer; this.raceStatusSerializer = raceStatusSerializer; @@ -182,6 +186,7 @@ public class RaceLogEventSerializer implements JsonSerializer, 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, Rac public void visit(RaceLogORCImpliedWindSourceEvent event) { chosenSerializer = orcImpliedWindSourceEventSerializer; } + + @Override + public void visit(RaceLogResultsAreOfficialEvent event) { + chosenSerializer = resultsAreOfficialEventSerializer; + } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogResultsAreOfficialEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogResultsAreOfficialEventSerializer.java new file mode 100755 index 00000000000..9907e2dd224 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogResultsAreOfficialEventSerializer.java @@ -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 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; + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java index b2519e38fbd..f825dd8e740 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java @@ -76,6 +76,7 @@ public class RaceLogEventSerializerTest { private JsonSerializer orcCertificateAssignmentEventSerializer; private JsonSerializer orcScratchBoatEventSerializer; private JsonSerializer orcSetImpliedWindEventSerializer; + private JsonSerializer 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 diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java index 3e8a96c2a44..932f74fa8dd 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java @@ -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) { diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Axel).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Axel).launch old mode 100644 new mode 100755 diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest Axel).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest Axel).launch old mode 100644 new mode 100755 diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceSummaryFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceSummaryFragment.java index f8142c472ff..a8ba867e6a9 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceSummaryFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceSummaryFragment.java @@ -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) { } }