diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java index ee6d4b05031..0fc3f4459ca 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java @@ -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(); diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/MarkPassingRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/MarkPassingRetrievalProcessor.java index 5f1b685ab4a..da98865c19c 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/MarkPassingRetrievalProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/MarkPassingRetrievalProcessor.java @@ -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 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) { diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverSpeedDetailsWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverSpeedDetailsWithContext.java index c7a19d422fb..483c8c55080 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverSpeedDetailsWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverSpeedDetailsWithContext.java @@ -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 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 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 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) { diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java index 8183cb8c7f3..c037496623b 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java @@ -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()); diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java index 76923fef37e..ad8aecd22f4 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java @@ -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 diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ManeuverType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ManeuverType.java index 6412b44b236..52eae29de56 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ManeuverType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ManeuverType.java @@ -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; } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java index 46e2684f8bf..286b25910ee 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java @@ -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(); } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java index 8be4cc22076..8bfcbfdde1c 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java @@ -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; diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/MasterDataImportClassLoaderServiceImpl.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/MasterDataImportClassLoaderServiceImpl.java index 9a670c0ab5c..1c964a665b9 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/MasterDataImportClassLoaderServiceImpl.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/MasterDataImportClassLoaderServiceImpl.java @@ -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 { diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java index 36080516843..e66c859c2e5 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java @@ -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 diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java index 18bd0aee11a..87881b9e7b1 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java @@ -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()); } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java index 50da2693b29..63f048a03a8 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java @@ -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) { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java index 29051ae5c5a..65128e5ee6a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java @@ -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); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java index 581e4b3d971..2f67d006701 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java @@ -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() { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java index e863dda5d82..3256751f6e4 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java @@ -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) { 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 diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index e8d8c42342c..9c6478a6fe5 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -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) { } @Override - public void removeTrackedRace(TrackedRace trackedRace) { + public void removeTrackedRace(TrackedRace trackedRace, Optional threadLocalTransporter) { } @Override - public void addRaceListener(RaceListener listener) { + public void addRaceListener(RaceListener listener, Optional 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) { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java index a8696f65bee..d9a97c5d1d2 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java @@ -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, diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 3aa07804563..9892d333e1f 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -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()); } /** diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index c852a1970b0..845e356789e 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -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(); for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 9cf7e5c5ece..c13e1cf65dd 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -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 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 maneuverSpotsBeforeMarkPassing = detectManeuvers( + maneuverDetails.getTimePointBefore(), markPassing.getTimePoint().minus(1)); + List 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 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 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 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 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 diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java index bdfacf6644e..eff6206b5d6 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java @@ -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) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java index 9ab81f3dfae..481d95885f4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java @@ -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(); + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/MarkPassingManeuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/MarkPassingManeuver.java deleted file mode 100755 index e12ae4e7218..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/MarkPassingManeuver.java +++ /dev/null @@ -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(); -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java index e752254ad08..1f0df86464a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java @@ -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 sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver); + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver, + Optional beforeAndAfterNotificationHandler); /** * Obtains the tracked race for race. 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 beforeAndAfterNotificationHandler); - void removeTrackedRace(TrackedRace trackedRace); + void removeTrackedRace(TrackedRace trackedRace, Optional 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 beforeAndAfterNotificationHandler); /** * Removes the given listener and returns a {@link Future} that will be completed diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java index fdc820b7904..990fa8cb327 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java @@ -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 sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver, + Optional threadLocalTransporter) { return (DynamicTrackedRace) super.createTrackedRace(raceDefinition, sidelines, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, - millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver); + millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver, threadLocalTransporter); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java index 6ad3b871da7..534ca3ed32b 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java @@ -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; + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java index bc72c7ea9c9..3872f48d81e 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java @@ -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 diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java index ec97c52b344..091a76f425a 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java @@ -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 diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/MarkPassingManeuverImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/MarkPassingManeuverImpl.java deleted file mode 100755 index 02dab346f74..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/MarkPassingManeuverImpl.java +++ /dev/null @@ -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(); - } - -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 5896c05ee17..cfde4bc9fac 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -549,7 +549,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl markPassingCalculator.stop(); } } - }); + }, /* Not relevant For replication */ Optional.empty()); } else { markPassingCalculator = null; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java index e1185b9adfc..138fb07c656 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java @@ -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) { 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) { + enqueEvent(listener -> listener.raceAdded(trackedRace), threadLocalTransporter); } - protected void enqueEvent(Consumer 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 fireEventCallback, Optional threadLocalTransporter) { final Set 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, 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) { 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) { + 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) { lockTrackedRacesForRead(); try { raceListeners.put(listener, listener); final List 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 sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver, + Optional 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; } } diff --git a/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF index 2d660db1059..8180f29087d 100755 --- a/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF @@ -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 diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java index 4e988439924..14318a1cd6f 100755 --- a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java +++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/Activator.java @@ -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()); } diff --git a/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MasterDataImportClassLoaderServiceImpl.java b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MasterDataImportClassLoaderServiceImpl.java new file mode 100755 index 00000000000..fabf75395f7 --- /dev/null +++ b/java/com.sap.sailing.expeditionconnector.persistence/src/com/sap/sailing/expeditionconnector/persistence/impl/MasterDataImportClassLoaderServiceImpl.java @@ -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(); + } + +} diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index 7e3155cd56f..c6990f69bd3 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -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) { } @Override - public void removeTrackedRace(TrackedRace trackedRace) { + public void removeTrackedRace(TrackedRace trackedRace, Optional threadLocalTransporter) { } @Override - public void addRaceListener(RaceListener listener) { + public void addRaceListener(RaceListener listener, Optional threadLocalTransporter) { } @Override @@ -326,7 +328,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, - boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) { + boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver, Optional threadLocalTransporter) { return null; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/ManeuverTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/ManeuverTypeFormatter.java index d6dd1df4c21..240e60b0e68 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/ManeuverTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/ManeuverTypeFormatter.java @@ -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: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index db57f3ef5f2..877c3df2677 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -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 result = new ArrayList(); 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(), diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java index d4bfa3f30d3..9026ed84863 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java @@ -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()); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java index be7c627a5f3..b3bb7108931 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java @@ -17,6 +17,7 @@ public class ManeuverJsonSerializer implements JsonSerializer { 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 { 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; } } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverWithEstimationDataJsonSerializer.java index 6d993cff554..ae4a6e3739e 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverWithEstimationDataJsonSerializer.java @@ -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)); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java old mode 100644 new mode 100755 index 8f86575fa5a..7dcc02f55b1 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java @@ -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; diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 19d2966913d..8d244c15438 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -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. 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. 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. 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); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java index b94b3655167..96509d108d4 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java @@ -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(); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java index a02dcd32655..328f99c8218 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RemoveLeaderboardTest.java @@ -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. 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 diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index 5296fd28d91..db69d72e47d 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -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; diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 42ea47298f0..2943c3a8d5a 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -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. 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 { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java index 7c24ea62cc0..a1983633ad7 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java @@ -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) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java index f7ebb1eca4a..9252bbb57c4 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java @@ -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); diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 98bbedfbfed..24957ca1388 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -38,21 +38,8 @@ -

