mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-01 00:30:46 +00:00
added GPSFixStore concept to TrackedRace
- needs to be passed through RacingEventServiceImpl -> lots of changes in test cases - changes in adapters, as GPSFixStore is also passed around in RaceAdapter etc.
This commit is contained in:
1 parent
07f5efe154
commit
78c848e594
67 files changed
+769
-425
No files matched your search
@@ -49,9 +49,8 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
|
||||
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.RaceListener;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -255,7 +254,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
|
||||
*/
|
||||
void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup);
|
||||
|
||||
DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
|
||||
DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed);
|
||||
|
||||
Regatta getOrCreateDefaultRegatta(String regattaBaseName, String boatClassName, Serializable id);
|
||||
@@ -481,14 +480,6 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
|
||||
DomainObjectFactory getDomainObjectFactory();
|
||||
|
||||
WindStore getWindStore();
|
||||
|
||||
/**
|
||||
* Store {@link GPSFix}es, that originate from devices that are not managed by a tracking provider
|
||||
* who himself persists the tracking data, e.g. in the case of smartphone tracking.
|
||||
*
|
||||
* Currently tracked races with a device mapping for this {@code device} are notified of this new fix.
|
||||
* @param device
|
||||
* @param fix
|
||||
*/
|
||||
void storeGPSFix(DeviceIdentifier device, GPSFix fix);
|
||||
|
||||
GPSFixStore getGPSFixStore();
|
||||
}
|
||||
+34
-63
@@ -93,6 +93,7 @@ import com.sap.sailing.domain.persistence.PersistenceFactory;
|
||||
import com.sap.sailing.domain.persistence.media.DBMediaTrack;
|
||||
import com.sap.sailing.domain.persistence.media.MediaDB;
|
||||
import com.sap.sailing.domain.persistence.media.MediaDBFactory;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStoreFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.racelog.impl.RaceLogEventAuthorImpl;
|
||||
@@ -101,7 +102,7 @@ import com.sap.sailing.domain.racelog.state.ReadonlyRaceState;
|
||||
import com.sap.sailing.domain.racelog.state.impl.RaceStateImpl;
|
||||
import com.sap.sailing.domain.racelog.state.impl.ReadonlyRaceStateImpl;
|
||||
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
@@ -247,14 +248,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
protected final DeviceConfigurationMapImpl configurationMap;
|
||||
|
||||
private final WindStore windStore;
|
||||
|
||||
/**
|
||||
* These tracks are not replicated in any way. They are only loaded on the master instance.
|
||||
* All management of device mappings has to occur via the master instance anyway, and as soon
|
||||
* as a portion of the track is mapped to an actual race, all the relevant fixes are replicated
|
||||
* with the existing replication features through that race.
|
||||
*/
|
||||
private final Map<DeviceIdentifier, DynamicTrack<GPSFix>> tracksByDevice;
|
||||
private final GPSFixStore gpsFixStore;
|
||||
|
||||
/**
|
||||
* If this service runs in the context of an OSGi environment, the activator should {@link #setBundleContext set the bundle context} on this
|
||||
@@ -275,8 +269,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
this(true, null);
|
||||
}
|
||||
|
||||
public RacingEventServiceImpl(WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
this(true, windStore, serviceFinderFactory);
|
||||
public RacingEventServiceImpl(WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
this(true, windStore, gpsFixStore, serviceFinderFactory);
|
||||
}
|
||||
|
||||
void setBundleContext(BundleContext bundleContext) {
|
||||
@@ -296,7 +290,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
* created and stored during previous service executions will initially be loaded.
|
||||
*/
|
||||
public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), clearPersistentCompetitorStore, serviceFinderFactory), null, serviceFinderFactory);
|
||||
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), clearPersistentCompetitorStore, serviceFinderFactory), null, null, serviceFinderFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,9 +307,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
* of {@link DeviceIdentifier}s of specific device types or the managing of the device-to-competitor
|
||||
* associations per race tracked.
|
||||
*/
|
||||
private RacingEventServiceImpl(boolean clearPersistentCompetitorStore, WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
private RacingEventServiceImpl(boolean clearPersistentCompetitorStore, WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory),
|
||||
clearPersistentCompetitorStore, serviceFinderFactory), windStore, serviceFinderFactory);
|
||||
clearPersistentCompetitorStore, serviceFinderFactory), windStore, gpsFixStore, serviceFinderFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,9 +321,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
* {@link DeviceIdentifier}s of specific device types or the managing of the device-to-competitor associations
|
||||
* per race tracked.
|
||||
*/
|
||||
private RacingEventServiceImpl(PersistentCompetitorStore persistentCompetitorStore, WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
private RacingEventServiceImpl(PersistentCompetitorStore persistentCompetitorStore, WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
this(persistentCompetitorStore.getDomainObjectFactory(), persistentCompetitorStore.getMongoObjectFactory(),
|
||||
persistentCompetitorStore.getBaseDomainFactory(), MediaDBFactory.INSTANCE.getDefaultMediaDB(), persistentCompetitorStore, windStore);
|
||||
persistentCompetitorStore.getBaseDomainFactory(), MediaDBFactory.INSTANCE.getDefaultMediaDB(), persistentCompetitorStore, windStore, gpsFixStore, serviceFinderFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,8 +331,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
* {@link PersistenceFactory#getDefaultDomainObjectFactory() default domain object factory}. This constructor should
|
||||
* be used for testing because it provides a transient {@link CompetitorStore} as required for competitor persistence.
|
||||
*/
|
||||
public RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, MediaDB mediaDB, WindStore windStore) {
|
||||
this(domainObjectFactory, mongoObjectFactory, domainObjectFactory.getBaseDomainFactory(), mediaDB, domainObjectFactory.getBaseDomainFactory().getCompetitorStore(), windStore);
|
||||
public RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, MediaDB mediaDB, WindStore windStore, GPSFixStore gpsFixStore) {
|
||||
this(domainObjectFactory, mongoObjectFactory, domainObjectFactory.getBaseDomainFactory(), mediaDB, domainObjectFactory.getBaseDomainFactory().getCompetitorStore(), windStore, gpsFixStore, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +345,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
* per race tracked.
|
||||
*/
|
||||
private RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory,
|
||||
com.sap.sailing.domain.base.DomainFactory baseDomainFactory, MediaDB mediaDb, CompetitorStore competitorStore, WindStore windStore) {
|
||||
com.sap.sailing.domain.base.DomainFactory baseDomainFactory, MediaDB mediaDb, CompetitorStore competitorStore,
|
||||
WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
|
||||
logger.info("Created " + this);
|
||||
this.baseDomainFactory = baseDomainFactory;
|
||||
this.domainObjectFactory = domainObjectFactory;
|
||||
@@ -367,7 +362,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
operationExecutionListeners = new ConcurrentHashMap<>();
|
||||
courseListeners = new ConcurrentHashMap<>();
|
||||
persistentRegattasForRaceIDs = new ConcurrentHashMap<>();
|
||||
tracksByDevice = new ConcurrentHashMap<>();
|
||||
this.raceLogReplicator = new RaceLogReplicator(this);
|
||||
this.raceLogScoringReplicator = new RaceLogScoringReplicator(this);
|
||||
this.mediaDB = mediaDb;
|
||||
@@ -380,6 +374,14 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
}
|
||||
}
|
||||
this.windStore = windStore;
|
||||
if (gpsFixStore == null) {
|
||||
try {
|
||||
gpsFixStore = MongoGPSFixStoreFactory.INSTANCE.getMongoGPSFixStore(mongoObjectFactory, domainObjectFactory, serviceFinderFactory);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.gpsFixStore = gpsFixStore;
|
||||
this.configurationMap = new DeviceConfigurationMapImpl();
|
||||
|
||||
// Add one default leaderboard that aggregates all races currently tracked by this service.
|
||||
@@ -393,7 +395,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
loadStoredLeaderboardsAndGroups();
|
||||
loadMediaLibary();
|
||||
loadStoredDeviceConfigurations();
|
||||
loadStoredTracks();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,21 +460,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStoredTracks() {
|
||||
/*
|
||||
* TODO load tracks in seperate thread, or even better: use proxy tracks, that on first load only
|
||||
* load metadata (beginning and end TimePoints of individual tracking sessions in track, and then
|
||||
* load the fixes when really needed.
|
||||
*/
|
||||
synchronized(tracksByDevice) {
|
||||
try {
|
||||
tracksByDevice.putAll(domainObjectFactory.loadAllGPSFixTracks());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Could not load tracks", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLeaderboard(Leaderboard leaderboard) {
|
||||
@@ -924,10 +910,10 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
Regatta regatta = regattaToAddTo == null ? null : getRegatta(regattaToAddTo);
|
||||
if (regatta == null) {
|
||||
// create tracker and use an existing or create a default regatta
|
||||
tracker = params.createRaceTracker(this, windStore);
|
||||
tracker = params.createRaceTracker(this, windStore, gpsFixStore);
|
||||
} else {
|
||||
// use the regatta selected by the RaceIdentifier regattaToAddTo
|
||||
tracker = params.createRaceTracker(regatta, this, windStore);
|
||||
tracker = params.createRaceTracker(regatta, this, windStore, gpsFixStore);
|
||||
assert tracker.getRegatta() == regatta;
|
||||
}
|
||||
synchronized (raceTrackersByRegatta) {
|
||||
@@ -962,6 +948,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
logger.warning("Wind store mismatch. Requested wind store: " + windStore
|
||||
+ ". Wind store in use by existing tracker: " + existingTrackersWindStore);
|
||||
}
|
||||
GPSFixStore existingTrackersGPSFixStore = tracker.getGPSFixStore();
|
||||
if (!existingTrackersGPSFixStore.equals(gpsFixStore)) {
|
||||
logger.warning("GPSFix store mismatch. Requested GPSFix store: " + gpsFixStore
|
||||
+ ". GPSFix store in use by existing tracker: " + existingTrackersGPSFixStore);
|
||||
}
|
||||
}
|
||||
if (timeoutInMilliseconds != -1) {
|
||||
scheduleAbortTrackerAfterInitialTimeout(tracker, timeoutInMilliseconds);
|
||||
@@ -1035,11 +1026,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
|
||||
public DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) {
|
||||
DynamicTrackedRegatta trackedRegatta = getOrCreateTrackedRegatta(getRegatta(raceIdentifier));
|
||||
RaceDefinition race = getRace(raceIdentifier);
|
||||
return trackedRegatta.createTrackedRace(race, Collections.<Sideline> emptyList(), windStore, delayToLiveInMillis,
|
||||
return trackedRegatta.createTrackedRace(race, Collections.<Sideline> emptyList(), windStore, gpsFixStore, delayToLiveInMillis,
|
||||
millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
|
||||
/* raceDefinitionSetToUpdate */null);
|
||||
}
|
||||
@@ -1092,7 +1083,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
// replicate the addition of the tracked race:
|
||||
CreateTrackedRace op = new CreateTrackedRace(trackedRace.getRaceIdentifier(), trackedRace.getWindStore(),
|
||||
trackedRace.getDelayToLiveInMillis(), trackedRace.getMillisecondsOverWhichToAverageWind(),
|
||||
trackedRace.getGPSFixStore(), trackedRace.getDelayToLiveInMillis(), trackedRace.getMillisecondsOverWhichToAverageWind(),
|
||||
trackedRace.getMillisecondsOverWhichToAverageSpeed());
|
||||
replicate(op);
|
||||
linkRaceToConfiguredLeaderboardColumns(trackedRace);
|
||||
@@ -2196,29 +2187,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private DynamicTrack<GPSFix> getStoredGPSFixTrack(DeviceIdentifier device) {
|
||||
//test first outside of sync block for the likely case that the track already exists in {@link #tracksByDevice}
|
||||
DynamicTrack<GPSFix> track = tracksByDevice.get(device);
|
||||
|
||||
if (track == null) {
|
||||
synchronized(tracksByDevice) {
|
||||
//now test again in sync, in case a track was added in the meantime
|
||||
if (tracksByDevice.get(device) == null) {
|
||||
try {
|
||||
track = domainObjectFactory.loadGPSFixTrack(device);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Could not load track", e);
|
||||
}
|
||||
tracksByDevice.put(device, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeGPSFix(DeviceIdentifier device, GPSFix fix) {
|
||||
getStoredGPSFixTrack(device).add(fix);
|
||||
public GPSFixStore getGPSFixStore() {
|
||||
return gpsFixStore;
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
@@ -414,7 +415,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilWindLoadingComplete() throws InterruptedException {
|
||||
public void waitUntilLoadingFromStoresComplete() throws InterruptedException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
@@ -544,4 +545,10 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixStore getGPSFixStore() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+19
-5
@@ -1,11 +1,13 @@
|
||||
package com.sap.sailing.server.operationaltransformation;
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
@@ -32,16 +34,27 @@ public class CreateTrackedRace extends AbstractRaceOperation<TrackedRace> {
|
||||
* {@link EmptyWindStore}.
|
||||
*/
|
||||
private transient final WindStore windStore;
|
||||
|
||||
/**
|
||||
* If a {@link GPSFixStore} is provided to this command, it will be used for the construction of the tracked race.
|
||||
* However, after de-serialization, the store will always be <code>null</code>, causing the use of an
|
||||
* {@link EmptyGPSFixStore}.
|
||||
*/
|
||||
private transient final GPSFixStore gpsFixStore;
|
||||
|
||||
/**
|
||||
* @param windStore
|
||||
* if <code>null</code>, an {@link EmptyWindStore} will be used. Note that the {@link #windStore} field
|
||||
* won't be serialized. A receiver of this operation will therefore always use an {@link EmptyWindStore}.
|
||||
* @param gpsFixStore
|
||||
* if <code>null</code>, an {@link EmptyGPSFixStore} will be used. Note that the {@link #gpsFixStore} field
|
||||
* won't be serialized. A receiver of this operation will therefore always use an {@link EmptyWindStore}.
|
||||
*/
|
||||
public CreateTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
|
||||
public CreateTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) {
|
||||
super(raceIdentifier);
|
||||
this.windStore = windStore;
|
||||
this.gpsFixStore = gpsFixStore;
|
||||
this.delayToLiveInMillis = delayToLiveInMillis;
|
||||
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
|
||||
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
|
||||
@@ -61,7 +74,8 @@ public class CreateTrackedRace extends AbstractRaceOperation<TrackedRace> {
|
||||
|
||||
@Override
|
||||
public TrackedRace internalApplyTo(RacingEventService toState) {
|
||||
return toState.createTrackedRace(getRaceIdentifier(), windStore == null ? EmptyWindStore.INSTANCE : windStore,
|
||||
return toState.createTrackedRace(getRaceIdentifier(), windStore == null ? EmptyWindStore.INSTANCE : windStore,
|
||||
gpsFixStore == null ? EmptyGPSFixStore.INSTANCE : gpsFixStore,
|
||||
delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user