mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-25 06:58:39 +00:00
Merge branch 'master' into bug2822_boatcolors
Change-Id: I19333a21e5d79726b1409eb319798f55b49faf79
This commit is contained in:
-8
@@ -1,8 +1,6 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
@@ -13,12 +11,6 @@ public interface HasManeuverContext extends HasWindOnTrackedLeg, HasManeuver, Ha
|
||||
@Connector(scanForStatistics = false)
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
@Dimension(messageKey = "ManeuverType", ordinal = 12)
|
||||
ManeuverType getManeuverType();
|
||||
|
||||
@Dimension(messageKey = "ToSide", ordinal = 16)
|
||||
NauticalSide getToSide();
|
||||
|
||||
@Dimension(messageKey = "TackBeforeManeuver")
|
||||
Tack getTackBeforeManeuver();
|
||||
|
||||
|
||||
+2
-4
@@ -7,10 +7,8 @@ import java.util.concurrent.ExecutorService;
|
||||
import com.sap.sailing.datamining.data.HasMarkPassingContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.datamining.impl.data.MarkPassingWithContext;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.components.Processor;
|
||||
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
|
||||
@@ -30,8 +28,8 @@ public class MarkPassingRetrievalProcessor extends AbstractRetrievalProcessor<Ha
|
||||
try {
|
||||
Iterable<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
|
||||
maneuversWithContext.add(new MarkPassingWithContext(element, (MarkPassingManeuver) maneuver));
|
||||
if (maneuver.isMarkPassing()) {
|
||||
maneuversWithContext.add(new MarkPassingWithContext(element, maneuver));
|
||||
}
|
||||
}
|
||||
} catch (NoWindException e) {
|
||||
|
||||
+4
-4
@@ -36,7 +36,7 @@ public class ManeuverSpeedDetailsWithContext implements HasManeuverSpeedDetailsC
|
||||
|
||||
@Override
|
||||
public NauticalSide getToSide() {
|
||||
return maneuverContext.getToSide();
|
||||
return maneuverContext.getManeuver().getToSide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +45,7 @@ public class ManeuverSpeedDetailsWithContext implements HasManeuverSpeedDetailsC
|
||||
double lastSpeedValue = 0;
|
||||
|
||||
Function<Integer, Integer> twaIterationFunction = ManeuverSpeedDetailsUtils
|
||||
.getTWAIterationFunctionForManeuverDirection(maneuverContext.getToSide());
|
||||
.getTWAIterationFunctionForManeuverDirection(getToSide());
|
||||
|
||||
for (int twa = maneuverEnteringTWA, i = 0; i < 360; ++i, twa = twaIterationFunction.apply(twa)) {
|
||||
if (maneuverSpeedPerTWA[twa] == 0 || lastSpeedValue == 0) {
|
||||
@@ -64,7 +64,7 @@ public class ManeuverSpeedDetailsWithContext implements HasManeuverSpeedDetailsC
|
||||
double firstSpeedValue = maneuverContext.getManeuverEnteringSpeed();
|
||||
|
||||
Function<Integer, Integer> twaIterationFunction = ManeuverSpeedDetailsUtils
|
||||
.getTWAIterationFunctionForManeuverDirection(maneuverContext.getToSide());
|
||||
.getTWAIterationFunctionForManeuverDirection(getToSide());
|
||||
|
||||
for (int twa = maneuverEnteringTWA, i = 0; i < 360; ++i, twa = twaIterationFunction.apply(twa)) {
|
||||
if (firstSpeedValue == 0) {
|
||||
@@ -85,7 +85,7 @@ public class ManeuverSpeedDetailsWithContext implements HasManeuverSpeedDetailsC
|
||||
double lastSpeedValue = 0;
|
||||
|
||||
Function<Integer, Integer> twaIterationFunction = ManeuverSpeedDetailsUtils
|
||||
.getTWAIterationFunctionForManeuverDirection(maneuverContext.getToSide());
|
||||
.getTWAIterationFunctionForManeuverDirection(getToSide());
|
||||
|
||||
for (int twa = maneuverEnteringTWA, i = 0; i < 360; ++i, twa = twaIterationFunction.apply(twa)) {
|
||||
if (maneuverSpeedPerTWA[twa] == 0 || lastSpeedValue == 0) {
|
||||
|
||||
-12
@@ -4,8 +4,6 @@ import com.sap.sailing.datamining.data.HasManeuverContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
@@ -88,16 +86,6 @@ public class ManeuverWithContext implements HasManeuverContext {
|
||||
return maneuver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManeuverType getManeuverType() {
|
||||
return getManeuver().getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getToSide() {
|
||||
return getDirectionChangeInDegreesForAnalysis() >= 0 ? NauticalSide.STARBOARD : NauticalSide.PORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAbsoluteDirectionChangeInDegrees() {
|
||||
return Math.abs(getDirectionChangeInDegreesForAnalysis());
|
||||
|
||||
+7
-7
@@ -7,22 +7,22 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
private static final long serialVersionUID = -337042113749307686L;
|
||||
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor;
|
||||
private final MarkPassingManeuver maneuver;
|
||||
private final Maneuver maneuver;
|
||||
|
||||
private Double absoluteRank;
|
||||
private boolean rankHasBeenInitialized;
|
||||
private Wind wind;
|
||||
|
||||
public MarkPassingWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, MarkPassingManeuver maneuver) {
|
||||
public MarkPassingWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, Maneuver markPassingManeuver) {
|
||||
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
|
||||
this.maneuver = maneuver;
|
||||
this.maneuver = markPassingManeuver;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,18 +31,18 @@ public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassingManeuver getManeuver() {
|
||||
public Maneuver getManeuver() {
|
||||
return maneuver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getWaypoint() {
|
||||
return getManeuver().getWaypointPassed();
|
||||
return getManeuver().getMarkPassing().getWaypoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getPassingSide() {
|
||||
return getManeuver().getSide();
|
||||
return getManeuver().getToSide();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public enum ManeuverType {
|
||||
HEAD_UP, BEAR_AWAY, TACK, JIBE, PENALTY_CIRCLE, MARK_PASSING, UNKNOWN;
|
||||
HEAD_UP, BEAR_AWAY, TACK, JIBE, PENALTY_CIRCLE, UNKNOWN;
|
||||
}
|
||||
+2
-1
@@ -11,6 +11,7 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -236,7 +237,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
}
|
||||
};
|
||||
raceHandle.getTrackedRegatta().addRaceListener(raceListener);
|
||||
raceHandle.getTrackedRegatta().addRaceListener(raceListener, Optional.empty());
|
||||
raceHandle.getTrackedRegatta().removeRaceListener(raceListener).get();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,6 @@ import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogFixTrack
|
||||
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
|
||||
import com.sap.sailing.server.MasterDataImportClassLoaderService;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.GPSFixJsonDeserializer;
|
||||
@@ -36,6 +35,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdent
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.GPSFixJsonHandlerImpl;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.replication.Replicable;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import com.sap.sailing.server.MasterDataImportClassLoaderService;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
|
||||
public class MasterDataImportClassLoaderServiceImpl implements MasterDataImportClassLoaderService {
|
||||
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -305,7 +306,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
raceColumn.setRaceIdentifier(fleet, trackedRegatta.getRegatta().getRaceIdentifier(raceDef));
|
||||
trackedRace = trackedRegatta.createTrackedRace(raceDef, sidelines, windStore,
|
||||
params.getDelayToLiveInMillis(), WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
|
||||
boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver);
|
||||
boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver,
|
||||
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty());
|
||||
notifyRaceCreationListeners();
|
||||
logger.info(String.format("Started tracking race-log race (%s)", raceLog));
|
||||
// this wakes up all waiting race handles
|
||||
|
||||
+3
-1
@@ -10,6 +10,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -487,7 +488,8 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
|
||||
// we already know our single RaceDefinition
|
||||
assert SwissTimingRaceTrackerImpl.this.race == race;
|
||||
}
|
||||
}, useInternalMarkPassingAlgorithm, raceLogResolver);
|
||||
}, useInternalMarkPassingAlgorithm, raceLogResolver,
|
||||
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty());
|
||||
notifyRaceCreationListeners();
|
||||
logger.info("Created SwissTiming RaceDefinition and TrackedRace for "+race.getName());
|
||||
}
|
||||
|
||||
+3
-1
@@ -10,6 +10,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -399,7 +400,8 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i
|
||||
TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS,
|
||||
WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
|
||||
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
|
||||
/* raceDefinitionSetToUpdate */ null, useInternalMarkPassingAlgorithm, raceLogResolver);
|
||||
/* raceDefinitionSetToUpdate */ null, useInternalMarkPassingAlgorithm, raceLogResolver,
|
||||
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty());
|
||||
trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0));
|
||||
TimePoint bestStartTimeKnownSoFar = bestStartTimePerRaceID.get(currentRaceID);
|
||||
if (bestStartTimeKnownSoFar != null) {
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -75,7 +76,7 @@ public class FetchTracksAndStoreLocallyTest extends OnlineTracTracBasedTest {
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {
|
||||
}
|
||||
});
|
||||
}, Optional.empty());
|
||||
super.completeSetupLaunchingControllerAndWaitForRaceDefinition(ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS);
|
||||
}
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -78,7 +79,7 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest {
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {
|
||||
}
|
||||
});
|
||||
}, Optional.empty());
|
||||
for (Receiver receiver : domainFactory
|
||||
.getUpdateReceivers(trackedRegatta, /* delayToLiveInMillis */0l,
|
||||
/* simulator */null, EmptyWindStore.INSTANCE, new DynamicRaceDefinitionSet() {
|
||||
|
||||
+7
-4
@@ -5,6 +5,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
@@ -33,6 +34,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
public class TestDeadlockInRegattaListener {
|
||||
@Rule
|
||||
@@ -56,13 +58,14 @@ public class TestDeadlockInRegattaListener {
|
||||
private static final long serialVersionUID = -3599667964201700780L;
|
||||
|
||||
@Override
|
||||
protected void notifyListenersAboutTrackedRaceRemoved(TrackedRace trackedRace) {
|
||||
protected void notifyListenersAboutTrackedRaceRemoved(TrackedRace trackedRace,
|
||||
Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
super.notifyListenersAboutTrackedRaceRemoved(trackedRace);
|
||||
super.notifyListenersAboutTrackedRaceRemoved(trackedRace, Optional.empty());
|
||||
}
|
||||
};
|
||||
RacingEventServiceImpl racingEventService = new RacingEventServiceImpl() {
|
||||
@@ -125,10 +128,10 @@ public class TestDeadlockInRegattaListener {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).start();
|
||||
trackedRegatta.addTrackedRace(trackedRace1);
|
||||
trackedRegatta.addTrackedRace(trackedRace1, Optional.empty());
|
||||
// the following runs into RacingEventService.getRaceTrackerByRegattaAndRaceIdentifier
|
||||
// which waits for the latch based on the override above while in synchronized RegattaListener.raceAdded
|
||||
new Thread(()->trackedRegatta.addTrackedRace(trackedRace2)).start();
|
||||
new Thread(()->trackedRegatta.addTrackedRace(trackedRace2, Optional.empty())).start();
|
||||
monitorOnRegattaListenerLatch.await();
|
||||
// the following awaits the latch in TrackedRegattaImpl.notifyListenersAboutTrackedRaceRemoved
|
||||
// after the write lock has been obtained but before the synchronized RegattaListener.raceRemoved method
|
||||
|
||||
+6
-4
@@ -4,6 +4,7 @@ import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -90,6 +91,7 @@ import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
private static final long serialVersionUID = 5827912985564121181L;
|
||||
@@ -597,15 +599,15 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedRace(TrackedRace trackedRace) {
|
||||
public void addTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrackedRace(TrackedRace trackedRace) {
|
||||
public void removeTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceListener(RaceListener listener) {
|
||||
public void addRaceListener(RaceListener listener, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -628,7 +630,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
WindStore windStore, long delayToLiveInMillis,
|
||||
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingcalculator,
|
||||
RaceLogResolver raceLogResolver) {
|
||||
RaceLogResolver raceLogResolver, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -4,6 +4,7 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -74,11 +75,11 @@ public class TrackedRegattaTest {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, Optional.empty());
|
||||
|
||||
DynamicTrackedRace race1 = createRace("R1");
|
||||
Thread thread1 = new Thread(() -> {
|
||||
regatta.addTrackedRace(race1);
|
||||
regatta.addTrackedRace(race1, Optional.empty());
|
||||
});
|
||||
thread1.start();
|
||||
// This ensures, that the add event is being processed but is not finished because
|
||||
@@ -88,7 +89,7 @@ public class TrackedRegattaTest {
|
||||
addPhaser.arriveAndAwaitAdvance();
|
||||
|
||||
Thread thread2 = new Thread(() -> {
|
||||
regatta.removeTrackedRace(race1);
|
||||
regatta.removeTrackedRace(race1, Optional.empty());
|
||||
});
|
||||
thread2.start();
|
||||
// If the implementation ensures that the events are fired in order,
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -694,7 +695,7 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
return trackedRegatta.createTrackedRace(race, sidelines,
|
||||
windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
|
||||
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
|
||||
raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver);
|
||||
raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver, Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-1
@@ -5,6 +5,7 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -245,7 +246,8 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
|
||||
DynamicTrackedRace trackedRace = getTrackedRegatta().createTrackedRace(race, sidelines,
|
||||
windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
|
||||
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
|
||||
raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver);
|
||||
raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver,
|
||||
/* Not needed because the RaceTracker is not active on a replica */ Optional.empty());
|
||||
if (runAfterCreatingTrackedRace != null) {
|
||||
runAfterCreatingTrackedRace.accept(trackedRace);
|
||||
}
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Logger;
|
||||
@@ -70,7 +71,7 @@ public class Simulator {
|
||||
stop(); // stop simulator when tracked race is removed from its regatta
|
||||
}
|
||||
}
|
||||
});
|
||||
}, /* No replication handling necessary */ Optional.empty());
|
||||
startWindPlayer();
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -70,7 +70,6 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
@@ -970,19 +969,18 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MARK_PASSING:
|
||||
// analyze all mark passings, not only those after this leg's start, to catch the mark passing
|
||||
// maneuver starting this leg, even if its time point is slightly before the mark passing starting this leg
|
||||
MarkPassingManeuver mpm = (MarkPassingManeuver) maneuver;
|
||||
if (mpm.getWaypointPassed() == trackedLeg.getLeg().getFrom()) {
|
||||
result.sideToWhichMarkAtLegStartWasRounded = mpm.getSide();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Do nothing here.
|
||||
* Throwing an exception destroys the toggling (and maybe other behaviour) of the leaderboard.
|
||||
*/
|
||||
}
|
||||
if(maneuver.isMarkPassing()) {
|
||||
// analyze all mark passings, not only those after this leg's start, to catch the mark passing
|
||||
// maneuver starting this leg, even if its time point is slightly before the mark passing starting this leg
|
||||
if (maneuver.getMarkPassing().getWaypoint() == trackedLeg.getLeg().getFrom()) {
|
||||
result.sideToWhichMarkAtLegStartWasRounded = maneuver.getToSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
result.averageManeuverLossInMeters = new HashMap<ManeuverType, Double>();
|
||||
for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE,
|
||||
|
||||
+147
-122
@@ -35,7 +35,6 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.ManeuverWithMainCurveBoundariesImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.ManeuverWithStableSpeedAndCourseBoundariesImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingManeuverImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.SpeedWithBearingStepImpl;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -416,30 +415,15 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
|
||||
maneuverMainCurveDetails.getTimePointBefore());
|
||||
TrackedLegOfCompetitor legAfterManeuver = trackedRace.getTrackedLeg(competitor,
|
||||
maneuverMainCurveDetails.getTimePointAfter());
|
||||
Waypoint waypointPassed = null; // set for MARK_PASSING maneuvers only
|
||||
NauticalSide sideToWhichWaypointWasPassed = null; // set for MARK_PASSING maneuvers only
|
||||
// check for mask passing first; a tacking / jibe-setting mark rounding thus takes precedence over being
|
||||
// detected as a penalty circle
|
||||
final TimePoint markPassingTimePoint;
|
||||
MarkPassing markPassing = null; // will remain null if no mark passing has been recorded within maneuver
|
||||
// boundaries
|
||||
// check whether a waypoint has been passed within maneuver
|
||||
if (legBeforeManeuver != legAfterManeuver
|
||||
// a maneuver at the start line is not to be considered a MARK_PASSING maneuver; show a tack as a tack
|
||||
&& legAfterManeuver != null
|
||||
&& legAfterManeuver.getLeg().getFrom() != trackedRace.getRace().getCourse().getFirstWaypoint()) {
|
||||
waypointPassed = legAfterManeuver.getLeg().getFrom();
|
||||
MarkPassing markPassing = trackedRace.getMarkPassing(competitor, waypointPassed);
|
||||
markPassingTimePoint = markPassing != null ? markPassing.getTimePoint() : maneuverDetails.getTimePoint();
|
||||
Position markPassingPosition = markPassing != null
|
||||
? competitorTrack.getEstimatedPosition(markPassingTimePoint, /* extrapolate */false)
|
||||
: maneuverPosition;
|
||||
sideToWhichWaypointWasPassed = maneuverDirection;
|
||||
// produce an additional mark passing maneuver; continue to analyze to catch jibe sets and kiwi drops
|
||||
maneuvers.add(new MarkPassingManeuverImpl(ManeuverType.MARK_PASSING, tackAfterManeuver, markPassingPosition,
|
||||
maneuverLoss, markPassingTimePoint, maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), waypointPassed,
|
||||
sideToWhichWaypointWasPassed));
|
||||
} else {
|
||||
markPassingTimePoint = null;
|
||||
Waypoint waypointPassed = legAfterManeuver.getLeg().getFrom();
|
||||
markPassing = trackedRace.getMarkPassing(competitor, waypointPassed);
|
||||
}
|
||||
BearingChangeAnalyzer bearingChangeAnalyzer = BearingChangeAnalyzer.INSTANCE;
|
||||
final Bearing courseBeforeManeuver = maneuverMainCurveDetails.getSpeedWithBearingBefore().getBearing();
|
||||
@@ -451,121 +435,162 @@ public class ManeuverDetectorImpl implements ManeuverDetector {
|
||||
int numberOfTacks = wind == null ? 0
|
||||
: bearingChangeAnalyzer.didPass(courseBeforeManeuver, mainCurveTotalCourseChangeInDegrees,
|
||||
courseAfterManeuver, wind.getFrom());
|
||||
if (markPassingTimePoint != null && (numberOfTacks + numberOfJibes > 0)) {
|
||||
// In case of a mark passing we need to split the maneuver analysis into the phase before and after
|
||||
// the mark passing. First of all, this is important to identify the correct maneuver time point for
|
||||
// each tack and jibe, second it is essential to call a penalty which is only the case if the tack and
|
||||
// the jibe are on the same side of the mark passing; otherwise this may have been a jibe set or a
|
||||
// kiwi drop.
|
||||
// Therefore, we recursively detect the maneuvers for the segment before and the segment after the
|
||||
// mark passing and add the results to our result.
|
||||
List<ManeuverSpot> maneuverSpots = detectManeuvers(maneuverDetails.getTimePointBefore(),
|
||||
markPassingTimePoint.minus(1));
|
||||
maneuverSpots.addAll(detectManeuvers(markPassingTimePoint.plus(1), maneuverDetails.getTimePointAfter()));
|
||||
maneuvers.addAll(getAllManeuversFromManeuverSpots(maneuverSpots));
|
||||
} else {
|
||||
// Either there was no mark passing, or the mark passing was not accompanied by a tack or a jibe.
|
||||
// For the first tack/jibe combination (they must alternate because the course changes in the same direction
|
||||
// and
|
||||
// the wind is considered sufficiently stable to not allow for two successive tacks or two successive jibes)
|
||||
// we create a PENALTY_CIRCLE maneuver and recurse for the time interval after the first penalty circle has
|
||||
// completed.
|
||||
if (numberOfTacks > 0 && numberOfJibes > 0 && markPassingTimePoint == null) {
|
||||
TimePoint firstPenaltyCircleCompletedAt = getTimePointOfCompletionOfFirstPenaltyCircle(
|
||||
maneuverMainCurveDetails.getTimePointBefore(), courseBeforeManeuver,
|
||||
maneuverMainCurveDetails.getSpeedWithBearingSteps(), wind);
|
||||
if (firstPenaltyCircleCompletedAt == null) {
|
||||
// This should really not happen!
|
||||
logger.warning(
|
||||
"Maneuver detection has failed to process penalty circle maneuver correctly, because getTimePointOfCompletionOfFirstPenaltyCircle() returned null. Race-Id: "
|
||||
+ trackedRace.getRace().getId() + ", Competitor: " + competitor.getName()
|
||||
+ ", Time point before maneuver: " + maneuverDetails.getTimePointBefore());
|
||||
// Use already detected maneuver details as fallback data to prevent Nullpointer
|
||||
firstPenaltyCircleCompletedAt = maneuverDetails.getTimePointAfter();
|
||||
if (numberOfTacks > 0 && numberOfJibes > 0) {
|
||||
boolean performPenaltyCircleAnalysis = true;
|
||||
if (markPassing != null) {
|
||||
// In case of a mark passing we need to split the maneuver analysis into the phase before and after
|
||||
// the mark passing to catch kiwi drops. First of all, this is important to identify the correct
|
||||
// maneuver time point for
|
||||
// each tack and jibe, second it is essential to call a penalty which is only the case if the tack and
|
||||
// the jibe are on the same side of the mark passing; otherwise this may have been a
|
||||
// kiwi drop.
|
||||
// Therefore, we recursively detect the maneuvers for the segment before and the segment after the
|
||||
// mark passing and add the results to our result.
|
||||
List<ManeuverSpot> maneuverSpotsBeforeMarkPassing = detectManeuvers(
|
||||
maneuverDetails.getTimePointBefore(), markPassing.getTimePoint().minus(1));
|
||||
List<ManeuverSpot> maneuverSpotsAfterMarkPassing = detectManeuvers(markPassing.getTimePoint().plus(1),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
// split the penalty circle maneuver only by mark passing time point if tacks or jibes are present on
|
||||
// both legs
|
||||
if (getNumberOfTacksAndJibesFromManeuverSpots(maneuverSpotsAfterMarkPassing) != 0
|
||||
&& getNumberOfTacksAndJibesFromManeuverSpots(maneuverSpotsAfterMarkPassing) != 0) {
|
||||
performPenaltyCircleAnalysis = false;
|
||||
maneuverSpotsBeforeMarkPassing.addAll(maneuverSpotsAfterMarkPassing);
|
||||
maneuvers.addAll(getAllManeuversFromManeuverSpots(maneuverSpotsBeforeMarkPassing));
|
||||
}
|
||||
}
|
||||
if (performPenaltyCircleAnalysis) {
|
||||
// Either there was no mark passing, or the mark passing was not accompanied by a tack or a jibe.
|
||||
// For the first tack/jibe combination (they must alternate because the course changes in the same
|
||||
// direction
|
||||
// and
|
||||
// the wind is considered sufficiently stable to not allow for two successive tacks or two successive
|
||||
// jibes)
|
||||
// we create a PENALTY_CIRCLE maneuver and recurse for the time interval after the first penalty circle
|
||||
// has
|
||||
// completed.
|
||||
List<Maneuver> additionalManeuversAfterFirstPenaltyCircle = null;
|
||||
if (numberOfTacks > 1 || numberOfJibes > 1) {
|
||||
TimePoint firstPenaltyCircleCompletedAt = getTimePointOfCompletionOfFirstPenaltyCircle(
|
||||
maneuverMainCurveDetails.getTimePointBefore(), courseBeforeManeuver,
|
||||
maneuverMainCurveDetails.getSpeedWithBearingSteps(), wind);
|
||||
final ManeuverCurveDetailsWithBearingSteps refinedPenaltyMainCurveDetails;
|
||||
final ManeuverCurveDetails refinedPenaltyDetails;
|
||||
if (firstPenaltyCircleCompletedAt == null) {
|
||||
// This should really not happen!
|
||||
logger.warning(
|
||||
"Maneuver detection has failed to process penalty circle maneuver correctly, because getTimePointOfCompletionOfFirstPenaltyCircle() returned null. Race-Id: "
|
||||
+ trackedRace.getRace().getId() + ", Competitor: " + competitor.getName()
|
||||
+ ", Time point before maneuver: " + maneuverDetails.getTimePointBefore());
|
||||
// Use already detected maneuver details as fallback data to prevent Nullpointer
|
||||
} else {
|
||||
refinedPenaltyMainCurveDetails = computeManeuverMainCurveDetails(
|
||||
maneuverMainCurveDetails.getTimePointBefore(), firstPenaltyCircleCompletedAt,
|
||||
maneuverDirection);
|
||||
if (refinedPenaltyMainCurveDetails == null) {
|
||||
// This should really not happen!
|
||||
logger.warning(
|
||||
"Maneuver detection has failed to process penalty circle maneuver correctly, because refinedPenaltyMainCurveDetails computation returned null. Race-Id: "
|
||||
+ trackedRace.getRace().getId() + ", Competitor: " + competitor.getName()
|
||||
+ ", Time point before maneuver: " + maneuverDetails.getTimePointBefore());
|
||||
// Use already detected maneuver main curve as fallback data to prevent Nullpointer
|
||||
} else {
|
||||
refinedPenaltyDetails = computeManeuverDetails(refinedPenaltyMainCurveDetails,
|
||||
maneuverDetails.getTimePointBefore(), firstPenaltyCircleCompletedAt);
|
||||
// after we've "consumed" one tack and one jibe, recursively find more maneuvers if tacks
|
||||
// and/or jibes
|
||||
// remain
|
||||
List<ManeuverSpot> maneuverSpots = detectManeuvers(firstPenaltyCircleCompletedAt,
|
||||
maneuverDetails.getTimePointAfter());
|
||||
additionalManeuversAfterFirstPenaltyCircle = getAllManeuversFromManeuverSpots(
|
||||
maneuverSpots);
|
||||
maneuverMainCurveDetails = refinedPenaltyMainCurveDetails;
|
||||
maneuverDetails = refinedPenaltyDetails;
|
||||
}
|
||||
}
|
||||
}
|
||||
maneuverType = ManeuverType.PENALTY_CIRCLE;
|
||||
ManeuverCurveDetailsWithBearingSteps refinedPenaltyMainCurveDetails = computeManeuverMainCurveDetails(
|
||||
maneuverMainCurveDetails.getTimePointBefore(), firstPenaltyCircleCompletedAt,
|
||||
maneuverDirection);
|
||||
|
||||
ManeuverCurveDetails refinedPenaltyDetails;
|
||||
if (refinedPenaltyMainCurveDetails == null) {
|
||||
// This should really not happen!
|
||||
logger.warning(
|
||||
"Maneuver detection has failed to process penalty circle maneuver correctly, because refinedPenaltyMainCurveDetails computation returned null. Race-Id: "
|
||||
+ trackedRace.getRace().getId() + ", Competitor: " + competitor.getName()
|
||||
+ ", Time point before maneuver: " + maneuverDetails.getTimePointBefore());
|
||||
// Use already detected maneuver main curve as fallback data to prevent Nullpointer
|
||||
refinedPenaltyMainCurveDetails = maneuverMainCurveDetails;
|
||||
refinedPenaltyDetails = maneuverDetails;
|
||||
firstPenaltyCircleCompletedAt = maneuverDetails.getTimePointAfter();
|
||||
} else {
|
||||
refinedPenaltyDetails = computeManeuverDetails(refinedPenaltyMainCurveDetails,
|
||||
maneuverDetails.getTimePointBefore(), firstPenaltyCircleCompletedAt);
|
||||
}
|
||||
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
|
||||
firstPenaltyCircleCompletedAt);
|
||||
Position penaltyPosition = competitorTrack.getEstimatedPosition(refinedPenaltyDetails.getTimePoint(),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
Position penaltyPosition = competitorTrack.getEstimatedPosition(maneuverDetails.getTimePoint(),
|
||||
/* extrapolate */ false);
|
||||
final Maneuver maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType,
|
||||
tackAfterManeuver, penaltyPosition, maneuverLoss, refinedPenaltyDetails.getTimePoint(),
|
||||
refinedPenaltyMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
refinedPenaltyDetails.extractCurveBoundariesOnly(),
|
||||
refinedPenaltyMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
|
||||
tackAfterManeuver, penaltyPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), markPassing);
|
||||
maneuvers.add(maneuver);
|
||||
// after we've "consumed" one tack and one jibe, recursively find more maneuvers if tacks and/or jibes
|
||||
// remain
|
||||
if (numberOfTacks > 1 || numberOfJibes > 1) {
|
||||
List<ManeuverSpot> maneuverSpots = detectManeuvers(firstPenaltyCircleCompletedAt,
|
||||
maneuverDetails.getTimePointAfter());
|
||||
maneuvers.addAll(getAllManeuversFromManeuverSpots(maneuverSpots));
|
||||
if (additionalManeuversAfterFirstPenaltyCircle != null) {
|
||||
maneuvers.addAll(additionalManeuversAfterFirstPenaltyCircle);
|
||||
}
|
||||
} else {
|
||||
final Maneuver maneuver;
|
||||
if (numberOfTacks > 0 || numberOfJibes > 0) {
|
||||
maneuverType = numberOfTacks > 0 ? ManeuverType.TACK : ManeuverType.JIBE;
|
||||
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
|
||||
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
|
||||
} else if (wind != null) {
|
||||
// heading up or bearing away
|
||||
Bearing windBearing = wind.getBearing();
|
||||
Bearing toWindBeforeManeuver = windBearing
|
||||
.getDifferenceTo(maneuverMainCurveDetails.getSpeedWithBearingBefore().getBearing());
|
||||
Bearing toWindAfterManeuver = windBearing
|
||||
.getDifferenceTo(maneuverMainCurveDetails.getSpeedWithBearingAfter().getBearing());
|
||||
maneuverType = Math.abs(toWindBeforeManeuver.getDegrees()) < Math
|
||||
.abs(toWindAfterManeuver.getDegrees()) ? ManeuverType.HEAD_UP : ManeuverType.BEAR_AWAY;
|
||||
// treat maneuver main curve details as main maneuver details, because the detected maneuver is
|
||||
// either HEAD_UP or BEAR_AWAY
|
||||
maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver,
|
||||
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
|
||||
} else {
|
||||
// no wind information; marking as UNKNOWN
|
||||
maneuverType = ManeuverType.UNKNOWN;
|
||||
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
|
||||
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond());
|
||||
}
|
||||
maneuvers.add(maneuver);
|
||||
}
|
||||
} else {
|
||||
final Maneuver maneuver;
|
||||
if (numberOfTacks > 0 || numberOfJibes > 0) {
|
||||
maneuverType = numberOfTacks > 0 ? ManeuverType.TACK : ManeuverType.JIBE;
|
||||
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
|
||||
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), markPassing);
|
||||
} else if (wind != null) {
|
||||
// heading up or bearing away
|
||||
Bearing windBearing = wind.getBearing();
|
||||
Bearing toWindBeforeManeuver = windBearing
|
||||
.getDifferenceTo(maneuverMainCurveDetails.getSpeedWithBearingBefore().getBearing());
|
||||
Bearing toWindAfterManeuver = windBearing
|
||||
.getDifferenceTo(maneuverMainCurveDetails.getSpeedWithBearingAfter().getBearing());
|
||||
maneuverType = Math.abs(toWindBeforeManeuver.getDegrees()) < Math.abs(toWindAfterManeuver.getDegrees())
|
||||
? ManeuverType.HEAD_UP : ManeuverType.BEAR_AWAY;
|
||||
// treat maneuver main curve details as main maneuver details, because the detected maneuver is
|
||||
// either HEAD_UP or BEAR_AWAY
|
||||
maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition,
|
||||
maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), markPassing);
|
||||
} else {
|
||||
// no wind information; marking as UNKNOWN
|
||||
maneuverType = ManeuverType.UNKNOWN;
|
||||
maneuverLoss = getManeuverLoss(maneuverDetails.getTimePointBefore(), maneuverDetails.getTimePoint(),
|
||||
maneuverDetails.getTimePointAfter());
|
||||
maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver,
|
||||
maneuverPosition, maneuverLoss, maneuverDetails.getTimePoint(),
|
||||
maneuverMainCurveDetails.extractCurveBoundariesOnly(),
|
||||
maneuverDetails.extractCurveBoundariesOnly(),
|
||||
maneuverMainCurveDetails.getMaxAngularVelocityInDegreesPerSecond(), markPassing);
|
||||
}
|
||||
maneuvers.add(maneuver);
|
||||
}
|
||||
return new ManeuverSpot(new ArrayList<>(douglasPeuckerFixesGroup), maneuverDirection, maneuvers,
|
||||
new WindMeasurement(maneuverDetails.getTimePoint(), maneuverPosition,
|
||||
wind == null ? null : wind.getBearing()));
|
||||
}
|
||||
|
||||
private int getNumberOfTacksAndJibesFromManeuverSpots(List<ManeuverSpot> maneuverSpots) {
|
||||
int tackAndJibeCount = 0;
|
||||
for (ManeuverSpot maneuverSpot : maneuverSpots) {
|
||||
for (Maneuver maneuver : maneuverSpot.getManeuvers()) {
|
||||
switch (maneuver.getType()) {
|
||||
case JIBE:
|
||||
case TACK:
|
||||
++tackAndJibeCount;
|
||||
break;
|
||||
case PENALTY_CIRCLE:
|
||||
tackAndJibeCount += 2;
|
||||
break;
|
||||
case BEAR_AWAY:
|
||||
case HEAD_UP:
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tackAndJibeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the maneuver loss as the distance projected onto the average course between entering and exiting the
|
||||
* maneuver that the boat lost compared to not having maneuvered. With this distance measure, the competitors speed
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
@@ -107,7 +108,7 @@ public abstract class AbstractTrackedRegattaAndRaceObserver implements TrackedRe
|
||||
RegattaListener.this.raceAdded(trackedRace);
|
||||
}
|
||||
};
|
||||
trackedRegatta.addRaceListener(raceListener);
|
||||
trackedRegatta.addRaceListener(raceListener, /* Not replicated */ Optional.empty());
|
||||
}
|
||||
|
||||
public synchronized void raceRemoved(TrackedRace trackedRace) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
@@ -42,6 +43,7 @@ public interface Maneuver extends GPSFix {
|
||||
*
|
||||
* @return The type of maneuver
|
||||
*/
|
||||
@Dimension(messageKey = "ManeuverType", ordinal = 12)
|
||||
ManeuverType getType();
|
||||
|
||||
/**
|
||||
@@ -50,7 +52,7 @@ public interface Maneuver extends GPSFix {
|
||||
*
|
||||
* @return The new tack after the performed maneuver
|
||||
*/
|
||||
@Dimension(messageKey = "Tack", ordinal = 13)
|
||||
@Dimension(messageKey = "Tack", ordinal = 14)
|
||||
Tack getNewTack();
|
||||
|
||||
/**
|
||||
@@ -141,4 +143,22 @@ public interface Maneuver extends GPSFix {
|
||||
*/
|
||||
Speed getLowestSpeed();
|
||||
|
||||
/**
|
||||
* Gets the mark passing which is contained within maneuver. In case if no mark passing was passed, {@code null} is
|
||||
* returned.
|
||||
*/
|
||||
MarkPassing getMarkPassing();
|
||||
|
||||
/**
|
||||
* Determines whether the maneuver is mark passing maneuver.
|
||||
*/
|
||||
@Dimension(messageKey = "MarkPassing", ordinal = 13)
|
||||
boolean isMarkPassing();
|
||||
|
||||
/**
|
||||
* Gets the direction of the maneuver. It corresponds to the direction of mark passing side.
|
||||
*/
|
||||
@Dimension(messageKey = "ToSide", ordinal = 16)
|
||||
NauticalSide getToSide();
|
||||
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
|
||||
public interface MarkPassingManeuver extends Maneuver {
|
||||
Waypoint getWaypointPassed();
|
||||
|
||||
NauticalSide getSide();
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
@@ -11,6 +12,7 @@ import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.impl.TrackedRaces;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
/**
|
||||
* Manages a set of {@link TrackedRace} objects that belong to the same {@link Regatta} (regatta, sailing regatta for a
|
||||
@@ -65,7 +67,8 @@ public interface TrackedRegatta extends Serializable {
|
||||
*/
|
||||
DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, WindStore windStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver);
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver,
|
||||
Optional<ThreadLocalTransporter> beforeAndAfterNotificationHandler);
|
||||
|
||||
/**
|
||||
* Obtains the tracked race for <code>race</code>. Blocks until the tracked race has been created
|
||||
@@ -79,16 +82,16 @@ public interface TrackedRegatta extends Serializable {
|
||||
*/
|
||||
TrackedRace getExistingTrackedRace(RaceDefinition race);
|
||||
|
||||
void addTrackedRace(TrackedRace trackedRace);
|
||||
void addTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> beforeAndAfterNotificationHandler);
|
||||
|
||||
void removeTrackedRace(TrackedRace trackedRace);
|
||||
void removeTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> beforeAndAfterNotificationHandler);
|
||||
|
||||
/**
|
||||
* Listener will be notified when {@link #addTrackedRace(TrackedRace)} is called and
|
||||
* upon registration for each tracked race already known. Therefore, the listener
|
||||
* won't miss any tracked race.
|
||||
*/
|
||||
void addRaceListener(RaceListener listener);
|
||||
void addRaceListener(RaceListener listener, Optional<ThreadLocalTransporter> beforeAndAfterNotificationHandler);
|
||||
|
||||
/**
|
||||
* Removes the given listener and returns a {@link Future} that will be completed
|
||||
|
||||
+6
-2
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
@@ -8,6 +10,7 @@ import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
public class DynamicTrackedRegattaImpl extends TrackedRegattaImpl implements DynamicTrackedRegatta {
|
||||
private static final long serialVersionUID = -90155868534737120L;
|
||||
@@ -35,9 +38,10 @@ public class DynamicTrackedRegattaImpl extends TrackedRegattaImpl implements Dyn
|
||||
@Override
|
||||
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, WindStore windStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) {
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver,
|
||||
Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
return (DynamicTrackedRace) super.createTrackedRace(raceDefinition, sidelines, windStore, delayToLiveInMillis,
|
||||
millisecondsOverWhichToAverageWind,
|
||||
millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver);
|
||||
millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver, threadLocalTransporter);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
@@ -9,6 +10,7 @@ import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.tracking.impl.AbstractGPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
@@ -25,11 +27,12 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve
|
||||
private final double maxAngularVelocityInDegreesPerSecond;
|
||||
private final ManeuverCurveBoundaries mainCurveBoundaries;
|
||||
private final ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries;
|
||||
private final MarkPassing markPassing;
|
||||
|
||||
public ManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLoss, TimePoint timePoint,
|
||||
ManeuverCurveBoundaries mainCurveBoundaries,
|
||||
ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries,
|
||||
double maxAngularVelocityInDegreesPerSecond) {
|
||||
double maxAngularVelocityInDegreesPerSecond, MarkPassing markPassing) {
|
||||
this.type = type;
|
||||
this.newTack = newTack;
|
||||
this.position = position;
|
||||
@@ -38,6 +41,7 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve
|
||||
this.mainCurveBoundaries = mainCurveBoundaries;
|
||||
this.maneuverCurveWithStableSpeedAndCourseBoundaries = maneuverCurveWithStableSpeedAndCourseBoundaries;
|
||||
this.maxAngularVelocityInDegreesPerSecond = maxAngularVelocityInDegreesPerSecond;
|
||||
this.markPassing = markPassing;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,7 +104,8 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve
|
||||
return super.toString() + " " + type + " on new tack " + newTack + " on position " + position
|
||||
+ " at time point " + timePoint + ", " + getManeuverBoundaries() + ", max. angular velocity: "
|
||||
+ maxAngularVelocityInDegreesPerSecond
|
||||
+ (getManeuverLoss() == null ? "" : " Lost approximately " + getManeuverLoss());
|
||||
+ (getManeuverLoss() == null ? "" : ", Lost approximately " + getManeuverLoss()) + ", Mark passing: "
|
||||
+ markPassing;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,4 +113,16 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve
|
||||
return maxAngularVelocityInDegreesPerSecond;
|
||||
}
|
||||
|
||||
public MarkPassing getMarkPassing() {
|
||||
return markPassing;
|
||||
}
|
||||
|
||||
public boolean isMarkPassing() {
|
||||
return markPassing != null;
|
||||
}
|
||||
|
||||
public NauticalSide getToSide() {
|
||||
return getMainCurveBoundaries().getDirectionChangeInDegrees() < 0 ? NauticalSide.PORT : NauticalSide.STARBOARD;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
@@ -21,9 +22,9 @@ public class ManeuverWithMainCurveBoundariesImpl extends ManeuverImpl {
|
||||
public ManeuverWithMainCurveBoundariesImpl(ManeuverType type, Tack newTack, Position position,
|
||||
Distance maneuverLoss, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries,
|
||||
ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries,
|
||||
double maxAngularVelocityInDegreesPerSecond) {
|
||||
double maxAngularVelocityInDegreesPerSecond, MarkPassing markPassing) {
|
||||
super(type, newTack, position, maneuverLoss, timePoint, mainCurveBoundaries,
|
||||
maneuverCurveWithStableSpeedAndCourseBoundaries, maxAngularVelocityInDegreesPerSecond);
|
||||
maneuverCurveWithStableSpeedAndCourseBoundaries, maxAngularVelocityInDegreesPerSecond, markPassing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
@@ -22,9 +23,9 @@ public class ManeuverWithStableSpeedAndCourseBoundariesImpl extends ManeuverImpl
|
||||
public ManeuverWithStableSpeedAndCourseBoundariesImpl(ManeuverType type, Tack newTack, Position position,
|
||||
Distance maneuverLoss, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries,
|
||||
ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries,
|
||||
double maxAngularVelocityInDegreesPerSecond) {
|
||||
double maxAngularVelocityInDegreesPerSecond, MarkPassing markPassing) {
|
||||
super(type, newTack, position, maneuverLoss, timePoint, mainCurveBoundaries,
|
||||
maneuverCurveWithStableSpeedAndCourseBoundaries, maxAngularVelocityInDegreesPerSecond);
|
||||
maneuverCurveWithStableSpeedAndCourseBoundaries, maxAngularVelocityInDegreesPerSecond, markPassing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class MarkPassingManeuverImpl extends ManeuverWithStableSpeedAndCourseBoundariesImpl
|
||||
implements MarkPassingManeuver {
|
||||
private static final long serialVersionUID = 8935348908557191614L;
|
||||
private final Waypoint waypointPassed;
|
||||
private final NauticalSide side;
|
||||
|
||||
public MarkPassingManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLoss,
|
||||
TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries,
|
||||
ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries,
|
||||
double maxAngularVelocityInDegreesPerSecond, Waypoint waypointPassed, NauticalSide side) {
|
||||
super(type, newTack, position, maneuverLoss, timePoint, mainCurveBoundaries,
|
||||
maneuverCurveWithStableSpeedAndCourseBoundaries, maxAngularVelocityInDegreesPerSecond);
|
||||
this.waypointPassed = waypointPassed;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getWaypointPassed() {
|
||||
return waypointPassed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getSide() {
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(super.toString());
|
||||
result.append(", passed waypoint " + getWaypointPassed() + " to " + getSide().name());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -549,7 +549,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
markPassingCalculator.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, /* Not relevant For replication */ Optional.empty());
|
||||
} else {
|
||||
markPassingCalculator = null;
|
||||
}
|
||||
|
||||
+43
-19
@@ -9,6 +9,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -34,6 +35,7 @@ import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
private static final long serialVersionUID = 6480508193567014285L;
|
||||
@@ -128,7 +130,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedRace(TrackedRace trackedRace) {
|
||||
public void addTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
final TrackedRace oldTrackedRace;
|
||||
lockTrackedRacesForWrite();
|
||||
try {
|
||||
@@ -136,39 +138,57 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
" with regatta hash code "+getRegatta().hashCode());
|
||||
oldTrackedRace = trackedRaces.put(trackedRace.getRace(), trackedRace);
|
||||
if (oldTrackedRace != trackedRace) {
|
||||
notifyListenersAboutTrackedRaceAdded(trackedRace);
|
||||
notifyListenersAboutTrackedRaceAdded(trackedRace, threadLocalTransporter);
|
||||
}
|
||||
} finally {
|
||||
unlockTrackedRacesAfterWrite();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyListenersAboutTrackedRaceAdded(TrackedRace trackedRace) {
|
||||
enqueEvent(listener -> listener.raceAdded(trackedRace));
|
||||
protected void notifyListenersAboutTrackedRaceAdded(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
enqueEvent(listener -> listener.raceAdded(trackedRace), threadLocalTransporter);
|
||||
}
|
||||
|
||||
protected void enqueEvent(Consumer<RaceListener> fireEventCallback) {
|
||||
/**
|
||||
* Firing events is handled through {@link #eventQueue} to ensure that events are fired in order. This method
|
||||
* enqueues an event while ensuring that the set of listeners is freezed to prevent events to be fired twice to a
|
||||
* specific listener. Any Listener attached after will receive a consistent set of already added {@link TrackedRace
|
||||
* TrackedRaces}. Firing events may not be done directly but only by using this method.
|
||||
*/
|
||||
protected void enqueEvent(Consumer<RaceListener> fireEventCallback, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
final Set<RaceListener> listenersToInform = new HashSet<>(raceListeners.keySet());
|
||||
threadLocalTransporter.ifPresent(ThreadLocalTransporter::rememberThreadLocalStates);
|
||||
eventQueue.addWork(() -> {
|
||||
for (RaceListener listener : listenersToInform) {
|
||||
fireEventCallback.accept(listener);
|
||||
}
|
||||
withBeforeAndAfterHandling(threadLocalTransporter, () -> {
|
||||
for (RaceListener listener : listenersToInform) {
|
||||
fireEventCallback.accept(listener);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void withBeforeAndAfterHandling(Optional<ThreadLocalTransporter> threadLocalTransporter, Runnable action) {
|
||||
threadLocalTransporter.ifPresent(ThreadLocalTransporter::pushThreadLocalStates);
|
||||
try {
|
||||
action.run();
|
||||
} finally {
|
||||
threadLocalTransporter.ifPresent(ThreadLocalTransporter::popThreadLocalStates);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrackedRace(TrackedRace trackedRace) {
|
||||
public void removeTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
lockTrackedRacesForWrite();
|
||||
try {
|
||||
trackedRaces.remove(trackedRace.getRace());
|
||||
notifyListenersAboutTrackedRaceRemoved(trackedRace);
|
||||
notifyListenersAboutTrackedRaceRemoved(trackedRace, threadLocalTransporter);
|
||||
} finally {
|
||||
unlockTrackedRacesAfterWrite();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyListenersAboutTrackedRaceRemoved(TrackedRace trackedRace) {
|
||||
enqueEvent(listener -> listener.raceRemoved(trackedRace));
|
||||
protected void notifyListenersAboutTrackedRaceRemoved(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
enqueEvent(listener -> listener.raceRemoved(trackedRace), threadLocalTransporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,7 +221,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
}
|
||||
}
|
||||
};
|
||||
addRaceListener(listener);
|
||||
addRaceListener(listener, Optional.empty());
|
||||
try {
|
||||
synchronized (mutex) {
|
||||
result = getExistingTrackedRace(race);
|
||||
@@ -232,16 +252,19 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceListener(RaceListener listener) {
|
||||
public void addRaceListener(RaceListener listener, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
lockTrackedRacesForRead();
|
||||
try {
|
||||
raceListeners.put(listener, listener);
|
||||
final List<TrackedRace> trackedRacesCopy = new ArrayList<>();
|
||||
Util.addAll(getTrackedRaces(), trackedRacesCopy);
|
||||
threadLocalTransporter.ifPresent(ThreadLocalTransporter::rememberThreadLocalStates);
|
||||
eventQueue.addWork(() -> {
|
||||
for (TrackedRace trackedRace : trackedRacesCopy) {
|
||||
listener.raceAdded(trackedRace);
|
||||
}
|
||||
withBeforeAndAfterHandling(threadLocalTransporter, () -> {
|
||||
for (TrackedRace trackedRace : trackedRacesCopy) {
|
||||
listener.raceAdded(trackedRace);
|
||||
}
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
unlockTrackedRacesAfterRead();
|
||||
@@ -284,7 +307,8 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines,
|
||||
WindStore windStore, long delayToLiveInMillis,
|
||||
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver) {
|
||||
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver,
|
||||
Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
logger.log(Level.INFO, "Creating DynamicTrackedRaceImpl for RaceDefinition " + raceDefinition.getName());
|
||||
DynamicTrackedRaceImpl result = new DynamicTrackedRaceImpl(this, raceDefinition, sidelines, windStore,
|
||||
delayToLiveInMillis, millisecondsOverWhichToAverageWind,
|
||||
@@ -295,7 +319,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
if (raceDefinitionSetToUpdate != null) {
|
||||
raceDefinitionSetToUpdate.addRaceDefinition(raceDefinition, result);
|
||||
}
|
||||
addTrackedRace(result);
|
||||
addTrackedRace(result, threadLocalTransporter);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ Require-Bundle: com.sap.sailing.domain.persistence,
|
||||
com.sap.sse.mongodb,
|
||||
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
|
||||
com.sap.sailing.expeditionconnector.common,
|
||||
com.sap.sailing.server.gateway.serialization.shared.android
|
||||
com.sap.sailing.server.gateway.serialization.shared.android,
|
||||
com.sap.sse
|
||||
Export-Package: com.sap.sailing.expeditionconnector.persistence
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import org.osgi.framework.ServiceRegistration;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionSensorDeviceIdentifier;
|
||||
import com.sap.sailing.expeditionconnector.persistence.ExpeditionGpsDeviceIdentifier;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
|
||||
@@ -37,6 +38,7 @@ public class Activator implements BundleActivator {
|
||||
Activator.context = bundleContext;
|
||||
registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionGpsDeviceIdentifierMongoHandler(), getDict(ExpeditionGpsDeviceIdentifier.TYPE)));
|
||||
registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new ExpeditionSensorDeviceIdentifierMongoHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
|
||||
registrations.add(context.registerService(MasterDataImportClassLoaderService.class, new MasterDataImportClassLoaderServiceImpl(), null));
|
||||
for (CollectionNames name : CollectionNames.values()) {
|
||||
MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.sap.sailing.expeditionconnector.persistence.impl;
|
||||
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
|
||||
public class MasterDataImportClassLoaderServiceImpl implements MasterDataImportClassLoaderService {
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.getClass().getClassLoader();
|
||||
}
|
||||
|
||||
}
|
||||
+6
-4
@@ -7,6 +7,7 @@ import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -77,6 +78,7 @@ import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
private static final long serialVersionUID = 5827912985564121181L;
|
||||
@@ -296,15 +298,15 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedRace(TrackedRace trackedRace) {
|
||||
public void addTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrackedRace(TrackedRace trackedRace) {
|
||||
public void removeTrackedRace(TrackedRace trackedRace, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceListener(RaceListener listener) {
|
||||
public void addRaceListener(RaceListener listener, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -326,7 +328,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, WindStore windStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
|
||||
long millisecondsOverWhichToAverageSpeed, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
|
||||
boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) {
|
||||
boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver, Optional<ThreadLocalTransporter> threadLocalTransporter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -11,8 +11,6 @@ public class ManeuverTypeFormatter {
|
||||
return stringMessages.headUp();
|
||||
case JIBE:
|
||||
return stringMessages.jibe();
|
||||
case MARK_PASSING:
|
||||
return stringMessages.markPassing();
|
||||
case PENALTY_CIRCLE:
|
||||
return stringMessages.penaltyCircle();
|
||||
case TACK:
|
||||
|
||||
+2
-4
@@ -204,7 +204,6 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.LeaderboardType;
|
||||
import com.sap.sailing.domain.common.LegIdentifier;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.NotFoundException;
|
||||
@@ -339,7 +338,6 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sailing.domain.tracking.RaceHandle;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
@@ -3425,14 +3423,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
List<ManeuverDTO> result = new ArrayList<ManeuverDTO>();
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
final ManeuverDTO maneuverDTO;
|
||||
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
|
||||
if (maneuver.isMarkPassing()) {
|
||||
maneuverDTO = new MarkpassingManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
|
||||
maneuver.getPosition(),
|
||||
maneuver.getTimePoint().asDate(),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()),
|
||||
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters(),
|
||||
((MarkPassingManeuver) maneuver).getSide());
|
||||
maneuver.getToSide());
|
||||
} else {
|
||||
maneuverDTO = new ManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
|
||||
maneuver.getPosition(),
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -104,7 +105,8 @@ public class TestStoringAndRetrievingWindTracksTest extends AbstractTracTracLive
|
||||
@Override
|
||||
public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) {
|
||||
}
|
||||
}, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
}, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class),
|
||||
Optional.empty());
|
||||
WindSource windSource = new WindSourceImpl(WindSourceType.WEB);
|
||||
Mongo myFirstMongo = newMongo();
|
||||
DB firstDatabase = myFirstMongo.getDB(dbConfiguration.getDatabaseName());
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ public class ManeuverJsonSerializer implements JsonSerializer<Maneuver> {
|
||||
public static final String POSITION_AND_TIME = "positionAndTime";
|
||||
public static final String MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND = "maxAngularVelocityInDegreesPerSecond";
|
||||
public static final String LOWEST_SPEED_IN_KNOTS = "lowestSpeedInKnots";
|
||||
public static final String MARK_PASSING = "markPassing";
|
||||
|
||||
private final GPSFixJsonSerializer gpsFixSerializer;
|
||||
private final DistanceJsonSerializer distanceSerializer;
|
||||
@@ -46,6 +47,7 @@ public class ManeuverJsonSerializer implements JsonSerializer<Maneuver> {
|
||||
result.put(POSITION_AND_TIME, gpsFixSerializer.serialize(maneuver));
|
||||
result.put(MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND, maneuver.getMaxAngularVelocityInDegreesPerSecond());
|
||||
result.put(LOWEST_SPEED_IN_KNOTS, maneuver.getLowestSpeed().getKnots());
|
||||
result.put(MARK_PASSING, maneuver.isMarkPassing());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ public class ManeuverWithEstimationDataJsonSerializer implements JsonSerializer<
|
||||
public static final String NEW_TACK = "newTack";
|
||||
public static final String POSITION_AND_TIME = "positionAndTime";
|
||||
public static final String MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND = "maxAngularVelocityInDegreesPerSecond";
|
||||
public final static String MARK_PASSING = "markPassing";
|
||||
public static final String MANEUVER_LOSS_IN_METERS = "maneuverLossInMeters";
|
||||
public static final String MAIN_CURVE_BOUNDARIES = "mainCurveBoundaries";
|
||||
public static final String MANEUVER_BOUNDARIES = "maneuverBoundaries";
|
||||
@@ -51,6 +52,7 @@ public class ManeuverWithEstimationDataJsonSerializer implements JsonSerializer<
|
||||
result.put(MANEUVER_TYPE, maneuver.getType() == null ? null : maneuver.getType().name());
|
||||
result.put(NEW_TACK, maneuver.getNewTack() == null ? null : maneuver.getNewTack().name());
|
||||
result.put(MAX_ANGULAR_VELOCITY_IN_DEGREES_PER_SECOND, maneuver.getMaxAngularVelocityInDegreesPerSecond());
|
||||
result.put(MARK_PASSING, maneuver.isMarkPassing());
|
||||
result.put(MANEUVER_LOSS_IN_METERS,
|
||||
maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getMeters());
|
||||
result.put(POSITION_AND_TIME, gpsFixSerializer.serialize(maneuver));
|
||||
|
||||
Regular → Executable
+3
-2
@@ -10,6 +10,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -87,7 +88,7 @@ public class StatisticsTest {
|
||||
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(END_OF_TRACKING));
|
||||
trackedRace.setStartTimeReceived(new MillisecondsTimePoint(START_OF_RACE));
|
||||
|
||||
regatta.addTrackedRace(trackedRace);
|
||||
regatta.addTrackedRace(trackedRace, Optional.empty());
|
||||
}
|
||||
|
||||
private TrackedRaceStatisticsCacheImpl getStatisticsCacheWithRegattaAdded() throws Exception {
|
||||
@@ -104,7 +105,7 @@ public class StatisticsTest {
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
}
|
||||
};
|
||||
regatta.addRaceListener(raceListener);
|
||||
regatta.addRaceListener(raceListener, Optional.empty());
|
||||
regatta.removeRaceListener(raceListener).get();
|
||||
|
||||
return trackedRaceStatisticsCache;
|
||||
|
||||
+7
-3
@@ -14,6 +14,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -85,17 +86,20 @@ public class RaceTrackerStartStopTest {
|
||||
trackedRegatta1.createTrackedRace(raceDef1, Collections.<Sideline> emptyList(),
|
||||
/* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l,
|
||||
/* millisecondsOverWhichToAverageWind */ 0l,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class),
|
||||
Optional.empty());
|
||||
regatta.addRace(raceDef2);
|
||||
trackedRegatta1.createTrackedRace(raceDef2, Collections.<Sideline> emptyList(),
|
||||
/* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l,
|
||||
/* millisecondsOverWhichToAverageWind */ 0l,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class),
|
||||
Optional.empty());
|
||||
regatta.addRace(raceDef3);
|
||||
trackedRegatta1.createTrackedRace(raceDef3, Collections.<Sideline> emptyList(),
|
||||
/* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l,
|
||||
/* millisecondsOverWhichToAverageWind */ 0l,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class),
|
||||
Optional.empty());
|
||||
Long trackerID1 = new Long(1);
|
||||
Long trackerID2 = new Long(2);
|
||||
Long trackerID3 = new Long(3);
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -102,7 +103,7 @@ public class RaceTrackerTest {
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {
|
||||
}
|
||||
});
|
||||
}, Optional.empty());
|
||||
synchronized (trackedRaces) {
|
||||
if (trackedRaces[0] == null) {
|
||||
trackedRaces.wait();
|
||||
|
||||
+3
-1
@@ -10,6 +10,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -101,7 +102,8 @@ public class RemoveLeaderboardTest {
|
||||
trackedRace = trackedRegatta1.createTrackedRace(raceDef1, Collections.<Sideline> emptyList(),
|
||||
/* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l,
|
||||
/* millisecondsOverWhichToAverageWind */ 0l,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class));
|
||||
/* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMong
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
|
||||
import com.sap.sailing.server.MasterDataImportClassLoaderService;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.RacingEventServiceMXBean;
|
||||
import com.sap.sailing.server.impl.preferences.model.BoatClassNotificationPreferences;
|
||||
@@ -43,6 +42,7 @@ import com.sap.sailing.server.impl.preferences.model.CompetitorNotificationPrefe
|
||||
import com.sap.sailing.server.notification.impl.SailingNotificationServiceImpl;
|
||||
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache;
|
||||
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCacheImpl;
|
||||
import com.sap.sse.MasterDataImportClassLoaderService;
|
||||
import com.sap.sse.common.TypeBasedServiceFinder;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.mail.MailService;
|
||||
|
||||
+8
-4
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -1753,13 +1754,15 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
RaceDefinition race = getRace(raceIdentifier);
|
||||
return trackedRegatta.createTrackedRace(race, Collections.<Sideline> emptyList(), windStore,
|
||||
delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
|
||||
/* raceDefinitionSetToUpdate */null, useMarkPassingCalculator, /* raceLogResolver */ this);
|
||||
/* raceDefinitionSetToUpdate */null, useMarkPassingCalculator, /* raceLogResolver */ this,
|
||||
Optional.of(this.getThreadLocalTransporterForCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster()));
|
||||
}
|
||||
|
||||
private void ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(
|
||||
DynamicTrackedRegatta trackedRegatta) {
|
||||
if (regattasObservedForDefaultLeaderboard.add(trackedRegatta)) {
|
||||
trackedRegatta.addRaceListener(new RaceAdditionListener());
|
||||
trackedRegatta.addRaceListener(new RaceAdditionListener(),
|
||||
Optional.of(this.getThreadLocalTransporterForCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1815,7 +1818,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
linkRaceToConfiguredLeaderboardColumns(trackedRace);
|
||||
TrackedRaceReplicatorAndNotifier trackedRaceReplicator = new TrackedRaceReplicatorAndNotifier(trackedRace);
|
||||
trackedRaceReplicators.put(trackedRace, trackedRaceReplicator);
|
||||
trackedRace.addListener(trackedRaceReplicator, /* fire wind already loaded */true, true);
|
||||
trackedRace.addListener(trackedRaceReplicator, /* fire wind already loaded */true, /* notifyAboutGPSFixesAlreadyLoaded */ true);
|
||||
|
||||
PolarFixCacheUpdater polarFixCacheUpdater = new PolarFixCacheUpdater(trackedRace);
|
||||
polarFixCacheUpdaters.put(trackedRace, polarFixCacheUpdater);
|
||||
@@ -2449,7 +2452,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
final int newSizeOfTrackedRaces;
|
||||
oldSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
|
||||
try {
|
||||
trackedRegatta.removeTrackedRace(trackedRace);
|
||||
trackedRegatta.removeTrackedRace(trackedRace, Optional.of(
|
||||
getThreadLocalTransporterForCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster()));
|
||||
newSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
|
||||
isTrackedRacesBecameEmpty = (oldSizeOfTrackedRaces > 0 && newSizeOfTrackedRaces == 0);
|
||||
} finally {
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@@ -477,7 +478,8 @@ public class ImportMasterDataOperation extends
|
||||
trackedRegatta.unlockTrackedRacesAfterRead();
|
||||
}
|
||||
for (TrackedRace raceToRemove : toRemove) {
|
||||
trackedRegatta.removeTrackedRace(raceToRemove);
|
||||
trackedRegatta.removeTrackedRace(raceToRemove, Optional.of(toState
|
||||
.getThreadLocalTransporterForCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster()));
|
||||
RaceDefinition race = existingRegatta.getRaceByName(raceToRemove
|
||||
.getRaceIdentifier().getRaceName());
|
||||
if (race != null) {
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -283,7 +284,7 @@ public class SimulationServiceImpl implements SimulationService {
|
||||
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
|
||||
SimulationRaceListener raceListener = new SimulationRaceListener();
|
||||
raceListeners.put(legIdentifier.getRegattaName(), raceListener);
|
||||
trackedRegatta.addRaceListener(raceListener);
|
||||
trackedRegatta.addRaceListener(raceListener, /* Not replicated */ Optional.empty());
|
||||
}
|
||||
if (!legListeners.containsKey(legIdentifier.getRaceIdentifier())) {
|
||||
TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
|
||||
|
||||
@@ -38,21 +38,8 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">February 2018</h2>
|
||||
<ul class="bulletList">
|
||||
<li>There is an integration with WindFinder (<a href="https://www.windfinder.com">https://www.windfinder.com</a>) now.
|
||||
In the Event edit dialog there is a new tab "WindFinder" where so-called "spot collection ID" can be entered.
|
||||
Such spot collection IDs are negotiated and manually selected by the WindFinder team upon request. Talk to
|
||||
the SAP Sailing Analytics team or to WindFinder directly to obtain one for your area.<p>
|
||||
When such spot collections are provided for an event, a "Weather Data" button will appear on the event
|
||||
landing page. Races whose tracking is started with wind tracking included will obtain wind information
|
||||
from the wind finder spots of the spot collections configured. When the wind arrows for those readings
|
||||
appear on the map, the bubble displayed when clicking them also contains a link to the respective
|
||||
WindFinder web page.<p>
|
||||
The links to the WindFinder page are sensitive to the event time. If it's a few days before the event,
|
||||
a forecast link will be used. During the event the actual report page is shown. Outside of these dates
|
||||
a general weather statistics page is shown for the venue.
|
||||
</li>
|
||||
<li>Further improvements of maneuver detection algorithm were introduced. Mark Passing is no longer regarded as a separate maneuver type. Instead, it is regarded as a supplementary information which gets appended to a maneuver instance. Therefore, a new filtering dimension was added for maneuver data mining, which is boolean and is named as "Mark Passing".</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">January 2018</h2>
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public class XMLExportTest extends OnlineTracTracBasedTest {
|
||||
String resultData = leaderboardData.getResultXML();
|
||||
assertNotNull(resultData);
|
||||
int resultDataLength = resultData.length();
|
||||
assertTrue("resultData length was " + resultDataLength + ", but expected to be > 261000", resultDataLength > 261000);
|
||||
assertTrue("resultData length was " + resultDataLength + ", but expected to be > 254000", resultDataLength > 254000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.sap.sse.operationaltransformation.Operation;
|
||||
import com.sap.sse.operationaltransformation.OperationWithTransformationSupport;
|
||||
import com.sap.sse.replication.impl.OperationWithResultWithIdWrapper;
|
||||
import com.sap.sse.util.ObjectInputStreamResolvingAgainstCache;
|
||||
import com.sap.sse.util.ThreadLocalTransporter;
|
||||
|
||||
/**
|
||||
* Represents a replicable part of an application. Such a replicable part is usually holder of application state and a
|
||||
@@ -199,6 +200,29 @@ public interface Replicable<S, O extends OperationWithResult<S, ?>> extends Repl
|
||||
*/
|
||||
void setCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster(boolean b);
|
||||
|
||||
default ThreadLocalTransporter getThreadLocalTransporterForCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster() {
|
||||
return new ThreadLocalTransporter() {
|
||||
private boolean currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster;
|
||||
private boolean currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMasterAtBeginningOfTask;
|
||||
|
||||
@Override
|
||||
public void rememberThreadLocalStates() {
|
||||
currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster = isCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushThreadLocalStates() {
|
||||
currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMasterAtBeginningOfTask = isCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster();
|
||||
setCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster(currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popThreadLocalStates() {
|
||||
setCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster(currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMasterAtBeginningOfTask);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If an operation equal to <code>operationWithResultWithIdWrapper</code> has previously been passed to a call to
|
||||
* {@link #addOperationSentToMasterForReplication(OperationWithResultWithIdWrapper)}, the call returns <code>true</code>
|
||||
|
||||
+1
-3
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.server;
|
||||
package com.sap.sse;
|
||||
|
||||
/**
|
||||
* Bundles which com.sap.sailing.server has no dependency to can use this service to let the master data importer
|
||||
@@ -13,7 +13,5 @@ package com.sap.sailing.server;
|
||||
*
|
||||
*/
|
||||
public interface MasterDataImportClassLoaderService {
|
||||
|
||||
ClassLoader getClassLoader();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.sap.sse.util;
|
||||
|
||||
/**
|
||||
* When a task is to be pushed out to another thread, {@link ThreadLocal} state may need
|
||||
* to be preserved and re-established so that the task, when executed by another thread
|
||||
* will see the {@link ThreadLocal}s' state just as it was when the task was created.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface ThreadLocalTransporter {
|
||||
/**
|
||||
* Call this method when your task is created and to be submitted to an executor.
|
||||
* This will store the state of all relevant {@link ThreadLocal}s in this object.
|
||||
*/
|
||||
void rememberThreadLocalStates();
|
||||
|
||||
/**
|
||||
* Call this at the beginning of the task that is executed on another thread. This method
|
||||
* will save the states of the relevant {@link ThreadLocal}s and will then establish their
|
||||
* state as remembered earlier by the {@link #rememberThreadLocalStates()} method.
|
||||
*/
|
||||
void pushThreadLocalStates();
|
||||
|
||||
/**
|
||||
* Call this in a {@code finally} block at the end of your task. This will restore the
|
||||
* relevant {@link ThreadLocal} states as they were remembered when {@link #pushThreadLocalStates()}
|
||||
* was invoked.
|
||||
*/
|
||||
void popThreadLocalStates();
|
||||
}
|
||||
Reference in New Issue
Block a user