mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-06 19:12:23 +00:00
Merge branch 'bug5217'
This commit is contained in:
commit
9ea955ae2c
24 files changed
+902
-53
No files matched your search
+11
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RegattaRegistry;
|
||||
import com.sap.sailing.domain.base.RemoteSailingServerReference;
|
||||
import com.sap.sailing.domain.base.SailingServerConfiguration;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.impl.DynamicBoat;
|
||||
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
|
||||
@@ -35,8 +36,10 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardRegistry;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
@@ -223,4 +226,12 @@ public interface DomainObjectFactory {
|
||||
TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> getRaceTrackingConnectivityParamsServiceFinder();
|
||||
|
||||
Wind loadWind(Document windDocument);
|
||||
|
||||
Map<RaceIdentifier, MarkPassingRaceFingerprint> loadFingerprintsForMarkPassingHashes();
|
||||
|
||||
/**
|
||||
* For races that have a {@link MarkPassingRaceFingerprint} stored in the database (see {@link #loadFingerprintsForMarkPassingHashes()})
|
||||
* a caller can load the corresponding mark passings with this method.
|
||||
*/
|
||||
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier);
|
||||
}
|
||||
+4
-1
@@ -188,5 +188,8 @@ public enum FieldNames {
|
||||
ORC_CERTIFICATE_TWS_14KT, ORC_CERTIFICATE_TWS_16KT, ORC_CERTIFICATE_TWS_20KT, ORC_CERTIFICATE_R52_PREDICTION, ORC_CERTIFICATE_R60_PREDICTION,
|
||||
ORC_CERTIFICATE_R75_PREDICTION, ORC_CERTIFICATE_R90_PREDICTION, ORC_CERTIFICATE_R110_PREDICTION, ORC_CERTIFICATE_R120_PREDICTION,
|
||||
ORC_CERTIFICATE_R135_PREDICTION, ORC_CERTIFICATE_R150_PREDICTION, ORC_CERTIFICATE_BOATCLASS, ORC_CERTIFICATE_LENGTH, ORC_CERTIFICATE_TWA_SPEED_PREDICTIONS,
|
||||
ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS, ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS, ORC_OTHER_RACE_IDENTIFIER, ORC_IMPLIED_WIND_SOURCE;
|
||||
ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS, ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS, ORC_OTHER_RACE_IDENTIFIER, ORC_IMPLIED_WIND_SOURCE,
|
||||
|
||||
// MARKPASSINGS collection
|
||||
MARK_PASSINGS_FINGERPRINT, MARK_PASSINGS;
|
||||
}
|
||||
+15
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -20,14 +21,18 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RemoteSailingServerReference;
|
||||
import com.sap.sailing.domain.base.SailingServerConfiguration;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.dto.AnniversaryType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
@@ -228,4 +233,14 @@ public interface MongoObjectFactory {
|
||||
void storeAnniversaryData(ConcurrentHashMap<Integer, Pair<DetailedRaceInfo, AnniversaryType>> knownAnniversaries);
|
||||
|
||||
Document storeWind(Wind wind);
|
||||
|
||||
/**
|
||||
* Stores a race's mark passings persistently.
|
||||
*
|
||||
* @param raceIdentifier
|
||||
* identifies the race to which the mark passings belong
|
||||
* @param fingerprint
|
||||
* a composite fingerprint of the race in the state at which the mark passings were captured
|
||||
*/
|
||||
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings);
|
||||
}
|
||||
+6
-1
@@ -120,6 +120,11 @@ public enum CollectionNames {
|
||||
/**
|
||||
* Contains the known anniversaries
|
||||
*/
|
||||
ANNIVERSARIES;
|
||||
ANNIVERSARIES,
|
||||
|
||||
/**
|
||||
* Contains the Hashes for the {@link MarkPassingHashFingerprint}
|
||||
*/
|
||||
MARKPASSINGS;
|
||||
|
||||
}
|
||||
+77
@@ -236,6 +236,8 @@ import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintImpl;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.FieldNames;
|
||||
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
|
||||
@@ -247,10 +249,12 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor;
|
||||
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.regattalog.RegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorWithBoatRefJsonDeserializer;
|
||||
@@ -395,6 +399,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #loadRaceIdentifier(Document)
|
||||
*/
|
||||
private void addRaceIdentifierToQuery(Document query, RaceIdentifier raceIdentifier) {
|
||||
query.put(FieldNames.EVENT_NAME.name(), raceIdentifier.getRegattaName());
|
||||
query.put(FieldNames.RACE_NAME.name(), raceIdentifier.getRaceName());
|
||||
}
|
||||
|
||||
static void ensureIndicesOnWindTracks(MongoCollection<Document> windTracks) {
|
||||
windTracks.createIndex(new Document(FieldNames.RACE_ID.name(), 1), new IndexOptions().name("windbyrace").background(false)); // for new programmatic access
|
||||
@@ -3158,5 +3170,70 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
public TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> getRaceTrackingConnectivityParamsServiceFinder() {
|
||||
return raceTrackingConnectivityParamsServiceFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RaceIdentifier, MarkPassingRaceFingerprint> loadFingerprintsForMarkPassingHashes() {
|
||||
final MongoCollection<Document> markPassingsCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
markPassingsCollection.createIndex(new Document()
|
||||
.append(FieldNames.EVENT_NAME.name(), 1)
|
||||
.append(FieldNames.RACE_NAME.name(), 1),
|
||||
new IndexOptions()
|
||||
.unique(true)
|
||||
.name("markpassingsbyeventandrace")
|
||||
.background(false));
|
||||
final Map<RaceIdentifier, MarkPassingRaceFingerprint> fingerprintHashMap = new HashMap<>();
|
||||
for (final Document currentDocument : markPassingsCollection.find()) {
|
||||
final Pair<RaceIdentifier, MarkPassingRaceFingerprint> fingerprint = loadMarkPassingsFingerprint(currentDocument);
|
||||
if (fingerprint != null && fingerprint.getB() != null) {
|
||||
fingerprintHashMap.put(fingerprint.getA(), fingerprint.getB());
|
||||
}
|
||||
}
|
||||
return fingerprintHashMap;
|
||||
}
|
||||
|
||||
private Pair<RaceIdentifier, MarkPassingRaceFingerprint> loadMarkPassingsFingerprint(final Document currentDocument) {
|
||||
final RaceIdentifier raceIdentifier = loadRaceIdentifier(currentDocument);
|
||||
MarkPassingRaceFingerprint fingerprint;
|
||||
try {
|
||||
final JSONObject json = Helpers.toJSONObjectSafe(
|
||||
new JSONParser().parse(((Document) currentDocument.get(FieldNames.MARK_PASSINGS_FINGERPRINT.name())).toJson()));
|
||||
fingerprint = new MarkPassingRaceFingerprintImpl(json);
|
||||
} catch (JsonDeserializationException | ParseException e) {
|
||||
logger.log(Level.WARNING, "Problem de-serializing mark passings from document; ignoring", e);
|
||||
fingerprint = null;
|
||||
}
|
||||
return new Pair<>(raceIdentifier, fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier) {
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> result;
|
||||
final Document query = new Document();
|
||||
addRaceIdentifierToQuery(query, raceIdentifier);
|
||||
final MongoCollection<Document> markPassingsCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
final Document doc = markPassingsCollection.find(query).first();
|
||||
if (doc != null) {
|
||||
result = new HashMap<>();
|
||||
final List<Document> markPassingsDoc = doc.getList(FieldNames.MARK_PASSINGS.name(), Document.class);
|
||||
for (final Document markPassingsForOneCompetitorDoc : markPassingsDoc) {
|
||||
final Serializable competitorId = markPassingsForOneCompetitorDoc.get(FieldNames.COMPETITOR_ID.name(), Serializable.class);
|
||||
final Competitor competitor = baseDomainFactory.getExistingCompetitorById(competitorId);
|
||||
for (final Document markPassingForWaypoint : markPassingsForOneCompetitorDoc.getList(FieldNames.MARK_PASSINGS.name(), Document.class)) {
|
||||
final Pair<Waypoint, MarkPassing> waypointAndMarkPassing = loadWaypointAndMarkPassing(competitor, markPassingForWaypoint);
|
||||
result.computeIfAbsent(competitor, c->new HashMap<>()).put(waypointAndMarkPassing.getA(), waypointAndMarkPassing.getB());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Pair<Waypoint, MarkPassing> loadWaypointAndMarkPassing(Competitor competitor, Document markPassingForWaypoint) {
|
||||
final Serializable waypointId = markPassingForWaypoint.get(FieldNames.WAYPOINT_ID.name(), Serializable.class);
|
||||
final Waypoint waypoint = baseDomainFactory.getExistingWaypointById(waypointId);
|
||||
final TimePoint timePoint = TimePoint.of(markPassingForWaypoint.getLong(FieldNames.TIME_AS_MILLIS.name()));
|
||||
final MarkPassing markPassing = new MarkPassingImpl(timePoint, waypoint, competitor);
|
||||
return new Pair<>(waypoint, markPassing);
|
||||
}
|
||||
}
|
||||
+31
@@ -126,10 +126,12 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreaking
|
||||
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.persistence.FieldNames;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -1964,5 +1966,34 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
document.append(FieldNames.ORC_CERTIFICATE.name(), createORCCertificateObject(event.getCertificate()));
|
||||
return storeRegattaLogEvent(regattaLikeIdentifier, document);
|
||||
}
|
||||
|
||||
private List<Document> storeMarkPassings(Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
|
||||
final List<Document> result = new ArrayList<>();
|
||||
for (final Entry<Competitor, Map<Waypoint, MarkPassing>> e : markPassings.entrySet()) {
|
||||
final Document competitorMarkPassings = new Document();
|
||||
competitorMarkPassings.put(FieldNames.COMPETITOR_ID.name(), e.getKey().getId());
|
||||
final List<Document> markPassingsList = new ArrayList<>();
|
||||
for (final Entry<Waypoint, MarkPassing> f : e.getValue().entrySet()) {
|
||||
final Document markPassingDoc = new Document();
|
||||
markPassingDoc.put(FieldNames.WAYPOINT_ID.name(), f.getKey().getId());
|
||||
markPassingDoc.put(FieldNames.TIME_AS_MILLIS.name(), f.getValue().getTimePoint().asMillis());
|
||||
markPassingsList.add(markPassingDoc);
|
||||
}
|
||||
competitorMarkPassings.put(FieldNames.MARK_PASSINGS.name(), markPassingsList);
|
||||
result.add(competitorMarkPassings);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void storeMarkPassings(RaceIdentifier raceIdentification, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
|
||||
MongoCollection<Document> markPassingCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
JSONObject fingerprintjson = fingerprint.toJson();
|
||||
final Document result = new Document();
|
||||
final Document fingerprintDoc = Document.parse(fingerprintjson.toString());
|
||||
result.put(FieldNames.MARK_PASSINGS_FINGERPRINT.name(), fingerprintDoc);
|
||||
storeRaceIdentifier(result, raceIdentification);
|
||||
final List<Document> markPassingsDoc = storeMarkPassings(markPassings);
|
||||
result.put(FieldNames.MARK_PASSINGS.name(), markPassingsDoc);
|
||||
markPassingCollection.insertOne(result);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -70,7 +70,7 @@ public interface SharedDomainFactory<RLR extends RaceLogResolver> extends Compet
|
||||
*/
|
||||
Waypoint createWaypoint(ControlPoint controlPoint, PassingInstruction passingInstruction);
|
||||
|
||||
Waypoint getExistingWaypointById(Waypoint waypointPrototype);
|
||||
Waypoint getExistingWaypointById(Serializable waypointId);
|
||||
|
||||
/**
|
||||
* Atomically checks if a waypoint by an equal {@link Waypoint#getId()} as <code>waypoint</code> exists in this domain factory's
|
||||
|
||||
+3
-3
@@ -271,16 +271,16 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getExistingWaypointById(Waypoint waypointPrototype) {
|
||||
public Waypoint getExistingWaypointById(Serializable waypointId) {
|
||||
synchronized (waypointCache) {
|
||||
expungeStaleWaypointCacheEntries();
|
||||
Waypoint result = null;
|
||||
Reference<Waypoint> ref = waypointCache.get(waypointPrototype.getId());
|
||||
Reference<Waypoint> ref = waypointCache.get(waypointId);
|
||||
if (ref != null) {
|
||||
result = ref.get();
|
||||
if (result == null) {
|
||||
// waypoint was finalized; remove entry from cache
|
||||
waypointCache.remove(waypointPrototype.getId());
|
||||
waypointCache.remove(waypointId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package com.sap.sailing.domain.test.markpassinghash;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory;
|
||||
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class MarkPassingRaceFingerprintJsonSerializationTest extends OnlineTracTracBasedTest {
|
||||
DynamicTrackedRaceImpl trackedRace1;
|
||||
DynamicTrackedRaceImpl trackedRace2;
|
||||
MarkPassingCalculator calculator1;
|
||||
MarkPassingCalculator calculator2;
|
||||
|
||||
public MarkPassingRaceFingerprintJsonSerializationTest() throws MalformedURLException, URISyntaxException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExpectedEventName() {
|
||||
return "Academy Tracking 2011";
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp("event_20110505_SailingTea", // Semifinale
|
||||
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
|
||||
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
|
||||
trackedRace1 = getTrackedRace();
|
||||
super.setUp();
|
||||
super.setUp("event_20110505_SailingTea", // Semifinale
|
||||
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
|
||||
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
|
||||
trackedRace2 = getTrackedRace();
|
||||
calculator1 = new MarkPassingCalculator(trackedRace1, false, false);
|
||||
calculator2 = new MarkPassingCalculator(trackedRace2, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonSerialization() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
MarkPassingRaceFingerprint output1 = factory.fromJson(json1);
|
||||
assertTrue("Original and de-serialized copy are equal", output1.matches(trackedRace1));
|
||||
assertEquals(fingerprint1, output1);
|
||||
assertEquals(fingerprint1.hashCode(), output1.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonSerializationWithChangesInMarkFixes() {
|
||||
DynamicTrackedRaceImpl testRace = trackedRace2;
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
// Change of the race should result in a different hash
|
||||
TimePoint epoch = new MillisecondsTimePoint(0l);
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
Map<String, Position> markPositions = new HashMap<String, Position>();
|
||||
markPositions.put("CR Start (1)", new DegreePosition(53.562944999999985, 10.010104000000046));
|
||||
markPositions.put("CR Start (2)", new DegreePosition(53.562944999999985, 10.010104000000046));
|
||||
markPositions.put("Leeward mark", new DegreePosition(53.562145000000015, 10.009252));
|
||||
markPositions.put("Luvtonne", new DegreePosition(53.560581899999995, 10.005657));
|
||||
for (Waypoint w : trackedRace2.getRace().getCourse().getWaypoints()) {
|
||||
for (Mark mark : w.getMarks()) {
|
||||
assert markPositions.containsKey(mark.getName());
|
||||
testRace.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), epoch));
|
||||
testRace.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), now));
|
||||
}
|
||||
}
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(testRace);
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaypointChangePassingInstruction() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Waypoint wp = trackedRace2.getRace().getCourse().getFirstWaypoint();
|
||||
ControlPoint cP = wp.getControlPoint();
|
||||
WaypointImpl wpNew = new WaypointImpl(cP, PassingInstruction.Gate);
|
||||
trackedRace2.getRace().getCourse().removeWaypoint(0);
|
||||
trackedRace2.getRace().getCourse().addWaypoint(0, wpNew);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(trackedRace2);
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlPointChange() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Mark gate1 = new MarkImpl("Gate1");
|
||||
Mark gate2 = new MarkImpl("Gate2");
|
||||
ControlPointWithTwoMarks cp = new ControlPointWithTwoMarksImpl(gate1, gate2, "cp", "");
|
||||
Waypoint wpNew = new WaypointImpl(cp, PassingInstruction.None);
|
||||
trackedRace2.getRace().getCourse().removeWaypoint(0);
|
||||
trackedRace2.getRace().getCourse().addWaypoint(0, wpNew);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(trackedRace2);
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompetitorFixChange() {
|
||||
DynamicTrackedRaceImpl testRace = trackedRace2;
|
||||
final DynamicTrackedRaceImpl secureRace = trackedRace2;
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Competitor firstCompetitor = trackedRace2.getRace().getCompetitors().iterator().next();
|
||||
secureRace.getTrack(firstCompetitor).lockForRead();
|
||||
GPSFixMoving firstFix;
|
||||
try {
|
||||
firstFix = testRace.getTrack(firstCompetitor).getRawFixes().iterator().next();
|
||||
} finally {
|
||||
secureRace.getTrack(firstCompetitor).unlockAfterRead();
|
||||
}
|
||||
SpeedWithBearing speed = firstFix.getSpeed();
|
||||
Position pos = firstFix.getPosition();
|
||||
TimePoint tp = firstFix.getTimePoint();
|
||||
DegreePosition degPos = new DegreePosition(pos.getLatDeg() + 0.05, pos.getLngDeg() + 0.05);
|
||||
GPSFixMoving gpsM = new GPSFixMovingImpl(degPos, tp, speed);
|
||||
testRace.getTrack(firstCompetitor).add(gpsM, true);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(testRace);
|
||||
assertFalse(fingerprint1.matches(testRace));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ Export-Package: com.sap.sailing.domain.base,
|
||||
com.sap.sailing.domain.markpassingcalculation,
|
||||
com.sap.sailing.domain.markpassingcalculation.impl,
|
||||
com.sap.sailing.domain.markpassingcalculation.splining,
|
||||
com.sap.sailing.domain.markpassinghash,
|
||||
com.sap.sailing.domain.markpassinghash.impl;x-friends:="com.sap.sailing.domain.persistence",
|
||||
com.sap.sailing.domain.masterdataimport,
|
||||
com.sap.sailing.domain.orc,
|
||||
com.sap.sailing.domain.orc.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.test,com.sap.sailing.mongodb.test",
|
||||
|
||||
+12
@@ -58,6 +58,11 @@ public class MarkPassingCalculator {
|
||||
private final MarkPassingUpdateListener listener;
|
||||
private final static ExecutorService executor = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor();
|
||||
private final LinkedBlockingQueue<StorePositionUpdateStrategy> queue;
|
||||
/**
|
||||
* This CALCULATOR_VERSION variable indicates the Version of the {@link MarkPassingCalculator} and must be changed manually when changing the calculator.
|
||||
* It should be changed by adding +1;
|
||||
*/
|
||||
private final int CALCULATOR_VERSION = 1;
|
||||
|
||||
/**
|
||||
* An "end marker" that can be {@link #enqueueUpdate(StorePositionUpdateStrategy) enqueued} in order to tell the {@link Listen}
|
||||
@@ -96,6 +101,9 @@ public class MarkPassingCalculator {
|
||||
*/
|
||||
private boolean listenerThreadStarted;
|
||||
|
||||
/**
|
||||
* Remember to change the {@link CALCULATOR_VERSION} when changing the MarkPassingCalculator
|
||||
*/
|
||||
public MarkPassingCalculator(DynamicTrackedRace race, boolean doListen, boolean waitForInitialMarkPassingCalculation) {
|
||||
if (doListen) {
|
||||
queue = new LinkedBlockingQueue<>();
|
||||
@@ -542,4 +550,8 @@ public class MarkPassingCalculator {
|
||||
public String toString() {
|
||||
return getClass().getName()+" for "+race+" with chooser "+chooser;
|
||||
}
|
||||
|
||||
public int getCalculatorVersion() {
|
||||
return CALCULATOR_VERSION;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.markpassinghash;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* An instance of this class represents a composite fingerprint of those components of a {@link TrackedRace} that are
|
||||
* relevant for computing the
|
||||
* {@link TrackedRace#getMarkPassing(com.sap.sailing.domain.base.Competitor, com.sap.sailing.domain.base.Waypoint) mark
|
||||
* passings} in the {@link MarkPassingCalculator}. It can be {@link #matches(TrackedRace) matched} against a {@link TrackedRace}
|
||||
* instance to see whether the {@link TrackedRace} will produce a set of {@link MarkPassing}s equal to that of the {@link TrackedRace}
|
||||
* from which this fingerprint was produced.<p>
|
||||
*
|
||||
* To produce a fingerprint from a {@link TrackedRace} or from a JSON representation use {@link MarkPassingRaceFingerprintFactory}.<p>
|
||||
*
|
||||
* The {@link #equals(Object)} and {@link #hashCode()} methods are defined based on the contents of this fingerprint.
|
||||
*
|
||||
* @author Fabian Kallenbach (i550803)
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface MarkPassingRaceFingerprint {
|
||||
/**
|
||||
* Returns a {@link JSONObject} of the hash values.
|
||||
*/
|
||||
JSONObject toJson();
|
||||
|
||||
/**
|
||||
* Incrementally computes the composite fingerprint of the {@code trackedRace} and compares to this fingerprint
|
||||
* component by component. The fingerprint components are computed in ascending order of computational complexity,
|
||||
* trying to fail early / fast.
|
||||
*
|
||||
* @return {@code true} if the {@code trackedRace} produces a fingerprint equal to this one if passed to
|
||||
* {@link MarkPassingRaceFingerprintFactory#createFingerprint(TrackedRace)}
|
||||
*/
|
||||
boolean matches(TrackedRace trackedRace);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.domain.markpassinghash;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintFactoryImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* Factory for the creation of a {@link MarkPassingRaceFingerprint}.
|
||||
*
|
||||
* @author Fabian Kallenbach (i550803)
|
||||
*/
|
||||
public interface MarkPassingRaceFingerprintFactory {
|
||||
MarkPassingRaceFingerprintFactory INSTANCE = new MarkPassingRaceFingerprintFactoryImpl();
|
||||
|
||||
/**
|
||||
* Creates a {@link MarkPassingRaceFingerprint} out of a given {@link TrackedRace}.
|
||||
*/
|
||||
MarkPassingRaceFingerprint createFingerprint(TrackedRace trackedRace);
|
||||
|
||||
/**
|
||||
* Creates a {@link MarkPassingRaceFingerprint} out of a given {@link JSONObject}.
|
||||
*/
|
||||
MarkPassingRaceFingerprint fromJson(JSONObject json);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.markpassinghash.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
public class MarkPassingRaceFingerprintFactoryImpl implements MarkPassingRaceFingerprintFactory {
|
||||
@Override
|
||||
public MarkPassingRaceFingerprint createFingerprint(TrackedRace trackedRace) {
|
||||
return new MarkPassingRaceFingerprintImpl(trackedRace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassingRaceFingerprint fromJson(JSONObject json) {
|
||||
return new MarkPassingRaceFingerprintImpl(json);
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
package com.sap.sailing.domain.markpassinghash.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public class MarkPassingRaceFingerprintImpl implements MarkPassingRaceFingerprint {
|
||||
private final int calculatorVersion;
|
||||
private final int competitorHash;
|
||||
private final TimePoint startOfTracking;
|
||||
private final TimePoint startTimeReceived;
|
||||
private final TimePoint endOfTracking;
|
||||
private final TimePoint startTimeFromRaceLog;
|
||||
private final TimePoint finishTimeFromRaceLog;
|
||||
private final int waypointsHash;
|
||||
private final int numberOfGPSFixes;
|
||||
private final int gpsFixesHash;
|
||||
|
||||
private static enum JSON_FIELDS {
|
||||
COMPETITOR_HASH, START_OF_TRACKING_AS_MILLIS, END_OF_TRACKING_AS_MILLIS, START_TIME_RECEIVED_AS_MILLIS, START_TIME_FROM_RACE_LOG_AS_MILLIS, FINISH_TIME_FROM_RACE_LOG_AS_MILLIS, WAYPOINTS_HASH, NUMBEROFGPSFIXES, GPSFIXES_HASH, RACE_ID, CALCULATOR_VERSION
|
||||
};
|
||||
|
||||
public MarkPassingRaceFingerprintImpl(TrackedRace trackedRace) {
|
||||
this.calculatorVersion = getCalculatorVersion(trackedRace);
|
||||
this.competitorHash = calculateHashForCompetitors(trackedRace);
|
||||
this.startOfTracking = trackedRace.getStartOfTracking();
|
||||
this.endOfTracking = trackedRace.getEndOfTracking();
|
||||
this.startTimeReceived = trackedRace.getStartTimeReceived();
|
||||
final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace
|
||||
.getStartAndFinishedTimeFromRaceLogs();
|
||||
this.startTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null
|
||||
: startAndFinishedTimeFromRaceLogs.getA();
|
||||
this.finishTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null
|
||||
: startAndFinishedTimeFromRaceLogs.getB();
|
||||
this.waypointsHash = calculateHashForWaypoints(trackedRace);
|
||||
this.numberOfGPSFixes = calculateHashForNumberOfGPSFixes(trackedRace);
|
||||
this.gpsFixesHash = calculateHashForGPSFixes(trackedRace);
|
||||
}
|
||||
|
||||
public MarkPassingRaceFingerprintImpl(JSONObject json) {
|
||||
this.calculatorVersion = ((Number) json.get(JSON_FIELDS.CALCULATOR_VERSION.name())).intValue();
|
||||
this.competitorHash = ((Number) json.get(JSON_FIELDS.COMPETITOR_HASH.name())).intValue();
|
||||
this.startOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name()));
|
||||
this.endOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name()));
|
||||
this.startTimeReceived = TimePoint.of((Long) json.get(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name()));
|
||||
this.startTimeFromRaceLog = TimePoint
|
||||
.of((Long) json.get(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name()));
|
||||
this.finishTimeFromRaceLog = TimePoint
|
||||
.of((Long) json.get(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name()));
|
||||
this.waypointsHash = ((Number) json.get(JSON_FIELDS.WAYPOINTS_HASH.name())).intValue();
|
||||
this.numberOfGPSFixes = ((Number) json.get(JSON_FIELDS.NUMBEROFGPSFIXES.name())).intValue();
|
||||
this.gpsFixesHash = ((Number) json.get(JSON_FIELDS.GPSFIXES_HASH.name())).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put(JSON_FIELDS.CALCULATOR_VERSION.name(), calculatorVersion);
|
||||
result.put(JSON_FIELDS.COMPETITOR_HASH.name(), competitorHash);
|
||||
result.put(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name(),
|
||||
startOfTracking == null ? null : startOfTracking.asMillis());
|
||||
result.put(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name(),
|
||||
endOfTracking == null ? null : endOfTracking.asMillis());
|
||||
result.put(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name(),
|
||||
startTimeReceived == null ? null : startTimeReceived.asMillis());
|
||||
result.put(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name(),
|
||||
startTimeFromRaceLog == null ? null : startTimeFromRaceLog.asMillis());
|
||||
result.put(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name(),
|
||||
finishTimeFromRaceLog == null ? null : finishTimeFromRaceLog.asMillis());
|
||||
result.put(JSON_FIELDS.WAYPOINTS_HASH.name(), waypointsHash);
|
||||
result.put(JSON_FIELDS.NUMBEROFGPSFIXES.name(), numberOfGPSFixes);
|
||||
result.put(JSON_FIELDS.GPSFIXES_HASH.name(), gpsFixesHash);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TrackedRace trackedRace) {
|
||||
final boolean result;
|
||||
if (!Util.equalsWithNull(calculatorVersion, getCalculatorVersion(trackedRace))) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(startOfTracking, trackedRace.getStartOfTracking())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(endOfTracking, trackedRace.getEndOfTracking())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(startTimeReceived, trackedRace.getStartTimeReceived())) {
|
||||
result = false;
|
||||
} else {
|
||||
final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace
|
||||
.getStartAndFinishedTimeFromRaceLogs();
|
||||
if (!Util.equalsWithNull(startTimeFromRaceLog,
|
||||
startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getA())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(finishTimeFromRaceLog,
|
||||
startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getB())) {
|
||||
result = false;
|
||||
} else if (waypointsHash != calculateHashForWaypoints(trackedRace)) {
|
||||
result = false;
|
||||
} else if (competitorHash != calculateHashForCompetitors(trackedRace)) {
|
||||
result = false;
|
||||
} else if (numberOfGPSFixes != calculateHashForNumberOfGPSFixes(trackedRace)) {
|
||||
result = false;
|
||||
} else if (gpsFixesHash != calculateHashForGPSFixes(trackedRace)) {
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getCalculatorVersion(TrackedRace trackedRace) {
|
||||
MarkPassingCalculator calculator = new MarkPassingCalculator((DynamicTrackedRace) trackedRace, false, false);
|
||||
int result = calculator.getCalculatorVersion();
|
||||
return result;
|
||||
}
|
||||
|
||||
private int calculateHashForCompetitors(TrackedRace trackedRace) {
|
||||
int hashForCompetitors = 1023;
|
||||
for (Competitor c : trackedRace.getRace().getCompetitors()) {
|
||||
hashForCompetitors = hashForCompetitors ^ c.getId().hashCode();
|
||||
}
|
||||
return hashForCompetitors;
|
||||
}
|
||||
|
||||
private int calculateHashForNumberOfGPSFixes(TrackedRace trackedRace) {
|
||||
int count = 0;
|
||||
for (Mark m : trackedRace.getMarks()) {
|
||||
count += trackedRace.getTrack(m).size();
|
||||
}
|
||||
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
count += trackedRace.getTrack(competitor).size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int calculateHashForGPSFixes(TrackedRace trackedRace) {
|
||||
int res = 511;
|
||||
for (Mark m : trackedRace.getMarks()) {
|
||||
final GPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getTrack(m);
|
||||
markTrack.lockForRead();
|
||||
try {
|
||||
for (GPSFix gf : markTrack.getFixes()) {
|
||||
res = res ^ gf.getTimePoint().hashCode();
|
||||
res = res ^ gf.getPosition().hashCode();
|
||||
}
|
||||
} finally {
|
||||
markTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
final GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(competitor);
|
||||
competitorTrack.lockForRead();
|
||||
try {
|
||||
for (GPSFixMoving gfm : competitorTrack.getFixes()) {
|
||||
res = res ^ gfm.getTimePoint().hashCode();
|
||||
res = res ^ gfm.getPosition().hashCode();
|
||||
res = res ^ gfm.getSpeed().getBearing().hashCode();
|
||||
res = res ^ Double.hashCode(gfm.getSpeed().getKnots());
|
||||
}
|
||||
} finally {
|
||||
competitorTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private int calculateHashForWaypoints(TrackedRace trackedRace) {
|
||||
Iterable<Waypoint> waypoints = trackedRace.getRace().getCourse().getWaypoints();
|
||||
int res = 0;
|
||||
for (Waypoint p : waypoints) {
|
||||
Iterable<Mark> marks = p.getMarks();
|
||||
for (Mark m : marks) {
|
||||
res = res ^ m.getId().hashCode();
|
||||
}
|
||||
res = res ^ p.getPassingInstructions().hashCode();
|
||||
res = (res << 5) - res; // we want to detect changes in order
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + calculatorVersion;
|
||||
result = prime * result + competitorHash;
|
||||
result = prime * result + ((endOfTracking == null) ? 0 : endOfTracking.hashCode());
|
||||
result = prime * result + ((finishTimeFromRaceLog == null) ? 0 : finishTimeFromRaceLog.hashCode());
|
||||
result = prime * result + gpsFixesHash;
|
||||
result = prime * result + numberOfGPSFixes;
|
||||
result = prime * result + ((startOfTracking == null) ? 0 : startOfTracking.hashCode());
|
||||
result = prime * result + ((startTimeFromRaceLog == null) ? 0 : startTimeFromRaceLog.hashCode());
|
||||
result = prime * result + ((startTimeReceived == null) ? 0 : startTimeReceived.hashCode());
|
||||
result = prime * result + waypointsHash;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkPassingRaceFingerprintImpl other = (MarkPassingRaceFingerprintImpl) obj;
|
||||
if (calculatorVersion != other.calculatorVersion)
|
||||
return false;
|
||||
if (competitorHash != other.competitorHash)
|
||||
return false;
|
||||
if (endOfTracking == null) {
|
||||
if (other.endOfTracking != null)
|
||||
return false;
|
||||
} else if (!endOfTracking.equals(other.endOfTracking))
|
||||
return false;
|
||||
if (finishTimeFromRaceLog == null) {
|
||||
if (other.finishTimeFromRaceLog != null)
|
||||
return false;
|
||||
} else if (!finishTimeFromRaceLog.equals(other.finishTimeFromRaceLog))
|
||||
return false;
|
||||
if (gpsFixesHash != other.gpsFixesHash)
|
||||
return false;
|
||||
if (numberOfGPSFixes != other.numberOfGPSFixes)
|
||||
return false;
|
||||
if (startOfTracking == null) {
|
||||
if (other.startOfTracking != null)
|
||||
return false;
|
||||
} else if (!startOfTracking.equals(other.startOfTracking))
|
||||
return false;
|
||||
if (startTimeFromRaceLog == null) {
|
||||
if (other.startTimeFromRaceLog != null)
|
||||
return false;
|
||||
} else if (!startTimeFromRaceLog.equals(other.startTimeFromRaceLog))
|
||||
return false;
|
||||
if (startTimeReceived == null) {
|
||||
if (other.startTimeReceived != null)
|
||||
return false;
|
||||
} else if (!startTimeReceived.equals(other.startTimeReceived))
|
||||
return false;
|
||||
if (waypointsHash != other.waypointsHash)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,13 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
/**
|
||||
* Mark passings have an {@link #equals(Object)} and {@link #hashCode()} implementation based
|
||||
* on their content, not their identity.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkPassing extends Timed {
|
||||
Waypoint getWaypoint();
|
||||
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
|
||||
/**
|
||||
* The Waypoint before an Offset Mark (see {@link PassingInstruction}) contains a MarkPassing for this mark, which does
|
||||
* not start an own leg but can be interesting for analysis.
|
||||
*
|
||||
* @author Nicolas Klose
|
||||
*
|
||||
*/
|
||||
public interface MarkPassingForOffsetWaypoint extends MarkPassing {
|
||||
|
||||
MarkPassing getOffsetPassing();
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
@@ -484,6 +487,21 @@ public interface TrackedRace
|
||||
* {@link MarkPassingCalculator }to block the thread until all calculations will be finished.
|
||||
*/
|
||||
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor, boolean waitForLatestUpdates);
|
||||
|
||||
/**
|
||||
* Fetches all mark passings for all competitors from this tracked race.
|
||||
*
|
||||
* @see #getMarkPassings(Competitor, boolean)
|
||||
*/
|
||||
default Map<Competitor, Map<Waypoint, MarkPassing>> getMarkPassings(boolean waitForLatestUpdates) {
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> result = new HashMap<>();
|
||||
for (final Competitor competitor : getRace().getCompetitors()) {
|
||||
for (final MarkPassing markPassing : getMarkPassings(competitor, waitForLatestUpdates)) {
|
||||
result.computeIfAbsent(competitor, k->new HashMap<>()).put(markPassing.getWaypoint(), markPassing);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This obtains the course's read lock before asking for the read lock for the <code>markPassings</code> structure.
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingForOffsetWaypoint;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class MarkPassingForOffsetImpl extends MarkPassingImpl implements MarkPassingForOffsetWaypoint {
|
||||
|
||||
private static final long serialVersionUID = -2168312586388285697L;
|
||||
private final MarkPassing markpassingforOffset;
|
||||
|
||||
|
||||
public MarkPassingForOffsetImpl(TimePoint timePoint, Waypoint waypoint, Competitor competitor, MarkPassing markPassingforOffset) {
|
||||
super(timePoint, waypoint, competitor);
|
||||
this.markpassingforOffset = markPassingforOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassing getOffsetPassing() {
|
||||
return markpassingforOffset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+37
@@ -43,4 +43,41 @@ public class MarkPassingImpl implements MarkPassing {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((competitor == null) ? 0 : competitor.hashCode());
|
||||
result = prime * result + ((timePoint == null) ? 0 : timePoint.hashCode());
|
||||
result = prime * result + ((waypoint == null) ? 0 : waypoint.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkPassingImpl other = (MarkPassingImpl) obj;
|
||||
if (competitor == null) {
|
||||
if (other.competitor != null)
|
||||
return false;
|
||||
} else if (!competitor.equals(other.competitor))
|
||||
return false;
|
||||
if (timePoint == null) {
|
||||
if (other.timePoint != null)
|
||||
return false;
|
||||
} else if (!timePoint.equals(other.timePoint))
|
||||
return false;
|
||||
if (waypoint == null) {
|
||||
if (other.waypoint != null)
|
||||
return false;
|
||||
} else if (!waypoint.equals(other.waypoint))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -120,7 +120,7 @@
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.gwt.ui"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:-UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false -Xmx4096m"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/>
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.sap.sailing.mongodb.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.PersistenceFactory;
|
||||
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.mongodb.MongoDBConfiguration;
|
||||
|
||||
public class MarkPassingRaceFingerprintConversionTest extends OnlineTracTracBasedTest {
|
||||
DynamicTrackedRaceImpl trackedRace1;
|
||||
DynamicTrackedRaceImpl trackedRace2;
|
||||
private final MongoDBConfiguration dbConfiguration;
|
||||
|
||||
public MarkPassingRaceFingerprintConversionTest() throws MalformedURLException, URISyntaxException {
|
||||
super();
|
||||
dbConfiguration = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
}
|
||||
|
||||
private MongoClient newMongo() throws UnknownHostException, MongoException {
|
||||
return MongoClients.create(dbConfiguration.getMongoClientURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExpectedEventName() {
|
||||
return "Academy Tracking 2011";
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
newMongo().getDatabase(dbConfiguration.getDatabaseName()).drop();
|
||||
super.setUp("event_20110505_SailingTea", // Semifinale
|
||||
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
|
||||
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
|
||||
trackedRace1 = getTrackedRace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringToAndLoadingFromMongo() throws UnknownHostException, MongoException {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint.matches(trackedRace1));
|
||||
MongoClient myFirstMongo = newMongo();
|
||||
MongoDatabase firstDatabase = myFirstMongo.getDatabase(dbConfiguration.getDatabaseName());
|
||||
RaceIdentifier raceIdentifier = trackedRace1.getRaceIdentifier();
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassings = trackedRace1.getMarkPassings(/* waitForLatestUpdates */ true);
|
||||
new MongoObjectFactoryImpl(firstDatabase).storeMarkPassings(raceIdentifier, fingerprint, markPassings);
|
||||
DomainObjectFactory dF = PersistenceFactory.INSTANCE.getDomainObjectFactory(dbConfiguration.getService(), getDomainFactory().getBaseDomainFactory());
|
||||
Map<RaceIdentifier, MarkPassingRaceFingerprint> fingerprintHashMap = dF.loadFingerprintsForMarkPassingHashes();
|
||||
MarkPassingRaceFingerprint fingerprintAfterDB = fingerprintHashMap.get(trackedRace1.getRaceIdentifier());
|
||||
assertTrue("Original and de-serialized copy are equal", fingerprintAfterDB.matches(trackedRace1));
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassingsLoaded = dF.loadMarkPassings(trackedRace1.getRaceIdentifier());
|
||||
assertEquals(markPassings, markPassingsLoaded);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.sap.sailing.server.interfaces;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* A registry to provide access to the {@link MarkPassingRaceFingerprint}s at the different levels of the domain model.
|
||||
* The following use cases exist:
|
||||
* <ul>
|
||||
* <li>Check for a {@link RaceIdentifier} whether mark passings have been stored persistently for the race identified by
|
||||
* that identifier; the result is a {@link MarkPassingRaceFingerprint} which can may or may not be
|
||||
* {@link MarkPassingRaceFingerprint#matches(com.sap.sailing.domain.tracking.TrackedRace) matched} by a
|
||||
* {@link TrackedRace}</li>
|
||||
* <li>If a matching fingerprint is stored in this registry for a race then {@link #loadMarkPassings(RaceIdentifier)
|
||||
* loading the mark passings} for that race and {@link DynamicTrackedRace#updateMarkPassings(Competitor, Iterable)
|
||||
* updating it to the tracked race} is the logical next step.</li>
|
||||
* <li>When a tracked race has reached a "stable" state, e.g., because it's {@link TrackedRace#getEndOfTracking()
|
||||
* end-of-tracking} time point has passed or its {@link TrackedRace#getStatus() status} has changed to
|
||||
* {@link TrackedRaceStatusEnum#FINISHED} then the latest mark passing calculation results can be
|
||||
* {@link #storeMarkPassings(RaceIdentifier, MarkPassingRaceFingerprint, Map) stored} together with the fingerprint
|
||||
* computed from the tracked race.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Fabian Kallenbach (i550803)
|
||||
*/
|
||||
public interface MarkPassingRaceFingerprintRegistry {
|
||||
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings);
|
||||
|
||||
MarkPassingRaceFingerprint getMarkPassingRaceFingerprint(RaceIdentifier raceIdentifier);
|
||||
|
||||
/**
|
||||
* <b>Precondition</b>: {@code fingerprint}.{@link MarkPassingRaceFingerprint#matches(com.sap.sailing.domain.tracking.TrackedRace) matches}
|
||||
* {@code (}{@link #getMarkPassingRaceFingerprint(RaceIdentifier) getMarkPassingRaceFingerprint(raceIdentifier)}{@code )}<p>
|
||||
*
|
||||
* If a mark passing race fingerprint is known for {@code raceIdentifier} in this registry and the
|
||||
*/
|
||||
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier);
|
||||
}
|
||||
+35
-2
@@ -187,6 +187,7 @@ import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
|
||||
@@ -255,6 +256,7 @@ import com.sap.sailing.server.impl.preferences.model.CompetitorNotificationPrefe
|
||||
import com.sap.sailing.server.interfaces.CourseAndMarkConfigurationFactory;
|
||||
import com.sap.sailing.server.interfaces.DataImportLockWithProgress;
|
||||
import com.sap.sailing.server.interfaces.KeywordQueryWithOptionalEventQualification;
|
||||
import com.sap.sailing.server.interfaces.MarkPassingRaceFingerprintRegistry;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.server.interfaces.RacingEventServiceOperation;
|
||||
import com.sap.sailing.server.interfaces.SimulationService;
|
||||
@@ -353,7 +355,8 @@ import com.sap.sse.util.ThreadPoolUtil;
|
||||
|
||||
public class RacingEventServiceImpl
|
||||
extends AbstractReplicableWithObjectInputStream<RacingEventService, RacingEventServiceOperation<?>>
|
||||
implements RacingEventService, ClearStateTestSupport, RegattaListener, LeaderboardRegistry, Replicator {
|
||||
implements RacingEventService, ClearStateTestSupport, RegattaListener, LeaderboardRegistry, MarkPassingRaceFingerprintRegistry,
|
||||
Replicator {
|
||||
private static final Logger logger = Logger.getLogger(RacingEventServiceImpl.class.getName());
|
||||
|
||||
/**
|
||||
@@ -446,6 +449,8 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa
|
||||
private final ConcurrentHashMap<String, Set<LeaderboardGroup>> leaderboardGroupsByName;
|
||||
|
||||
private final ConcurrentHashMap<UUID, LeaderboardGroup> leaderboardGroupsByID;
|
||||
|
||||
private final ConcurrentHashMap<RaceIdentifier, MarkPassingRaceFingerprint> markPassingRaceFingerprints;
|
||||
|
||||
/**
|
||||
* See {@link #leaderboardsByNameLock}
|
||||
@@ -879,8 +884,9 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa
|
||||
leaderboardGroupsByName = new ConcurrentHashMap<>();
|
||||
leaderboardGroupsByID = new ConcurrentHashMap<>();
|
||||
leaderboardGroupsByNameLock = new NamedReentrantReadWriteLock("leaderboardGroupsByName for " + this, /* fair */false);
|
||||
leaderboardsByName = new ConcurrentHashMap<String, Leaderboard>();
|
||||
leaderboardsByName = new ConcurrentHashMap<>();
|
||||
leaderboardsByNameLock = new NamedReentrantReadWriteLock("leaderboardsByName for " + this, /* fair */false);
|
||||
markPassingRaceFingerprints = new ConcurrentHashMap<>();
|
||||
courseListeners = new ConcurrentHashMap<>();
|
||||
persistentRegattasForRaceIDs = new ConcurrentHashMap<>();
|
||||
simulationService = SimulationServiceFactory.INSTANCE.getService(simulatorExecutor, this);
|
||||
@@ -911,6 +917,7 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa
|
||||
loadMediaLibary();
|
||||
loadStoredDeviceConfigurations();
|
||||
loadAllRemoteSailingServersAndSchedulePeriodicEventCacheRefresh();
|
||||
loadMarkPassingRaceFingerprints();
|
||||
// Stores all events which run through a data migration
|
||||
// Remark: must be called after loadLinksFromEventsToLeaderboardGroups(), otherwise would loose the Event -> LeaderboardGroup relation
|
||||
for (Pair<Event, Boolean> eventAndRequireStoreFlag : loadedEventsWithRequireStoreFlag) {
|
||||
@@ -932,6 +939,32 @@ implements RacingEventService, ClearStateTestSupport, RegattaListener, Leaderboa
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMarkPassingRaceFingerprints() {
|
||||
markPassingRaceFingerprints.putAll(domainObjectFactory.loadFingerprintsForMarkPassingHashes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint,
|
||||
Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
|
||||
mongoObjectFactory.storeMarkPassings(raceIdentifier, fingerprint, markPassings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassingRaceFingerprint getMarkPassingRaceFingerprint(RaceIdentifier raceIdentifier) {
|
||||
return markPassingRaceFingerprints.get(raceIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier) {
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> result;
|
||||
if (markPassingRaceFingerprints.containsKey(raceIdentifier)) {
|
||||
result = domainObjectFactory.loadMarkPassings(raceIdentifier);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ClassLoaderRegistry getMasterDataClassLoaders() {
|
||||
return masterDataClassLoaders;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user