February 2018

    -
  • There is an integration with WindFinder (https://www.windfinder.com) 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.

    - 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.

    - 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. -

  • +
  • 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".

January 2018

diff --git a/java/com.sap.sailing.xmlexport.test/src/com/sap/sailing/xmlexport/test/XMLExportTest.java b/java/com.sap.sailing.xmlexport.test/src/com/sap/sailing/xmlexport/test/XMLExportTest.java index 7036a876740..9ea495d1fba 100644 --- a/java/com.sap.sailing.xmlexport.test/src/com/sap/sailing/xmlexport/test/XMLExportTest.java +++ b/java/com.sap.sailing.xmlexport.test/src/com/sap/sailing/xmlexport/test/XMLExportTest.java @@ -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); } } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/Replicable.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/Replicable.java index ffd538d7e33..cedaccf1e60 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/Replicable.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/Replicable.java @@ -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> 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 operationWithResultWithIdWrapper has previously been passed to a call to * {@link #addOperationSentToMasterForReplication(OperationWithResultWithIdWrapper)}, the call returns true diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/MasterDataImportClassLoaderService.java b/java/com.sap.sse/src/com/sap/sse/MasterDataImportClassLoaderService.java similarity index 93% rename from java/com.sap.sailing.server/src/com/sap/sailing/server/MasterDataImportClassLoaderService.java rename to java/com.sap.sse/src/com/sap/sse/MasterDataImportClassLoaderService.java index 01b4fe666d7..21f90b2c3d6 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/MasterDataImportClassLoaderService.java +++ b/java/com.sap.sse/src/com/sap/sse/MasterDataImportClassLoaderService.java @@ -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(); - } diff --git a/java/com.sap.sse/src/com/sap/sse/util/ThreadLocalTransporter.java b/java/com.sap.sse/src/com/sap/sse/util/ThreadLocalTransporter.java new file mode 100755 index 00000000000..423efb401dd --- /dev/null +++ b/java/com.sap.sse/src/com/sap/sse/util/ThreadLocalTransporter.java @@ -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(); +}