mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-21 19:50:01 +00:00
Merge branch 'bug3236'
This commit is contained in:
commit
77ee20db15
8 files changed
+269
-65
No files matched your search
+26
@@ -191,12 +191,38 @@ public class MongoGPSFixStoreImpl implements MongoGPSFixStore {
|
||||
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
|
||||
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException {
|
||||
List<DeviceMapping<Competitor>> mappings = new DeviceCompetitorMappingFinder<>(log).analyze().get(competitor);
|
||||
if (mappings != null) {
|
||||
for (DeviceMapping<Competitor> mapping : mappings) {
|
||||
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
|
||||
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
|
||||
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, DeviceMapping<Mark> mapping)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark,
|
||||
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
List<DeviceMapping<Mark>> mappings = new DeviceMarkMappingFinder<>(log).analyze().get(mark);
|
||||
if (mappings != null) {
|
||||
for (DeviceMapping<Mark> mapping : mappings) {
|
||||
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
|
||||
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
|
||||
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
|
||||
throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
|
||||
@@ -18,4 +18,5 @@ Import-Package: com.sap.sailing.domain.abstractlog.regatta.events,
|
||||
com.sap.sailing.domain.persistence.racelog.tracking.impl,
|
||||
com.sap.sailing.domain.tractracadapter,
|
||||
com.sap.sailing.server.impl,
|
||||
com.sap.sse.concurrent,
|
||||
javax.servlet.http;version="2.5.0"
|
||||
+1
-1
@@ -79,7 +79,7 @@ public class AbstractGPSFixStoreTest {
|
||||
mark, 0, new MillisecondsTimePoint(from), new MillisecondsTimePoint(to)));
|
||||
}
|
||||
|
||||
protected void testLength(Track<?> track, long expected) {
|
||||
protected void testNumberOfRawFixes(Track<?> track, long expected) {
|
||||
track.lockForRead();
|
||||
assertEquals(expected, size(track.getRawFixes()));
|
||||
track.unlockAfterRead();
|
||||
|
||||
+48
-4
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
@@ -49,6 +50,49 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
private Integer previouslyLoading = null;
|
||||
private int numFixesReceived = 0;
|
||||
|
||||
@Test
|
||||
public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException,
|
||||
NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
|
||||
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
|
||||
DeviceIdentifier markDevice = new SmartphoneImeiIdentifier("imei2");
|
||||
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
|
||||
new WaypointImpl(mark2) }));
|
||||
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
|
||||
map(comp, device, 0, 10000);
|
||||
map(mark, markDevice, 0, 10000);
|
||||
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40)); // before
|
||||
store.storeFix(device, createFix(1100, 10, 20, 30, 40)); // in
|
||||
store.storeFix(device, createFix(2100, 10, 20, 30, 40)); // after
|
||||
store.storeFix(markDevice, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40));
|
||||
store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40));
|
||||
|
||||
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
|
||||
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
|
||||
null, /* endDate */null, null, null, "a", null));
|
||||
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
|
||||
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false,
|
||||
OneDesignRankingMetric::new, mock(RaceLogResolver.class));
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000));
|
||||
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2000));
|
||||
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog);
|
||||
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 1);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
// now extend the tracking interval of the tracked race and assert that the additional fixes are loaded
|
||||
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2500), /* wait for fixes to load */ true);
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 2);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(0), /* wait for fixes to load */ true);
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 3);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areFixesStoredInDb() throws TransformationException, NoCorrespondingServiceRegisteredException,
|
||||
InterruptedException {
|
||||
@@ -88,10 +132,10 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog);
|
||||
|
||||
testLength(trackedRace.getTrack(comp), 10002);
|
||||
testLength(trackedRace.getTrack(comp2), 1);
|
||||
testLength(trackedRace.getOrCreateTrack(mark), 1);
|
||||
testLength(trackedRace.getOrCreateTrack(mark2), 1);
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 10002);
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp2), 1);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark2), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+11
-1
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.WithID;
|
||||
|
||||
@@ -64,6 +65,15 @@ public enum EmptyGPSFixStore implements GPSFixStore {
|
||||
@Override
|
||||
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
|
||||
throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
|
||||
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark,
|
||||
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
}
|
||||
}
|
||||
+29
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.WithID;
|
||||
|
||||
@@ -23,12 +24,40 @@ public interface GPSFixStore {
|
||||
void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
|
||||
Competitor competitor) throws TransformationException;
|
||||
|
||||
/**
|
||||
* Load all fixes within the start and end time point (inclusive) that correspond to the
|
||||
* {@link DeviceCompetitorMappingEvent}s found in the {@code raceLog}.
|
||||
*
|
||||
* @param start
|
||||
* if <code>null</code>, the start of the time range for which to load fixes is only constrained by the
|
||||
* device mapping intervals
|
||||
* @param end
|
||||
* if <code>null</code>, the end of the time range for which to load fixes is only constrained by the
|
||||
* device mapping intervals
|
||||
*/
|
||||
void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
|
||||
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException;
|
||||
|
||||
/**
|
||||
* Load all fixes that correspond to the {@link DeviceMarkMappingEvent}s found in the {@code raceLog}.
|
||||
*/
|
||||
void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark) throws TransformationException,
|
||||
NoCorrespondingServiceRegisteredException;
|
||||
|
||||
/**
|
||||
* Load all fixes within the start and end time point (inclusive) that correspond to the
|
||||
* {@link DeviceMarkMappingEvent}s found in the {@code raceLog}.
|
||||
*
|
||||
* @param start
|
||||
* if <code>null</code>, the start of the time range for which to load fixes is only constrained by the
|
||||
* device mapping intervals
|
||||
* @param end
|
||||
* if <code>null</code>, the end of the time range for which to load fixes is only constrained by the
|
||||
* device mapping intervals
|
||||
*/
|
||||
void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark, TimePoint start,
|
||||
TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException;
|
||||
|
||||
/**
|
||||
* Load all fixes that correspond to the {@code mapping}.
|
||||
*/
|
||||
|
||||
+32
-9
@@ -143,12 +143,14 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public void recordFix(Competitor competitor, GPSFixMoving fix) {
|
||||
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = getTrack(competitor);
|
||||
if (track != null) {
|
||||
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.FINEST)) {
|
||||
logger.finest(""+competitor.getName() + ": " + fix);
|
||||
if (isFixWithinStartAndEndOfTracking(fix.getTimePoint())) {
|
||||
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = getTrack(competitor);
|
||||
if (track != null) {
|
||||
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.FINEST)) {
|
||||
logger.finest(""+competitor.getName() + ": " + fix);
|
||||
}
|
||||
track.addGPSFix(fix); // the track notifies this tracked race which in turn notifies its listeners
|
||||
}
|
||||
track.addGPSFix(fix); // the track notifies this tracked race which in turn notifies its listeners
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,12 +163,21 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public void recordFix(Mark mark, GPSFix fix) {
|
||||
if ((getStartOfTracking() == null || getStartOfTracking().compareTo(fix.getTimePoint()) <= 0) &&
|
||||
(getEndOfTracking() == null || getEndOfTracking().compareTo(fix.getTimePoint()) >= 0)) {
|
||||
final TimePoint fixTimePoint = fix.getTimePoint();
|
||||
if (isFixWithinStartAndEndOfTracking(fixTimePoint)) {
|
||||
getOrCreateTrack(mark).addGPSFix(fix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A time point is considered "in" if it is (inclusively) between {@link #getStartOfTracking()} and {@link #getEndOfTracking()}.
|
||||
* A <code>null</code> value for one of the two interval demarcations means an open-ended interval.
|
||||
*/
|
||||
private boolean isFixWithinStartAndEndOfTracking(final TimePoint fixTimePoint) {
|
||||
return (getStartOfTracking() == null || getStartOfTracking().compareTo(fixTimePoint) <= 0) &&
|
||||
(getEndOfTracking() == null || getEndOfTracking().compareTo(fixTimePoint) >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMillisecondsOverWhichToAverageSpeed(long millisecondsOverWhichToAverageSpeed) {
|
||||
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
|
||||
@@ -613,16 +624,28 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public void setStartOfTrackingReceived(TimePoint startOfTrackingReceived) {
|
||||
setStartOfTrackingReceived(startOfTrackingReceived, /* waitForGPSFixesToLoad */ false);
|
||||
}
|
||||
|
||||
public void setStartOfTrackingReceived(TimePoint startOfTrackingReceived, final boolean waitForGPSFixesToLoad) {
|
||||
if (!Util.equalsWithNull(startOfTrackingReceived, getStartOfTracking())) {
|
||||
super.setStartOfTrackingReceived(startOfTrackingReceived);
|
||||
super.setStartOfTrackingReceived(startOfTrackingReceived, waitForGPSFixesToLoad);
|
||||
notifyListenersStartOfTrackingChanged(getStartOfTracking());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEndOfTrackingReceived(TimePoint endOfTrackingReceived) {
|
||||
setEndOfTrackingReceived(endOfTrackingReceived, /* waitForGPSFixesToLoad */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-interface method, mainly for testing purposes; callers can ask to wait for the loading of fixes in the
|
||||
* potentially extended tracking interval to finish before returning from this method.
|
||||
*/
|
||||
public void setEndOfTrackingReceived(final TimePoint endOfTrackingReceived, final boolean waitForGPSFixesToLoad) {
|
||||
if (!Util.equalsWithNull(endOfTrackingReceived, getEndOfTracking())) {
|
||||
super.setEndOfTrackingReceived(endOfTrackingReceived);
|
||||
super.setEndOfTrackingReceived(endOfTrackingReceived, waitForGPSFixesToLoad);
|
||||
notifyListenersEndOfTrackingChanged(getEndOfTracking());
|
||||
}
|
||||
}
|
||||
|
||||
+121
-50
@@ -1795,12 +1795,35 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
return startTimeReceived;
|
||||
}
|
||||
|
||||
protected void setStartOfTrackingReceived(TimePoint startOfTracking) {
|
||||
protected void setStartOfTrackingReceived(final TimePoint startOfTracking, final boolean waitForGPSFixesToLoad) {
|
||||
final TimePoint oldStartOfTracking = this.startOfTrackingReceived;
|
||||
this.startOfTrackingReceived = startOfTracking;
|
||||
if (!Util.equalsWithNull(oldStartOfTracking, startOfTracking) &&
|
||||
(startOfTracking == null || (oldStartOfTracking != null && startOfTracking.before(oldStartOfTracking)))) {
|
||||
logger.info("Loading fixes after start of tracking for "+getRace()+" has been extended from "+oldStartOfTracking+" to "+startOfTracking);
|
||||
loadGPSFixesForExtendedTimeRange(startOfTracking, oldStartOfTracking, waitForGPSFixesToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setEndOfTrackingReceived(TimePoint endOfTracking) {
|
||||
private void loadGPSFixesForExtendedTimeRange(final TimePoint start, final TimePoint end, final boolean waitForGPSFixesToLoad) {
|
||||
for (final RaceLog raceLog : attachedRaceLogs.values()) {
|
||||
loadFixesForLog(raceLog, /* no need to addLogToMap because log has already been added during attachRaceLog */ null,
|
||||
/* startOfTimeWindowToLoad */ start, /* endOfTimeWindowToLoad */ end, waitForGPSFixesToLoad);
|
||||
}
|
||||
for (final RegattaLog regattaLog : attachedRegattaLogs.values()) {
|
||||
loadFixesForLog(regattaLog, /* no need to addLogToMap because log has already been added during attachRaceLog */ null,
|
||||
/* startOfTimeWindowToLoad */ start, /* endOfTimeWindowToLoad */ end, waitForGPSFixesToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setEndOfTrackingReceived(final TimePoint endOfTracking, final boolean waitForGPSFixesToLoad) {
|
||||
final TimePoint oldEndOfTracking = this.endOfTrackingReceived;
|
||||
this.endOfTrackingReceived = endOfTracking;
|
||||
if (!Util.equalsWithNull(oldEndOfTracking, endOfTracking) &&
|
||||
(endOfTracking == null || (oldEndOfTracking != null && endOfTracking.after(oldEndOfTracking)))) {
|
||||
logger.info("Loading fixes after end of tracking for "+getRace()+" has been extended from "+oldEndOfTracking+" to "+endOfTracking);
|
||||
loadGPSFixesForExtendedTimeRange(oldEndOfTracking, endOfTracking, waitForGPSFixesToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3135,62 +3158,110 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race. The <code>log</code> is added to the
|
||||
* <code>addLogToMap</code> data structure while holding this object's monitor, and all waiters on this object are notified when done
|
||||
* with adding the <code>log</code> to <code>addLogToMap</code>.
|
||||
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race. The <code>log</code>
|
||||
* is added to the <code>addLogToMap</code> data structure while holding <code>this</code> object's monitor, and all
|
||||
* waiters on <code>this</code> object are notified when done with adding the <code>log</code> to
|
||||
* <code>addLogToMap</code>.
|
||||
*/
|
||||
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> void loadFixesForLog(
|
||||
final LogT log, ConcurrentHashMap<Serializable, LogT> addLogToMap) {
|
||||
final LogT log, ConcurrentHashMap<Serializable, LogT> addLogToMap, final boolean waitForGPSFixesToLoad) {
|
||||
if (log != null) {
|
||||
// Use the new log, that possibly contains device mappings, to load GPSFix tracks from the DB
|
||||
// When this tracked race is to be serialized, wait for the loading from stores to complete.
|
||||
new Thread("Mongo mark and competitor track loader for tracked race " + getRace().getName() + ", log "
|
||||
+ log.getId()) {
|
||||
@Override
|
||||
public void run() {
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
LockUtil.lockForWrite(getLoadingFromGPSFixStoreLock());
|
||||
synchronized (TrackedRaceImpl.this) {
|
||||
loadingFromGPSFixStore = true; // indicates that the serialization lock is now safely held
|
||||
addLogToMap.put(log.getId(), log);
|
||||
TrackedRaceImpl.this.notifyAll();
|
||||
}
|
||||
try {
|
||||
logger.info("Started loading competitor tracks for " + getRace().getName() + " for log " + log.getId());
|
||||
for (Competitor competitor : race.getCompetitors()) {
|
||||
try {
|
||||
gpsFixStore.loadCompetitorTrack(
|
||||
(DynamicGPSFixTrack<Competitor, GPSFixMoving>) tracks.get(competitor), log,
|
||||
competitor);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + competitor, e);
|
||||
}
|
||||
}
|
||||
logger.info("Finished loading competitor tracks for " + getRace().getName());
|
||||
logger.info("Started loading mark tracks for " + getRace().getName());
|
||||
for (Mark mark : getMarksFromRaceAndLogs()) {
|
||||
try {
|
||||
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark), log, mark);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + mark, e);
|
||||
}
|
||||
}
|
||||
logger.info("Finished loading mark tracks for " + getRace().getName());
|
||||
} finally {
|
||||
synchronized (TrackedRaceImpl.this) {
|
||||
loadingFromGPSFixStore = false;
|
||||
TrackedRaceImpl.this.notifyAll();
|
||||
}
|
||||
LockUtil.unlockAfterWrite(getLoadingFromGPSFixStoreLock());
|
||||
LockUtil.unlockAfterRead(getSerializationLock());
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
final TimePoint startOfTimeWindowToLoad = getStartOfTracking();
|
||||
final TimePoint endOfTimeWindowToLoad = getEndOfTracking();
|
||||
loadFixesForLog(log, addLogToMap, startOfTimeWindowToLoad, endOfTimeWindowToLoad, waitForGPSFixesToLoad);
|
||||
} else {
|
||||
logger.severe("Got a request to attach log for an empty log!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race, cropped to the time
|
||||
* interval specified by <code>startOfTimeWindowToLoad</code> and </code>endOfTimeWindowToLoad</code>. The
|
||||
* <code>log</code> is added to the <code>addLogToMap</code> data structure (if <code>addLogToMap</code> is not
|
||||
* <code>null</code>) while holding <code>this</code> object's monitor, and all waiters on <code>this</code> object
|
||||
* are notified when done with adding the <code>log</code> to <code>addLogToMap</code>.
|
||||
*
|
||||
* @param addLogToMap
|
||||
* the log will be added to this map after the {@link #loadingFromGPSFixStore} flag has been set and only when the
|
||||
* loading thread has successfully acquired the write lock of {@link #getLoadingFromGPSFixStoreLock()}. When done loading,
|
||||
* all waiters on <code>this</code> object will be notified. If the map is not <code>null</code>, the key used to enter
|
||||
* the log is the {@link AbstractLog#getId() log's ID}.
|
||||
* @param startOfTimeWindowToLoad
|
||||
* the time to use to crop the beginning of the time range for which to load fixes; if <code>null</code>,
|
||||
* the start of the time range will be restricted only by the device mapping intervals
|
||||
* @param endOfTimeWindowToLoad
|
||||
* the time to use to crop the end of the time range for which to load fixes; if <code>null</code>,
|
||||
* the end of the time range will be restricted only by the device mapping intervals
|
||||
*/
|
||||
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> void loadFixesForLog(final LogT log,
|
||||
ConcurrentHashMap<Serializable, LogT> addLogToMap, final TimePoint startOfTimeWindowToLoad,
|
||||
final TimePoint endOfTimeWindowToLoad, final boolean waitForGPSFixesToLoad) {
|
||||
Thread t = new Thread("Mongo mark and competitor track loader for tracked race " + getRace().getName() + ", log "
|
||||
+ log.getId()+" from "+startOfTimeWindowToLoad+" to "+endOfTimeWindowToLoad) {
|
||||
@Override
|
||||
public void run() {
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
LockUtil.lockForWrite(getLoadingFromGPSFixStoreLock());
|
||||
synchronized (TrackedRaceImpl.this) {
|
||||
loadingFromGPSFixStore = true; // indicates that the serialization lock is now safely held
|
||||
if (addLogToMap != null) {
|
||||
addLogToMap.put(log.getId(), log);
|
||||
}
|
||||
TrackedRaceImpl.this.notifyAll();
|
||||
}
|
||||
try {
|
||||
logger.info("Started loading competitor tracks for " + getRace().getName() + " for log " + log.getId());
|
||||
for (Competitor competitor : race.getCompetitors()) {
|
||||
try {
|
||||
gpsFixStore.loadCompetitorTrack(
|
||||
(DynamicGPSFixTrack<Competitor, GPSFixMoving>) tracks.get(competitor), log,
|
||||
competitor, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + competitor, e);
|
||||
}
|
||||
}
|
||||
logger.info("Finished loading competitor tracks for " + getRace().getName());
|
||||
logger.info("Started loading mark tracks for " + getRace().getName());
|
||||
for (Mark mark : getMarksFromRaceAndLogs()) {
|
||||
try {
|
||||
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark),
|
||||
log, mark, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + mark, e);
|
||||
}
|
||||
}
|
||||
logger.info("Finished loading mark tracks for " + getRace().getName());
|
||||
} finally {
|
||||
synchronized (TrackedRaceImpl.this) {
|
||||
loadingFromGPSFixStore = false;
|
||||
TrackedRaceImpl.this.notifyAll();
|
||||
}
|
||||
LockUtil.unlockAfterWrite(getLoadingFromGPSFixStoreLock());
|
||||
LockUtil.unlockAfterRead(getSerializationLock());
|
||||
logger.info("Thread "+getName()+" done.");
|
||||
}
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
if (waitForGPSFixesToLoad) {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.WARNING, "Got interrupted while waiting for loading of GPS fixes from log "+log+" to finish", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if currently the race is loading GPS fixes from the {@link GPSFixStore}. Clients may {@link Object#wait()} on <code>this</code>
|
||||
* object and will be notified whenever a change of this flag's value occurs.
|
||||
*/
|
||||
public boolean isLoadingFromGPSFixStore() {
|
||||
return loadingFromGPSFixStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can trigger fixes to be loaded, if there are {@link DeviceMapping}s in the {@code RaceLog}.
|
||||
* If multiple race logs are attached, the threads that are spawned to load fixes will do so one
|
||||
@@ -3198,7 +3269,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
*/
|
||||
@Override
|
||||
public void attachRaceLog(final RaceLog raceLog) {
|
||||
loadFixesForLog(raceLog, attachedRaceLogs);
|
||||
loadFixesForLog(raceLog, attachedRaceLogs, /* wait for fixes to load */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3243,7 +3314,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
@Override
|
||||
public void attachRegattaLog(RegattaLog regattaLog) {
|
||||
loadFixesForLog(regattaLog, attachedRegattaLogs);
|
||||
loadFixesForLog(regattaLog, attachedRegattaLogs, /* wait for fixes to load */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in new issue
Block a user