0c9b63b3b8aee88285df424d35a4fce80a8105f9
Gemfile.lock
| ... | ... | @@ -70,7 +70,7 @@ GEM |
| 70 | 70 | faraday_middleware (1.2.1) |
| 71 | 71 | faraday (~> 1.0) |
| 72 | 72 | fastimage (2.4.0) |
| 73 | - fastlane (2.227.1) |
|
| 73 | + fastlane (2.228.0) |
|
| 74 | 74 | CFPropertyList (>= 2.3, < 4.0.0) |
| 75 | 75 | addressable (>= 2.8, < 3.0.0) |
| 76 | 76 | artifactory (~> 3.0) |
| ... | ... | @@ -134,12 +134,12 @@ GEM |
| 134 | 134 | google-apis-core (>= 0.11.0, < 2.a) |
| 135 | 135 | google-apis-storage_v1 (0.31.0) |
| 136 | 136 | google-apis-core (>= 0.11.0, < 2.a) |
| 137 | - google-cloud-core (1.8.0) |
|
| 137 | + google-cloud-core (1.7.1) |
|
| 138 | 138 | google-cloud-env (>= 1.0, < 3.a) |
| 139 | 139 | google-cloud-errors (~> 1.0) |
| 140 | 140 | google-cloud-env (1.6.0) |
| 141 | 141 | faraday (>= 0.17.3, < 3.0) |
| 142 | - google-cloud-errors (1.5.0) |
|
| 142 | + google-cloud-errors (1.4.0) |
|
| 143 | 143 | google-cloud-storage (1.47.0) |
| 144 | 144 | addressable (~> 2.8) |
| 145 | 145 | digest-crc (~> 0.4) |
| ... | ... | @@ -176,14 +176,14 @@ GEM |
| 176 | 176 | optparse (0.6.0) |
| 177 | 177 | os (1.1.4) |
| 178 | 178 | plist (3.7.2) |
| 179 | - public_suffix (6.0.1) |
|
| 179 | + public_suffix (5.1.1) |
|
| 180 | 180 | rake (13.2.1) |
| 181 | 181 | representable (3.2.0) |
| 182 | 182 | declarative (< 0.1.0) |
| 183 | 183 | trailblazer-option (>= 0.1.1, < 0.2.0) |
| 184 | 184 | uber (< 0.2.0) |
| 185 | 185 | retriable (3.1.2) |
| 186 | - rexml (3.4.1) |
|
| 186 | + rexml (3.4.2) |
|
| 187 | 187 | rouge (3.28.0) |
| 188 | 188 | ruby2_keywords (0.0.5) |
| 189 | 189 | rubyzip (2.4.1) |
| ... | ... | @@ -231,4 +231,4 @@ DEPENDENCIES |
| 231 | 231 | json (> 0) |
| 232 | 232 | |
| 233 | 233 | BUNDLED WITH |
| 234 | - 2.6.1 |
|
| 234 | + 2.4.21 |
Home.md
| ... | ... | @@ -62,6 +62,7 @@ SAP is at the center of today’s technology revolution, developing innovations |
| 62 | 62 | * [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]] |
| 63 | 63 | * [[Upgrading an EC2 image|wiki/info/landscape/upgrading-ec2-image]] |
| 64 | 64 | * [[Creating a webserver EC2 image from scratch|wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch]] |
| 65 | + * [[Upgrading Operating System Across Landscape|wiki/info/landscape/operating-system-upgrade]] |
|
| 65 | 66 | * [[EC2 mail relaying vs. Amazon Simple E-Mail Service (SES)|wiki/info/landscape/mail-relaying]] |
| 66 | 67 | * [[Establishing support@sapsailing.com with AWS SES, SNS, and Lambda|wiki/info/landscape/support-email]] |
| 67 | 68 | * [[Creating an EC2 image for a MongoDB Replica Set from scratch|wiki/info/landscape/creating-ec2-mongodb-image-from-scratch]] |
java/com.sap.sailing.aiagent/META-INF/MANIFEST.MF
| ... | ... | @@ -10,9 +10,7 @@ Automatic-Module-Name: com.sap.sailing.aiagent |
| 10 | 10 | Import-Package: com.sap.sailing.aiagent.interfaces, |
| 11 | 11 | com.sap.sailing.domain.abstractlog.race, |
| 12 | 12 | com.sap.sailing.domain.abstractlog.regatta, |
| 13 | - com.sap.sailing.domain.leaderboard, |
|
| 14 | - com.sap.sailing.domain.tracking, |
|
| 15 | - com.sap.sailing.domain.tracking.impl, |
|
| 13 | + com.sap.sailing.domain.shared.tracking, |
|
| 16 | 14 | com.sap.sse.common, |
| 17 | 15 | com.sap.sse.concurrent, |
| 18 | 16 | com.sap.sse.security.shared, |
java/com.sap.sailing.aiagent/src/com/sap/sailing/aiagent/impl/RaceListener.java
| ... | ... | @@ -27,7 +27,7 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 28 | 28 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 29 | 29 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 30 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 30 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 31 | 31 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 32 | 32 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 33 | 33 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java
| ... | ... | @@ -22,7 +22,7 @@ import com.sap.sailing.domain.common.NoWindException; |
| 22 | 22 | import com.sap.sailing.domain.common.Position; |
| 23 | 23 | import com.sap.sailing.domain.common.Wind; |
| 24 | 24 | import com.sap.sailing.domain.common.racelog.RacingProcedureType; |
| 25 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 25 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 26 | 26 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 27 | 27 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 28 | 28 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startlineadvantage/precalculation/StartlineAdvantageCalculationDataRetriever.java
| ... | ... | @@ -15,8 +15,8 @@ import com.sap.sailing.domain.common.confidence.BearingWithConfidence; |
| 15 | 15 | import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 17 | 17 | import com.sap.sailing.domain.polars.PolarDataService; |
| 18 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 18 | 19 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 19 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 20 | 20 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 21 | 21 | import com.sap.sse.common.Speed; |
| 22 | 22 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java
| ... | ... | @@ -29,15 +29,15 @@ import com.sap.sailing.domain.common.Tack; |
| 29 | 29 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 30 | 30 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 31 | 31 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 32 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 33 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 32 | 34 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 33 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 34 | 35 | import com.sap.sailing.domain.tracking.Maneuver; |
| 35 | 36 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 36 | 37 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 37 | 38 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 38 | 39 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 39 | 40 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 40 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 41 | 41 | import com.sap.sse.common.Bearing; |
| 42 | 42 | import com.sap.sse.common.Distance; |
| 43 | 43 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java
| ... | ... | @@ -34,8 +34,8 @@ import com.sap.sailing.domain.common.NoWindException; |
| 34 | 34 | import com.sap.sailing.domain.common.Position; |
| 35 | 35 | import com.sap.sailing.domain.common.WindSource; |
| 36 | 36 | import com.sap.sailing.domain.common.WindSourceType; |
| 37 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 38 | -import com.sap.sailing.domain.tracking.Track; |
|
| 37 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 38 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 39 | 39 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 40 | 40 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 41 | 41 | import com.sap.sailing.geocoding.ReverseGeocoder; |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java
| ... | ... | @@ -31,6 +31,7 @@ public enum BoatClassMasterdata { |
| 31 | 31 | BAVARIA_CRUISER_46 ("Bavaria Cruiser 46", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B46", "B 46", "BAVARIACRUISER46"), |
| 32 | 32 | BB10M ("BB 10m", true, 10.00, 2.30, BoatHullType.MONOHULL, true, "Dansk BB10M klub"), |
| 33 | 33 | BENETEAU_FIRST_35 ("Benetau First 35", true, 10.66, 3.636, BoatHullType.MONOHULL, true, "First 35"), |
| 34 | + BENETEAU_FIRST_36 ("Benetau First 36", true, 11.98, 3.80, BoatHullType.MONOHULL, true, "First 36"), |
|
| 34 | 35 | BENETEAU_FIRST_45 ("Benetau First 45", true, 13.68, 4.202, BoatHullType.MONOHULL, true, "First 45"), |
| 35 | 36 | BRASSFAHRT_I ("Brassfahrt I", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 1"), |
| 36 | 37 | BRASSFAHRT_II ("Brassfahrt II", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 2"), |
| ... | ... | @@ -49,6 +50,7 @@ public enum BoatClassMasterdata { |
| 49 | 50 | DELPHIA_24 ("Delphia 24", true, 7.70, 2.50, BoatHullType.MONOHULL, true, "Delphia 24 One Design", "Delphia 24 OD"), |
| 50 | 51 | DYAS("Dyas", true, 7.15, 1.95, BoatHullType.MONOHULL, true), |
| 51 | 52 | ELAN350("Elan 350", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan 350 Performance"), |
| 53 | + ELAN_E4("Elan E4", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan E4"), |
|
| 52 | 54 | EXTREME_40 ("Extreme 40", false, 12.2, 6.60, BoatHullType.CATAMARAN, true, "Extreme-40", "Extreme40", "ESS40", "ess"), |
| 53 | 55 | D_35 ("D35", false, 10.81, 6.89, BoatHullType.CATAMARAN, false), |
| 54 | 56 | ELLIOTT_6M ("Elliott 6m", true, 6.0, 2.35, BoatHullType.MONOHULL, true, "Elliott6m"), |
java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/IgtimiFixTrackTest.java
| ... | ... | @@ -23,8 +23,8 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.AWS; |
| 23 | 23 | import com.sap.sailing.domain.igtimiadapter.datatypes.Fix; |
| 24 | 24 | import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM; |
| 25 | 25 | import com.sap.sailing.domain.igtimiadapter.datatypes.Type; |
| 26 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 27 | -import com.sap.sailing.domain.tracking.Track; |
|
| 28 | 28 | import com.sap.sse.common.TimePoint; |
| 29 | 29 | import com.sap.sse.common.Util; |
| 30 | 30 | import com.sap.sse.common.impl.DegreeBearingImpl; |
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnection.java
| ... | ... | @@ -13,9 +13,9 @@ import org.json.simple.parser.ParseException; |
| 13 | 13 | import com.igtimi.IgtimiStream.Msg; |
| 14 | 14 | import com.sap.sailing.domain.igtimiadapter.datatypes.Fix; |
| 15 | 15 | import com.sap.sailing.domain.igtimiadapter.datatypes.Type; |
| 16 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 16 | 17 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 17 | 18 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 18 | -import com.sap.sailing.domain.tracking.Track; |
|
| 19 | 19 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 20 | 20 | import com.sap.sse.common.TimePoint; |
| 21 | 21 | import com.sap.sse.security.shared.HasPermissions; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java
| ... | ... | @@ -45,8 +45,8 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServi |
| 45 | 45 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 46 | 46 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 47 | 47 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 48 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 48 | 49 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 49 | -import com.sap.sailing.domain.tracking.Track; |
|
| 50 | 50 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
| 51 | 51 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 52 | 52 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
| ... | ... | @@ -68,10 +68,10 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 68 | 68 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager; |
| 69 | 69 | import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper; |
| 70 | 70 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 71 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 71 | 72 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 72 | 73 | import com.sap.sailing.domain.tracking.RaceHandle; |
| 73 | 74 | import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler; |
| 74 | -import com.sap.sailing.domain.tracking.Track; |
|
| 75 | 75 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 76 | 76 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 77 | 77 | import com.sap.sailing.server.impl.RacingEventServiceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
| ... | ... | @@ -88,13 +88,13 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 88 | 88 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker; |
| 89 | 89 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 90 | 90 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 91 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 91 | 92 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 92 | 93 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 93 | 94 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 94 | 95 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 95 | 96 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 96 | 97 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 97 | -import com.sap.sailing.domain.tracking.Track; |
|
| 98 | 98 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 99 | 99 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 100 | 100 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java
| ... | ... | @@ -38,7 +38,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 38 | 38 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 39 | 39 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker; |
| 40 | 40 | import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest; |
| 41 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 41 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 42 | 42 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 43 | 43 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 44 | 44 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java
| ... | ... | @@ -64,6 +64,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 64 | 64 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 65 | 65 | import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; |
| 66 | 66 | import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 67 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 67 | 68 | import com.sap.sailing.domain.tracking.AbstractRaceTrackerBaseImpl; |
| 68 | 69 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 69 | 70 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| ... | ... | @@ -73,7 +74,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 73 | 74 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 74 | 75 | import com.sap.sailing.domain.tracking.WindStore; |
| 75 | 76 | import com.sap.sailing.domain.tracking.WindTrack; |
| 76 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 77 | 77 | import com.sap.sse.common.Util; |
| 78 | 78 | import com.sap.sse.common.Util.Pair; |
| 79 | 79 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
| ... | ... | @@ -44,19 +44,19 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper; |
| 44 | 44 | import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 45 | 45 | import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory; |
| 46 | 46 | import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 47 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 48 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 47 | 49 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 48 | 50 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 49 | 51 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 50 | 52 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 51 | 53 | import com.sap.sailing.domain.tracking.Maneuver; |
| 52 | 54 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 53 | -import com.sap.sailing.domain.tracking.Track; |
|
| 54 | 55 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 55 | 56 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 56 | 57 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 57 | 58 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 58 | 59 | import com.sap.sailing.domain.tracking.impl.OutlierFilter; |
| 59 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 60 | 60 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 61 | 61 | import com.sap.sse.common.Duration; |
| 62 | 62 | import com.sap.sse.common.MultiTimeRange; |
java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF
| ... | ... | @@ -55,9 +55,9 @@ Export-Package: com.sap.sailing.domain.abstractlog, |
| 55 | 55 | com.sap.sailing.domain.racelogtracking, |
| 56 | 56 | com.sap.sailing.domain.racelogtracking.impl, |
| 57 | 57 | com.sap.sailing.domain.resultimport, |
| 58 | + com.sap.sailing.domain.shared.tracking, |
|
| 59 | + com.sap.sailing.domain.shared.tracking.impl, |
|
| 58 | 60 | com.sap.sailing.domain.statistics, |
| 59 | - com.sap.sailing.domain.statistics.impl, |
|
| 60 | - com.sap.sailing.domain.tracking, |
|
| 61 | - com.sap.sailing.domain.tracking.impl |
|
| 61 | + com.sap.sailing.domain.statistics.impl |
|
| 62 | 62 | Automatic-Module-Name: com.sap.sailing.domain.shared.android |
| 63 | 63 | Import-Package: com.sap.sse.security.shared |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLog.java
| ... | ... | @@ -7,7 +7,7 @@ import java.util.NavigableSet; |
| 7 | 7 | import java.util.UUID; |
| 8 | 8 | |
| 9 | 9 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 10 | -import com.sap.sailing.domain.tracking.Track; |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 11 | 11 | import com.sap.sse.common.TimePoint; |
| 12 | 12 | import com.sap.sse.common.WithID; |
| 13 | 13 |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
| ... | ... | @@ -24,9 +24,9 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 24 | 24 | import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventComparator; |
| 25 | 25 | import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; |
| 26 | 26 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 27 | -import com.sap.sailing.domain.tracking.Track; |
|
| 28 | -import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView; |
|
| 29 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 27 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 28 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 29 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 30 | 30 | import com.sap.sse.common.Timed; |
| 31 | 31 | import com.sap.sse.common.Util; |
| 32 | 32 | import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java
| ... | ... | @@ -6,7 +6,7 @@ import java.util.Comparator; |
| 6 | 6 | import com.sap.sailing.domain.abstractlog.AbstractLogEvent; |
| 7 | 7 | import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 9 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 10 | 10 | import com.sap.sse.common.Timed; |
| 11 | 11 | import com.sap.sse.common.Util; |
| 12 | 12 |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java
| ... | ... | @@ -7,7 +7,7 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl; |
| 7 | 7 | import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 9 | 9 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 10 | -import com.sap.sailing.domain.tracking.Track; |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 11 | 11 | |
| 12 | 12 | public abstract class RaceLogAnalyzer<ResultType> extends BaseLogAnalyzer |
| 13 | 13 | <RaceLog, RaceLogEvent, RaceLogEventVisitor, ResultType> { |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java
| ... | ... | @@ -16,7 +16,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 16 | 16 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 17 | 17 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 18 | 18 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 19 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 19 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 20 | 20 | import com.sap.sse.common.Duration; |
| 21 | 21 | import com.sap.sse.common.TimePoint; |
| 22 | 22 | import com.sap.sse.common.scalablevalue.ScalableValue; |
| ... | ... | @@ -273,8 +273,8 @@ public class NoAddingRaceLogWrapper implements RaceLog { |
| 273 | 273 | |
| 274 | 274 | @Override |
| 275 | 275 | public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, |
| 276 | - com.sap.sailing.domain.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache, |
|
| 277 | - com.sap.sailing.domain.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) { |
|
| 276 | + com.sap.sailing.domain.shared.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache, |
|
| 277 | + com.sap.sailing.domain.shared.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) { |
|
| 278 | 278 | return innerRaceLog.getValueSum(from, to, nullElement, adder, cache, valueCalculator); |
| 279 | 279 | } |
| 280 | 280 | } |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java
| ... | ... | @@ -8,8 +8,8 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 9 | 9 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 10 | 10 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 11 | -import com.sap.sailing.domain.tracking.Track; |
|
| 12 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | 15 | * {@link Track} implementation for {@link RaceLogEvent}s. |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java
| ... | ... | @@ -5,7 +5,7 @@ import java.util.Locale; |
| 5 | 5 | import java.util.Map; |
| 6 | 6 | import java.util.Set; |
| 7 | 7 | |
| 8 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 8 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 9 | 9 | import com.sap.sse.common.NamedWithID; |
| 10 | 10 | import com.sap.sse.common.Renamable; |
| 11 | 11 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java
| ... | ... | @@ -6,7 +6,7 @@ import java.util.UUID; |
| 6 | 6 | import com.sap.sailing.domain.base.EventBase; |
| 7 | 7 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 8 | 8 | import com.sap.sailing.domain.base.Venue; |
| 9 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 10 | 10 | import com.sap.sse.common.TimePoint; |
| 11 | 11 | |
| 12 | 12 | /** |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/AddResult.java
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import javax.swing.plaf.basic.BasicSliderUI.TrackListener; |
|
| 4 | + |
|
| 5 | +/** |
|
| 6 | + * The result of trying to add a fix to a {@link Track}. Used when notifying {@link TrackListener}s. |
|
| 7 | + * This allows listeners, in particular, to distinguish between the add and replace scenario. |
|
| 8 | + * |
|
| 9 | + * @author Axel Uhl (D043530) |
|
| 10 | + * |
|
| 11 | + */ |
|
| 12 | +public enum AddResult { |
|
| 13 | + NOT_ADDED, ADDED, REPLACED; |
|
| 14 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/FixAcceptancePredicate.java
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +/** |
|
| 4 | + * A predicate for a fix, for use in |
|
| 5 | + * {@link Track#getInterpolatedValue(com.sap.sse.common.TimePoint, com.sap.sse.common.Util.Function)}, used, e.g., to |
|
| 6 | + * provide a rule for when a fix shall be accepted during the search for surrounding fixes. |
|
| 7 | + * |
|
| 8 | + * @author Axel Uhl (d043530) |
|
| 9 | + * |
|
| 10 | + * @param <FixType> |
|
| 11 | + */ |
|
| 12 | +public interface FixAcceptancePredicate<FixType> { |
|
| 13 | + boolean isAcceptFix(FixType fix); |
|
| 14 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/LineDetails.java
| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.base.Mark; |
|
| 4 | +import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | +import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | +import com.sap.sailing.domain.common.Position; |
|
| 7 | +import com.sap.sse.common.Bearing; |
|
| 8 | +import com.sap.sse.common.Distance; |
|
| 9 | +import com.sap.sse.common.TimePoint; |
|
| 10 | + |
|
| 11 | +/** |
|
| 12 | + * For a line such as a start or a finish line, tells the line's length at a given time, which side is |
|
| 13 | + * {@link NauticalSide#PORT port} and which is {@link NauticalSide#STARBOARD starboard} when approaching the line, |
|
| 14 | + * and---if wind information is available---its angle to a true wind direction and the advantageous side in approaching |
|
| 15 | + * direction as well as how much the advantageous side is ahead. The wind-dependent information |
|
| 16 | + * will all be <code>null</code> if no wind data is available. |
|
| 17 | + * |
|
| 18 | + * @author Axel Uhl (d043530) |
|
| 19 | + * |
|
| 20 | + */ |
|
| 21 | +public interface LineDetails { |
|
| 22 | + TimePoint getTimePoint(); |
|
| 23 | + |
|
| 24 | + Waypoint getWaypoint(); |
|
| 25 | + |
|
| 26 | + Distance getLength(); |
|
| 27 | + |
|
| 28 | + Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); |
|
| 29 | + |
|
| 30 | + NauticalSide getAdvantageousSideWhileApproachingLine(); |
|
| 31 | + |
|
| 32 | + Mark getStarboardMarkWhileApproachingLine(); |
|
| 33 | + |
|
| 34 | + Mark getPortMarkWhileApproachingLine(); |
|
| 35 | + |
|
| 36 | + Distance getAdvantage(); |
|
| 37 | + |
|
| 38 | + Position getPortMarkPosition(); |
|
| 39 | + |
|
| 40 | + Position getStarboardMarkPosition(); |
|
| 41 | + |
|
| 42 | + default Mark getAdvantageousMark() { |
|
| 43 | + return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkWhileApproachingLine() : getStarboardMarkWhileApproachingLine(); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + default Position getAdvantageousMarkPosition() { |
|
| 47 | + return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkPosition() : getStarboardMarkPosition(); |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + default Bearing getBearingFromStarboardToPortWhenApproachingLine() { |
|
| 51 | + return getStarboardMarkPosition().getBearingGreatCircle(getPortMarkPosition()); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + default Bearing getBearingFromPortToStarboardWhenApproachingLine() { |
|
| 55 | + return getPortMarkPosition().getBearingGreatCircle(getStarboardMarkPosition()); |
|
| 56 | + } |
|
| 57 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/MappedTrack.java
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.common.Timed; |
|
| 4 | + |
|
| 5 | +/** |
|
| 6 | + * {@link Track} specialization, which is mapped to a specific type of items. |
|
| 7 | + * |
|
| 8 | + * @param <ItemType> |
|
| 9 | + * the type of item this track is mapped to |
|
| 10 | + * @param <FixType> |
|
| 11 | + * the type of fix that is contained in this track |
|
| 12 | + */ |
|
| 13 | +public interface MappedTrack<ItemType, FixType extends Timed> extends Track<FixType> { |
|
| 14 | + |
|
| 15 | + /** |
|
| 16 | + * @return the item this track is mapped to. |
|
| 17 | + */ |
|
| 18 | + ItemType getTrackedItem(); |
|
| 19 | + |
|
| 20 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/Track.java
| ... | ... | @@ -0,0 +1,259 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | +import java.util.concurrent.locks.ReadWriteLock; |
|
| 6 | +import java.util.function.Function; |
|
| 7 | + |
|
| 8 | +import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 10 | +import com.sap.sse.common.Duration; |
|
| 11 | +import com.sap.sse.common.TimePoint; |
|
| 12 | +import com.sap.sse.common.Timed; |
|
| 13 | +import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 14 | + |
|
| 15 | +/** |
|
| 16 | + * A track records {@link Timed} items for an object of type <code>ItemType</code>. It allows clients to ask for a value |
|
| 17 | + * close to a given {@link TimePoint}. The track manages a time-based set of raw fixes. An implementation may have an |
|
| 18 | + * understanding of how to eliminate outliers. For example, if a track implementation knows it's tracking boats, it may |
|
| 19 | + * consider fixes that the boat cannot possibly have reached due to its speed and direction change limitations as |
|
| 20 | + * outliers. The set of fixes with outliers filtered out can be obtained using {@link #getFixes} whereas |
|
| 21 | + * {@link #getRawFixes()} returns the unfiltered, raw fixes. If an implementation has no idea what an outlier is, |
|
| 22 | + * both methods will return the same fix sequence.<p> |
|
| 23 | + * |
|
| 24 | + * With tracks, concurrency is an important issue. Threads may want to modify a track while other threads may want to |
|
| 25 | + * read from it. Several methods such as {@link #getLastFixAtOrBefore(TimePoint)} return a single fix and can manage |
|
| 26 | + * concurrency internally. However, those methods returning a collection of fixes, such as {@link #getFixes()} or an |
|
| 27 | + * iterator over a collection of fixes, such as {@link #getFixesIterator(TimePoint, boolean)}, need special treatment. |
|
| 28 | + * Until we internalize such iterations (see bug 824, http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=824), |
|
| 29 | + * callers need to manage a read lock which is part of a {@link ReadWriteLock} managed by this track. Callers do so |
|
| 30 | + * by calling {@link #lockForRead} and {@link #unlockAfterRead}. |
|
| 31 | + * |
|
| 32 | + * @author Axel Uhl (d043530) |
|
| 33 | + */ |
|
| 34 | +public interface Track<FixType extends Timed> extends Serializable { |
|
| 35 | + /** |
|
| 36 | + * An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}. |
|
| 37 | + * |
|
| 38 | + * @author Axel Uhl (D043530) |
|
| 39 | + * |
|
| 40 | + * @param <T> |
|
| 41 | + */ |
|
| 42 | + static interface Adder<T> { |
|
| 43 | + /** |
|
| 44 | + * Adds two elements of type {@code T}. Neither argument must be {@code null}. |
|
| 45 | + */ |
|
| 46 | + T add(T t1, T t2); |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + static interface TimeRangeValueCalculator<T> { |
|
| 50 | + /** |
|
| 51 | + * Calculates a value for fixes across a time range. When the method is called, |
|
| 52 | + * a read lock will previously have been {@link Track#lockForRead obtained} before, |
|
| 53 | + * so an implementing class does not need to worry about acquiring the lock. |
|
| 54 | + */ |
|
| 55 | + T calculate(TimePoint from, TimePoint to); |
|
| 56 | + } |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * Locks this track for reading by the calling thread. If the thread already holds the lock for this track, |
|
| 60 | + * the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a <code>finally</code> |
|
| 61 | + * block to release the lock under all possible circumstances. Failure to do so will inevitably lead to |
|
| 62 | + * deadlocks! |
|
| 63 | + */ |
|
| 64 | + void lockForRead(); |
|
| 65 | + |
|
| 66 | + /** |
|
| 67 | + * Decrements the hold count for this track's read lock for the calling thread. If it goes to zero, the lock will be |
|
| 68 | + * released and other readers or a writer can obtain the lock. Make sure to call this method in a |
|
| 69 | + * <code>finally</code> block for each {@link #lockForRead()} invocation. |
|
| 70 | + */ |
|
| 71 | + void unlockAfterRead(); |
|
| 72 | + |
|
| 73 | + /** |
|
| 74 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 75 | + * will be thrown in case the caller has failed to do so. |
|
| 76 | + * |
|
| 77 | + * @return the smoothened fixes |
|
| 78 | + */ |
|
| 79 | + Iterable<FixType> getFixes(); |
|
| 80 | + |
|
| 81 | + /** |
|
| 82 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 83 | + * will be thrown in case the caller has failed to do so. |
|
| 84 | + * |
|
| 85 | + * @return The smoothened fixes between from and to. |
|
| 86 | + */ |
|
| 87 | + Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive); |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 91 | + * will be thrown in case the caller has failed to do so. |
|
| 92 | + */ |
|
| 93 | + Iterable<FixType> getRawFixes(); |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Returns <code>null</code> if no such fix exists. |
|
| 97 | + */ |
|
| 98 | + FixType getLastFixAtOrBefore(TimePoint timePoint); |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * Returns <code>null</code> if no such fix exists. |
|
| 102 | + */ |
|
| 103 | + FixType getLastFixBefore(TimePoint timePoint); |
|
| 104 | + |
|
| 105 | + /** |
|
| 106 | + * Returns <code>null</code> if no such fix exists. |
|
| 107 | + */ |
|
| 108 | + FixType getLastRawFixAtOrBefore(TimePoint timePoint); |
|
| 109 | + |
|
| 110 | + /** |
|
| 111 | + * Returns <code>null</code> if no such fix exists. |
|
| 112 | + */ |
|
| 113 | + FixType getFirstFixAtOrAfter(TimePoint timePoint); |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Returns <code>null</code> if no such fix exists. |
|
| 117 | + */ |
|
| 118 | + FixType getFirstRawFixAtOrAfter(TimePoint timePoint); |
|
| 119 | + |
|
| 120 | + /** |
|
| 121 | + * Returns <code>null</code> if no such fix exists. |
|
| 122 | + */ |
|
| 123 | + FixType getLastRawFixBefore(TimePoint timePoint); |
|
| 124 | + |
|
| 125 | + /** |
|
| 126 | + * Returns <code>null</code> if no such fix exists. |
|
| 127 | + */ |
|
| 128 | + FixType getFirstRawFixAfter(TimePoint timePoint); |
|
| 129 | + |
|
| 130 | + /** |
|
| 131 | + * Returns <code>null</code> if no such fix exists. |
|
| 132 | + */ |
|
| 133 | + FixType getFirstFixAfter(TimePoint timePoint); |
|
| 134 | + |
|
| 135 | + /** |
|
| 136 | + * The first fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 137 | + * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 138 | + */ |
|
| 139 | + FixType getFirstRawFix(); |
|
| 140 | + |
|
| 141 | + /** |
|
| 142 | + * The last fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 143 | + * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 144 | + */ |
|
| 145 | + FixType getLastRawFix(); |
|
| 146 | + |
|
| 147 | + /** |
|
| 148 | + * Interpolates an aspect of the fixes in this track for a given {@code timePoint}. If {@code timePoint} matches |
|
| 149 | + * exactly a fix in this track, that fix is used. If this track is empty, {@code null} is returned. If the |
|
| 150 | + * {@code timePoint} is after the last fix of this track, the last fix is used; if before the first fix, the first |
|
| 151 | + * fix is used. |
|
| 152 | + * <p> |
|
| 153 | + * |
|
| 154 | + * The fix(es) are converted to {@link ScalableValue}s using the {@code converter} which gives callers a choice |
|
| 155 | + * which aspect of the fixes to project and interpolate. If more than one value results because two fixes (one |
|
| 156 | + * before, one after) are used, linear interpolation based on the fixes' time points takes place. |
|
| 157 | + * <p> |
|
| 158 | + * |
|
| 159 | + * Example: for a track of {@link GPSFixMoving} fixes the course over ground shall be determined for a given time |
|
| 160 | + * point. The call would look like this: |
|
| 161 | + * {@code getInterpolatedValue(timePoint, f->new ScalableBearing(f.getSpeed().getBearing()))} |
|
| 162 | + * |
|
| 163 | + * @return the projected interpolated value, typed by what the {@link ScalableValue#divide(double)} method returns. |
|
| 164 | + */ |
|
| 165 | + <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 166 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter); |
|
| 167 | + |
|
| 168 | + /** |
|
| 169 | + * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 170 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator exclude outliers (see |
|
| 171 | + * also {@link #getFixes()} and returns the remaining fixes without any smoothening or dampening applied. |
|
| 172 | + * |
|
| 173 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 174 | + * will be thrown in case the caller has failed to do so. |
|
| 175 | + */ |
|
| 176 | + Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 177 | + |
|
| 178 | + /** |
|
| 179 | + * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 180 | + * <code>inclusive</code> is <code>true</code>) and that ends at the <code>endingAt</code> time point or just before |
|
| 181 | + * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the smoothened fixes (see |
|
| 182 | + * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 183 | + * |
|
| 184 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an |
|
| 185 | + * exception will be thrown in case the caller has failed to do so. |
|
| 186 | + * |
|
| 187 | + * @param startingAt |
|
| 188 | + * if <code>null</code>, starts with the first fix available |
|
| 189 | + * @param endingAt |
|
| 190 | + * if <code>null</code>., ends with the last fix available |
|
| 191 | + */ |
|
| 192 | + Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 193 | + |
|
| 194 | + /** |
|
| 195 | + * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 196 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 197 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 198 | + * |
|
| 199 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 200 | + * will be thrown in case the caller has failed to do so. |
|
| 201 | + */ |
|
| 202 | + Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 203 | + |
|
| 204 | + /** |
|
| 205 | + * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 206 | + * <code>startingAtInclusive</code> is <code>true</code>) and ending at the <code>endingAt</code> time point or just before |
|
| 207 | + * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the raw fixes (see also |
|
| 208 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 209 | + * |
|
| 210 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 211 | + * will be thrown in case the caller has failed to do so. |
|
| 212 | + */ |
|
| 213 | + Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 214 | + |
|
| 215 | + /** |
|
| 216 | + * Returns a descending iterator starting at the first fix before <code>startingAt</code> (or "at or before" in case |
|
| 217 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the smoothened fixes (see |
|
| 218 | + * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 219 | + * |
|
| 220 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 221 | + * will be thrown in case the caller has failed to do so. |
|
| 222 | + */ |
|
| 223 | + Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 224 | + |
|
| 225 | + /** |
|
| 226 | + * Returns a descending iterator starting at the first raw fix before <code>startingAt</code> (or "at or before" in case |
|
| 227 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 228 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 229 | + * |
|
| 230 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 231 | + * will be thrown in case the caller has failed to do so. |
|
| 232 | + */ |
|
| 233 | + Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 234 | + |
|
| 235 | + /** |
|
| 236 | + * @return the average duration between two fixes (outliers removed) in this track or <code>null</code> if there is not |
|
| 237 | + * more than one fix in the track |
|
| 238 | + */ |
|
| 239 | + Duration getAverageIntervalBetweenFixes(); |
|
| 240 | + |
|
| 241 | + /** |
|
| 242 | + * @return the average duration between two fixes (outliers <em>not</em> removed) in this track or <code>null</code> if there is not |
|
| 243 | + * more than one raw fix in the track |
|
| 244 | + */ |
|
| 245 | + Duration getAverageIntervalBetweenRawFixes(); |
|
| 246 | + |
|
| 247 | + <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator); |
|
| 248 | + |
|
| 249 | + /** |
|
| 250 | + * @return the number of raw fixes contained in the Track. |
|
| 251 | + */ |
|
| 252 | + int size(); |
|
| 253 | + |
|
| 254 | + /** |
|
| 255 | + * Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty |
|
| 256 | + */ |
|
| 257 | + boolean isEmpty(); |
|
| 258 | + |
|
| 259 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/TrackingConnectorInfo.java
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | + |
|
| 2 | +package com.sap.sailing.domain.shared.tracking; |
|
| 3 | + |
|
| 4 | +import java.io.Serializable; |
|
| 5 | + |
|
| 6 | +/** |
|
| 7 | + * Identifies the tracking connector that was used to create a TrackedRace. |
|
| 8 | + * Further the Connector can provide a webUrl, that leads to an event web page. |
|
| 9 | + */ |
|
| 10 | +public interface TrackingConnectorInfo extends Serializable { |
|
| 11 | + |
|
| 12 | + /** |
|
| 13 | + * gets the name associated with the tracking technology used for the Race |
|
| 14 | + */ |
|
| 15 | + String getTrackingConnectorName(); |
|
| 16 | + |
|
| 17 | + /** |
|
| 18 | + * gets a {@link String} representation of the default web-URL associated with the tracking technology used for the Race. |
|
| 19 | + * may be {@code null} if there is none provided in the adapter. |
|
| 20 | + */ |
|
| 21 | + String getTrackingConnectorDefaultUrl(); |
|
| 22 | + |
|
| 23 | + /** |
|
| 24 | + * gets a {@link String} representation of the web-URL associated with the Event. |
|
| 25 | + * may be {@code null} if the API of the respective Tracking-Service does not provide a URL. |
|
| 26 | + */ |
|
| 27 | + String getWebUrl(); |
|
| 28 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/LineDetailsImpl.java
| ... | ... | @@ -0,0 +1,91 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.base.Mark; |
|
| 4 | +import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | +import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | +import com.sap.sailing.domain.common.Position; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 8 | +import com.sap.sse.common.Bearing; |
|
| 9 | +import com.sap.sse.common.Distance; |
|
| 10 | +import com.sap.sse.common.TimePoint; |
|
| 11 | + |
|
| 12 | +public class LineDetailsImpl implements LineDetails { |
|
| 13 | + private final TimePoint timePoint; |
|
| 14 | + private final Waypoint waypoint; |
|
| 15 | + private final Distance length; |
|
| 16 | + private final Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 17 | + private final NauticalSide advantageousSidewhileApproachingLine; |
|
| 18 | + private final Distance advantage; |
|
| 19 | + private final Mark portMarkWhileApproachingLine; |
|
| 20 | + private final Mark starboardMarkWhileApproachingLine; |
|
| 21 | + private final Position portMarkPosition; |
|
| 22 | + private final Position starboardMarkPosition; |
|
| 23 | + |
|
| 24 | + public LineDetailsImpl(TimePoint timePoint, Waypoint waypoint, Distance length, |
|
| 25 | + Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind, NauticalSide advantageousSideWhileApproachingLine, |
|
| 26 | + Distance advantage, Mark portMarkWhileApproachingLine, Mark starboardMarkWhileApproachingLine, |
|
| 27 | + Position portMarkPosition, Position starboardMarkPosition) { |
|
| 28 | + super(); |
|
| 29 | + this.timePoint = timePoint; |
|
| 30 | + this.waypoint = waypoint; |
|
| 31 | + this.length = length; |
|
| 32 | + this.angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 33 | + this.advantageousSidewhileApproachingLine = advantageousSideWhileApproachingLine; |
|
| 34 | + this.advantage = advantage; |
|
| 35 | + this.portMarkWhileApproachingLine = portMarkWhileApproachingLine; |
|
| 36 | + this.starboardMarkWhileApproachingLine = starboardMarkWhileApproachingLine; |
|
| 37 | + this.portMarkPosition = portMarkPosition; |
|
| 38 | + this.starboardMarkPosition = starboardMarkPosition; |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | + @Override |
|
| 42 | + public TimePoint getTimePoint() { |
|
| 43 | + return timePoint; |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + @Override |
|
| 47 | + public Waypoint getWaypoint() { |
|
| 48 | + return waypoint; |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + @Override |
|
| 52 | + public Distance getLength() { |
|
| 53 | + return length; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + @Override |
|
| 57 | + public Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind() { |
|
| 58 | + return angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + @Override |
|
| 62 | + public NauticalSide getAdvantageousSideWhileApproachingLine() { |
|
| 63 | + return advantageousSidewhileApproachingLine; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + @Override |
|
| 67 | + public Distance getAdvantage() { |
|
| 68 | + return advantage; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + @Override |
|
| 72 | + public Mark getStarboardMarkWhileApproachingLine() { |
|
| 73 | + return starboardMarkWhileApproachingLine; |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public Mark getPortMarkWhileApproachingLine() { |
|
| 78 | + return portMarkWhileApproachingLine; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + @Override |
|
| 82 | + public Position getPortMarkPosition() { |
|
| 83 | + return portMarkPosition; |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + @Override |
|
| 87 | + public Position getStarboardMarkPosition() { |
|
| 88 | + return starboardMarkPosition; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/MappedTrackImpl.java
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 4 | +import com.sap.sse.common.Timed; |
|
| 5 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 6 | + |
|
| 7 | +/** |
|
| 8 | + * Default implementation of {@link MappedTrack} interface. |
|
| 9 | + * |
|
| 10 | + * @param <ItemType> |
|
| 11 | + * the type of item this track is mapped to |
|
| 12 | + * @param <FixType> |
|
| 13 | + * the type of fix that is contained in this track |
|
| 14 | + */ |
|
| 15 | +public class MappedTrackImpl<ItemType, FixType extends Timed> extends TrackImpl<FixType> |
|
| 16 | + implements MappedTrack<ItemType, FixType> { |
|
| 17 | + |
|
| 18 | + private static final long serialVersionUID = 6165693342087329096L; |
|
| 19 | + |
|
| 20 | + private final ItemType trackedItem; |
|
| 21 | + |
|
| 22 | + /** @see TrackImpl#TrackImpl(String) */ |
|
| 23 | + public MappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) { |
|
| 24 | + super(nameForReadWriteLock); |
|
| 25 | + this.trackedItem = trackedItem; |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + /** @see TrackImpl#TrackImpl(ArrayListNavigableSet, String) */ |
|
| 29 | + protected MappedTrackImpl(ItemType trackedItem, ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 30 | + super(fixes, nameForReadWriteLock); |
|
| 31 | + this.trackedItem = trackedItem; |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + @Override |
|
| 35 | + public ItemType getTrackedItem() { |
|
| 36 | + return trackedItem; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/PartialNavigableSetView.java
| ... | ... | @@ -0,0 +1,387 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.util.ArrayList; |
|
| 4 | +import java.util.Collection; |
|
| 5 | +import java.util.Comparator; |
|
| 6 | +import java.util.Iterator; |
|
| 7 | +import java.util.List; |
|
| 8 | +import java.util.NavigableSet; |
|
| 9 | +import java.util.NoSuchElementException; |
|
| 10 | +import java.util.SortedSet; |
|
| 11 | +import java.util.TreeSet; |
|
| 12 | + |
|
| 13 | +/** |
|
| 14 | + * A view on a {@link NavigableSet} which suppresses some entries based on some configurable rule. |
|
| 15 | + * The {@link #size()} operation is expensive because it requires a full scan. {@link #isEmpty()} is |
|
| 16 | + * much cheaper because it suffices to find one element passing the filter rule. The filtering rule |
|
| 17 | + * has to be expressed by subclsses implementing the {@link #isValid(Object)} method. |
|
| 18 | + * |
|
| 19 | + * @author Axel Uhl (d043530) |
|
| 20 | + * |
|
| 21 | + * @param <E> |
|
| 22 | + */ |
|
| 23 | +public abstract class PartialNavigableSetView<E> implements NavigableSet<E> { |
|
| 24 | + private final NavigableSet<E> set; |
|
| 25 | + |
|
| 26 | + private class PartialNavigableSetViewWithSameValidityAsEnclosing extends PartialNavigableSetView<E> { |
|
| 27 | + public PartialNavigableSetViewWithSameValidityAsEnclosing(NavigableSet<E> set) { |
|
| 28 | + super(set); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + @Override |
|
| 32 | + protected boolean isValid(E e) { |
|
| 33 | + return PartialNavigableSetView.this.isValid(e); |
|
| 34 | + } |
|
| 35 | + } |
|
| 36 | + |
|
| 37 | + private class FilteringIterator implements Iterator<E> { |
|
| 38 | + /** |
|
| 39 | + * The iterator is always kept one step "ahead" in order to know whether there really is a next element. The |
|
| 40 | + * next valid element is fetched and stored in {@link #nextValid} and {@link #hasNext} is set to |
|
| 41 | + * <code>true</code>. |
|
| 42 | + */ |
|
| 43 | + private Iterator<E> iter; |
|
| 44 | + |
|
| 45 | + private E nextValid; |
|
| 46 | + |
|
| 47 | + private boolean hasNext; |
|
| 48 | + |
|
| 49 | + private boolean hasLastNext; |
|
| 50 | + |
|
| 51 | + private E lastNext; |
|
| 52 | + |
|
| 53 | + public FilteringIterator() { |
|
| 54 | + iter = getSet().iterator(); |
|
| 55 | + hasLastNext = false; |
|
| 56 | + advance(); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + private void advance() { |
|
| 60 | + if (iter.hasNext()) { |
|
| 61 | + E next = iter.next(); |
|
| 62 | + while (!isValid(next) && iter.hasNext()) { |
|
| 63 | + next = iter.next(); |
|
| 64 | + } |
|
| 65 | + if (isValid(next)) { |
|
| 66 | + nextValid = next; |
|
| 67 | + hasNext = true; |
|
| 68 | + } else { |
|
| 69 | + hasNext = false; |
|
| 70 | + } |
|
| 71 | + } else { |
|
| 72 | + hasNext = false; |
|
| 73 | + } |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public boolean hasNext() { |
|
| 78 | + return hasNext; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + @Override |
|
| 82 | + public E next() { |
|
| 83 | + if (hasNext) { |
|
| 84 | + E result = nextValid; |
|
| 85 | + advance(); |
|
| 86 | + hasLastNext = true; |
|
| 87 | + lastNext = result; |
|
| 88 | + return result; |
|
| 89 | + } else { |
|
| 90 | + throw new NoSuchElementException(); |
|
| 91 | + } |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + @Override |
|
| 95 | + public void remove() { |
|
| 96 | + if (!hasLastNext) { |
|
| 97 | + throw new IllegalStateException("next() was not called before remove()"); |
|
| 98 | + } else { |
|
| 99 | + PartialNavigableSetView.this.remove(lastNext); |
|
| 100 | + hasLastNext = false; |
|
| 101 | + } |
|
| 102 | + } |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + public PartialNavigableSetView(NavigableSet<E> set) { |
|
| 106 | + this.set = set; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + /** |
|
| 110 | + * Subclasses need to implement this method. For elements to be eliminated from the view represented by this |
|
| 111 | + * object, return <code>false</code> for such an element being passed to this method. |
|
| 112 | + */ |
|
| 113 | + abstract protected boolean isValid(E e); |
|
| 114 | + |
|
| 115 | + @Override |
|
| 116 | + public Comparator<? super E> comparator() { |
|
| 117 | + return getSet().comparator(); |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + public NavigableSet<E> descendingSet() { |
|
| 121 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().descendingSet()); |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + @Override |
|
| 125 | + public Iterator<E> descendingIterator() { |
|
| 126 | + return descendingSet().iterator(); |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + @Override |
|
| 130 | + public E first() { |
|
| 131 | + E first = getSet().first(); |
|
| 132 | + while (first != null && !isValid(first)) { |
|
| 133 | + first = getSet().higher(first); |
|
| 134 | + } |
|
| 135 | + if (first == null) { |
|
| 136 | + throw new NoSuchElementException(); |
|
| 137 | + } else { |
|
| 138 | + return first; |
|
| 139 | + } |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + @Override |
|
| 143 | + public E last() { |
|
| 144 | + E last = getSet().last(); |
|
| 145 | + while (last != null && !isValid(last)) { |
|
| 146 | + last = getSet().lower(last); |
|
| 147 | + } |
|
| 148 | + if (last == null) { |
|
| 149 | + throw new NoSuchElementException(); |
|
| 150 | + } else { |
|
| 151 | + return last; |
|
| 152 | + } |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + @Override |
|
| 156 | + public int size() { |
|
| 157 | + int size = 0; |
|
| 158 | + for (E e : getSet()) { |
|
| 159 | + if (isValid(e)) { |
|
| 160 | + size++; |
|
| 161 | + } |
|
| 162 | + } |
|
| 163 | + return size; |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + @Override |
|
| 167 | + public boolean isEmpty() { |
|
| 168 | + for (E e : getSet()) { |
|
| 169 | + if (isValid(e)) { |
|
| 170 | + return false; |
|
| 171 | + } |
|
| 172 | + } |
|
| 173 | + return true; |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + @SuppressWarnings("unchecked") |
|
| 177 | + @Override |
|
| 178 | + public boolean contains(Object o) { |
|
| 179 | + return getSet().contains(o) && isValid((E) o); |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + @Override |
|
| 183 | + public Object[] toArray() { |
|
| 184 | + List<E> l = new ArrayList<E>(); |
|
| 185 | + for (E e : getSet()) { |
|
| 186 | + if (isValid(e)) { |
|
| 187 | + l.add(e); |
|
| 188 | + } |
|
| 189 | + } |
|
| 190 | + return l.toArray(); |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + @SuppressWarnings("unchecked") |
|
| 194 | + @Override |
|
| 195 | + public <T> T[] toArray(T[] a) { |
|
| 196 | + List<T> l = new ArrayList<T>(); |
|
| 197 | + for (E e : getSet()) { |
|
| 198 | + if (isValid(e)) { |
|
| 199 | + l.add((T) e); |
|
| 200 | + } |
|
| 201 | + } |
|
| 202 | + return l.toArray(a); |
|
| 203 | + |
|
| 204 | + } |
|
| 205 | + |
|
| 206 | + @Override |
|
| 207 | + public boolean add(E e) { |
|
| 208 | + return getSet().add(e); |
|
| 209 | + } |
|
| 210 | + |
|
| 211 | + @Override |
|
| 212 | + public boolean remove(Object o) { |
|
| 213 | + return getSet().remove(o); |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + @SuppressWarnings("unchecked") |
|
| 217 | + @Override |
|
| 218 | + public boolean containsAll(Collection<?> c) { |
|
| 219 | + for (Object o : c) { |
|
| 220 | + if (!isValid((E) o) || !getSet().contains(o)) { |
|
| 221 | + return false; |
|
| 222 | + } |
|
| 223 | + } |
|
| 224 | + return true; |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + @Override |
|
| 228 | + public boolean addAll(Collection<? extends E> c) { |
|
| 229 | + return getSet().addAll(c); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + @Override |
|
| 233 | + public boolean retainAll(Collection<?> c) { |
|
| 234 | + return getSet().retainAll(c); |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + @Override |
|
| 238 | + public boolean removeAll(Collection<?> c) { |
|
| 239 | + return getSet().removeAll(c); |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + @Override |
|
| 243 | + public void clear() { |
|
| 244 | + getSet().clear(); |
|
| 245 | + } |
|
| 246 | + |
|
| 247 | + @Override |
|
| 248 | + public E lower(E e) { |
|
| 249 | + E result = getSet().lower(e); |
|
| 250 | + while (result != null && !isValid(result)) { |
|
| 251 | + result = getSet().lower(result); |
|
| 252 | + } |
|
| 253 | + return result; |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + /** |
|
| 257 | + * goes one left on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 258 | + * <code>false</code> |
|
| 259 | + */ |
|
| 260 | + protected E lowerInternal(E e) { |
|
| 261 | + return getSet().lower(e); |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + /** |
|
| 265 | + * goes one right on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 266 | + * <code>false</code> |
|
| 267 | + */ |
|
| 268 | + protected E higherInternal(E e) { |
|
| 269 | + return getSet().higher(e); |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + @Override |
|
| 273 | + public E floor(E e) { |
|
| 274 | + E result = getSet().floor(e); |
|
| 275 | + while (result != null && !isValid(result)) { |
|
| 276 | + result = getSet().lower(result); |
|
| 277 | + } |
|
| 278 | + return result; |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + @Override |
|
| 282 | + public E ceiling(E e) { |
|
| 283 | + E result = getSet().ceiling(e); |
|
| 284 | + while (result != null && !isValid(result)) { |
|
| 285 | + result = getSet().higher(result); |
|
| 286 | + } |
|
| 287 | + return result; |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + @Override |
|
| 291 | + public E higher(E e) { |
|
| 292 | + E result = getSet().higher(e); |
|
| 293 | + while (result != null && !isValid(result)) { |
|
| 294 | + result = getSet().higher(result); |
|
| 295 | + } |
|
| 296 | + return result; |
|
| 297 | + } |
|
| 298 | + |
|
| 299 | + /** |
|
| 300 | + * Removes all raw fixes that have {@link #isValid(Object)}==<code>false</code> and the first element to have |
|
| 301 | + * {@link #isValid(Object)}==<code>true</code>. This latter element is returned. If no such element exists, |
|
| 302 | + * <code>null</code> is returned. It is hence possible that invalid raw fixes are removed but stil <code>null</code> |
|
| 303 | + * is returned. |
|
| 304 | + */ |
|
| 305 | + @Override |
|
| 306 | + public E pollFirst() { |
|
| 307 | + E result = getSet().first(); |
|
| 308 | + while (result != null && !isValid(result)) { |
|
| 309 | + getSet().remove(result); |
|
| 310 | + result = getSet().first(); |
|
| 311 | + } |
|
| 312 | + return result; |
|
| 313 | + } |
|
| 314 | + |
|
| 315 | + @Override |
|
| 316 | + public E pollLast() { |
|
| 317 | + E result = getSet().last(); |
|
| 318 | + while (result != null && !isValid(result)) { |
|
| 319 | + getSet().remove(result); |
|
| 320 | + result = getSet().last(); |
|
| 321 | + } |
|
| 322 | + return result; |
|
| 323 | + } |
|
| 324 | + |
|
| 325 | + @Override |
|
| 326 | + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { |
|
| 327 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().subSet(fromElement, fromInclusive, toElement, toInclusive)); |
|
| 328 | + } |
|
| 329 | + |
|
| 330 | + @Override |
|
| 331 | + public NavigableSet<E> headSet(E toElement, boolean inclusive) { |
|
| 332 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().headSet(toElement, inclusive)); |
|
| 333 | + } |
|
| 334 | + |
|
| 335 | + @Override |
|
| 336 | + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { |
|
| 337 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().tailSet(fromElement, inclusive)); |
|
| 338 | + } |
|
| 339 | + |
|
| 340 | + @Override |
|
| 341 | + public NavigableSet<E> subSet(E fromElement, E toElement) { |
|
| 342 | + SortedSet<E> subSet = set.subSet(fromElement, toElement); |
|
| 343 | + if (subSet instanceof NavigableSet<?>) { |
|
| 344 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) subSet); |
|
| 345 | + } else { |
|
| 346 | + TreeSet<E> result = new TreeSet<E>(subSet); |
|
| 347 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 348 | + } |
|
| 349 | + } |
|
| 350 | + |
|
| 351 | + @Override |
|
| 352 | + public NavigableSet<E> headSet(E toElement) { |
|
| 353 | + SortedSet<E> headSet = set.headSet(toElement); |
|
| 354 | + if (headSet instanceof NavigableSet<?>) { |
|
| 355 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) headSet); |
|
| 356 | + } else { |
|
| 357 | + TreeSet<E> result = new TreeSet<E>(headSet); |
|
| 358 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 359 | + } |
|
| 360 | + } |
|
| 361 | + |
|
| 362 | + @Override |
|
| 363 | + public NavigableSet<E> tailSet(E fromElement) { |
|
| 364 | + SortedSet<E> tailSet = set.tailSet(fromElement); |
|
| 365 | + if (tailSet instanceof NavigableSet<?>) { |
|
| 366 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) tailSet); |
|
| 367 | + } else { |
|
| 368 | + TreeSet<E> result = new TreeSet<E>(tailSet); |
|
| 369 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 370 | + } |
|
| 371 | + } |
|
| 372 | + |
|
| 373 | + |
|
| 374 | + @Override |
|
| 375 | + public Iterator<E> iterator() { |
|
| 376 | + return new FilteringIterator(); |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + @Override |
|
| 380 | + public String toString() { |
|
| 381 | + return new ArrayList<E>(this).toString(); |
|
| 382 | + } |
|
| 383 | + |
|
| 384 | + protected NavigableSet<E> getSet() { |
|
| 385 | + return set; |
|
| 386 | + } |
|
| 387 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TimeRangeCache.java
| ... | ... | @@ -0,0 +1,221 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.util.Comparator; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | +import java.util.LinkedHashMap; |
|
| 6 | +import java.util.Map.Entry; |
|
| 7 | +import java.util.NavigableSet; |
|
| 8 | + |
|
| 9 | +import com.sap.sse.common.TimePoint; |
|
| 10 | +import com.sap.sse.common.Util; |
|
| 11 | +import com.sap.sse.common.Util.Pair; |
|
| 12 | +import com.sap.sse.concurrent.LockUtil; |
|
| 13 | +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 14 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 15 | + |
|
| 16 | +/** |
|
| 17 | + * This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used in |
|
| 18 | + * a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of |
|
| 19 | + * pairs where the first pair component represents a <code>from</code> parameter used in the calculation's time range |
|
| 20 | + * and the second pair component represents the result of the calculation for this parameter combination. |
|
| 21 | + * <p> |
|
| 22 | + * |
|
| 23 | + * For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order |
|
| 24 | + * to be able to efficiently extend a cache entry for a single <code>to</code> fix, the navigable sets containing the |
|
| 25 | + * <code>from</code> fixes and results are ordered such that earlier fixes come later in the set. This way, extending |
|
| 26 | + * the cache entry for a <code>to</code> fix to an earlier <code>from</code> fix only requires appending to the set. |
|
| 27 | + * <p> |
|
| 28 | + * |
|
| 29 | + * <b>Invalidation</b>: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's |
|
| 30 | + * time point are removed from this cache. Additionally, the fix insertion may have an impact on the |
|
| 31 | + * {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and |
|
| 32 | + * therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the |
|
| 33 | + * cache entries for the time points at or after the previous fix also need to be removed. |
|
| 34 | + * <p> |
|
| 35 | + * |
|
| 36 | + * <b>Cache use</b>: When a result across a time range is to be computed the calculating method should first look for a |
|
| 37 | + * cache entry for the <code>to</code> parameter. If one is found, the earliest entry in the navigable set for the |
|
| 38 | + * navigable set of <code>from</code> and result values that is at or after the requested <code>from</code> time point |
|
| 39 | + * is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the |
|
| 40 | + * <code>from</code> value found in the cache as the new <code>to</code> value, and the <code>from</code> value |
|
| 41 | + * originally passed to the calculating method as <code>from</code> again. If no entry is found in the cache entry for |
|
| 42 | + * <code>to</code> that is at or after the requested <code>from</code> time, the result has to be computed "from |
|
| 43 | + * scratch." |
|
| 44 | + * <p> |
|
| 45 | + * |
|
| 46 | + * If a cache entry for <code>to</code> is not found, the latest cache entry before it is looked up. If one is found, |
|
| 47 | + * the result for the time range between the <code>to</code> time point requested and the <code>to</code> time point |
|
| 48 | + * found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is |
|
| 49 | + * computed by iterating backwards all the way to <code>from</code>. |
|
| 50 | + * <p> |
|
| 51 | + * |
|
| 52 | + * Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the |
|
| 53 | + * result to the cache. |
|
| 54 | + * |
|
| 55 | + * @author Axel Uhl (D043530) |
|
| 56 | + */ |
|
| 57 | +public class TimeRangeCache<T> { |
|
| 58 | + public static final int MAX_SIZE = 100; |
|
| 59 | + |
|
| 60 | + private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> timeRangeCache; |
|
| 61 | + |
|
| 62 | + /** |
|
| 63 | + * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is |
|
| 64 | + * defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or |
|
| 65 | + * having been added by {@link #cache(TimePoint, TimePoint, Object)}. |
|
| 66 | + * <p> |
|
| 67 | + * |
|
| 68 | + * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements, |
|
| 69 | + * the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading |
|
| 70 | + * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash |
|
| 71 | + * map that counts access as "use" has a modifying effect on its internal structures. |
|
| 72 | + * <p> |
|
| 73 | + * |
|
| 74 | + * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the |
|
| 75 | + * alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.<p> |
|
| 76 | + * |
|
| 77 | + * Read access is to be <code>synchronized<code> using this field's mutex; write access only happens under the |
|
| 78 | + * {@link #lock write lock} and therefore will have no contenders. |
|
| 79 | + */ |
|
| 80 | + private final LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void> lruCache; |
|
| 81 | + |
|
| 82 | + private final NamedReentrantReadWriteLock lock; |
|
| 83 | + |
|
| 84 | + private static final Comparator<Util.Pair<TimePoint, ?>> timePointInPairComparator = new Comparator<Util.Pair<TimePoint, ?>>() { |
|
| 85 | + @Override |
|
| 86 | + public int compare(Util.Pair<TimePoint, ?> o1, Util.Pair<TimePoint, ?> o2) { |
|
| 87 | + return o1.getA().compareTo(o2.getA()); |
|
| 88 | + } |
|
| 89 | + }; |
|
| 90 | + |
|
| 91 | + public TimeRangeCache(String nameForLockLogging) { |
|
| 92 | + lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true); |
|
| 93 | + this.timeRangeCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>>(timePointInPairComparator); |
|
| 94 | + this.lruCache = new LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void>(/* initial capacity */ 10, /* load factor */ 0.75f, |
|
| 95 | + /* access-based ordering */ true) { |
|
| 96 | + private static final long serialVersionUID = -6568235517111733193L; |
|
| 97 | + |
|
| 98 | + @Override |
|
| 99 | + protected boolean removeEldestEntry(Entry<Pair<TimePoint, TimePoint>, Void> eldest) { |
|
| 100 | + final boolean expunge = size() > MAX_SIZE; |
|
| 101 | + if (expunge) { |
|
| 102 | + removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB()); |
|
| 103 | + } |
|
| 104 | + return expunge; |
|
| 105 | + } |
|
| 106 | + }; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + public int size() { |
|
| 110 | + return lruCache.size(); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + private void removeCacheEntry(TimePoint from, TimePoint to) { |
|
| 114 | + assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock |
|
| 115 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 116 | + if (entryForTo.getA().equals(to)) { |
|
| 117 | + Pair<TimePoint, T> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 118 | + if (entryForFrom.getA().equals(from)) { |
|
| 119 | + entryForTo.getB().remove(entryForFrom); |
|
| 120 | + if (entryForTo.getB().isEmpty()) { |
|
| 121 | + timeRangeCache.remove(entryForTo); |
|
| 122 | + } |
|
| 123 | + } |
|
| 124 | + } |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * Looks up the entry closest to but no later than <code>to</code>. If not found, <code>null</code> is returned. If |
|
| 129 | + * found, the earliest pair of from/result that is at or after <code>from</code> will be returned, together with |
|
| 130 | + * the <code>to</code> value of the entry. If there is no entry that is at or after <code>from</code>, |
|
| 131 | + * <code>null</code> is returned. |
|
| 132 | + */ |
|
| 133 | + public Util.Pair<TimePoint, Util.Pair<TimePoint, T>> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) { |
|
| 134 | + LockUtil.lockForRead(lock); |
|
| 135 | + try { |
|
| 136 | + Util.Pair<TimePoint, Util.Pair<TimePoint, T>> result = null; |
|
| 137 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 138 | + if (entryForTo != null) { |
|
| 139 | + final Util.Pair<TimePoint, T> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 140 | + if (fromCeiling != null) { |
|
| 141 | + result = new Util.Pair<TimePoint, Util.Pair<TimePoint, T>>(entryForTo.getA(), fromCeiling); |
|
| 142 | + } |
|
| 143 | + } |
|
| 144 | + // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using |
|
| 145 | + // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry |
|
| 146 | + // modifies the internal parts of the data structure which is not thread safe. |
|
| 147 | + synchronized (lruCache) { // ping the "perfect match" although it may not even have existed in the cache |
|
| 148 | + lruCache.get(new Util.Pair<TimePoint, TimePoint>(from, to)); |
|
| 149 | + } |
|
| 150 | + return result; |
|
| 151 | + } finally { |
|
| 152 | + LockUtil.unlockAfterRead(lock); |
|
| 153 | + } |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + /** |
|
| 157 | + * Removes all cache entries that have a <code>to</code> time point that is at or after <code>timePoint</code>. |
|
| 158 | + */ |
|
| 159 | + public void invalidateAllAtOrLaterThan(TimePoint timePoint) { |
|
| 160 | + LockUtil.lockForWrite(lock); |
|
| 161 | + try { |
|
| 162 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummy = createDummy(timePoint); |
|
| 163 | + NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true); |
|
| 164 | + for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> i=toRemove.iterator(); i.hasNext(); ) { |
|
| 165 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryToRemove = i.next(); |
|
| 166 | + assert entryToRemove.getA().compareTo(timePoint) >= 0; |
|
| 167 | + for (Pair<TimePoint, T> fromAndResult : entryToRemove.getB()) { |
|
| 168 | + lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA())); |
|
| 169 | + } |
|
| 170 | + i.remove(); |
|
| 171 | + } |
|
| 172 | + } finally { |
|
| 173 | + LockUtil.unlockAfterWrite(lock); |
|
| 174 | + } |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + private NavigableSet<Util.Pair<TimePoint, T>> getEntryForTo(TimePoint to) { |
|
| 178 | + NavigableSet<Util.Pair<TimePoint, T>> result = null; |
|
| 179 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummyForTo = createDummy(to); |
|
| 180 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(dummyForTo); |
|
| 181 | + if (entryForTo != null && entryForTo.getA().equals(to)) { |
|
| 182 | + result = entryForTo.getB(); |
|
| 183 | + } |
|
| 184 | + return result; |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + public void cache(TimePoint from, TimePoint to, T result) { |
|
| 188 | + LockUtil.lockForWrite(lock); |
|
| 189 | + try { |
|
| 190 | + NavigableSet<Util.Pair<TimePoint, T>> entryForTo = getEntryForTo(to); |
|
| 191 | + if (entryForTo == null) { |
|
| 192 | + entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, T>>(timePointInPairComparator); |
|
| 193 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>( |
|
| 194 | + to, entryForTo); |
|
| 195 | + timeRangeCache.add(pairForTo); |
|
| 196 | + } |
|
| 197 | + entryForTo.add(new Util.Pair<TimePoint, T>(from, result)); |
|
| 198 | + lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null); |
|
| 199 | + } finally { |
|
| 200 | + LockUtil.unlockAfterWrite(lock); |
|
| 201 | + } |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> createDummy(TimePoint to) { |
|
| 205 | + return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(to, null); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + /** |
|
| 209 | + * Removes all contents from this cache |
|
| 210 | + */ |
|
| 211 | + public void clear() { |
|
| 212 | + LockUtil.lockForWrite(lock); |
|
| 213 | + try { |
|
| 214 | + timeRangeCache.clear(); |
|
| 215 | + lruCache.clear(); |
|
| 216 | + } finally { |
|
| 217 | + LockUtil.unlockAfterWrite(lock); |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + } |
|
| 221 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TimedComparator.java
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Comparator; |
|
| 5 | + |
|
| 6 | +import com.sap.sse.common.Timed; |
|
| 7 | + |
|
| 8 | +public class TimedComparator implements Comparator<Timed>, Serializable { |
|
| 9 | + private static final long serialVersionUID = 1604511471599854988L; |
|
| 10 | + public static final Comparator<Timed> INSTANCE = new TimedComparator(); |
|
| 11 | + |
|
| 12 | + @Override |
|
| 13 | + public int compare(Timed o1, Timed o2) { |
|
| 14 | + return o1.getTimePoint().compareTo(o2.getTimePoint()); |
|
| 15 | + } |
|
| 16 | +} |
|
| 17 | + |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TrackImpl.java
| ... | ... | @@ -0,0 +1,544 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.io.IOException; |
|
| 4 | +import java.io.ObjectOutputStream; |
|
| 5 | +import java.util.ConcurrentModificationException; |
|
| 6 | +import java.util.Iterator; |
|
| 7 | +import java.util.NavigableSet; |
|
| 8 | +import java.util.function.Function; |
|
| 9 | + |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.FixAcceptancePredicate; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 13 | +import com.sap.sse.common.Duration; |
|
| 14 | +import com.sap.sse.common.TimePoint; |
|
| 15 | +import com.sap.sse.common.Timed; |
|
| 16 | +import com.sap.sse.common.Util; |
|
| 17 | +import com.sap.sse.common.Util.Pair; |
|
| 18 | +import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 19 | +import com.sap.sse.concurrent.LockUtil; |
|
| 20 | +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 21 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 22 | +import com.sap.sse.shared.util.impl.UnmodifiableNavigableSet; |
|
| 23 | + |
|
| 24 | +public class TrackImpl<FixType extends Timed> implements Track<FixType> { |
|
| 25 | + private static final long serialVersionUID = -4075853657857657528L; |
|
| 26 | + /** |
|
| 27 | + * The fixes, ordered by their time points |
|
| 28 | + */ |
|
| 29 | + private final ArrayListNavigableSet<Timed> fixes; |
|
| 30 | + |
|
| 31 | + private final NamedReentrantReadWriteLock readWriteLock; |
|
| 32 | + |
|
| 33 | + protected static class DummyTimed implements Timed { |
|
| 34 | + private static final long serialVersionUID = 6047311973718918856L; |
|
| 35 | + private final TimePoint timePoint; |
|
| 36 | + public DummyTimed(TimePoint timePoint) { |
|
| 37 | + super(); |
|
| 38 | + this.timePoint = timePoint; |
|
| 39 | + } |
|
| 40 | + @Override |
|
| 41 | + public TimePoint getTimePoint() { |
|
| 42 | + return timePoint; |
|
| 43 | + } |
|
| 44 | + @Override |
|
| 45 | + public String toString() { |
|
| 46 | + return timePoint.toString(); |
|
| 47 | + } |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + public TrackImpl(String nameForReadWriteLock) { |
|
| 51 | + this(new ArrayListNavigableSet<Timed>(TimedComparator.INSTANCE), nameForReadWriteLock); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + protected TrackImpl(ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 55 | + this.readWriteLock = new NamedReentrantReadWriteLock(nameForReadWriteLock, /* fair */ false); |
|
| 56 | + this.fixes = fixes; |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + /** |
|
| 60 | + * Synchronize the serialization such that no fixes are added while serializing |
|
| 61 | + */ |
|
| 62 | + private void writeObject(ObjectOutputStream s) throws IOException { |
|
| 63 | + lockForRead(); |
|
| 64 | + try { |
|
| 65 | + s.defaultWriteObject(); |
|
| 66 | + } finally { |
|
| 67 | + unlockAfterRead(); |
|
| 68 | + } |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + @Override |
|
| 72 | + public void lockForRead() { |
|
| 73 | + LockUtil.lockForRead(readWriteLock); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public void unlockAfterRead() { |
|
| 78 | + LockUtil.unlockAfterRead(readWriteLock); |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + protected void lockForWrite() { |
|
| 82 | + LockUtil.lockForWrite(readWriteLock); |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + protected void unlockAfterWrite() { |
|
| 86 | + LockUtil.unlockAfterWrite(readWriteLock); |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and {@link #unlockAfterRead()} |
|
| 91 | + * to avoid {@link ConcurrentModificationException}s. Should they modify the structure returned, they have to use |
|
| 92 | + * {@link #lockForWrite()} and {@link #unlockAfterWrite()}, respectively. |
|
| 93 | + */ |
|
| 94 | + protected NavigableSet<FixType> getInternalRawFixes() { |
|
| 95 | + @SuppressWarnings("unchecked") |
|
| 96 | + NavigableSet<FixType> result = (NavigableSet<FixType>) fixes; |
|
| 97 | + return result; |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * asserts that the calling thread holds at least one of read and write lock |
|
| 102 | + */ |
|
| 103 | + protected void assertReadLock() { |
|
| 104 | + if (readWriteLock.getReadHoldCount() < 1 && readWriteLock.getWriteHoldCount() < 1) { |
|
| 105 | + throw new IllegalStateException("Caller must obtain read lock using lockForRead() before calling this method"); |
|
| 106 | + } |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + protected void assertWriteLock() { |
|
| 110 | + if (readWriteLock.getWriteHoldCount() < 1) { |
|
| 111 | + throw new IllegalStateException("Caller must obtain write lock using lockForWrite() before calling this method"); |
|
| 112 | + } |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and |
|
| 117 | + * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s. |
|
| 118 | + * |
|
| 119 | + * @return the smoothened fixes ordered by their time points; this implementation simply delegates to |
|
| 120 | + * {@link #getInternalRawFixes()} because for only {@link Timed} fixes we can't know how to remove outliers. |
|
| 121 | + * Subclasses that constrain the <code>FixType</code> may provide smoothening implementations. |
|
| 122 | + */ |
|
| 123 | + protected NavigableSet<FixType> getInternalFixes() { |
|
| 124 | + NavigableSet<FixType> result = getInternalRawFixes(); |
|
| 125 | + return result; |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + /** |
|
| 129 | + * Iterates the fixes with outliers getting skipped, in the order of their time points. |
|
| 130 | + * Relies on {@link #getInternalFixes()} to void the track view from outliers. |
|
| 131 | + */ |
|
| 132 | + @Override |
|
| 133 | + public NavigableSet<FixType> getFixes() { |
|
| 134 | + assertReadLock(); |
|
| 135 | + return new UnmodifiableNavigableSet<FixType>(getInternalFixes()); |
|
| 136 | + } |
|
| 137 | + |
|
| 138 | + @Override |
|
| 139 | + public Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive) { |
|
| 140 | + return getFixes().subSet(getDummyFix(from), fromInclusive, getDummyFix(to), toInclusive); |
|
| 141 | + } |
|
| 142 | + |
|
| 143 | + /** |
|
| 144 | + * Iterates over the raw sequence of fixes, all potential outliers included |
|
| 145 | + */ |
|
| 146 | + @Override |
|
| 147 | + public NavigableSet<FixType> getRawFixes() { |
|
| 148 | + assertReadLock(); |
|
| 149 | + return new UnmodifiableNavigableSet<FixType>(getInternalRawFixes()); |
|
| 150 | + } |
|
| 151 | + |
|
| 152 | + @Override |
|
| 153 | + public FixType getLastFixAtOrBefore(TimePoint timePoint) { |
|
| 154 | + return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null); |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 158 | + lockForRead(); |
|
| 159 | + try { |
|
| 160 | + final NavigableSet<FixType> headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 161 | + for (final Iterator<FixType> i=headSet.descendingIterator(); i.hasNext(); ) { |
|
| 162 | + final FixType next = i.next(); |
|
| 163 | + if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 164 | + return next; |
|
| 165 | + } |
|
| 166 | + } |
|
| 167 | + return null; |
|
| 168 | + } finally { |
|
| 169 | + unlockAfterRead(); |
|
| 170 | + } |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + @Override |
|
| 174 | + public FixType getLastFixBefore(TimePoint timePoint) { |
|
| 175 | + lockForRead(); |
|
| 176 | + try { |
|
| 177 | + return (FixType) getInternalFixes().lower(getDummyFix(timePoint)); |
|
| 178 | + } finally { |
|
| 179 | + unlockAfterRead(); |
|
| 180 | + } |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + @Override |
|
| 184 | + public FixType getLastRawFixAtOrBefore(TimePoint timePoint) { |
|
| 185 | + lockForRead(); |
|
| 186 | + try { |
|
| 187 | + return (FixType) getInternalRawFixes().floor(getDummyFix(timePoint)); |
|
| 188 | + } finally { |
|
| 189 | + unlockAfterRead(); |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + @Override |
|
| 194 | + public FixType getFirstRawFixAtOrAfter(TimePoint timePoint) { |
|
| 195 | + lockForRead(); |
|
| 196 | + try { |
|
| 197 | + return (FixType) getInternalRawFixes().ceiling(getDummyFix(timePoint)); |
|
| 198 | + } finally { |
|
| 199 | + unlockAfterRead(); |
|
| 200 | + } |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + @Override |
|
| 204 | + public FixType getFirstFixAtOrAfter(TimePoint timePoint) { |
|
| 205 | + return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 209 | + lockForRead(); |
|
| 210 | + try { |
|
| 211 | + final NavigableSet<FixType> tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 212 | + for (final FixType next : tailSet) { |
|
| 213 | + if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 214 | + return next; |
|
| 215 | + } |
|
| 216 | + } |
|
| 217 | + return null; |
|
| 218 | + } finally { |
|
| 219 | + unlockAfterRead(); |
|
| 220 | + } |
|
| 221 | + } |
|
| 222 | + |
|
| 223 | + @Override |
|
| 224 | + public FixType getLastRawFixBefore(TimePoint timePoint) { |
|
| 225 | + lockForRead(); |
|
| 226 | + try { |
|
| 227 | + return (FixType) getInternalRawFixes().lower(getDummyFix(timePoint)); |
|
| 228 | + } finally { |
|
| 229 | + unlockAfterRead(); |
|
| 230 | + } |
|
| 231 | + } |
|
| 232 | + |
|
| 233 | + @Override |
|
| 234 | + public FixType getFirstFixAfter(TimePoint timePoint) { |
|
| 235 | + lockForRead(); |
|
| 236 | + try { |
|
| 237 | + return (FixType) getInternalFixes().higher(getDummyFix(timePoint)); |
|
| 238 | + } finally { |
|
| 239 | + unlockAfterRead(); |
|
| 240 | + } |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + @Override |
|
| 244 | + public FixType getFirstRawFixAfter(TimePoint timePoint) { |
|
| 245 | + lockForRead(); |
|
| 246 | + try { |
|
| 247 | + return (FixType) getInternalRawFixes().higher(getDummyFix(timePoint)); |
|
| 248 | + } finally { |
|
| 249 | + unlockAfterRead(); |
|
| 250 | + } |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + @Override |
|
| 254 | + public FixType getFirstRawFix() { |
|
| 255 | + lockForRead(); |
|
| 256 | + try { |
|
| 257 | + if (getInternalFixes().isEmpty()) { |
|
| 258 | + return null; |
|
| 259 | + } else { |
|
| 260 | + return (FixType) getInternalFixes().first(); |
|
| 261 | + } |
|
| 262 | + } finally { |
|
| 263 | + unlockAfterRead(); |
|
| 264 | + } |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + @Override |
|
| 268 | + public FixType getLastRawFix() { |
|
| 269 | + lockForRead(); |
|
| 270 | + try { |
|
| 271 | + if (getInternalRawFixes().isEmpty()) { |
|
| 272 | + return null; |
|
| 273 | + } else { |
|
| 274 | + return (FixType) getInternalRawFixes().last(); |
|
| 275 | + } |
|
| 276 | + } finally { |
|
| 277 | + unlockAfterRead(); |
|
| 278 | + } |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + /** |
|
| 282 | + * @param fixAcceptancePredicate |
|
| 283 | + * if not {@code null}, adjacent fixes will be skipped as long as this predicate does not |
|
| 284 | + * {@link FixAcceptancePredicate#isAcceptFix(Object) accept} the fix. This can, e.g., be used to skip |
|
| 285 | + * fixes that don't have values in a dimension required. If {@code null}, the next fixes left and right |
|
| 286 | + * (including the exact {@code timePoint} if a fix exists there) will be used without further check. |
|
| 287 | + */ |
|
| 288 | + private Pair<FixType, FixType> getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 289 | + FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate); |
|
| 290 | + FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate); |
|
| 291 | + com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right); |
|
| 292 | + return result; |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) { |
|
| 296 | + final T acc; |
|
| 297 | + if (timePoint1.equals(timePoint2)) { |
|
| 298 | + acc = value1.add(value2).divide(2); |
|
| 299 | + } else { |
|
| 300 | + long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis()); |
|
| 301 | + long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis()); |
|
| 302 | + acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2); |
|
| 303 | + } |
|
| 304 | + return acc; |
|
| 305 | + } |
|
| 306 | + |
|
| 307 | + @Override |
|
| 308 | + public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 309 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter) { |
|
| 310 | + return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null); |
|
| 311 | + } |
|
| 312 | + |
|
| 313 | + protected <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 314 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 315 | + final ValueType result; |
|
| 316 | + Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate); |
|
| 317 | + if (fixPair.getA() == null) { |
|
| 318 | + if (fixPair.getB() == null) { |
|
| 319 | + result = null; |
|
| 320 | + } else { |
|
| 321 | + result = converter.apply(fixPair.getB()).divide(1); |
|
| 322 | + } |
|
| 323 | + } else { |
|
| 324 | + if (fixPair.getB() == null || fixPair.getA() == fixPair.getB()) { |
|
| 325 | + result = converter.apply(fixPair.getA()).divide(1); |
|
| 326 | + } else { |
|
| 327 | + result = timeBasedAverage(timePoint, |
|
| 328 | + converter.apply(fixPair.getA()), fixPair.getA().getTimePoint(), |
|
| 329 | + converter.apply(fixPair.getB()), fixPair.getB().getTimePoint()); |
|
| 330 | + } |
|
| 331 | + } |
|
| 332 | + return result; |
|
| 333 | + } |
|
| 334 | + |
|
| 335 | + @Override |
|
| 336 | + public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 337 | + assertReadLock(); |
|
| 338 | + return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 339 | + } |
|
| 340 | + |
|
| 341 | + @Override |
|
| 342 | + public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, |
|
| 343 | + boolean endingAtInclusive) { |
|
| 344 | + assertReadLock(); |
|
| 345 | + return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 346 | + } |
|
| 347 | + |
|
| 348 | + @Override |
|
| 349 | + public Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 350 | + assertReadLock(); |
|
| 351 | + Iterator<FixType> result = (Iterator<FixType>) getInternalFixes().headSet( |
|
| 352 | + getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 353 | + return result; |
|
| 354 | + } |
|
| 355 | + |
|
| 356 | + /** |
|
| 357 | + * Creates a dummy fix that conforms to <code>FixType</code>. This in particular means that subclasses |
|
| 358 | + * instantiating <code>FixType</code> with a specific class need to redefine this method so as to return |
|
| 359 | + * a dummy fix complying with their instantiation type used for <code>FixType</code>. Otherwise, a |
|
| 360 | + * {@link ClassCastException} may result upon certain operations performed with the fix returned by |
|
| 361 | + * this method. |
|
| 362 | + */ |
|
| 363 | + protected FixType getDummyFix(TimePoint timePoint) { |
|
| 364 | + @SuppressWarnings("unchecked") |
|
| 365 | + FixType result = (FixType) new DummyTimed(timePoint); |
|
| 366 | + return result; |
|
| 367 | + } |
|
| 368 | + |
|
| 369 | + @Override |
|
| 370 | + public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 371 | + assertReadLock(); |
|
| 372 | + return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 373 | + } |
|
| 374 | + |
|
| 375 | + private Iterator<FixType> getTimeConstrainedFixesIterator(NavigableSet<FixType> set, TimePoint startingAt, boolean startingAtInclusive, |
|
| 376 | + TimePoint endingAt, boolean endingAtInclusive) { |
|
| 377 | + assertReadLock(); |
|
| 378 | + if (startingAt != null && endingAt != null) { |
|
| 379 | + set = set.subSet(getDummyFix(startingAt), startingAtInclusive, getDummyFix(endingAt), endingAtInclusive); |
|
| 380 | + } else if (endingAt != null) { |
|
| 381 | + set = set.headSet(getDummyFix(endingAt), endingAtInclusive); |
|
| 382 | + } else if (startingAt != null) { |
|
| 383 | + set = set.tailSet(getDummyFix(startingAt), startingAtInclusive); |
|
| 384 | + } |
|
| 385 | + Iterator<FixType> result = set.iterator(); |
|
| 386 | + return result; |
|
| 387 | + } |
|
| 388 | + |
|
| 389 | + @Override |
|
| 390 | + public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, |
|
| 391 | + TimePoint endingAt, boolean endingAtInclusive) { |
|
| 392 | + assertReadLock(); |
|
| 393 | + return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 394 | + } |
|
| 395 | + |
|
| 396 | + @Override |
|
| 397 | + public Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 398 | + assertReadLock(); |
|
| 399 | + Iterator<FixType> result = (Iterator<FixType>) getInternalRawFixes().headSet( |
|
| 400 | + getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 401 | + return result; |
|
| 402 | + } |
|
| 403 | + |
|
| 404 | + protected boolean add(FixType fix) { |
|
| 405 | + return add(fix, /* replace */ false); |
|
| 406 | + } |
|
| 407 | + |
|
| 408 | + /** |
|
| 409 | + * @return {@code true} if the fix was added or replaced; {@code false} in case no change was performed |
|
| 410 | + */ |
|
| 411 | + protected boolean add(FixType fix, boolean replace) { |
|
| 412 | + lockForWrite(); |
|
| 413 | + try { |
|
| 414 | + final AddResult addResult = addWithoutLocking(fix, replace); |
|
| 415 | + return addResult == AddResult.ADDED || addResult == AddResult.REPLACED; |
|
| 416 | + } finally { |
|
| 417 | + unlockAfterWrite(); |
|
| 418 | + } |
|
| 419 | + } |
|
| 420 | + |
|
| 421 | + /** |
|
| 422 | + * The caller must ensure to hold the write lock for this track when calling this method. |
|
| 423 | + * |
|
| 424 | + * @param replace |
|
| 425 | + * whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the |
|
| 426 | + * comparator used for the {@link #fixes} set. By default this is a comparator only comparing the |
|
| 427 | + * fixes' time stamps. Subclasses may use different comparator implementations. |
|
| 428 | + */ |
|
| 429 | + protected AddResult addWithoutLocking(FixType fix, boolean replace) { |
|
| 430 | + final AddResult result; |
|
| 431 | + final boolean added = getInternalRawFixes().add(fix); |
|
| 432 | + if (!added && replace) { |
|
| 433 | + getInternalRawFixes().remove(fix); |
|
| 434 | + result = getInternalRawFixes().add(fix) ? AddResult.REPLACED : AddResult.NOT_ADDED; |
|
| 435 | + } else { |
|
| 436 | + result = added ? AddResult.ADDED : AddResult.NOT_ADDED; |
|
| 437 | + } |
|
| 438 | + return result; |
|
| 439 | + } |
|
| 440 | + |
|
| 441 | + @Override |
|
| 442 | + public Duration getAverageIntervalBetweenFixes() { |
|
| 443 | + lockForRead(); |
|
| 444 | + try { |
|
| 445 | + final Duration result; |
|
| 446 | + final int size = getRawFixes().size(); |
|
| 447 | + if (size > 1) { |
|
| 448 | + result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 449 | + } else { |
|
| 450 | + result = null; |
|
| 451 | + } |
|
| 452 | + return result; |
|
| 453 | + } finally { |
|
| 454 | + unlockAfterRead(); |
|
| 455 | + } |
|
| 456 | + } |
|
| 457 | + |
|
| 458 | + @Override |
|
| 459 | + public Duration getAverageIntervalBetweenRawFixes() { |
|
| 460 | + lockForRead(); |
|
| 461 | + try { |
|
| 462 | + final Duration result; |
|
| 463 | + final int size = getRawFixes().size(); |
|
| 464 | + if (size > 1) { |
|
| 465 | + result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 466 | + } else { |
|
| 467 | + result = null; |
|
| 468 | + } |
|
| 469 | + return result; |
|
| 470 | + } finally { |
|
| 471 | + unlockAfterRead(); |
|
| 472 | + } |
|
| 473 | + } |
|
| 474 | + |
|
| 475 | + @Override |
|
| 476 | + public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 477 | + return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator); |
|
| 478 | + } |
|
| 479 | + |
|
| 480 | + private <T> T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement, |
|
| 481 | + Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 482 | + T result; |
|
| 483 | + if (!from.before(to)) { |
|
| 484 | + result = nullElement; |
|
| 485 | + } else { |
|
| 486 | + boolean perfectCacheHit = false; |
|
| 487 | + lockForRead(); |
|
| 488 | + try { |
|
| 489 | + Util.Pair<TimePoint, Util.Pair<TimePoint, T>> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to); |
|
| 490 | + if (bestCacheEntry != null) { |
|
| 491 | + perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below |
|
| 492 | + // compute the missing stretches between best cache entry's "from" and our "from" and the cache |
|
| 493 | + // entry's "to" and our "to" |
|
| 494 | + T valueFromFromToBeginningOfCacheEntry = nullElement; |
|
| 495 | + T valueFromEndOfCacheEntryToTo = nullElement; |
|
| 496 | + if (!bestCacheEntry.getB().getA().equals(from)) { |
|
| 497 | + assert bestCacheEntry.getB().getA().after(from); |
|
| 498 | + perfectCacheHit = false; |
|
| 499 | + valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry |
|
| 500 | + .getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 501 | + } |
|
| 502 | + if (!bestCacheEntry.getA().equals(to)) { |
|
| 503 | + assert bestCacheEntry.getA().before(to); |
|
| 504 | + perfectCacheHit = false; |
|
| 505 | + valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to, |
|
| 506 | + recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 507 | + } |
|
| 508 | + if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) { |
|
| 509 | + result = null; |
|
| 510 | + } else { |
|
| 511 | + result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()), |
|
| 512 | + valueFromEndOfCacheEntryToTo); |
|
| 513 | + } |
|
| 514 | + } else { |
|
| 515 | + if (from.compareTo(to) < 0) { |
|
| 516 | + result = valueCalculator.calculate(from, to); |
|
| 517 | + } else { |
|
| 518 | + result = nullElement; |
|
| 519 | + } |
|
| 520 | + } |
|
| 521 | + // run the cache update while still holding the read lock; this avoids bug4629 where a cache invalidation |
|
| 522 | + // caused by fix insertions can come after the result calculation and before the cache update |
|
| 523 | + if (!perfectCacheHit && recursionDepth == 0) { |
|
| 524 | + cache.cache(from, to, result); |
|
| 525 | + } |
|
| 526 | + } finally { |
|
| 527 | + unlockAfterRead(); |
|
| 528 | + } |
|
| 529 | + } |
|
| 530 | + return result; |
|
| 531 | + } |
|
| 532 | + |
|
| 533 | + |
|
| 534 | + |
|
| 535 | + @Override |
|
| 536 | + public int size() { |
|
| 537 | + return fixes.size(); |
|
| 538 | + } |
|
| 539 | + |
|
| 540 | + @Override |
|
| 541 | + public boolean isEmpty() { |
|
| 542 | + return fixes.isEmpty(); |
|
| 543 | + } |
|
| 544 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TrackingConnectorInfoImpl.java
| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 4 | + |
|
| 5 | +public class TrackingConnectorInfoImpl implements TrackingConnectorInfo { |
|
| 6 | + private static final long serialVersionUID = 7970268841592389145L; |
|
| 7 | + private final String trackingConnectorName; |
|
| 8 | + private final String TtackingConnectorDefaultUrl; |
|
| 9 | + private final String webUrl; |
|
| 10 | + |
|
| 11 | + public TrackingConnectorInfoImpl(String trackingConnectorName, String trackingConnectorDefaultUrl, String webUrl) { |
|
| 12 | + super(); |
|
| 13 | + this.trackingConnectorName = trackingConnectorName; |
|
| 14 | + TtackingConnectorDefaultUrl = trackingConnectorDefaultUrl; |
|
| 15 | + this.webUrl = webUrl; |
|
| 16 | + } |
|
| 17 | + |
|
| 18 | + public String getTrackingConnectorDefaultUrl() { |
|
| 19 | + return TtackingConnectorDefaultUrl; |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + public String getTrackingConnectorName() { |
|
| 23 | + return trackingConnectorName; |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public String getWebUrl() { |
|
| 27 | + return webUrl; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + @Override |
|
| 31 | + public int hashCode() { |
|
| 32 | + final int prime = 31; |
|
| 33 | + int result = 1; |
|
| 34 | + result = prime * result + ((TtackingConnectorDefaultUrl == null) ? 0 : TtackingConnectorDefaultUrl.hashCode()); |
|
| 35 | + result = prime * result + ((trackingConnectorName == null) ? 0 : trackingConnectorName.hashCode()); |
|
| 36 | + result = prime * result + ((webUrl == null) ? 0 : webUrl.hashCode()); |
|
| 37 | + return result; |
|
| 38 | + } |
|
| 39 | + |
|
| 40 | + @Override |
|
| 41 | + public boolean equals(Object obj) { |
|
| 42 | + if (this == obj) |
|
| 43 | + return true; |
|
| 44 | + if (obj == null) |
|
| 45 | + return false; |
|
| 46 | + if (getClass() != obj.getClass()) |
|
| 47 | + return false; |
|
| 48 | + TrackingConnectorInfoImpl other = (TrackingConnectorInfoImpl) obj; |
|
| 49 | + if (TtackingConnectorDefaultUrl == null) { |
|
| 50 | + if (other.TtackingConnectorDefaultUrl != null) |
|
| 51 | + return false; |
|
| 52 | + } else if (!TtackingConnectorDefaultUrl.equals(other.TtackingConnectorDefaultUrl)) |
|
| 53 | + return false; |
|
| 54 | + if (trackingConnectorName == null) { |
|
| 55 | + if (other.trackingConnectorName != null) |
|
| 56 | + return false; |
|
| 57 | + } else if (!trackingConnectorName.equals(other.trackingConnectorName)) |
|
| 58 | + return false; |
|
| 59 | + if (webUrl == null) { |
|
| 60 | + if (other.webUrl != null) |
|
| 61 | + return false; |
|
| 62 | + } else if (!webUrl.equals(other.webUrl)) |
|
| 63 | + return false; |
|
| 64 | + return true; |
|
| 65 | + } |
|
| 66 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/AddResult.java
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import javax.swing.plaf.basic.BasicSliderUI.TrackListener; |
|
| 4 | - |
|
| 5 | -/** |
|
| 6 | - * The result of trying to add a fix to a {@link Track}. Used when notifying {@link TrackListener}s. |
|
| 7 | - * This allows listeners, in particular, to distinguish between the add and replace scenario. |
|
| 8 | - * |
|
| 9 | - * @author Axel Uhl (D043530) |
|
| 10 | - * |
|
| 11 | - */ |
|
| 12 | -public enum AddResult { |
|
| 13 | - NOT_ADDED, ADDED, REPLACED; |
|
| 14 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/FixAcceptancePredicate.java
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -/** |
|
| 4 | - * A predicate for a fix, for use in |
|
| 5 | - * {@link Track#getInterpolatedValue(com.sap.sse.common.TimePoint, com.sap.sse.common.Util.Function)}, used, e.g., to |
|
| 6 | - * provide a rule for when a fix shall be accepted during the search for surrounding fixes. |
|
| 7 | - * |
|
| 8 | - * @author Axel Uhl (d043530) |
|
| 9 | - * |
|
| 10 | - * @param <FixType> |
|
| 11 | - */ |
|
| 12 | -public interface FixAcceptancePredicate<FixType> { |
|
| 13 | - boolean isAcceptFix(FixType fix); |
|
| 14 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/LineDetails.java
| ... | ... | @@ -1,57 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.base.Mark; |
|
| 4 | -import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | -import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | -import com.sap.sailing.domain.common.Position; |
|
| 7 | -import com.sap.sse.common.Bearing; |
|
| 8 | -import com.sap.sse.common.Distance; |
|
| 9 | -import com.sap.sse.common.TimePoint; |
|
| 10 | - |
|
| 11 | -/** |
|
| 12 | - * For a line such as a start or a finish line, tells the line's length at a given time, which side is |
|
| 13 | - * {@link NauticalSide#PORT port} and which is {@link NauticalSide#STARBOARD starboard} when approaching the line, |
|
| 14 | - * and---if wind information is available---its angle to a true wind direction and the advantageous side in approaching |
|
| 15 | - * direction as well as how much the advantageous side is ahead. The wind-dependent information |
|
| 16 | - * will all be <code>null</code> if no wind data is available. |
|
| 17 | - * |
|
| 18 | - * @author Axel Uhl (d043530) |
|
| 19 | - * |
|
| 20 | - */ |
|
| 21 | -public interface LineDetails { |
|
| 22 | - TimePoint getTimePoint(); |
|
| 23 | - |
|
| 24 | - Waypoint getWaypoint(); |
|
| 25 | - |
|
| 26 | - Distance getLength(); |
|
| 27 | - |
|
| 28 | - Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); |
|
| 29 | - |
|
| 30 | - NauticalSide getAdvantageousSideWhileApproachingLine(); |
|
| 31 | - |
|
| 32 | - Mark getStarboardMarkWhileApproachingLine(); |
|
| 33 | - |
|
| 34 | - Mark getPortMarkWhileApproachingLine(); |
|
| 35 | - |
|
| 36 | - Distance getAdvantage(); |
|
| 37 | - |
|
| 38 | - Position getPortMarkPosition(); |
|
| 39 | - |
|
| 40 | - Position getStarboardMarkPosition(); |
|
| 41 | - |
|
| 42 | - default Mark getAdvantageousMark() { |
|
| 43 | - return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkWhileApproachingLine() : getStarboardMarkWhileApproachingLine(); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - default Position getAdvantageousMarkPosition() { |
|
| 47 | - return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkPosition() : getStarboardMarkPosition(); |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - default Bearing getBearingFromStarboardToPortWhenApproachingLine() { |
|
| 51 | - return getStarboardMarkPosition().getBearingGreatCircle(getPortMarkPosition()); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - default Bearing getBearingFromPortToStarboardWhenApproachingLine() { |
|
| 55 | - return getPortMarkPosition().getBearingGreatCircle(getStarboardMarkPosition()); |
|
| 56 | - } |
|
| 57 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/MappedTrack.java
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import com.sap.sse.common.Timed; |
|
| 4 | - |
|
| 5 | -/** |
|
| 6 | - * {@link Track} specialization, which is mapped to a specific type of items. |
|
| 7 | - * |
|
| 8 | - * @param <ItemType> |
|
| 9 | - * the type of item this track is mapped to |
|
| 10 | - * @param <FixType> |
|
| 11 | - * the type of fix that is contained in this track |
|
| 12 | - */ |
|
| 13 | -public interface MappedTrack<ItemType, FixType extends Timed> extends Track<FixType> { |
|
| 14 | - |
|
| 15 | - /** |
|
| 16 | - * @return the item this track is mapped to. |
|
| 17 | - */ |
|
| 18 | - ItemType getTrackedItem(); |
|
| 19 | - |
|
| 20 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java
| ... | ... | @@ -1,259 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | -import java.util.Iterator; |
|
| 5 | -import java.util.concurrent.locks.ReadWriteLock; |
|
| 6 | -import java.util.function.Function; |
|
| 7 | - |
|
| 8 | -import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 9 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 10 | -import com.sap.sse.common.Duration; |
|
| 11 | -import com.sap.sse.common.TimePoint; |
|
| 12 | -import com.sap.sse.common.Timed; |
|
| 13 | -import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 14 | - |
|
| 15 | -/** |
|
| 16 | - * A track records {@link Timed} items for an object of type <code>ItemType</code>. It allows clients to ask for a value |
|
| 17 | - * close to a given {@link TimePoint}. The track manages a time-based set of raw fixes. An implementation may have an |
|
| 18 | - * understanding of how to eliminate outliers. For example, if a track implementation knows it's tracking boats, it may |
|
| 19 | - * consider fixes that the boat cannot possibly have reached due to its speed and direction change limitations as |
|
| 20 | - * outliers. The set of fixes with outliers filtered out can be obtained using {@link #getFixes} whereas |
|
| 21 | - * {@link #getRawFixes()} returns the unfiltered, raw fixes. If an implementation has no idea what an outlier is, |
|
| 22 | - * both methods will return the same fix sequence.<p> |
|
| 23 | - * |
|
| 24 | - * With tracks, concurrency is an important issue. Threads may want to modify a track while other threads may want to |
|
| 25 | - * read from it. Several methods such as {@link #getLastFixAtOrBefore(TimePoint)} return a single fix and can manage |
|
| 26 | - * concurrency internally. However, those methods returning a collection of fixes, such as {@link #getFixes()} or an |
|
| 27 | - * iterator over a collection of fixes, such as {@link #getFixesIterator(TimePoint, boolean)}, need special treatment. |
|
| 28 | - * Until we internalize such iterations (see bug 824, http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=824), |
|
| 29 | - * callers need to manage a read lock which is part of a {@link ReadWriteLock} managed by this track. Callers do so |
|
| 30 | - * by calling {@link #lockForRead} and {@link #unlockAfterRead}. |
|
| 31 | - * |
|
| 32 | - * @author Axel Uhl (d043530) |
|
| 33 | - */ |
|
| 34 | -public interface Track<FixType extends Timed> extends Serializable { |
|
| 35 | - /** |
|
| 36 | - * An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}. |
|
| 37 | - * |
|
| 38 | - * @author Axel Uhl (D043530) |
|
| 39 | - * |
|
| 40 | - * @param <T> |
|
| 41 | - */ |
|
| 42 | - static interface Adder<T> { |
|
| 43 | - /** |
|
| 44 | - * Adds two elements of type {@code T}. Neither argument must be {@code null}. |
|
| 45 | - */ |
|
| 46 | - T add(T t1, T t2); |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - static interface TimeRangeValueCalculator<T> { |
|
| 50 | - /** |
|
| 51 | - * Calculates a value for fixes across a time range. When the method is called, |
|
| 52 | - * a read lock will previously have been {@link Track#lockForRead obtained} before, |
|
| 53 | - * so an implementing class does not need to worry about acquiring the lock. |
|
| 54 | - */ |
|
| 55 | - T calculate(TimePoint from, TimePoint to); |
|
| 56 | - } |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * Locks this track for reading by the calling thread. If the thread already holds the lock for this track, |
|
| 60 | - * the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a <code>finally</code> |
|
| 61 | - * block to release the lock under all possible circumstances. Failure to do so will inevitably lead to |
|
| 62 | - * deadlocks! |
|
| 63 | - */ |
|
| 64 | - void lockForRead(); |
|
| 65 | - |
|
| 66 | - /** |
|
| 67 | - * Decrements the hold count for this track's read lock for the calling thread. If it goes to zero, the lock will be |
|
| 68 | - * released and other readers or a writer can obtain the lock. Make sure to call this method in a |
|
| 69 | - * <code>finally</code> block for each {@link #lockForRead()} invocation. |
|
| 70 | - */ |
|
| 71 | - void unlockAfterRead(); |
|
| 72 | - |
|
| 73 | - /** |
|
| 74 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 75 | - * will be thrown in case the caller has failed to do so. |
|
| 76 | - * |
|
| 77 | - * @return the smoothened fixes |
|
| 78 | - */ |
|
| 79 | - Iterable<FixType> getFixes(); |
|
| 80 | - |
|
| 81 | - /** |
|
| 82 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 83 | - * will be thrown in case the caller has failed to do so. |
|
| 84 | - * |
|
| 85 | - * @return The smoothened fixes between from and to. |
|
| 86 | - */ |
|
| 87 | - Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive); |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 91 | - * will be thrown in case the caller has failed to do so. |
|
| 92 | - */ |
|
| 93 | - Iterable<FixType> getRawFixes(); |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Returns <code>null</code> if no such fix exists. |
|
| 97 | - */ |
|
| 98 | - FixType getLastFixAtOrBefore(TimePoint timePoint); |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * Returns <code>null</code> if no such fix exists. |
|
| 102 | - */ |
|
| 103 | - FixType getLastFixBefore(TimePoint timePoint); |
|
| 104 | - |
|
| 105 | - /** |
|
| 106 | - * Returns <code>null</code> if no such fix exists. |
|
| 107 | - */ |
|
| 108 | - FixType getLastRawFixAtOrBefore(TimePoint timePoint); |
|
| 109 | - |
|
| 110 | - /** |
|
| 111 | - * Returns <code>null</code> if no such fix exists. |
|
| 112 | - */ |
|
| 113 | - FixType getFirstFixAtOrAfter(TimePoint timePoint); |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Returns <code>null</code> if no such fix exists. |
|
| 117 | - */ |
|
| 118 | - FixType getFirstRawFixAtOrAfter(TimePoint timePoint); |
|
| 119 | - |
|
| 120 | - /** |
|
| 121 | - * Returns <code>null</code> if no such fix exists. |
|
| 122 | - */ |
|
| 123 | - FixType getLastRawFixBefore(TimePoint timePoint); |
|
| 124 | - |
|
| 125 | - /** |
|
| 126 | - * Returns <code>null</code> if no such fix exists. |
|
| 127 | - */ |
|
| 128 | - FixType getFirstRawFixAfter(TimePoint timePoint); |
|
| 129 | - |
|
| 130 | - /** |
|
| 131 | - * Returns <code>null</code> if no such fix exists. |
|
| 132 | - */ |
|
| 133 | - FixType getFirstFixAfter(TimePoint timePoint); |
|
| 134 | - |
|
| 135 | - /** |
|
| 136 | - * The first fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 137 | - * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 138 | - */ |
|
| 139 | - FixType getFirstRawFix(); |
|
| 140 | - |
|
| 141 | - /** |
|
| 142 | - * The last fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 143 | - * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 144 | - */ |
|
| 145 | - FixType getLastRawFix(); |
|
| 146 | - |
|
| 147 | - /** |
|
| 148 | - * Interpolates an aspect of the fixes in this track for a given {@code timePoint}. If {@code timePoint} matches |
|
| 149 | - * exactly a fix in this track, that fix is used. If this track is empty, {@code null} is returned. If the |
|
| 150 | - * {@code timePoint} is after the last fix of this track, the last fix is used; if before the first fix, the first |
|
| 151 | - * fix is used. |
|
| 152 | - * <p> |
|
| 153 | - * |
|
| 154 | - * The fix(es) are converted to {@link ScalableValue}s using the {@code converter} which gives callers a choice |
|
| 155 | - * which aspect of the fixes to project and interpolate. If more than one value results because two fixes (one |
|
| 156 | - * before, one after) are used, linear interpolation based on the fixes' time points takes place. |
|
| 157 | - * <p> |
|
| 158 | - * |
|
| 159 | - * Example: for a track of {@link GPSFixMoving} fixes the course over ground shall be determined for a given time |
|
| 160 | - * point. The call would look like this: |
|
| 161 | - * {@code getInterpolatedValue(timePoint, f->new ScalableBearing(f.getSpeed().getBearing()))} |
|
| 162 | - * |
|
| 163 | - * @return the projected interpolated value, typed by what the {@link ScalableValue#divide(double)} method returns. |
|
| 164 | - */ |
|
| 165 | - <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 166 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter); |
|
| 167 | - |
|
| 168 | - /** |
|
| 169 | - * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 170 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator exclude outliers (see |
|
| 171 | - * also {@link #getFixes()} and returns the remaining fixes without any smoothening or dampening applied. |
|
| 172 | - * |
|
| 173 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 174 | - * will be thrown in case the caller has failed to do so. |
|
| 175 | - */ |
|
| 176 | - Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 177 | - |
|
| 178 | - /** |
|
| 179 | - * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 180 | - * <code>inclusive</code> is <code>true</code>) and that ends at the <code>endingAt</code> time point or just before |
|
| 181 | - * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the smoothened fixes (see |
|
| 182 | - * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 183 | - * |
|
| 184 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an |
|
| 185 | - * exception will be thrown in case the caller has failed to do so. |
|
| 186 | - * |
|
| 187 | - * @param startingAt |
|
| 188 | - * if <code>null</code>, starts with the first fix available |
|
| 189 | - * @param endingAt |
|
| 190 | - * if <code>null</code>., ends with the last fix available |
|
| 191 | - */ |
|
| 192 | - Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 193 | - |
|
| 194 | - /** |
|
| 195 | - * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 196 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 197 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 198 | - * |
|
| 199 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 200 | - * will be thrown in case the caller has failed to do so. |
|
| 201 | - */ |
|
| 202 | - Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 203 | - |
|
| 204 | - /** |
|
| 205 | - * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 206 | - * <code>startingAtInclusive</code> is <code>true</code>) and ending at the <code>endingAt</code> time point or just before |
|
| 207 | - * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the raw fixes (see also |
|
| 208 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 209 | - * |
|
| 210 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 211 | - * will be thrown in case the caller has failed to do so. |
|
| 212 | - */ |
|
| 213 | - Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 214 | - |
|
| 215 | - /** |
|
| 216 | - * Returns a descending iterator starting at the first fix before <code>startingAt</code> (or "at or before" in case |
|
| 217 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the smoothened fixes (see |
|
| 218 | - * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 219 | - * |
|
| 220 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 221 | - * will be thrown in case the caller has failed to do so. |
|
| 222 | - */ |
|
| 223 | - Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 224 | - |
|
| 225 | - /** |
|
| 226 | - * Returns a descending iterator starting at the first raw fix before <code>startingAt</code> (or "at or before" in case |
|
| 227 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 228 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 229 | - * |
|
| 230 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 231 | - * will be thrown in case the caller has failed to do so. |
|
| 232 | - */ |
|
| 233 | - Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 234 | - |
|
| 235 | - /** |
|
| 236 | - * @return the average duration between two fixes (outliers removed) in this track or <code>null</code> if there is not |
|
| 237 | - * more than one fix in the track |
|
| 238 | - */ |
|
| 239 | - Duration getAverageIntervalBetweenFixes(); |
|
| 240 | - |
|
| 241 | - /** |
|
| 242 | - * @return the average duration between two fixes (outliers <em>not</em> removed) in this track or <code>null</code> if there is not |
|
| 243 | - * more than one raw fix in the track |
|
| 244 | - */ |
|
| 245 | - Duration getAverageIntervalBetweenRawFixes(); |
|
| 246 | - |
|
| 247 | - <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator); |
|
| 248 | - |
|
| 249 | - /** |
|
| 250 | - * @return the number of raw fixes contained in the Track. |
|
| 251 | - */ |
|
| 252 | - int size(); |
|
| 253 | - |
|
| 254 | - /** |
|
| 255 | - * Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty |
|
| 256 | - */ |
|
| 257 | - boolean isEmpty(); |
|
| 258 | - |
|
| 259 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/TrackingConnectorInfo.java
| ... | ... | @@ -1,28 +0,0 @@ |
| 1 | - |
|
| 2 | -package com.sap.sailing.domain.tracking; |
|
| 3 | - |
|
| 4 | -import java.io.Serializable; |
|
| 5 | - |
|
| 6 | -/** |
|
| 7 | - * Identifies the tracking connector that was used to create a TrackedRace. |
|
| 8 | - * Further the Connector can provide a webUrl, that leads to an event web page. |
|
| 9 | - */ |
|
| 10 | -public interface TrackingConnectorInfo extends Serializable { |
|
| 11 | - |
|
| 12 | - /** |
|
| 13 | - * gets the name associated with the tracking technology used for the Race |
|
| 14 | - */ |
|
| 15 | - String getTrackingConnectorName(); |
|
| 16 | - |
|
| 17 | - /** |
|
| 18 | - * gets a {@link String} representation of the default web-URL associated with the tracking technology used for the Race. |
|
| 19 | - * may be {@code null} if there is none provided in the adapter. |
|
| 20 | - */ |
|
| 21 | - String getTrackingConnectorDefaultUrl(); |
|
| 22 | - |
|
| 23 | - /** |
|
| 24 | - * gets a {@link String} representation of the web-URL associated with the Event. |
|
| 25 | - * may be {@code null} if the API of the respective Tracking-Service does not provide a URL. |
|
| 26 | - */ |
|
| 27 | - String getWebUrl(); |
|
| 28 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java
| ... | ... | @@ -1,91 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.base.Mark; |
|
| 4 | -import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | -import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | -import com.sap.sailing.domain.common.Position; |
|
| 7 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 8 | -import com.sap.sse.common.Bearing; |
|
| 9 | -import com.sap.sse.common.Distance; |
|
| 10 | -import com.sap.sse.common.TimePoint; |
|
| 11 | - |
|
| 12 | -public class LineDetailsImpl implements LineDetails { |
|
| 13 | - private final TimePoint timePoint; |
|
| 14 | - private final Waypoint waypoint; |
|
| 15 | - private final Distance length; |
|
| 16 | - private final Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 17 | - private final NauticalSide advantageousSidewhileApproachingLine; |
|
| 18 | - private final Distance advantage; |
|
| 19 | - private final Mark portMarkWhileApproachingLine; |
|
| 20 | - private final Mark starboardMarkWhileApproachingLine; |
|
| 21 | - private final Position portMarkPosition; |
|
| 22 | - private final Position starboardMarkPosition; |
|
| 23 | - |
|
| 24 | - public LineDetailsImpl(TimePoint timePoint, Waypoint waypoint, Distance length, |
|
| 25 | - Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind, NauticalSide advantageousSideWhileApproachingLine, |
|
| 26 | - Distance advantage, Mark portMarkWhileApproachingLine, Mark starboardMarkWhileApproachingLine, |
|
| 27 | - Position portMarkPosition, Position starboardMarkPosition) { |
|
| 28 | - super(); |
|
| 29 | - this.timePoint = timePoint; |
|
| 30 | - this.waypoint = waypoint; |
|
| 31 | - this.length = length; |
|
| 32 | - this.angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 33 | - this.advantageousSidewhileApproachingLine = advantageousSideWhileApproachingLine; |
|
| 34 | - this.advantage = advantage; |
|
| 35 | - this.portMarkWhileApproachingLine = portMarkWhileApproachingLine; |
|
| 36 | - this.starboardMarkWhileApproachingLine = starboardMarkWhileApproachingLine; |
|
| 37 | - this.portMarkPosition = portMarkPosition; |
|
| 38 | - this.starboardMarkPosition = starboardMarkPosition; |
|
| 39 | - } |
|
| 40 | - |
|
| 41 | - @Override |
|
| 42 | - public TimePoint getTimePoint() { |
|
| 43 | - return timePoint; |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - @Override |
|
| 47 | - public Waypoint getWaypoint() { |
|
| 48 | - return waypoint; |
|
| 49 | - } |
|
| 50 | - |
|
| 51 | - @Override |
|
| 52 | - public Distance getLength() { |
|
| 53 | - return length; |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - @Override |
|
| 57 | - public Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind() { |
|
| 58 | - return angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 59 | - } |
|
| 60 | - |
|
| 61 | - @Override |
|
| 62 | - public NauticalSide getAdvantageousSideWhileApproachingLine() { |
|
| 63 | - return advantageousSidewhileApproachingLine; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - @Override |
|
| 67 | - public Distance getAdvantage() { |
|
| 68 | - return advantage; |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - @Override |
|
| 72 | - public Mark getStarboardMarkWhileApproachingLine() { |
|
| 73 | - return starboardMarkWhileApproachingLine; |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public Mark getPortMarkWhileApproachingLine() { |
|
| 78 | - return portMarkWhileApproachingLine; |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - @Override |
|
| 82 | - public Position getPortMarkPosition() { |
|
| 83 | - return portMarkPosition; |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - @Override |
|
| 87 | - public Position getStarboardMarkPosition() { |
|
| 88 | - return starboardMarkPosition; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/MappedTrackImpl.java
| ... | ... | @@ -1,39 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.tracking.MappedTrack; |
|
| 4 | -import com.sap.sse.common.Timed; |
|
| 5 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 6 | - |
|
| 7 | -/** |
|
| 8 | - * Default implementation of {@link MappedTrack} interface. |
|
| 9 | - * |
|
| 10 | - * @param <ItemType> |
|
| 11 | - * the type of item this track is mapped to |
|
| 12 | - * @param <FixType> |
|
| 13 | - * the type of fix that is contained in this track |
|
| 14 | - */ |
|
| 15 | -public class MappedTrackImpl<ItemType, FixType extends Timed> extends TrackImpl<FixType> |
|
| 16 | - implements MappedTrack<ItemType, FixType> { |
|
| 17 | - |
|
| 18 | - private static final long serialVersionUID = 6165693342087329096L; |
|
| 19 | - |
|
| 20 | - private final ItemType trackedItem; |
|
| 21 | - |
|
| 22 | - /** @see TrackImpl#TrackImpl(String) */ |
|
| 23 | - public MappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) { |
|
| 24 | - super(nameForReadWriteLock); |
|
| 25 | - this.trackedItem = trackedItem; |
|
| 26 | - } |
|
| 27 | - |
|
| 28 | - /** @see TrackImpl#TrackImpl(ArrayListNavigableSet, String) */ |
|
| 29 | - protected MappedTrackImpl(ItemType trackedItem, ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 30 | - super(fixes, nameForReadWriteLock); |
|
| 31 | - this.trackedItem = trackedItem; |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - @Override |
|
| 35 | - public ItemType getTrackedItem() { |
|
| 36 | - return trackedItem; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/PartialNavigableSetView.java
| ... | ... | @@ -1,387 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.util.ArrayList; |
|
| 4 | -import java.util.Collection; |
|
| 5 | -import java.util.Comparator; |
|
| 6 | -import java.util.Iterator; |
|
| 7 | -import java.util.List; |
|
| 8 | -import java.util.NavigableSet; |
|
| 9 | -import java.util.NoSuchElementException; |
|
| 10 | -import java.util.SortedSet; |
|
| 11 | -import java.util.TreeSet; |
|
| 12 | - |
|
| 13 | -/** |
|
| 14 | - * A view on a {@link NavigableSet} which suppresses some entries based on some configurable rule. |
|
| 15 | - * The {@link #size()} operation is expensive because it requires a full scan. {@link #isEmpty()} is |
|
| 16 | - * much cheaper because it suffices to find one element passing the filter rule. The filtering rule |
|
| 17 | - * has to be expressed by subclsses implementing the {@link #isValid(Object)} method. |
|
| 18 | - * |
|
| 19 | - * @author Axel Uhl (d043530) |
|
| 20 | - * |
|
| 21 | - * @param <E> |
|
| 22 | - */ |
|
| 23 | -public abstract class PartialNavigableSetView<E> implements NavigableSet<E> { |
|
| 24 | - private final NavigableSet<E> set; |
|
| 25 | - |
|
| 26 | - private class PartialNavigableSetViewWithSameValidityAsEnclosing extends PartialNavigableSetView<E> { |
|
| 27 | - public PartialNavigableSetViewWithSameValidityAsEnclosing(NavigableSet<E> set) { |
|
| 28 | - super(set); |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - @Override |
|
| 32 | - protected boolean isValid(E e) { |
|
| 33 | - return PartialNavigableSetView.this.isValid(e); |
|
| 34 | - } |
|
| 35 | - } |
|
| 36 | - |
|
| 37 | - private class FilteringIterator implements Iterator<E> { |
|
| 38 | - /** |
|
| 39 | - * The iterator is always kept one step "ahead" in order to know whether there really is a next element. The |
|
| 40 | - * next valid element is fetched and stored in {@link #nextValid} and {@link #hasNext} is set to |
|
| 41 | - * <code>true</code>. |
|
| 42 | - */ |
|
| 43 | - private Iterator<E> iter; |
|
| 44 | - |
|
| 45 | - private E nextValid; |
|
| 46 | - |
|
| 47 | - private boolean hasNext; |
|
| 48 | - |
|
| 49 | - private boolean hasLastNext; |
|
| 50 | - |
|
| 51 | - private E lastNext; |
|
| 52 | - |
|
| 53 | - public FilteringIterator() { |
|
| 54 | - iter = getSet().iterator(); |
|
| 55 | - hasLastNext = false; |
|
| 56 | - advance(); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - private void advance() { |
|
| 60 | - if (iter.hasNext()) { |
|
| 61 | - E next = iter.next(); |
|
| 62 | - while (!isValid(next) && iter.hasNext()) { |
|
| 63 | - next = iter.next(); |
|
| 64 | - } |
|
| 65 | - if (isValid(next)) { |
|
| 66 | - nextValid = next; |
|
| 67 | - hasNext = true; |
|
| 68 | - } else { |
|
| 69 | - hasNext = false; |
|
| 70 | - } |
|
| 71 | - } else { |
|
| 72 | - hasNext = false; |
|
| 73 | - } |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public boolean hasNext() { |
|
| 78 | - return hasNext; |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - @Override |
|
| 82 | - public E next() { |
|
| 83 | - if (hasNext) { |
|
| 84 | - E result = nextValid; |
|
| 85 | - advance(); |
|
| 86 | - hasLastNext = true; |
|
| 87 | - lastNext = result; |
|
| 88 | - return result; |
|
| 89 | - } else { |
|
| 90 | - throw new NoSuchElementException(); |
|
| 91 | - } |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - @Override |
|
| 95 | - public void remove() { |
|
| 96 | - if (!hasLastNext) { |
|
| 97 | - throw new IllegalStateException("next() was not called before remove()"); |
|
| 98 | - } else { |
|
| 99 | - PartialNavigableSetView.this.remove(lastNext); |
|
| 100 | - hasLastNext = false; |
|
| 101 | - } |
|
| 102 | - } |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - public PartialNavigableSetView(NavigableSet<E> set) { |
|
| 106 | - this.set = set; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - /** |
|
| 110 | - * Subclasses need to implement this method. For elements to be eliminated from the view represented by this |
|
| 111 | - * object, return <code>false</code> for such an element being passed to this method. |
|
| 112 | - */ |
|
| 113 | - abstract protected boolean isValid(E e); |
|
| 114 | - |
|
| 115 | - @Override |
|
| 116 | - public Comparator<? super E> comparator() { |
|
| 117 | - return getSet().comparator(); |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - public NavigableSet<E> descendingSet() { |
|
| 121 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().descendingSet()); |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - @Override |
|
| 125 | - public Iterator<E> descendingIterator() { |
|
| 126 | - return descendingSet().iterator(); |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - @Override |
|
| 130 | - public E first() { |
|
| 131 | - E first = getSet().first(); |
|
| 132 | - while (first != null && !isValid(first)) { |
|
| 133 | - first = getSet().higher(first); |
|
| 134 | - } |
|
| 135 | - if (first == null) { |
|
| 136 | - throw new NoSuchElementException(); |
|
| 137 | - } else { |
|
| 138 | - return first; |
|
| 139 | - } |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - @Override |
|
| 143 | - public E last() { |
|
| 144 | - E last = getSet().last(); |
|
| 145 | - while (last != null && !isValid(last)) { |
|
| 146 | - last = getSet().lower(last); |
|
| 147 | - } |
|
| 148 | - if (last == null) { |
|
| 149 | - throw new NoSuchElementException(); |
|
| 150 | - } else { |
|
| 151 | - return last; |
|
| 152 | - } |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - @Override |
|
| 156 | - public int size() { |
|
| 157 | - int size = 0; |
|
| 158 | - for (E e : getSet()) { |
|
| 159 | - if (isValid(e)) { |
|
| 160 | - size++; |
|
| 161 | - } |
|
| 162 | - } |
|
| 163 | - return size; |
|
| 164 | - } |
|
| 165 | - |
|
| 166 | - @Override |
|
| 167 | - public boolean isEmpty() { |
|
| 168 | - for (E e : getSet()) { |
|
| 169 | - if (isValid(e)) { |
|
| 170 | - return false; |
|
| 171 | - } |
|
| 172 | - } |
|
| 173 | - return true; |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - @SuppressWarnings("unchecked") |
|
| 177 | - @Override |
|
| 178 | - public boolean contains(Object o) { |
|
| 179 | - return getSet().contains(o) && isValid((E) o); |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - @Override |
|
| 183 | - public Object[] toArray() { |
|
| 184 | - List<E> l = new ArrayList<E>(); |
|
| 185 | - for (E e : getSet()) { |
|
| 186 | - if (isValid(e)) { |
|
| 187 | - l.add(e); |
|
| 188 | - } |
|
| 189 | - } |
|
| 190 | - return l.toArray(); |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - @SuppressWarnings("unchecked") |
|
| 194 | - @Override |
|
| 195 | - public <T> T[] toArray(T[] a) { |
|
| 196 | - List<T> l = new ArrayList<T>(); |
|
| 197 | - for (E e : getSet()) { |
|
| 198 | - if (isValid(e)) { |
|
| 199 | - l.add((T) e); |
|
| 200 | - } |
|
| 201 | - } |
|
| 202 | - return l.toArray(a); |
|
| 203 | - |
|
| 204 | - } |
|
| 205 | - |
|
| 206 | - @Override |
|
| 207 | - public boolean add(E e) { |
|
| 208 | - return getSet().add(e); |
|
| 209 | - } |
|
| 210 | - |
|
| 211 | - @Override |
|
| 212 | - public boolean remove(Object o) { |
|
| 213 | - return getSet().remove(o); |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - @SuppressWarnings("unchecked") |
|
| 217 | - @Override |
|
| 218 | - public boolean containsAll(Collection<?> c) { |
|
| 219 | - for (Object o : c) { |
|
| 220 | - if (!isValid((E) o) || !getSet().contains(o)) { |
|
| 221 | - return false; |
|
| 222 | - } |
|
| 223 | - } |
|
| 224 | - return true; |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - @Override |
|
| 228 | - public boolean addAll(Collection<? extends E> c) { |
|
| 229 | - return getSet().addAll(c); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - @Override |
|
| 233 | - public boolean retainAll(Collection<?> c) { |
|
| 234 | - return getSet().retainAll(c); |
|
| 235 | - } |
|
| 236 | - |
|
| 237 | - @Override |
|
| 238 | - public boolean removeAll(Collection<?> c) { |
|
| 239 | - return getSet().removeAll(c); |
|
| 240 | - } |
|
| 241 | - |
|
| 242 | - @Override |
|
| 243 | - public void clear() { |
|
| 244 | - getSet().clear(); |
|
| 245 | - } |
|
| 246 | - |
|
| 247 | - @Override |
|
| 248 | - public E lower(E e) { |
|
| 249 | - E result = getSet().lower(e); |
|
| 250 | - while (result != null && !isValid(result)) { |
|
| 251 | - result = getSet().lower(result); |
|
| 252 | - } |
|
| 253 | - return result; |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - /** |
|
| 257 | - * goes one left on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 258 | - * <code>false</code> |
|
| 259 | - */ |
|
| 260 | - protected E lowerInternal(E e) { |
|
| 261 | - return getSet().lower(e); |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - /** |
|
| 265 | - * goes one right on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 266 | - * <code>false</code> |
|
| 267 | - */ |
|
| 268 | - protected E higherInternal(E e) { |
|
| 269 | - return getSet().higher(e); |
|
| 270 | - } |
|
| 271 | - |
|
| 272 | - @Override |
|
| 273 | - public E floor(E e) { |
|
| 274 | - E result = getSet().floor(e); |
|
| 275 | - while (result != null && !isValid(result)) { |
|
| 276 | - result = getSet().lower(result); |
|
| 277 | - } |
|
| 278 | - return result; |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - @Override |
|
| 282 | - public E ceiling(E e) { |
|
| 283 | - E result = getSet().ceiling(e); |
|
| 284 | - while (result != null && !isValid(result)) { |
|
| 285 | - result = getSet().higher(result); |
|
| 286 | - } |
|
| 287 | - return result; |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - @Override |
|
| 291 | - public E higher(E e) { |
|
| 292 | - E result = getSet().higher(e); |
|
| 293 | - while (result != null && !isValid(result)) { |
|
| 294 | - result = getSet().higher(result); |
|
| 295 | - } |
|
| 296 | - return result; |
|
| 297 | - } |
|
| 298 | - |
|
| 299 | - /** |
|
| 300 | - * Removes all raw fixes that have {@link #isValid(Object)}==<code>false</code> and the first element to have |
|
| 301 | - * {@link #isValid(Object)}==<code>true</code>. This latter element is returned. If no such element exists, |
|
| 302 | - * <code>null</code> is returned. It is hence possible that invalid raw fixes are removed but stil <code>null</code> |
|
| 303 | - * is returned. |
|
| 304 | - */ |
|
| 305 | - @Override |
|
| 306 | - public E pollFirst() { |
|
| 307 | - E result = getSet().first(); |
|
| 308 | - while (result != null && !isValid(result)) { |
|
| 309 | - getSet().remove(result); |
|
| 310 | - result = getSet().first(); |
|
| 311 | - } |
|
| 312 | - return result; |
|
| 313 | - } |
|
| 314 | - |
|
| 315 | - @Override |
|
| 316 | - public E pollLast() { |
|
| 317 | - E result = getSet().last(); |
|
| 318 | - while (result != null && !isValid(result)) { |
|
| 319 | - getSet().remove(result); |
|
| 320 | - result = getSet().last(); |
|
| 321 | - } |
|
| 322 | - return result; |
|
| 323 | - } |
|
| 324 | - |
|
| 325 | - @Override |
|
| 326 | - public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { |
|
| 327 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().subSet(fromElement, fromInclusive, toElement, toInclusive)); |
|
| 328 | - } |
|
| 329 | - |
|
| 330 | - @Override |
|
| 331 | - public NavigableSet<E> headSet(E toElement, boolean inclusive) { |
|
| 332 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().headSet(toElement, inclusive)); |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - @Override |
|
| 336 | - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { |
|
| 337 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().tailSet(fromElement, inclusive)); |
|
| 338 | - } |
|
| 339 | - |
|
| 340 | - @Override |
|
| 341 | - public NavigableSet<E> subSet(E fromElement, E toElement) { |
|
| 342 | - SortedSet<E> subSet = set.subSet(fromElement, toElement); |
|
| 343 | - if (subSet instanceof NavigableSet<?>) { |
|
| 344 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) subSet); |
|
| 345 | - } else { |
|
| 346 | - TreeSet<E> result = new TreeSet<E>(subSet); |
|
| 347 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 348 | - } |
|
| 349 | - } |
|
| 350 | - |
|
| 351 | - @Override |
|
| 352 | - public NavigableSet<E> headSet(E toElement) { |
|
| 353 | - SortedSet<E> headSet = set.headSet(toElement); |
|
| 354 | - if (headSet instanceof NavigableSet<?>) { |
|
| 355 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) headSet); |
|
| 356 | - } else { |
|
| 357 | - TreeSet<E> result = new TreeSet<E>(headSet); |
|
| 358 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 359 | - } |
|
| 360 | - } |
|
| 361 | - |
|
| 362 | - @Override |
|
| 363 | - public NavigableSet<E> tailSet(E fromElement) { |
|
| 364 | - SortedSet<E> tailSet = set.tailSet(fromElement); |
|
| 365 | - if (tailSet instanceof NavigableSet<?>) { |
|
| 366 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) tailSet); |
|
| 367 | - } else { |
|
| 368 | - TreeSet<E> result = new TreeSet<E>(tailSet); |
|
| 369 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 370 | - } |
|
| 371 | - } |
|
| 372 | - |
|
| 373 | - |
|
| 374 | - @Override |
|
| 375 | - public Iterator<E> iterator() { |
|
| 376 | - return new FilteringIterator(); |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - @Override |
|
| 380 | - public String toString() { |
|
| 381 | - return new ArrayList<E>(this).toString(); |
|
| 382 | - } |
|
| 383 | - |
|
| 384 | - protected NavigableSet<E> getSet() { |
|
| 385 | - return set; |
|
| 386 | - } |
|
| 387 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java
| ... | ... | @@ -1,221 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.util.Comparator; |
|
| 4 | -import java.util.Iterator; |
|
| 5 | -import java.util.LinkedHashMap; |
|
| 6 | -import java.util.Map.Entry; |
|
| 7 | -import java.util.NavigableSet; |
|
| 8 | - |
|
| 9 | -import com.sap.sse.common.TimePoint; |
|
| 10 | -import com.sap.sse.common.Util; |
|
| 11 | -import com.sap.sse.common.Util.Pair; |
|
| 12 | -import com.sap.sse.concurrent.LockUtil; |
|
| 13 | -import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 14 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 15 | - |
|
| 16 | -/** |
|
| 17 | - * This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used in |
|
| 18 | - * a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of |
|
| 19 | - * pairs where the first pair component represents a <code>from</code> parameter used in the calculation's time range |
|
| 20 | - * and the second pair component represents the result of the calculation for this parameter combination. |
|
| 21 | - * <p> |
|
| 22 | - * |
|
| 23 | - * For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order |
|
| 24 | - * to be able to efficiently extend a cache entry for a single <code>to</code> fix, the navigable sets containing the |
|
| 25 | - * <code>from</code> fixes and results are ordered such that earlier fixes come later in the set. This way, extending |
|
| 26 | - * the cache entry for a <code>to</code> fix to an earlier <code>from</code> fix only requires appending to the set. |
|
| 27 | - * <p> |
|
| 28 | - * |
|
| 29 | - * <b>Invalidation</b>: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's |
|
| 30 | - * time point are removed from this cache. Additionally, the fix insertion may have an impact on the |
|
| 31 | - * {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and |
|
| 32 | - * therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the |
|
| 33 | - * cache entries for the time points at or after the previous fix also need to be removed. |
|
| 34 | - * <p> |
|
| 35 | - * |
|
| 36 | - * <b>Cache use</b>: When a result across a time range is to be computed the calculating method should first look for a |
|
| 37 | - * cache entry for the <code>to</code> parameter. If one is found, the earliest entry in the navigable set for the |
|
| 38 | - * navigable set of <code>from</code> and result values that is at or after the requested <code>from</code> time point |
|
| 39 | - * is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the |
|
| 40 | - * <code>from</code> value found in the cache as the new <code>to</code> value, and the <code>from</code> value |
|
| 41 | - * originally passed to the calculating method as <code>from</code> again. If no entry is found in the cache entry for |
|
| 42 | - * <code>to</code> that is at or after the requested <code>from</code> time, the result has to be computed "from |
|
| 43 | - * scratch." |
|
| 44 | - * <p> |
|
| 45 | - * |
|
| 46 | - * If a cache entry for <code>to</code> is not found, the latest cache entry before it is looked up. If one is found, |
|
| 47 | - * the result for the time range between the <code>to</code> time point requested and the <code>to</code> time point |
|
| 48 | - * found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is |
|
| 49 | - * computed by iterating backwards all the way to <code>from</code>. |
|
| 50 | - * <p> |
|
| 51 | - * |
|
| 52 | - * Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the |
|
| 53 | - * result to the cache. |
|
| 54 | - * |
|
| 55 | - * @author Axel Uhl (D043530) |
|
| 56 | - */ |
|
| 57 | -public class TimeRangeCache<T> { |
|
| 58 | - public static final int MAX_SIZE = 100; |
|
| 59 | - |
|
| 60 | - private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> timeRangeCache; |
|
| 61 | - |
|
| 62 | - /** |
|
| 63 | - * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is |
|
| 64 | - * defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or |
|
| 65 | - * having been added by {@link #cache(TimePoint, TimePoint, Object)}. |
|
| 66 | - * <p> |
|
| 67 | - * |
|
| 68 | - * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements, |
|
| 69 | - * the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading |
|
| 70 | - * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash |
|
| 71 | - * map that counts access as "use" has a modifying effect on its internal structures. |
|
| 72 | - * <p> |
|
| 73 | - * |
|
| 74 | - * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the |
|
| 75 | - * alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.<p> |
|
| 76 | - * |
|
| 77 | - * Read access is to be <code>synchronized<code> using this field's mutex; write access only happens under the |
|
| 78 | - * {@link #lock write lock} and therefore will have no contenders. |
|
| 79 | - */ |
|
| 80 | - private final LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void> lruCache; |
|
| 81 | - |
|
| 82 | - private final NamedReentrantReadWriteLock lock; |
|
| 83 | - |
|
| 84 | - private static final Comparator<Util.Pair<TimePoint, ?>> timePointInPairComparator = new Comparator<Util.Pair<TimePoint, ?>>() { |
|
| 85 | - @Override |
|
| 86 | - public int compare(Util.Pair<TimePoint, ?> o1, Util.Pair<TimePoint, ?> o2) { |
|
| 87 | - return o1.getA().compareTo(o2.getA()); |
|
| 88 | - } |
|
| 89 | - }; |
|
| 90 | - |
|
| 91 | - public TimeRangeCache(String nameForLockLogging) { |
|
| 92 | - lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true); |
|
| 93 | - this.timeRangeCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>>(timePointInPairComparator); |
|
| 94 | - this.lruCache = new LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void>(/* initial capacity */ 10, /* load factor */ 0.75f, |
|
| 95 | - /* access-based ordering */ true) { |
|
| 96 | - private static final long serialVersionUID = -6568235517111733193L; |
|
| 97 | - |
|
| 98 | - @Override |
|
| 99 | - protected boolean removeEldestEntry(Entry<Pair<TimePoint, TimePoint>, Void> eldest) { |
|
| 100 | - final boolean expunge = size() > MAX_SIZE; |
|
| 101 | - if (expunge) { |
|
| 102 | - removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB()); |
|
| 103 | - } |
|
| 104 | - return expunge; |
|
| 105 | - } |
|
| 106 | - }; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - public int size() { |
|
| 110 | - return lruCache.size(); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - private void removeCacheEntry(TimePoint from, TimePoint to) { |
|
| 114 | - assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock |
|
| 115 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 116 | - if (entryForTo.getA().equals(to)) { |
|
| 117 | - Pair<TimePoint, T> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 118 | - if (entryForFrom.getA().equals(from)) { |
|
| 119 | - entryForTo.getB().remove(entryForFrom); |
|
| 120 | - if (entryForTo.getB().isEmpty()) { |
|
| 121 | - timeRangeCache.remove(entryForTo); |
|
| 122 | - } |
|
| 123 | - } |
|
| 124 | - } |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * Looks up the entry closest to but no later than <code>to</code>. If not found, <code>null</code> is returned. If |
|
| 129 | - * found, the earliest pair of from/result that is at or after <code>from</code> will be returned, together with |
|
| 130 | - * the <code>to</code> value of the entry. If there is no entry that is at or after <code>from</code>, |
|
| 131 | - * <code>null</code> is returned. |
|
| 132 | - */ |
|
| 133 | - public Util.Pair<TimePoint, Util.Pair<TimePoint, T>> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) { |
|
| 134 | - LockUtil.lockForRead(lock); |
|
| 135 | - try { |
|
| 136 | - Util.Pair<TimePoint, Util.Pair<TimePoint, T>> result = null; |
|
| 137 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 138 | - if (entryForTo != null) { |
|
| 139 | - final Util.Pair<TimePoint, T> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 140 | - if (fromCeiling != null) { |
|
| 141 | - result = new Util.Pair<TimePoint, Util.Pair<TimePoint, T>>(entryForTo.getA(), fromCeiling); |
|
| 142 | - } |
|
| 143 | - } |
|
| 144 | - // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using |
|
| 145 | - // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry |
|
| 146 | - // modifies the internal parts of the data structure which is not thread safe. |
|
| 147 | - synchronized (lruCache) { // ping the "perfect match" although it may not even have existed in the cache |
|
| 148 | - lruCache.get(new Util.Pair<TimePoint, TimePoint>(from, to)); |
|
| 149 | - } |
|
| 150 | - return result; |
|
| 151 | - } finally { |
|
| 152 | - LockUtil.unlockAfterRead(lock); |
|
| 153 | - } |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - /** |
|
| 157 | - * Removes all cache entries that have a <code>to</code> time point that is at or after <code>timePoint</code>. |
|
| 158 | - */ |
|
| 159 | - public void invalidateAllAtOrLaterThan(TimePoint timePoint) { |
|
| 160 | - LockUtil.lockForWrite(lock); |
|
| 161 | - try { |
|
| 162 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummy = createDummy(timePoint); |
|
| 163 | - NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true); |
|
| 164 | - for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> i=toRemove.iterator(); i.hasNext(); ) { |
|
| 165 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryToRemove = i.next(); |
|
| 166 | - assert entryToRemove.getA().compareTo(timePoint) >= 0; |
|
| 167 | - for (Pair<TimePoint, T> fromAndResult : entryToRemove.getB()) { |
|
| 168 | - lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA())); |
|
| 169 | - } |
|
| 170 | - i.remove(); |
|
| 171 | - } |
|
| 172 | - } finally { |
|
| 173 | - LockUtil.unlockAfterWrite(lock); |
|
| 174 | - } |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - private NavigableSet<Util.Pair<TimePoint, T>> getEntryForTo(TimePoint to) { |
|
| 178 | - NavigableSet<Util.Pair<TimePoint, T>> result = null; |
|
| 179 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummyForTo = createDummy(to); |
|
| 180 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(dummyForTo); |
|
| 181 | - if (entryForTo != null && entryForTo.getA().equals(to)) { |
|
| 182 | - result = entryForTo.getB(); |
|
| 183 | - } |
|
| 184 | - return result; |
|
| 185 | - } |
|
| 186 | - |
|
| 187 | - public void cache(TimePoint from, TimePoint to, T result) { |
|
| 188 | - LockUtil.lockForWrite(lock); |
|
| 189 | - try { |
|
| 190 | - NavigableSet<Util.Pair<TimePoint, T>> entryForTo = getEntryForTo(to); |
|
| 191 | - if (entryForTo == null) { |
|
| 192 | - entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, T>>(timePointInPairComparator); |
|
| 193 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>( |
|
| 194 | - to, entryForTo); |
|
| 195 | - timeRangeCache.add(pairForTo); |
|
| 196 | - } |
|
| 197 | - entryForTo.add(new Util.Pair<TimePoint, T>(from, result)); |
|
| 198 | - lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null); |
|
| 199 | - } finally { |
|
| 200 | - LockUtil.unlockAfterWrite(lock); |
|
| 201 | - } |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> createDummy(TimePoint to) { |
|
| 205 | - return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(to, null); |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - /** |
|
| 209 | - * Removes all contents from this cache |
|
| 210 | - */ |
|
| 211 | - public void clear() { |
|
| 212 | - LockUtil.lockForWrite(lock); |
|
| 213 | - try { |
|
| 214 | - timeRangeCache.clear(); |
|
| 215 | - lruCache.clear(); |
|
| 216 | - } finally { |
|
| 217 | - LockUtil.unlockAfterWrite(lock); |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - } |
|
| 221 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimedComparator.java
| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | -import java.util.Comparator; |
|
| 5 | - |
|
| 6 | -import com.sap.sse.common.Timed; |
|
| 7 | - |
|
| 8 | -public class TimedComparator implements Comparator<Timed>, Serializable { |
|
| 9 | - private static final long serialVersionUID = 1604511471599854988L; |
|
| 10 | - public static final Comparator<Timed> INSTANCE = new TimedComparator(); |
|
| 11 | - |
|
| 12 | - @Override |
|
| 13 | - public int compare(Timed o1, Timed o2) { |
|
| 14 | - return o1.getTimePoint().compareTo(o2.getTimePoint()); |
|
| 15 | - } |
|
| 16 | -} |
|
| 17 | - |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java
| ... | ... | @@ -1,544 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.io.IOException; |
|
| 4 | -import java.io.ObjectOutputStream; |
|
| 5 | -import java.util.ConcurrentModificationException; |
|
| 6 | -import java.util.Iterator; |
|
| 7 | -import java.util.NavigableSet; |
|
| 8 | -import java.util.function.Function; |
|
| 9 | - |
|
| 10 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 11 | -import com.sap.sailing.domain.tracking.FixAcceptancePredicate; |
|
| 12 | -import com.sap.sailing.domain.tracking.Track; |
|
| 13 | -import com.sap.sse.common.Duration; |
|
| 14 | -import com.sap.sse.common.TimePoint; |
|
| 15 | -import com.sap.sse.common.Timed; |
|
| 16 | -import com.sap.sse.common.Util; |
|
| 17 | -import com.sap.sse.common.Util.Pair; |
|
| 18 | -import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 19 | -import com.sap.sse.concurrent.LockUtil; |
|
| 20 | -import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 21 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 22 | -import com.sap.sse.shared.util.impl.UnmodifiableNavigableSet; |
|
| 23 | - |
|
| 24 | -public class TrackImpl<FixType extends Timed> implements Track<FixType> { |
|
| 25 | - private static final long serialVersionUID = -4075853657857657528L; |
|
| 26 | - /** |
|
| 27 | - * The fixes, ordered by their time points |
|
| 28 | - */ |
|
| 29 | - private final ArrayListNavigableSet<Timed> fixes; |
|
| 30 | - |
|
| 31 | - private final NamedReentrantReadWriteLock readWriteLock; |
|
| 32 | - |
|
| 33 | - protected static class DummyTimed implements Timed { |
|
| 34 | - private static final long serialVersionUID = 6047311973718918856L; |
|
| 35 | - private final TimePoint timePoint; |
|
| 36 | - public DummyTimed(TimePoint timePoint) { |
|
| 37 | - super(); |
|
| 38 | - this.timePoint = timePoint; |
|
| 39 | - } |
|
| 40 | - @Override |
|
| 41 | - public TimePoint getTimePoint() { |
|
| 42 | - return timePoint; |
|
| 43 | - } |
|
| 44 | - @Override |
|
| 45 | - public String toString() { |
|
| 46 | - return timePoint.toString(); |
|
| 47 | - } |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - public TrackImpl(String nameForReadWriteLock) { |
|
| 51 | - this(new ArrayListNavigableSet<Timed>(TimedComparator.INSTANCE), nameForReadWriteLock); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - protected TrackImpl(ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 55 | - this.readWriteLock = new NamedReentrantReadWriteLock(nameForReadWriteLock, /* fair */ false); |
|
| 56 | - this.fixes = fixes; |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * Synchronize the serialization such that no fixes are added while serializing |
|
| 61 | - */ |
|
| 62 | - private void writeObject(ObjectOutputStream s) throws IOException { |
|
| 63 | - lockForRead(); |
|
| 64 | - try { |
|
| 65 | - s.defaultWriteObject(); |
|
| 66 | - } finally { |
|
| 67 | - unlockAfterRead(); |
|
| 68 | - } |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - @Override |
|
| 72 | - public void lockForRead() { |
|
| 73 | - LockUtil.lockForRead(readWriteLock); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public void unlockAfterRead() { |
|
| 78 | - LockUtil.unlockAfterRead(readWriteLock); |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - protected void lockForWrite() { |
|
| 82 | - LockUtil.lockForWrite(readWriteLock); |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - protected void unlockAfterWrite() { |
|
| 86 | - LockUtil.unlockAfterWrite(readWriteLock); |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and {@link #unlockAfterRead()} |
|
| 91 | - * to avoid {@link ConcurrentModificationException}s. Should they modify the structure returned, they have to use |
|
| 92 | - * {@link #lockForWrite()} and {@link #unlockAfterWrite()}, respectively. |
|
| 93 | - */ |
|
| 94 | - protected NavigableSet<FixType> getInternalRawFixes() { |
|
| 95 | - @SuppressWarnings("unchecked") |
|
| 96 | - NavigableSet<FixType> result = (NavigableSet<FixType>) fixes; |
|
| 97 | - return result; |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * asserts that the calling thread holds at least one of read and write lock |
|
| 102 | - */ |
|
| 103 | - protected void assertReadLock() { |
|
| 104 | - if (readWriteLock.getReadHoldCount() < 1 && readWriteLock.getWriteHoldCount() < 1) { |
|
| 105 | - throw new IllegalStateException("Caller must obtain read lock using lockForRead() before calling this method"); |
|
| 106 | - } |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - protected void assertWriteLock() { |
|
| 110 | - if (readWriteLock.getWriteHoldCount() < 1) { |
|
| 111 | - throw new IllegalStateException("Caller must obtain write lock using lockForWrite() before calling this method"); |
|
| 112 | - } |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and |
|
| 117 | - * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s. |
|
| 118 | - * |
|
| 119 | - * @return the smoothened fixes ordered by their time points; this implementation simply delegates to |
|
| 120 | - * {@link #getInternalRawFixes()} because for only {@link Timed} fixes we can't know how to remove outliers. |
|
| 121 | - * Subclasses that constrain the <code>FixType</code> may provide smoothening implementations. |
|
| 122 | - */ |
|
| 123 | - protected NavigableSet<FixType> getInternalFixes() { |
|
| 124 | - NavigableSet<FixType> result = getInternalRawFixes(); |
|
| 125 | - return result; |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - /** |
|
| 129 | - * Iterates the fixes with outliers getting skipped, in the order of their time points. |
|
| 130 | - * Relies on {@link #getInternalFixes()} to void the track view from outliers. |
|
| 131 | - */ |
|
| 132 | - @Override |
|
| 133 | - public NavigableSet<FixType> getFixes() { |
|
| 134 | - assertReadLock(); |
|
| 135 | - return new UnmodifiableNavigableSet<FixType>(getInternalFixes()); |
|
| 136 | - } |
|
| 137 | - |
|
| 138 | - @Override |
|
| 139 | - public Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive) { |
|
| 140 | - return getFixes().subSet(getDummyFix(from), fromInclusive, getDummyFix(to), toInclusive); |
|
| 141 | - } |
|
| 142 | - |
|
| 143 | - /** |
|
| 144 | - * Iterates over the raw sequence of fixes, all potential outliers included |
|
| 145 | - */ |
|
| 146 | - @Override |
|
| 147 | - public NavigableSet<FixType> getRawFixes() { |
|
| 148 | - assertReadLock(); |
|
| 149 | - return new UnmodifiableNavigableSet<FixType>(getInternalRawFixes()); |
|
| 150 | - } |
|
| 151 | - |
|
| 152 | - @Override |
|
| 153 | - public FixType getLastFixAtOrBefore(TimePoint timePoint) { |
|
| 154 | - return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null); |
|
| 155 | - } |
|
| 156 | - |
|
| 157 | - private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 158 | - lockForRead(); |
|
| 159 | - try { |
|
| 160 | - final NavigableSet<FixType> headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 161 | - for (final Iterator<FixType> i=headSet.descendingIterator(); i.hasNext(); ) { |
|
| 162 | - final FixType next = i.next(); |
|
| 163 | - if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 164 | - return next; |
|
| 165 | - } |
|
| 166 | - } |
|
| 167 | - return null; |
|
| 168 | - } finally { |
|
| 169 | - unlockAfterRead(); |
|
| 170 | - } |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - @Override |
|
| 174 | - public FixType getLastFixBefore(TimePoint timePoint) { |
|
| 175 | - lockForRead(); |
|
| 176 | - try { |
|
| 177 | - return (FixType) getInternalFixes().lower(getDummyFix(timePoint)); |
|
| 178 | - } finally { |
|
| 179 | - unlockAfterRead(); |
|
| 180 | - } |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - @Override |
|
| 184 | - public FixType getLastRawFixAtOrBefore(TimePoint timePoint) { |
|
| 185 | - lockForRead(); |
|
| 186 | - try { |
|
| 187 | - return (FixType) getInternalRawFixes().floor(getDummyFix(timePoint)); |
|
| 188 | - } finally { |
|
| 189 | - unlockAfterRead(); |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - @Override |
|
| 194 | - public FixType getFirstRawFixAtOrAfter(TimePoint timePoint) { |
|
| 195 | - lockForRead(); |
|
| 196 | - try { |
|
| 197 | - return (FixType) getInternalRawFixes().ceiling(getDummyFix(timePoint)); |
|
| 198 | - } finally { |
|
| 199 | - unlockAfterRead(); |
|
| 200 | - } |
|
| 201 | - } |
|
| 202 | - |
|
| 203 | - @Override |
|
| 204 | - public FixType getFirstFixAtOrAfter(TimePoint timePoint) { |
|
| 205 | - return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null); |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 209 | - lockForRead(); |
|
| 210 | - try { |
|
| 211 | - final NavigableSet<FixType> tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 212 | - for (final FixType next : tailSet) { |
|
| 213 | - if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 214 | - return next; |
|
| 215 | - } |
|
| 216 | - } |
|
| 217 | - return null; |
|
| 218 | - } finally { |
|
| 219 | - unlockAfterRead(); |
|
| 220 | - } |
|
| 221 | - } |
|
| 222 | - |
|
| 223 | - @Override |
|
| 224 | - public FixType getLastRawFixBefore(TimePoint timePoint) { |
|
| 225 | - lockForRead(); |
|
| 226 | - try { |
|
| 227 | - return (FixType) getInternalRawFixes().lower(getDummyFix(timePoint)); |
|
| 228 | - } finally { |
|
| 229 | - unlockAfterRead(); |
|
| 230 | - } |
|
| 231 | - } |
|
| 232 | - |
|
| 233 | - @Override |
|
| 234 | - public FixType getFirstFixAfter(TimePoint timePoint) { |
|
| 235 | - lockForRead(); |
|
| 236 | - try { |
|
| 237 | - return (FixType) getInternalFixes().higher(getDummyFix(timePoint)); |
|
| 238 | - } finally { |
|
| 239 | - unlockAfterRead(); |
|
| 240 | - } |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - @Override |
|
| 244 | - public FixType getFirstRawFixAfter(TimePoint timePoint) { |
|
| 245 | - lockForRead(); |
|
| 246 | - try { |
|
| 247 | - return (FixType) getInternalRawFixes().higher(getDummyFix(timePoint)); |
|
| 248 | - } finally { |
|
| 249 | - unlockAfterRead(); |
|
| 250 | - } |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - @Override |
|
| 254 | - public FixType getFirstRawFix() { |
|
| 255 | - lockForRead(); |
|
| 256 | - try { |
|
| 257 | - if (getInternalFixes().isEmpty()) { |
|
| 258 | - return null; |
|
| 259 | - } else { |
|
| 260 | - return (FixType) getInternalFixes().first(); |
|
| 261 | - } |
|
| 262 | - } finally { |
|
| 263 | - unlockAfterRead(); |
|
| 264 | - } |
|
| 265 | - } |
|
| 266 | - |
|
| 267 | - @Override |
|
| 268 | - public FixType getLastRawFix() { |
|
| 269 | - lockForRead(); |
|
| 270 | - try { |
|
| 271 | - if (getInternalRawFixes().isEmpty()) { |
|
| 272 | - return null; |
|
| 273 | - } else { |
|
| 274 | - return (FixType) getInternalRawFixes().last(); |
|
| 275 | - } |
|
| 276 | - } finally { |
|
| 277 | - unlockAfterRead(); |
|
| 278 | - } |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - /** |
|
| 282 | - * @param fixAcceptancePredicate |
|
| 283 | - * if not {@code null}, adjacent fixes will be skipped as long as this predicate does not |
|
| 284 | - * {@link FixAcceptancePredicate#isAcceptFix(Object) accept} the fix. This can, e.g., be used to skip |
|
| 285 | - * fixes that don't have values in a dimension required. If {@code null}, the next fixes left and right |
|
| 286 | - * (including the exact {@code timePoint} if a fix exists there) will be used without further check. |
|
| 287 | - */ |
|
| 288 | - private Pair<FixType, FixType> getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 289 | - FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate); |
|
| 290 | - FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate); |
|
| 291 | - com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right); |
|
| 292 | - return result; |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) { |
|
| 296 | - final T acc; |
|
| 297 | - if (timePoint1.equals(timePoint2)) { |
|
| 298 | - acc = value1.add(value2).divide(2); |
|
| 299 | - } else { |
|
| 300 | - long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis()); |
|
| 301 | - long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis()); |
|
| 302 | - acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2); |
|
| 303 | - } |
|
| 304 | - return acc; |
|
| 305 | - } |
|
| 306 | - |
|
| 307 | - @Override |
|
| 308 | - public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 309 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter) { |
|
| 310 | - return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null); |
|
| 311 | - } |
|
| 312 | - |
|
| 313 | - protected <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 314 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 315 | - final ValueType result; |
|
| 316 | - Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate); |
|
| 317 | - if (fixPair.getA() == null) { |
|
| 318 | - if (fixPair.getB() == null) { |
|
| 319 | - result = null; |
|
| 320 | - } else { |
|
| 321 | - result = converter.apply(fixPair.getB()).divide(1); |
|
| 322 | - } |
|
| 323 | - } else { |
|
| 324 | - if (fixPair.getB() == null || fixPair.getA() == fixPair.getB()) { |
|
| 325 | - result = converter.apply(fixPair.getA()).divide(1); |
|
| 326 | - } else { |
|
| 327 | - result = timeBasedAverage(timePoint, |
|
| 328 | - converter.apply(fixPair.getA()), fixPair.getA().getTimePoint(), |
|
| 329 | - converter.apply(fixPair.getB()), fixPair.getB().getTimePoint()); |
|
| 330 | - } |
|
| 331 | - } |
|
| 332 | - return result; |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - @Override |
|
| 336 | - public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 337 | - assertReadLock(); |
|
| 338 | - return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 339 | - } |
|
| 340 | - |
|
| 341 | - @Override |
|
| 342 | - public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, |
|
| 343 | - boolean endingAtInclusive) { |
|
| 344 | - assertReadLock(); |
|
| 345 | - return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 346 | - } |
|
| 347 | - |
|
| 348 | - @Override |
|
| 349 | - public Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 350 | - assertReadLock(); |
|
| 351 | - Iterator<FixType> result = (Iterator<FixType>) getInternalFixes().headSet( |
|
| 352 | - getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 353 | - return result; |
|
| 354 | - } |
|
| 355 | - |
|
| 356 | - /** |
|
| 357 | - * Creates a dummy fix that conforms to <code>FixType</code>. This in particular means that subclasses |
|
| 358 | - * instantiating <code>FixType</code> with a specific class need to redefine this method so as to return |
|
| 359 | - * a dummy fix complying with their instantiation type used for <code>FixType</code>. Otherwise, a |
|
| 360 | - * {@link ClassCastException} may result upon certain operations performed with the fix returned by |
|
| 361 | - * this method. |
|
| 362 | - */ |
|
| 363 | - protected FixType getDummyFix(TimePoint timePoint) { |
|
| 364 | - @SuppressWarnings("unchecked") |
|
| 365 | - FixType result = (FixType) new DummyTimed(timePoint); |
|
| 366 | - return result; |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - @Override |
|
| 370 | - public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 371 | - assertReadLock(); |
|
| 372 | - return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 373 | - } |
|
| 374 | - |
|
| 375 | - private Iterator<FixType> getTimeConstrainedFixesIterator(NavigableSet<FixType> set, TimePoint startingAt, boolean startingAtInclusive, |
|
| 376 | - TimePoint endingAt, boolean endingAtInclusive) { |
|
| 377 | - assertReadLock(); |
|
| 378 | - if (startingAt != null && endingAt != null) { |
|
| 379 | - set = set.subSet(getDummyFix(startingAt), startingAtInclusive, getDummyFix(endingAt), endingAtInclusive); |
|
| 380 | - } else if (endingAt != null) { |
|
| 381 | - set = set.headSet(getDummyFix(endingAt), endingAtInclusive); |
|
| 382 | - } else if (startingAt != null) { |
|
| 383 | - set = set.tailSet(getDummyFix(startingAt), startingAtInclusive); |
|
| 384 | - } |
|
| 385 | - Iterator<FixType> result = set.iterator(); |
|
| 386 | - return result; |
|
| 387 | - } |
|
| 388 | - |
|
| 389 | - @Override |
|
| 390 | - public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, |
|
| 391 | - TimePoint endingAt, boolean endingAtInclusive) { |
|
| 392 | - assertReadLock(); |
|
| 393 | - return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 394 | - } |
|
| 395 | - |
|
| 396 | - @Override |
|
| 397 | - public Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 398 | - assertReadLock(); |
|
| 399 | - Iterator<FixType> result = (Iterator<FixType>) getInternalRawFixes().headSet( |
|
| 400 | - getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 401 | - return result; |
|
| 402 | - } |
|
| 403 | - |
|
| 404 | - protected boolean add(FixType fix) { |
|
| 405 | - return add(fix, /* replace */ false); |
|
| 406 | - } |
|
| 407 | - |
|
| 408 | - /** |
|
| 409 | - * @return {@code true} if the fix was added or replaced; {@code false} in case no change was performed |
|
| 410 | - */ |
|
| 411 | - protected boolean add(FixType fix, boolean replace) { |
|
| 412 | - lockForWrite(); |
|
| 413 | - try { |
|
| 414 | - final AddResult addResult = addWithoutLocking(fix, replace); |
|
| 415 | - return addResult == AddResult.ADDED || addResult == AddResult.REPLACED; |
|
| 416 | - } finally { |
|
| 417 | - unlockAfterWrite(); |
|
| 418 | - } |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - /** |
|
| 422 | - * The caller must ensure to hold the write lock for this track when calling this method. |
|
| 423 | - * |
|
| 424 | - * @param replace |
|
| 425 | - * whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the |
|
| 426 | - * comparator used for the {@link #fixes} set. By default this is a comparator only comparing the |
|
| 427 | - * fixes' time stamps. Subclasses may use different comparator implementations. |
|
| 428 | - */ |
|
| 429 | - protected AddResult addWithoutLocking(FixType fix, boolean replace) { |
|
| 430 | - final AddResult result; |
|
| 431 | - final boolean added = getInternalRawFixes().add(fix); |
|
| 432 | - if (!added && replace) { |
|
| 433 | - getInternalRawFixes().remove(fix); |
|
| 434 | - result = getInternalRawFixes().add(fix) ? AddResult.REPLACED : AddResult.NOT_ADDED; |
|
| 435 | - } else { |
|
| 436 | - result = added ? AddResult.ADDED : AddResult.NOT_ADDED; |
|
| 437 | - } |
|
| 438 | - return result; |
|
| 439 | - } |
|
| 440 | - |
|
| 441 | - @Override |
|
| 442 | - public Duration getAverageIntervalBetweenFixes() { |
|
| 443 | - lockForRead(); |
|
| 444 | - try { |
|
| 445 | - final Duration result; |
|
| 446 | - final int size = getRawFixes().size(); |
|
| 447 | - if (size > 1) { |
|
| 448 | - result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 449 | - } else { |
|
| 450 | - result = null; |
|
| 451 | - } |
|
| 452 | - return result; |
|
| 453 | - } finally { |
|
| 454 | - unlockAfterRead(); |
|
| 455 | - } |
|
| 456 | - } |
|
| 457 | - |
|
| 458 | - @Override |
|
| 459 | - public Duration getAverageIntervalBetweenRawFixes() { |
|
| 460 | - lockForRead(); |
|
| 461 | - try { |
|
| 462 | - final Duration result; |
|
| 463 | - final int size = getRawFixes().size(); |
|
| 464 | - if (size > 1) { |
|
| 465 | - result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 466 | - } else { |
|
| 467 | - result = null; |
|
| 468 | - } |
|
| 469 | - return result; |
|
| 470 | - } finally { |
|
| 471 | - unlockAfterRead(); |
|
| 472 | - } |
|
| 473 | - } |
|
| 474 | - |
|
| 475 | - @Override |
|
| 476 | - public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 477 | - return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator); |
|
| 478 | - } |
|
| 479 | - |
|
| 480 | - private <T> T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement, |
|
| 481 | - Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 482 | - T result; |
|
| 483 | - if (!from.before(to)) { |
|
| 484 | - result = nullElement; |
|
| 485 | - } else { |
|
| 486 | - boolean perfectCacheHit = false; |
|
| 487 | - lockForRead(); |
|
| 488 | - try { |
|
| 489 | - Util.Pair<TimePoint, Util.Pair<TimePoint, T>> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to); |
|
| 490 | - if (bestCacheEntry != null) { |
|
| 491 | - perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below |
|
| 492 | - // compute the missing stretches between best cache entry's "from" and our "from" and the cache |
|
| 493 | - // entry's "to" and our "to" |
|
| 494 | - T valueFromFromToBeginningOfCacheEntry = nullElement; |
|
| 495 | - T valueFromEndOfCacheEntryToTo = nullElement; |
|
| 496 | - if (!bestCacheEntry.getB().getA().equals(from)) { |
|
| 497 | - assert bestCacheEntry.getB().getA().after(from); |
|
| 498 | - perfectCacheHit = false; |
|
| 499 | - valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry |
|
| 500 | - .getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 501 | - } |
|
| 502 | - if (!bestCacheEntry.getA().equals(to)) { |
|
| 503 | - assert bestCacheEntry.getA().before(to); |
|
| 504 | - perfectCacheHit = false; |
|
| 505 | - valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to, |
|
| 506 | - recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 507 | - } |
|
| 508 | - if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) { |
|
| 509 | - result = null; |
|
| 510 | - } else { |
|
| 511 | - result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()), |
|
| 512 | - valueFromEndOfCacheEntryToTo); |
|
| 513 | - } |
|
| 514 | - } else { |
|
| 515 | - if (from.compareTo(to) < 0) { |
|
| 516 | - result = valueCalculator.calculate(from, to); |
|
| 517 | - } else { |
|
| 518 | - result = nullElement; |
|
| 519 | - } |
|
| 520 | - } |
|
| 521 | - // run the cache update while still holding the read lock; this avoids bug4629 where a cache invalidation |
|
| 522 | - // caused by fix insertions can come after the result calculation and before the cache update |
|
| 523 | - if (!perfectCacheHit && recursionDepth == 0) { |
|
| 524 | - cache.cache(from, to, result); |
|
| 525 | - } |
|
| 526 | - } finally { |
|
| 527 | - unlockAfterRead(); |
|
| 528 | - } |
|
| 529 | - } |
|
| 530 | - return result; |
|
| 531 | - } |
|
| 532 | - |
|
| 533 | - |
|
| 534 | - |
|
| 535 | - @Override |
|
| 536 | - public int size() { |
|
| 537 | - return fixes.size(); |
|
| 538 | - } |
|
| 539 | - |
|
| 540 | - @Override |
|
| 541 | - public boolean isEmpty() { |
|
| 542 | - return fixes.isEmpty(); |
|
| 543 | - } |
|
| 544 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackingConnectorInfoImpl.java
| ... | ... | @@ -1,66 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 4 | - |
|
| 5 | -public class TrackingConnectorInfoImpl implements TrackingConnectorInfo { |
|
| 6 | - private static final long serialVersionUID = 7970268841592389145L; |
|
| 7 | - private final String trackingConnectorName; |
|
| 8 | - private final String TtackingConnectorDefaultUrl; |
|
| 9 | - private final String webUrl; |
|
| 10 | - |
|
| 11 | - public TrackingConnectorInfoImpl(String trackingConnectorName, String trackingConnectorDefaultUrl, String webUrl) { |
|
| 12 | - super(); |
|
| 13 | - this.trackingConnectorName = trackingConnectorName; |
|
| 14 | - TtackingConnectorDefaultUrl = trackingConnectorDefaultUrl; |
|
| 15 | - this.webUrl = webUrl; |
|
| 16 | - } |
|
| 17 | - |
|
| 18 | - public String getTrackingConnectorDefaultUrl() { |
|
| 19 | - return TtackingConnectorDefaultUrl; |
|
| 20 | - } |
|
| 21 | - |
|
| 22 | - public String getTrackingConnectorName() { |
|
| 23 | - return trackingConnectorName; |
|
| 24 | - } |
|
| 25 | - |
|
| 26 | - public String getWebUrl() { |
|
| 27 | - return webUrl; |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - @Override |
|
| 31 | - public int hashCode() { |
|
| 32 | - final int prime = 31; |
|
| 33 | - int result = 1; |
|
| 34 | - result = prime * result + ((TtackingConnectorDefaultUrl == null) ? 0 : TtackingConnectorDefaultUrl.hashCode()); |
|
| 35 | - result = prime * result + ((trackingConnectorName == null) ? 0 : trackingConnectorName.hashCode()); |
|
| 36 | - result = prime * result + ((webUrl == null) ? 0 : webUrl.hashCode()); |
|
| 37 | - return result; |
|
| 38 | - } |
|
| 39 | - |
|
| 40 | - @Override |
|
| 41 | - public boolean equals(Object obj) { |
|
| 42 | - if (this == obj) |
|
| 43 | - return true; |
|
| 44 | - if (obj == null) |
|
| 45 | - return false; |
|
| 46 | - if (getClass() != obj.getClass()) |
|
| 47 | - return false; |
|
| 48 | - TrackingConnectorInfoImpl other = (TrackingConnectorInfoImpl) obj; |
|
| 49 | - if (TtackingConnectorDefaultUrl == null) { |
|
| 50 | - if (other.TtackingConnectorDefaultUrl != null) |
|
| 51 | - return false; |
|
| 52 | - } else if (!TtackingConnectorDefaultUrl.equals(other.TtackingConnectorDefaultUrl)) |
|
| 53 | - return false; |
|
| 54 | - if (trackingConnectorName == null) { |
|
| 55 | - if (other.trackingConnectorName != null) |
|
| 56 | - return false; |
|
| 57 | - } else if (!trackingConnectorName.equals(other.trackingConnectorName)) |
|
| 58 | - return false; |
|
| 59 | - if (webUrl == null) { |
|
| 60 | - if (other.webUrl != null) |
|
| 61 | - return false; |
|
| 62 | - } else if (!webUrl.equals(other.webUrl)) |
|
| 63 | - return false; |
|
| 64 | - return true; |
|
| 65 | - } |
|
| 66 | -} |
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java
| ... | ... | @@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 41 | 41 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 42 | 42 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 43 | 43 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 44 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 44 | 45 | import com.sap.sailing.domain.swisstimingadapter.Course; |
| 45 | 46 | import com.sap.sailing.domain.swisstimingadapter.DomainFactory; |
| 46 | 47 | import com.sap.sailing.domain.swisstimingadapter.Fix; |
| ... | ... | @@ -70,7 +71,6 @@ import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 70 | 71 | import com.sap.sailing.domain.tracking.WindStore; |
| 71 | 72 | import com.sap.sailing.domain.tracking.WindTrack; |
| 72 | 73 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 73 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 74 | 74 | import com.sap.sailing.domain.tracking.impl.UpdateHandler; |
| 75 | 75 | import com.sap.sse.common.Distance; |
| 76 | 76 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java
| ... | ... | @@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 41 | 41 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 42 | 42 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 43 | 43 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 44 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 44 | 45 | import com.sap.sailing.domain.swisstimingadapter.DomainFactory; |
| 45 | 46 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; |
| 46 | 47 | import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; |
| ... | ... | @@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindTrack; |
| 60 | 61 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 61 | 62 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 62 | 63 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 63 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 64 | 64 | import com.sap.sse.common.Bearing; |
| 65 | 65 | import com.sap.sse.common.TimePoint; |
| 66 | 66 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/AbstractCandidateFilterTestSupport.java
| ... | ... | @@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach; |
| 12 | 12 | |
| 13 | 13 | import com.sap.sailing.domain.base.Waypoint; |
| 14 | 14 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 15 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 16 | 16 | import com.sap.sse.common.Duration; |
| 17 | 17 | import com.sap.sse.common.TimePoint; |
| 18 | 18 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java
| ... | ... | @@ -25,11 +25,11 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 25 | 25 | import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl; |
| 26 | 26 | import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 28 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 28 | 29 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 29 | 30 | import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; |
| 30 | 31 | import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl; |
| 31 | 32 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 32 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 33 | 33 | import com.sap.sse.common.Distance; |
| 34 | 34 | import com.sap.sse.common.TimePoint; |
| 35 | 35 | import com.sap.sse.common.impl.DegreeBearingImpl; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java
| ... | ... | @@ -15,7 +15,7 @@ import com.sap.sailing.domain.base.Boat; |
| 15 | 15 | import com.sap.sailing.domain.base.Competitor; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 17 | 17 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 18 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 18 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 19 | 19 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 20 | 20 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 21 | 21 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java
| ... | ... | @@ -74,12 +74,12 @@ import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; |
| 74 | 74 | import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; |
| 75 | 75 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 76 | 76 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 77 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 77 | 78 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; |
| 78 | 79 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 79 | 80 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 80 | 81 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 81 | 82 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 82 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 83 | 83 | import com.sap.sse.common.Duration; |
| 84 | 84 | import com.sap.sse.common.TimePoint; |
| 85 | 85 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java
| ... | ... | @@ -32,12 +32,12 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme; |
| 32 | 32 | import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; |
| 33 | 33 | import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; |
| 34 | 34 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 35 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 35 | 36 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; |
| 36 | 37 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks; |
| 37 | 38 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 38 | 39 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 39 | 40 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 40 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 41 | 41 | import com.sap.sse.common.Duration; |
| 42 | 42 | import com.sap.sse.common.TimePoint; |
| 43 | 43 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java
| ... | ... | @@ -14,9 +14,9 @@ import com.sap.sailing.domain.common.impl.MeterDistance; |
| 14 | 14 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 15 | 15 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 17 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 17 | 18 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 18 | 19 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 19 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 20 | 20 | import com.sap.sse.common.Bearing; |
| 21 | 21 | import com.sap.sse.common.Distance; |
| 22 | 22 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PartialNavigableSetViewTest.java
| ... | ... | @@ -12,7 +12,7 @@ import java.util.TreeSet; |
| 12 | 12 | import org.junit.jupiter.api.BeforeEach; |
| 13 | 13 | import org.junit.jupiter.api.Test; |
| 14 | 14 | |
| 15 | -import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 16 | 16 | |
| 17 | 17 | public class PartialNavigableSetViewTest { |
| 18 | 18 | private PartialNavigableSetView<Integer> fullSet; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java
| ... | ... | @@ -20,7 +20,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 20 | 20 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 21 | 21 | import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; |
| 22 | 22 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 23 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 23 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 24 | 24 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 25 | 25 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 26 | 26 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java
| ... | ... | @@ -26,8 +26,8 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 26 | 26 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 27 | 27 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 28 | 28 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 29 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 29 | 30 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 30 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 31 | 31 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| 32 | 32 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; |
| 33 | 33 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
| ... | ... | @@ -44,14 +44,14 @@ import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleExceptio |
| 44 | 44 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 45 | 45 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 46 | 46 | import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl; |
| 47 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 48 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 47 | 49 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 48 | 50 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 49 | 51 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 50 | 52 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; |
| 51 | 53 | import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl; |
| 52 | 54 | import com.sap.sailing.domain.tracking.impl.MaxSpeedCache; |
| 53 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 54 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 55 | 55 | import com.sap.sse.common.AbstractBearing; |
| 56 | 56 | import com.sap.sse.common.Bearing; |
| 57 | 57 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
| ... | ... | @@ -64,6 +64,8 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 64 | 64 | import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 65 | 65 | import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; |
| 66 | 66 | import com.sap.sailing.domain.regattalike.RegattaLikeListener; |
| 67 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 68 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 67 | 69 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 68 | 70 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 69 | 71 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| ... | ... | @@ -71,7 +73,6 @@ import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 71 | 73 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 72 | 74 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 73 | 75 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 74 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 75 | 76 | import com.sap.sailing.domain.tracking.Maneuver; |
| 76 | 77 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 77 | 78 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -86,7 +87,6 @@ import com.sap.sailing.domain.tracking.TrackedLeg; |
| 86 | 87 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 87 | 88 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 88 | 89 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 89 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 90 | 90 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 91 | 91 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 92 | 92 | import com.sap.sailing.domain.tracking.WindPositionMode; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java
| ... | ... | @@ -53,9 +53,10 @@ import com.sap.sailing.domain.leaderboard.impl.RankAndRankComparable; |
| 53 | 53 | import com.sap.sailing.domain.polars.PolarDataService; |
| 54 | 54 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 55 | 55 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 56 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 57 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 56 | 58 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 57 | 59 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 58 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 59 | 60 | import com.sap.sailing.domain.tracking.Maneuver; |
| 60 | 61 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 61 | 62 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -69,7 +70,6 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 69 | 70 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 70 | 71 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 71 | 72 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 72 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 73 | 73 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 74 | 74 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 75 | 75 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
| ... | ... | @@ -69,6 +69,8 @@ import com.sap.sailing.domain.racelog.RaceLogStore; |
| 69 | 69 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 70 | 70 | import com.sap.sailing.domain.ranking.RankingMetricsFactory; |
| 71 | 71 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 72 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 73 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 72 | 74 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 73 | 75 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 74 | 76 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| ... | ... | @@ -78,13 +80,11 @@ import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; |
| 78 | 80 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 79 | 81 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 80 | 82 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 81 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 82 | 83 | import com.sap.sailing.domain.tracking.WindStore; |
| 83 | 84 | import com.sap.sailing.domain.tracking.WindTrack; |
| 84 | 85 | import com.sap.sailing.domain.tracking.impl.FinishTimeUpdateHandler; |
| 85 | 86 | import com.sap.sailing.domain.tracking.impl.RaceAbortedHandler; |
| 86 | 87 | import com.sap.sailing.domain.tracking.impl.StartTimeUpdateHandler; |
| 87 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 88 | 88 | import com.sap.sailing.domain.tractracadapter.DomainFactory; |
| 89 | 89 | import com.sap.sailing.domain.tractracadapter.JSONService; |
| 90 | 90 | import com.sap.sailing.domain.tractracadapter.MetadataParser; |
java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java
| ... | ... | @@ -23,13 +23,13 @@ import com.sap.sailing.domain.common.PassingInstruction; |
| 23 | 23 | import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 24 | 24 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 25 | 25 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 27 | 28 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 28 | 29 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 29 | 30 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 30 | 31 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 31 | 32 | import com.sap.sailing.domain.tracking.WindStore; |
| 32 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 33 | 33 | import com.sap.sailing.domain.tractracadapter.DomainFactory; |
| 34 | 34 | import com.sap.sailing.domain.tractracadapter.TracTracAdapter; |
| 35 | 35 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.windfinderadapter/META-INF/MANIFEST.MF
| ... | ... | @@ -7,7 +7,8 @@ Bundle-Version: 1.0.0.qualifier |
| 7 | 7 | Bundle-Activator: com.sap.sailing.domain.windfinderadapter.impl.Activator |
| 8 | 8 | Bundle-Vendor: SAP |
| 9 | 9 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 10 | -Import-Package: com.sap.sse.security, |
|
| 10 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 11 | + com.sap.sse.security, |
|
| 11 | 12 | org.osgi.framework;version="1.3.0", |
| 12 | 13 | org.osgi.util.tracker;version="1.5.1" |
| 13 | 14 | Bundle-ActivationPolicy: lazy |
java/com.sap.sailing.domain.yellowbrickadapter/src/com/sap/sailing/domain/yellowbrickadapter/impl/YellowBrickRaceTrackerImpl.java
| ... | ... | @@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 47 | 47 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 48 | 48 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 49 | 49 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 50 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 50 | 51 | import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl; |
| 51 | 52 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 52 | 53 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| ... | ... | @@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindStore; |
| 60 | 61 | import com.sap.sailing.domain.tracking.WindTrack; |
| 61 | 62 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 62 | 63 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 63 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 64 | 64 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRace; |
| 65 | 65 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRaceTrackingConnectivityParams; |
| 66 | 66 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickTrackingAdapter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java
| ... | ... | @@ -24,8 +24,8 @@ import com.sap.sailing.domain.common.WindSourceType; |
| 24 | 24 | import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition; |
| 25 | 25 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 26 | 26 | import com.sap.sailing.domain.leaderboard.LeaderboardGroup; |
| 27 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 27 | 28 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 28 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 29 | 29 | import com.sap.sailing.geocoding.ReverseGeocoder; |
| 30 | 30 | import com.sap.sse.common.TimePoint; |
| 31 | 31 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java
| ... | ... | @@ -10,10 +10,10 @@ import java.util.NavigableSet; |
| 10 | 10 | import com.sap.sailing.domain.base.Competitor; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 12 | 12 | import com.sap.sailing.domain.maneuverdetection.IncrementalApproximatedFixesCalculator; |
| 13 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 13 | 14 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 14 | 15 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 15 | 16 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 16 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 17 | 17 | import com.sap.sse.common.Duration; |
| 18 | 18 | import com.sap.sse.common.TimePoint; |
| 19 | 19 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java
| ... | ... | @@ -30,9 +30,9 @@ import com.sap.sailing.domain.markpassingcalculation.impl.CandidateFinderImpl; |
| 30 | 30 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint; |
| 31 | 31 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory; |
| 32 | 32 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 33 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 33 | 34 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 34 | 35 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 35 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 36 | 36 | import com.sap.sse.common.TimePoint; |
| 37 | 37 | import com.sap.sse.common.Util; |
| 38 | 38 | import com.sap.sse.common.Util.Pair; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java
| ... | ... | @@ -10,7 +10,7 @@ import com.sap.sailing.domain.base.Mark; |
| 10 | 10 | import com.sap.sailing.domain.base.Waypoint; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 14 | 14 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 15 | 15 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 16 | 16 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java
| ... | ... | @@ -29,13 +29,13 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 29 | 29 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 30 | 30 | import com.sap.sailing.domain.markpassingcalculation.CandidateChooser; |
| 31 | 31 | import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
| 32 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 32 | 33 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 33 | 34 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 34 | 35 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 35 | 36 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 36 | 37 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 37 | 38 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 38 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 39 | 39 | import com.sap.sse.common.Distance; |
| 40 | 40 | import com.sap.sse.common.Duration; |
| 41 | 41 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImpl.java
| ... | ... | @@ -35,13 +35,13 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 35 | 35 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 36 | 36 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 37 | 37 | import com.sap.sailing.domain.markpassingcalculation.CandidateFinder; |
| 38 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 38 | 39 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 39 | 40 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 40 | 41 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 41 | 42 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| 42 | 43 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 43 | 44 | import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl; |
| 44 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 45 | 45 | import com.sap.sse.common.Bearing; |
| 46 | 46 | import com.sap.sse.common.Distance; |
| 47 | 47 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java
| ... | ... | @@ -21,7 +21,7 @@ import com.sap.sailing.domain.base.Mark; |
| 21 | 21 | import com.sap.sailing.domain.base.Waypoint; |
| 22 | 22 | import com.sap.sailing.domain.common.Position; |
| 23 | 23 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 24 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 24 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 25 | 25 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 26 | 26 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 27 | 27 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DummyTrackedRace.java
| ... | ... | @@ -43,6 +43,8 @@ import com.sap.sailing.domain.leaderboard.impl.RankAndRankComparable; |
| 43 | 43 | import com.sap.sailing.domain.polars.PolarDataService; |
| 44 | 44 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 45 | 45 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 46 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 47 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 46 | 48 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 47 | 49 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 48 | 50 | import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicMappedTrack.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 3 | 4 | import com.sap.sse.common.Timed; |
| 4 | 5 | |
| 5 | 6 | /** |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrack.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 3 | 4 | import com.sap.sse.common.Timed; |
| 4 | 5 | |
| 5 | 6 | public interface DynamicTrack<FixType extends Timed> extends Track<FixType> { |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java
| ... | ... | @@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.confidence.Weigher; |
| 10 | 10 | import com.sap.sailing.domain.common.impl.KnotSpeedImpl; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 13 | 14 | import com.sap.sse.common.Distance; |
| 14 | 15 | import com.sap.sse.common.Duration; |
| 15 | 16 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSTrackListener.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 4 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 4 | 5 | |
| 5 | 6 | public interface GPSTrackListener<ItemType, FixType extends GPSFix> extends TrackListener { |
| 6 | 7 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java
| ... | ... | @@ -17,6 +17,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 17 | 17 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 18 | 18 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 20 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 20 | 21 | import com.sap.sse.common.TimePoint; |
| 21 | 22 | |
| 22 | 23 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingHandler.java
| ... | ... | @@ -20,6 +20,7 @@ import com.sap.sailing.domain.base.impl.DynamicTeam; |
| 20 | 20 | import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; |
| 21 | 21 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 22 | 22 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 23 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 23 | 24 | import com.sap.sse.common.Color; |
| 24 | 25 | import com.sap.sse.common.Duration; |
| 25 | 26 | import com.sap.sse.util.ThreadLocalTransporter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/SensorFixTrack.java
| ... | ... | @@ -3,6 +3,8 @@ package com.sap.sailing.domain.tracking; |
| 3 | 3 | import java.io.Serializable; |
| 4 | 4 | |
| 5 | 5 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 6 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 6 | 8 | import com.sap.sse.common.WithID; |
| 7 | 9 | |
| 8 | 10 | /** |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/SensorFixTrackListener.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 4 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | 7 | * Listener to observe a {@link com.sap.sailing.domain.tracking.SensorFixTrack} to get informed whenever a new fix is |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
| ... | ... | @@ -69,6 +69,8 @@ import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 69 | 69 | import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 70 | 70 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 71 | 71 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 72 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 73 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 72 | 74 | import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; |
| 73 | 75 | import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; |
| 74 | 76 | import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java
| ... | ... | @@ -12,6 +12,7 @@ import com.sap.sailing.domain.base.impl.TrackedRaces; |
| 12 | 12 | import com.sap.sailing.domain.common.NoWindException; |
| 13 | 13 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 14 | 14 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 15 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 15 | 16 | import com.sap.sse.common.TimePoint; |
| 16 | 17 | import com.sap.sse.metering.HasCPUMeter; |
| 17 | 18 | import com.sap.sse.util.ThreadLocalTransporter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java
| ... | ... | @@ -12,7 +12,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 13 | 13 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 14 | 14 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 15 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 16 | 16 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 17 | 17 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 18 | 18 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java
| ... | ... | @@ -10,12 +10,13 @@ import com.sap.sailing.domain.common.scalablevalue.impl.ScalableDistance; |
| 10 | 10 | import com.sap.sailing.domain.common.tracking.BravoExtendedFix; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.BravoFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 14 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 14 | 16 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 15 | 17 | import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; |
| 16 | 18 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 17 | 19 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 18 | -import com.sap.sailing.domain.tracking.Track; |
|
| 19 | 20 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 20 | 21 | import com.sap.sse.common.Bearing; |
| 21 | 22 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java
| ... | ... | @@ -17,7 +17,8 @@ import com.sap.sailing.domain.base.Competitor; |
| 17 | 17 | import com.sap.sailing.domain.common.SpeedWithBearing; |
| 18 | 18 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 20 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 20 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 21 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 21 | 22 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 22 | 23 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 23 | 24 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java
| ... | ... | @@ -19,11 +19,12 @@ import com.sap.sailing.domain.common.NoWindException; |
| 19 | 19 | import com.sap.sailing.domain.common.impl.MeterDistance; |
| 20 | 20 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 21 | 21 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 22 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 22 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 23 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 24 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 23 | 25 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 24 | 26 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 25 | 27 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 26 | -import com.sap.sailing.domain.tracking.Track; |
|
| 27 | 28 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 28 | 29 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 29 | 30 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicMappedTrackImpl.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking.impl; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.impl.MappedTrackImpl; |
|
| 3 | 4 | import com.sap.sailing.domain.tracking.DynamicMappedTrack; |
| 4 | 5 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 5 | 6 | import com.sap.sse.common.Timed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackImpl.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking.impl; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 3 | 4 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 4 | 5 | import com.sap.sse.common.Timed; |
| 5 | 6 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java
| ... | ... | @@ -48,7 +48,8 @@ import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
| 48 | 48 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 49 | 49 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 50 | 50 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 51 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 51 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 52 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 52 | 53 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 53 | 54 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 54 | 55 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| ... | ... | @@ -65,7 +66,6 @@ import com.sap.sailing.domain.tracking.TrackFactory; |
| 65 | 66 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 66 | 67 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 67 | 68 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 68 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 69 | 69 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 70 | 70 | import com.sap.sailing.domain.tracking.WindStore; |
| 71 | 71 | import com.sap.sailing.domain.tracking.WindTrack; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
| ... | ... | @@ -33,12 +33,15 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 33 | 33 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 34 | 34 | import com.sap.sailing.domain.common.tracking.WithValidityCache; |
| 35 | 35 | import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper; |
| 36 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 36 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 37 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 38 | +import com.sap.sailing.domain.shared.tracking.impl.MappedTrackImpl; |
|
| 39 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 40 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 37 | 41 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 38 | 42 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 39 | 43 | import com.sap.sailing.domain.tracking.SpeedWithBearingStep; |
| 40 | 44 | import com.sap.sailing.domain.tracking.SpeedWithBearingStepsIterable; |
| 41 | -import com.sap.sailing.domain.tracking.Track; |
|
| 42 | 45 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 43 | 46 | import com.sap.sse.common.Bearing; |
| 44 | 47 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/MaxSpeedCache.java
| ... | ... | @@ -9,7 +9,7 @@ import java.util.NavigableSet; |
| 9 | 9 | |
| 10 | 10 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 12 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 13 | 13 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 14 | 14 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 15 | 15 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/SensorFixTrackImpl.java
| ... | ... | @@ -4,7 +4,7 @@ import java.io.Serializable; |
| 4 | 4 | import java.util.function.Consumer; |
| 5 | 5 | |
| 6 | 6 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 7 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 8 | 8 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 9 | 9 | import com.sap.sailing.domain.tracking.SensorFixTrack; |
| 10 | 10 | import com.sap.sailing.domain.tracking.SensorFixTrackListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java
| ... | ... | @@ -27,7 +27,8 @@ import com.sap.sailing.domain.common.impl.WindImpl; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 28 | 28 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 29 | 29 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 30 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 30 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 31 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 31 | 32 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 32 | 33 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 33 | 34 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
| ... | ... | @@ -141,13 +141,17 @@ import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 141 | 141 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 142 | 142 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 143 | 143 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 144 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 145 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 146 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 147 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 148 | +import com.sap.sailing.domain.shared.tracking.impl.LineDetailsImpl; |
|
| 149 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 144 | 150 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 145 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 146 | 151 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 147 | 152 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 148 | 153 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 149 | 154 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 150 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 151 | 155 | import com.sap.sailing.domain.tracking.Maneuver; |
| 152 | 156 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 153 | 157 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -155,7 +159,6 @@ import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 155 | 159 | import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; |
| 156 | 160 | import com.sap.sailing.domain.tracking.RaceListener; |
| 157 | 161 | import com.sap.sailing.domain.tracking.SensorFixTrack; |
| 158 | -import com.sap.sailing.domain.tracking.Track; |
|
| 159 | 162 | import com.sap.sailing.domain.tracking.TrackFactory; |
| 160 | 163 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 161 | 164 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| ... | ... | @@ -163,7 +166,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 163 | 166 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 164 | 167 | import com.sap.sailing.domain.tracking.TrackedRaceWithWindEssentials; |
| 165 | 168 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 166 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 167 | 169 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 168 | 170 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 169 | 171 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java
| ... | ... | @@ -24,12 +24,12 @@ import com.sap.sailing.domain.base.Sideline; |
| 24 | 24 | import com.sap.sailing.domain.common.NoWindException; |
| 25 | 25 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 26 | 26 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 27 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 27 | 28 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 28 | 29 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 29 | 30 | import com.sap.sailing.domain.tracking.RaceListener; |
| 30 | 31 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 31 | 32 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 32 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 33 | 33 | import com.sap.sailing.domain.tracking.WindStore; |
| 34 | 34 | import com.sap.sse.common.TimePoint; |
| 35 | 35 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/VirtualWindTrackImpl.java
| ... | ... | @@ -4,6 +4,7 @@ import java.util.NavigableSet; |
| 4 | 4 | |
| 5 | 5 | import com.sap.sailing.domain.common.Position; |
| 6 | 6 | import com.sap.sailing.domain.common.Wind; |
| 7 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 7 | 8 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 8 | 9 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
| 9 | 10 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/WindTrackImpl.java
| ... | ... | @@ -23,6 +23,7 @@ import com.sap.sailing.domain.common.tracking.impl.PreciseCompactWindImpl; |
| 23 | 23 | import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl; |
| 24 | 24 | import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager; |
| 25 | 25 | import com.sap.sailing.domain.confidence.ConfidenceFactory; |
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.WindListener; |
| 27 | 28 | import com.sap.sailing.domain.tracking.WindTrack; |
| 28 | 29 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
| ... | ... | @@ -46,6 +46,7 @@ public class BoatClassImageResolver { |
| 46 | 46 | boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46Icon()); |
| 47 | 47 | boatClassIconsMap.put(BoatClassMasterdata.BB10M.getDisplayName(), imageResources.BB10MIcon()); |
| 48 | 48 | boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_35.getDisplayName(), imageResources.BeneteauFirst35Icon()); |
| 49 | + boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_36.getDisplayName(), imageResources.BeneteauFirst36Icon()); |
|
| 49 | 50 | boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_45.getDisplayName(), imageResources.BeneteauFirst45Icon()); |
| 50 | 51 | boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_I.getDisplayName(), imageResources.Brassfahrt1Icon()); |
| 51 | 52 | boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_II.getDisplayName(), imageResources.Brassfahrt2Icon()); |
| ... | ... | @@ -63,6 +64,7 @@ public class BoatClassImageResolver { |
| 63 | 64 | boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon()); |
| 64 | 65 | boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon()); |
| 65 | 66 | boatClassIconsMap.put(BoatClassMasterdata.ELAN350.getDisplayName(), imageResources.Elan350Icon()); |
| 67 | + boatClassIconsMap.put(BoatClassMasterdata.ELAN_E4.getDisplayName(), imageResources.ElanE4Icon()); |
|
| 66 | 68 | boatClassIconsMap.put(BoatClassMasterdata.ELLIOTT_6M.getDisplayName(), imageResources.Elliott6mIcon()); |
| 67 | 69 | boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon()); |
| 68 | 70 | boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
| ... | ... | @@ -96,6 +96,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 96 | 96 | @ImageOptions(preventInlining = true) |
| 97 | 97 | ImageResource BeneteauFirst35Icon(); |
| 98 | 98 | |
| 99 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png") |
|
| 100 | + @ImageOptions(preventInlining = true) |
|
| 101 | + ImageResource BeneteauFirst36Icon(); |
|
| 102 | + |
|
| 99 | 103 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png") |
| 100 | 104 | @ImageOptions(preventInlining = true) |
| 101 | 105 | ImageResource BeneteauFirst45Icon(); |
| ... | ... | @@ -152,6 +156,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 152 | 156 | @ImageOptions(preventInlining = true) |
| 153 | 157 | ImageResource Elan350Icon(); |
| 154 | 158 | |
| 159 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png") |
|
| 160 | + @ImageOptions(preventInlining = true) |
|
| 161 | + ImageResource ElanE4Icon(); |
|
| 162 | + |
|
| 155 | 163 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EUROPE_INT.png") |
| 156 | 164 | @ImageOptions(preventInlining = true) |
| 157 | 165 | ImageResource EuropeIcon(); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -5,6 +5,10 @@ |
| 5 | 5 | <div id="mainContent"> |
| 6 | 6 | <h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4> |
| 7 | 7 | <div class="innerContent"> |
| 8 | + <h5 class="articleSubheadline">September 2025</h5> |
|
| 9 | + <ul class="bulletList"> |
|
| 10 | + <li>Added CSV export for polar data histograms in Data Mining.</li> |
|
| 11 | + </ul> |
|
| 8 | 12 | <h5 class="articleSubheadline">June 2025</h5> |
| 9 | 13 | <ul class="bulletList"> |
| 10 | 14 | <li>Bug fix: the refresh interval in the Races/Tracking list ran into an overflow when races |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
| ... | ... | @@ -2521,4 +2521,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, |
| 2521 | 2521 | String successfullyCopiedPairings(); |
| 2522 | 2522 | String selectFromRaceColumn(); |
| 2523 | 2523 | String selectToRaceColumn(); |
| 2524 | + String exportTWAHistogramToCsv(); |
|
| 2525 | + String exportWindSpeedHistogramToCsv(); |
|
| 2524 | 2526 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
| ... | ... | @@ -2556,4 +2556,6 @@ selectRaceColumnsWhosePairingsToCopy=Race columns whose pairings to copy |
| 2556 | 2556 | errorCopyingPairings=Error copying pairings: {0} |
| 2557 | 2557 | successfullyCopiedPairings=Successfully copied pairings |
| 2558 | 2558 | selectFromRaceColumn=Select race column from where to start copying pairings |
| 2559 | -selectToRaceColumn=Select race colunm to where to copy pairings |
|
| ... | ... | \ No newline at end of file |
| 0 | +selectToRaceColumn=Select race colunm to where to copy pairings |
|
| 1 | +exportTWAHistogramToCsv=Export True Wind Angle histogram to CSV |
|
| 2 | +exportWindSpeedHistogramToCsv=Export Wind Speed histogram to CSV |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
| ... | ... | @@ -2550,4 +2550,6 @@ selectRaceColumnsWhosePairingsToCopy=Rennspalten auswählen, deren Zuordnungen k |
| 2550 | 2550 | errorCopyingPairings=Fehler beim Kopieren der Zuordnungen: {0} |
| 2551 | 2551 | successfullyCopiedPairings=Zuordnungen erfolgreich kopiert |
| 2552 | 2552 | selectFromRaceColumn=Spalte auswählen, ab der die Zuordnungen kopiert werden sollen |
| 2553 | -selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen |
|
| ... | ... | \ No newline at end of file |
| 0 | +selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen |
|
| 1 | +exportTWAHistogramToCsv=Histogramm der wahren Windwinkel in CSV exportieren |
|
| 2 | +exportWindSpeedHistogramToCsv=Histogramm der wahren Windgeschwindigkeiten in CSV exportieren |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java
| ... | ... | @@ -46,15 +46,15 @@ public class BoatClassVectorGraphicsResolver { |
| 46 | 46 | BoatClassMasterdata.HOBIE_WILD_CAT, BoatClassMasterdata.HOBIE_16, BoatClassMasterdata.HOBIE_TIGER, |
| 47 | 47 | BoatClassMasterdata.A_CAT, BoatClassMasterdata.TORNADO, BoatClassMasterdata.FLYING_PHANTOM, BoatClassMasterdata.ORC_MULTIHULL); |
| 48 | 48 | BoatClassVectorGraphics keelBoatWithGennaker = new KeelBoatWithGennakerVectorGraphics(BoatClassMasterdata.ELAN350, |
| 49 | - BoatClassMasterdata.FIRST_CLASS_7_5, |
|
| 49 | + BoatClassMasterdata.ELAN_E4, BoatClassMasterdata.FIRST_CLASS_7_5, |
|
| 50 | 50 | BoatClassMasterdata.J70, BoatClassMasterdata.J80, BoatClassMasterdata.J92, BoatClassMasterdata.J92S, |
| 51 | 51 | BoatClassMasterdata.B_ONE, BoatClassMasterdata.IRC, BoatClassMasterdata.LASER_SB3, BoatClassMasterdata.LONGTZE, |
| 52 | 52 | BoatClassMasterdata.RS_FEVA, BoatClassMasterdata.RS_TERA, BoatClassMasterdata.RS_VENTURE, |
| 53 | 53 | BoatClassMasterdata.RS_VAREO, |
| 54 | 54 | BoatClassMasterdata.RS100, BoatClassMasterdata.RS21, BoatClassMasterdata.TP52, |
| 55 | 55 | BoatClassMasterdata.CLUB_SWAN_50, BoatClassMasterdata.BAVARIA_CRUISER_41S, BoatClassMasterdata.BAVARIA_CRUISER_45, |
| 56 | - BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.BENETEAU_FIRST_35, BoatClassMasterdata.BENETEAU_FIRST_45, |
|
| 57 | - BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99, |
|
| 56 | + BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.BENETEAU_FIRST_35, BoatClassMasterdata.BENETEAU_FIRST_36, |
|
| 57 | + BoatClassMasterdata.BENETEAU_FIRST_45, BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99, |
|
| 58 | 58 | BoatClassMasterdata.BB10M, BoatClassMasterdata.WAYFARER, BoatClassMasterdata.X_332, |
| 59 | 59 | BoatClassMasterdata.BRASSFAHRT_I, BoatClassMasterdata.BRASSFAHRT_II, BoatClassMasterdata.BRASSFAHRT_III, |
| 60 | 60 | BoatClassMasterdata.BRASSFAHRT_IV, BoatClassMasterdata.BRASSFAHRT_V, |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarmining/PolarBackendResultsPresenter.java
| ... | ... | @@ -59,7 +59,6 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 59 | 59 | public PolarBackendResultsPresenter(Component<?> parent, ComponentContext<?> context, |
| 60 | 60 | StringMessages stringMessages) { |
| 61 | 61 | super(parent, context, stringMessages); |
| 62 | - |
|
| 63 | 62 | polarChart = ChartFactory.createPolarChart(); |
| 64 | 63 | polarChart.getYAxis().setMin(0); |
| 65 | 64 | polarChartWrapperPanel = new SimpleLayoutPanel() { |
| ... | ... | @@ -70,7 +69,6 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 70 | 69 | } |
| 71 | 70 | }; |
| 72 | 71 | polarChartWrapperPanel.add(polarChart); |
| 73 | - |
|
| 74 | 72 | speedChart = ChartFactory.createSpeedChart(stringMessages); |
| 75 | 73 | angleChart = ChartFactory.createAngleChart(stringMessages); |
| 76 | 74 | speedAndAngleChart = new DockLayoutPanel(Unit.PCT) { |
| ... | ... | @@ -84,16 +82,12 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 84 | 82 | }; |
| 85 | 83 | speedAndAngleChart.addNorth(speedChart, 50); |
| 86 | 84 | speedAndAngleChart.addSouth(angleChart, 50); |
| 87 | - |
|
| 88 | 85 | dockLayoutPanel = new DockLayoutPanel(Unit.PCT); |
| 89 | 86 | dockLayoutPanel.addWest(polarChartWrapperPanel, 40); |
| 90 | 87 | dockLayoutPanel.addEast(speedAndAngleChart, 60); |
| 91 | - |
|
| 92 | 88 | ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
| 93 | - |
|
| 94 | 89 | Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
| 95 | 90 | new ClickHandler() { |
| 96 | - |
|
| 97 | 91 | @Override |
| 98 | 92 | public void onClick(ClickEvent event) { |
| 99 | 93 | chartToCsvExporter.exportChartAsCsvToClipboard(polarChart); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarmining/PolarResultsPresenter.java
| ... | ... | @@ -22,7 +22,6 @@ import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions; |
| 22 | 22 | |
| 23 | 23 | import com.google.gwt.core.client.Scheduler; |
| 24 | 24 | import com.google.gwt.dom.client.Style.Unit; |
| 25 | -import com.google.gwt.event.dom.client.ClickEvent; |
|
| 26 | 25 | import com.google.gwt.event.dom.client.ClickHandler; |
| 27 | 26 | import com.google.gwt.user.client.ui.Button; |
| 28 | 27 | import com.google.gwt.user.client.ui.DockLayoutPanel; |
| ... | ... | @@ -104,15 +103,16 @@ public class PolarResultsPresenter extends AbstractSailingResultsPresenter<Setti |
| 104 | 103 | dockLayoutPanel = new DockLayoutPanel(Unit.PCT); |
| 105 | 104 | dockLayoutPanel.addWest(polarChartWrapperPanel, 40); |
| 106 | 105 | dockLayoutPanel.addEast(histogramChartsWrapperPanel, 60); |
| 107 | - ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
|
| 108 | - Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
|
| 109 | - new ClickHandler() { |
|
| 110 | - @Override |
|
| 111 | - public void onClick(ClickEvent event) { |
|
| 112 | - chartToCsvExporter.exportChartAsCsvToClipboard(polarChart); |
|
| 113 | - } |
|
| 114 | - }); |
|
| 106 | + final ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
|
| 107 | + final Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
|
| 108 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(polarChart)); |
|
| 115 | 109 | addControl(exportStatisticsCurveToCsvButton); |
| 110 | + final Button exportTWAHistogramToCsvButton = new Button(stringMessages.exportTWAHistogramToCsv(), |
|
| 111 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(dataCountHistogramChart)); |
|
| 112 | + addControl(exportTWAHistogramToCsvButton); |
|
| 113 | + final Button exportWindSpeedHistogramToCsvButton = new Button(stringMessages.exportWindSpeedHistogramToCsv(), |
|
| 114 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(dataCountPerAngleHistogramChart)); |
|
| 115 | + addControl(exportWindSpeedHistogramToCsvButton); |
|
| 116 | 116 | setSeriesShowAndHideHandler(); |
| 117 | 117 | } |
| 118 | 118 | |
| ... | ... | @@ -230,6 +230,7 @@ public class PolarResultsPresenter extends AbstractSailingResultsPresenter<Setti |
| 230 | 230 | if (point != null) { |
| 231 | 231 | Map<Double, Integer> histogramDataForAngle = histogramData.get(index); |
| 232 | 232 | Series dataCountPerAngleSeries = dataCountPerAngleHistogramChart.createSeries(); |
| 233 | + dataCountPerAngleSeries.setName(key.asString() + " - " + convertedAngle + stringMessages.degreesShort()); |
|
| 233 | 234 | // Iterating over the histogram data without sorting the x coordinates ascending leads |
| 234 | 235 | // to a massive occurrence of Highcharts error 15, freezing the complete UI |
| 235 | 236 | List<Double> sortedAngles = new ArrayList<>(histogramDataForAngle.keySet()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
| ... | ... | @@ -293,6 +293,9 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 293 | 293 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 294 | 294 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 295 | 295 | import com.sap.sailing.domain.sharding.ShardingContext; |
| 296 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 297 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 298 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 296 | 299 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; |
| 297 | 300 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory; |
| 298 | 301 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingArchiveConfiguration; |
| ... | ... | @@ -308,14 +311,11 @@ import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 308 | 311 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 309 | 312 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 310 | 313 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 311 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 312 | 314 | import com.sap.sailing.domain.tracking.Maneuver; |
| 313 | 315 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 314 | -import com.sap.sailing.domain.tracking.Track; |
|
| 315 | 316 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 316 | 317 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 317 | 318 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 318 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 319 | 319 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 320 | 320 | import com.sap.sailing.domain.tracking.WindTrack; |
| 321 | 321 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/TrackingConnectorInfoDTO.java
| ... | ... | @@ -2,7 +2,7 @@ package com.sap.sailing.gwt.ui.shared; |
| 2 | 2 | |
| 3 | 3 | import com.google.gwt.core.shared.GwtIncompatible; |
| 4 | 4 | import com.google.gwt.user.client.rpc.IsSerializable; |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | 6 | |
| 7 | 7 | public class TrackingConnectorInfoDTO implements IsSerializable { |
| 8 | 8 | private String trackingConnectorName; |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png differ |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png differ |
java/com.sap.sailing.ingestion/src/main/java/com/sap/sailing/ingestion/FixCombinationLambda.java
| ... | ... | @@ -23,7 +23,7 @@ import com.amazonaws.services.lambda.runtime.Context; |
| 23 | 23 | import com.amazonaws.services.lambda.runtime.RequestStreamHandler; |
| 24 | 24 | import com.sap.sailing.domain.common.DeviceIdentifier; |
| 25 | 25 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 26 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 27 | 27 | import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer; |
| 28 | 28 | import com.sap.sailing.server.gateway.deserialization.impl.Helpers; |
| 29 | 29 | import com.sap.sailing.server.gateway.serialization.impl.GPSFixMovingJsonSerializer; |
java/com.sap.sailing.nmeaconnector/META-INF/MANIFEST.MF
| ... | ... | @@ -12,7 +12,8 @@ Require-Bundle: com.sap.sailing.domain, |
| 12 | 12 | com.sap.sse.common, |
| 13 | 13 | net.sf.marineapi, |
| 14 | 14 | com.sap.sailing.declination |
| 15 | -Import-Package: org.osgi.framework |
|
| 15 | +Import-Package: org.osgi.framework, |
|
| 16 | + com.sap.sailing.domain.shared.tracking |
|
| 16 | 17 | Bundle-ActivationPolicy: lazy |
| 17 | 18 | Bundle-Activator: com.sap.sailing.nmeaconnector.impl.Activator |
| 18 | 19 | Automatic-Module-Name: com.sap.sailing.nmeaconnector |
java/com.sap.sailing.nmeaconnector/src/com/sap/sailing/nmeaconnector/impl/KnotSpeedWithBearingAndTimepoint.java
| ... | ... | @@ -1,13 +1,12 @@ |
| 1 | 1 | package com.sap.sailing.nmeaconnector.impl; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; |
| 4 | -import com.sap.sailing.domain.tracking.Track; |
|
| 5 | 4 | import com.sap.sailing.nmeaconnector.TimedSpeedWithBearing; |
| 6 | 5 | import com.sap.sse.common.Bearing; |
| 7 | 6 | import com.sap.sse.common.TimePoint; |
| 8 | 7 | |
| 9 | 8 | /** |
| 10 | - * Can be used as a wind vector in a {@link Track} because it has a {@link TimePoint}. |
|
| 9 | + * Can be used as a wind vector in a {@code Track} because it has a {@link TimePoint}. |
|
| 11 | 10 | * |
| 12 | 11 | * @author Axel Uhl (d043530) |
| 13 | 12 | * |
java/com.sap.sailing.polars.datamining.shared/src/com/sap/sailing/polars/datamining/shared/PolarAggregation.java
| ... | ... | @@ -15,6 +15,10 @@ public interface PolarAggregation extends Serializable { |
| 15 | 15 | |
| 16 | 16 | PolarDataMiningSettings getSettings(); |
| 17 | 17 | |
| 18 | + /** |
|
| 19 | + * Keys are the angles in degrees (0...359). The {@link Double} key of the inner map is the histogram x-value (e.g. |
|
| 20 | + * 5.0 for wind speed 4.5...5.5 with step 1.0). |
|
| 21 | + */ |
|
| 18 | 22 | Map<Integer, Map<Double, Integer>> getCountHistogramPerAngle(); |
| 19 | 23 | |
| 20 | 24 | } |
java/com.sap.sailing.polars.datamining.shared/src/com/sap/sailing/polars/datamining/shared/PolarAggregationImpl.java
| ... | ... | @@ -11,8 +11,12 @@ public class PolarAggregationImpl implements PolarAggregation { |
| 11 | 11 | private double[] sumSpeedsPerAngle = new double[360]; |
| 12 | 12 | private int[] countPerAngle = new int[360]; |
| 13 | 13 | /** |
| 14 | - * FIXME Right now the histogram data is only valid, if the results are grouped by windrange. |
|
| 15 | - * Otherwise the column-indices of different ranges are mixed in one histogram. |
|
| 14 | + * FIXME Right now the histogram data is only valid, if the results are grouped by windrange. Otherwise the |
|
| 15 | + * column-indices of different ranges are mixed in one histogram. |
|
| 16 | + * <p> |
|
| 17 | + * |
|
| 18 | + * Keys are the angles in degrees (0...359). The {@link Double} key of the inner map is the histogram x-value (e.g. |
|
| 19 | + * 5.0 for wind speed 4.5...5.5 with step 1.0). |
|
| 16 | 20 | */ |
| 17 | 21 | private Map<Integer, Map<Double, Integer>> histogramData; |
| 18 | 22 | private int count = 0; |
| ... | ... | @@ -80,6 +84,4 @@ public class PolarAggregationImpl implements PolarAggregation { |
| 80 | 84 | public Map<Integer, Map<Double, Integer>> getCountHistogramPerAngle() { |
| 81 | 85 | return histogramData; |
| 82 | 86 | } |
| 83 | - |
|
| 84 | - |
|
| 85 | 87 | } |
java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF
| ... | ... | @@ -6,6 +6,8 @@ Bundle-Version: 1.0.0.qualifier |
| 6 | 6 | Bundle-Vendor: SAP |
| 7 | 7 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 8 | 8 | Fragment-Host: com.sap.sailing.polars |
| 9 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 10 | + com.sap.sailing.domain.shared.tracking.impl |
|
| 9 | 11 | Require-Bundle: org.hamcrest;bundle-version="2.2.0", |
| 10 | 12 | org.mockito.mockito-core;bundle-version="1.10.14", |
| 11 | 13 | junit-jupiter-api;bundle-version="5.11.3", |
java/com.sap.sailing.polars/META-INF/MANIFEST.MF
| ... | ... | @@ -39,6 +39,7 @@ Require-Bundle: |
| 39 | 39 | org.apache.httpcomponents.httpcore;bundle-version="4.4.9", |
| 40 | 40 | com.sap.sailing.shared.server |
| 41 | 41 | Import-Package: |
| 42 | + com.sap.sailing.domain.shared.tracking, |
|
| 42 | 43 | com.sap.sailing.util, |
| 43 | 44 | javax.ws.rs;version="1.1.1", |
| 44 | 45 | javax.ws.rs.core;version="1.1.1", |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/EventBaseJsonDeserializer.java
| ... | ... | @@ -16,7 +16,7 @@ import com.sap.sailing.domain.base.EventBase; |
| 16 | 16 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 17 | 17 | import com.sap.sailing.domain.base.Venue; |
| 18 | 18 | import com.sap.sailing.domain.base.impl.StrippedEventImpl; |
| 19 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 19 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 20 | 20 | import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer; |
| 21 | 21 | import com.sap.sse.common.TimePoint; |
| 22 | 22 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/TrackingConnectorInfoJsonDeserializer.java
| ... | ... | @@ -2,8 +2,8 @@ package com.sap.sailing.server.gateway.deserialization.impl; |
| 2 | 2 | |
| 3 | 3 | import org.json.simple.JSONObject; |
| 4 | 4 | |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 6 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 7 | 7 | import com.sap.sailing.server.gateway.serialization.impl.TrackingConnectorInfoJsonSerializer; |
| 8 | 8 | import com.sap.sse.shared.json.JsonDeserializationException; |
| 9 | 9 | import com.sap.sse.shared.json.JsonDeserializer; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java
| ... | ... | @@ -8,7 +8,7 @@ import com.sap.sailing.domain.base.CourseBase; |
| 8 | 8 | import com.sap.sailing.domain.base.Leg; |
| 9 | 9 | import com.sap.sailing.domain.base.Waypoint; |
| 10 | 10 | import com.sap.sailing.domain.common.NauticalSide; |
| 11 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 12 | 12 | import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseWithGeometryJsonSerializer.CourseGeometry; |
| 13 | 13 | import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; |
| 14 | 14 | import com.sap.sse.common.Bearing; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java
| ... | ... | @@ -10,7 +10,7 @@ import org.json.simple.JSONObject; |
| 10 | 10 | import com.sap.sailing.domain.base.EventBase; |
| 11 | 11 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 12 | 12 | import com.sap.sailing.domain.base.Venue; |
| 13 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 14 | 14 | import com.sap.sse.shared.json.JsonSerializer; |
| 15 | 15 | import com.sap.sse.shared.media.ImageDescriptor; |
| 16 | 16 | import com.sap.sse.shared.media.VideoDescriptor; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/TrackingConnectorInfoJsonSerializer.java
| ... | ... | @@ -2,7 +2,7 @@ package com.sap.sailing.server.gateway.serialization.impl; |
| 2 | 2 | |
| 3 | 3 | import org.json.simple.JSONObject; |
| 4 | 4 | |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | 6 | import com.sap.sse.shared.json.JsonSerializer; |
| 7 | 7 | |
| 8 | 8 | public class TrackingConnectorInfoJsonSerializer implements JsonSerializer<TrackingConnectorInfo> { |
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
| ... | ... | @@ -115,9 +115,9 @@ import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 115 | 115 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; |
| 116 | 116 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 117 | 117 | import com.sap.sailing.domain.sharding.ShardingContext; |
| 118 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 118 | 119 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 119 | 120 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 120 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 121 | 121 | import com.sap.sailing.domain.tracking.Maneuver; |
| 122 | 122 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 123 | 123 | import com.sap.sailing.domain.tracking.RaceWindCalculator; |
java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java
| ... | ... | @@ -94,6 +94,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier; |
| 94 | 94 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 95 | 95 | import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 96 | 96 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 97 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 97 | 98 | import com.sap.sailing.domain.statistics.Statistics; |
| 98 | 99 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 99 | 100 | import com.sap.sailing.domain.tracking.RaceHandle; |
| ... | ... | @@ -106,7 +107,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 106 | 107 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 107 | 108 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 108 | 109 | import com.sap.sailing.domain.tracking.TrackerManager; |
| 109 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 110 | 110 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 111 | 111 | import com.sap.sailing.domain.tracking.WindStore; |
| 112 | 112 | import com.sap.sailing.domain.tracking.WindTracker; |
java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java
| ... | ... | @@ -4,10 +4,10 @@ import com.sap.sailing.domain.base.RaceDefinition; |
| 4 | 4 | import com.sap.sailing.domain.base.Regatta; |
| 5 | 5 | import com.sap.sailing.domain.common.RaceIdentifier; |
| 6 | 6 | import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; |
| 7 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 7 | 8 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 8 | 9 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 9 | 10 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 10 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 11 | 11 | import com.sap.sailing.domain.tracking.WindStore; |
| 12 | 12 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 13 | 13 | import com.sap.sailing.server.interfaces.RacingEventService; |
java/com.sap.sailing.server.trackfiles/META-INF/MANIFEST.MF
| ... | ... | @@ -21,6 +21,7 @@ Import-Package: com.sap.sailing.domain.abstractlog, |
| 21 | 21 | com.sap.sailing.domain.abstractlog.regatta.events.impl, |
| 22 | 22 | com.sap.sailing.domain.common.sensordata, |
| 23 | 23 | com.sap.sailing.domain.racelogtracking, |
| 24 | + com.sap.sailing.domain.shared.tracking, |
|
| 24 | 25 | com.sap.sse.security.shared, |
| 25 | 26 | javax.xml.bind, |
| 26 | 27 | javax.xml.bind.annotation, |
java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/TrackReaderImpl.java
| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.server.trackfiles.impl; |
| 2 | 2 | |
| 3 | -import com.sap.sailing.domain.tracking.Track; |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 4 | 4 | import com.sap.sse.common.Timed; |
| 5 | 5 | |
| 6 | 6 | class TrackReaderImpl<E, T extends Timed> implements TrackReader<E, T>, IterableLocker { |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
| ... | ... | @@ -222,8 +222,9 @@ import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 222 | 222 | import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 223 | 223 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 224 | 224 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 225 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 226 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 225 | 227 | import com.sap.sailing.domain.statistics.Statistics; |
| 226 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 227 | 228 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 228 | 229 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 229 | 230 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| ... | ... | @@ -244,7 +245,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 244 | 245 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 245 | 246 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 246 | 247 | import com.sap.sailing.domain.tracking.TrackedRegattaListener; |
| 247 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 248 | 248 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 249 | 249 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 250 | 250 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.server/src/com/sap/sailing/server/security/PermissionAwareRaceTrackingHandler.java
| ... | ... | @@ -27,13 +27,13 @@ import com.sap.sailing.domain.common.RegattaNameAndRaceName; |
| 27 | 27 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 28 | 28 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 29 | 29 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 30 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 30 | 31 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 31 | 32 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 32 | 33 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 33 | 34 | import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler; |
| 34 | 35 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 35 | 36 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 36 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 37 | 37 | import com.sap.sailing.domain.tracking.WindStore; |
| 38 | 38 | import com.sap.sse.common.Color; |
| 39 | 39 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java
| ... | ... | @@ -40,7 +40,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 40 | 40 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 41 | 41 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 42 | 42 | import com.sap.sailing.domain.polars.PolarDataService; |
| 43 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 43 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 44 | 44 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 45 | 45 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 46 | 46 | import com.sap.sailing.domain.tracking.RaceListener; |
java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java
| ... | ... | @@ -18,8 +18,8 @@ import com.sap.sailing.domain.common.Wind; |
| 18 | 18 | import com.sap.sailing.domain.common.WindSource; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 20 | 20 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 21 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 21 | 22 | import com.sap.sailing.domain.tracking.AbstractTrackedRegattaAndRaceObserver; |
| 22 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 23 | 23 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 24 | 24 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 25 | 25 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF
| ... | ... | @@ -3,6 +3,7 @@ Bundle-ManifestVersion: 2 |
| 3 | 3 | Bundle-Name: Test |
| 4 | 4 | Bundle-SymbolicName: com.sap.sailing.simulator.test |
| 5 | 5 | Bundle-Version: 1.0.0.qualifier |
| 6 | +Import-Package: com.sap.sailing.domain.shared.tracking |
|
| 6 | 7 | Bundle-Vendor: SAP |
| 7 | 8 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 8 | 9 | Require-Bundle: com.sap.sailing.simulator;bundle-version="1.0.0", |
java/com.sap.sailing.simulator/META-INF/MANIFEST.MF
| ... | ... | @@ -14,7 +14,8 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", |
| 14 | 14 | org.apache.httpcomponents.httpclient;bundle-version="4.5.5", |
| 15 | 15 | org.json.simple;bundle-version="1.1.0" |
| 16 | 16 | Bundle-ActivationPolicy: lazy |
| 17 | -Import-Package: org.osgi.framework;version="1.6.0" |
|
| 17 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 18 | + org.osgi.framework;version="1.6.0" |
|
| 18 | 19 | Bundle-ClassPath: . |
| 19 | 20 | Export-Package: com.sap.sailing.simulator, |
| 20 | 21 | com.sap.sailing.simulator.impl, |
java/com.sap.sailing.www/release_notes_admin.html
| ... | ... | @@ -23,6 +23,10 @@ |
| 23 | 23 | <div class="mainContent"> |
| 24 | 24 | <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
| 25 | 25 | <div class="innerContent"> |
| 26 | + <h2 class="articleSubheadline">September 2025</h2> |
|
| 27 | + <ul class="bulletList"> |
|
| 28 | + <li>Added boat classes "Elan E4" and "Beneteau First 36" to the list of boat classes.<p> |
|
| 29 | + </ul> |
|
| 26 | 30 | <h2 class="articleSubheadline">August 2025</h2> |
| 27 | 31 | <ul class="bulletList"> |
| 28 | 32 | <li>Pairings, e.g., in league set-ups, can not be copied across regattas/leaderboards. |
java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sse.datamining.ui.client; |
| 2 | 2 | |
| 3 | -import java.util.ArrayList; |
|
| 4 | -import java.util.List; |
|
| 3 | +import java.util.HashMap; |
|
| 4 | +import java.util.Map; |
|
| 5 | +import java.util.Map.Entry; |
|
| 6 | +import java.util.TreeMap; |
|
| 5 | 7 | |
| 6 | 8 | import org.moxieapps.gwt.highcharts.client.Chart; |
| 7 | 9 | import org.moxieapps.gwt.highcharts.client.Point; |
| ... | ... | @@ -42,38 +44,33 @@ public class ChartToCsvExporter { |
| 42 | 44 | private static String createCsvExportContentForStatisticsCurve(Chart chartToExport) { |
| 43 | 45 | if (chartToExport != null && chartToExport.getSeries().length > 0) { |
| 44 | 46 | final StringBuilder csvStr = new StringBuilder("Series name"); |
| 45 | - int minX = Integer.MAX_VALUE; |
|
| 47 | + final Map<Number, Integer> columnIndexByXValue = new HashMap<>(); |
|
| 46 | 48 | { |
| 47 | - final List<String> columnNames = new ArrayList<>(); |
|
| 49 | + final TreeMap<Number, String> orderedColumnNames = new TreeMap<>((a, b) -> Double.compare(a.doubleValue(), b.doubleValue())); |
|
| 48 | 50 | // collect column names across all series; some may not have points for all columns |
| 49 | 51 | for (final Series series : chartToExport.getSeries()) { |
| 50 | - for (final Point p : series.getPoints()) { |
|
| 51 | - if (p.getX().intValue() < minX) { |
|
| 52 | - minX = p.getX().intValue(); |
|
| 53 | - } |
|
| 54 | - } |
|
| 55 | 52 | for (Point point : series.getPoints()) { |
| 56 | - final String pointName = point.getName(); |
|
| 57 | - final String columnName = pointName != null && !pointName.isEmpty() ? pointName : point.getX().toString(); |
|
| 58 | - while (columnNames.size() <= point.getX().intValue()-minX) { |
|
| 59 | - columnNames.add(columnNames.size(), null); |
|
| 60 | - } |
|
| 61 | - columnNames.set(point.getX().intValue()-minX, columnName); |
|
| 53 | + final String columnName = getColumnName(point); |
|
| 54 | + orderedColumnNames.put(point.getX(), columnName); |
|
| 62 | 55 | } |
| 63 | 56 | } |
| 64 | - for (final String columnName : columnNames) { |
|
| 57 | + for (final String columnName : orderedColumnNames.values()) { |
|
| 65 | 58 | csvStr.append(';'); |
| 66 | 59 | if (columnName != null && !columnName.isEmpty()) { |
| 67 | 60 | csvStr.append(columnName); |
| 68 | 61 | } |
| 69 | 62 | } |
| 63 | + int columnIndex = 0; |
|
| 64 | + for (final Entry<Number, String> e : orderedColumnNames.entrySet()) { |
|
| 65 | + columnIndexByXValue.put(e.getKey(), columnIndex++); |
|
| 66 | + } |
|
| 70 | 67 | } |
| 71 | 68 | csvStr.append("\r\n"); |
| 72 | 69 | for (Series series : chartToExport.getSeries()) { |
| 73 | 70 | csvStr.append(series.getName()); |
| 74 | 71 | int lastXIndex = -1; |
| 75 | 72 | for (Point point : series.getPoints()) { |
| 76 | - while (lastXIndex < point.getX().intValue()-minX) { |
|
| 73 | + while (lastXIndex < columnIndexByXValue.get(point.getX())) { |
|
| 77 | 74 | csvStr.append(';'); |
| 78 | 75 | lastXIndex++; |
| 79 | 76 | } |
| ... | ... | @@ -87,4 +84,10 @@ public class ChartToCsvExporter { |
| 87 | 84 | return "Statistics are empty"; |
| 88 | 85 | } |
| 89 | 86 | |
| 87 | + private static String getColumnName(Point point) { |
|
| 88 | + final String pointName = point.getName(); |
|
| 89 | + final String columnName = pointName != null && !pointName.isEmpty() ? pointName : point.getX().toString(); |
|
| 90 | + return columnName; |
|
| 91 | + } |
|
| 92 | + |
|
| 90 | 93 | } |
java/com.tractrac.clientmodule/META-INF/MANIFEST.MF
| ... | ... | @@ -2,7 +2,7 @@ Manifest-Version: 1.0 |
| 2 | 2 | Bundle-ManifestVersion: 2 |
| 3 | 3 | Bundle-Name: TracTrac Client module |
| 4 | 4 | Bundle-SymbolicName: com.tractrac.clientmodule |
| 5 | -Bundle-Version: 4.0.3 |
|
| 5 | +Bundle-Version: 4.0.4 |
|
| 6 | 6 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 7 | 7 | Bundle-ClassPath: ., |
| 8 | 8 | lib/TracAPI.jar |
java/com.tractrac.clientmodule/README.txt
| ... | ... | @@ -16,6 +16,18 @@ It contains also some files: |
| 16 | 16 | - Manifest.txt -> manifest used to create the test.jar file |
| 17 | 17 | |
| 18 | 18 | ******************************************** |
| 19 | + TracAPI 4.0.4 |
|
| 20 | +******************************************** |
|
| 21 | +This is the final version. It keeps the backward compatibility. This version has been compiled with |
|
| 22 | +Java 21 but the target compatibility continues being Java 8. |
|
| 23 | + |
|
| 24 | + Release date: 01/10/2025 |
|
| 25 | + |
|
| 26 | + 1) Bugs |
|
| 27 | + |
|
| 28 | + - The heartbeat monitoring the connection status can stop working (Reported by Jorge Piera, 01/10/2025) |
|
| 29 | + |
|
| 30 | +******************************************** |
|
| 19 | 31 | TracAPI 4.0.3 |
| 20 | 32 | ******************************************** |
| 21 | 33 | This is the final version. It keeps the backward compatibility. The new parameter files will be encoded in UTF-8, |
java/com.tractrac.clientmodule/lib/TracAPI-src.jar
java/com.tractrac.clientmodule/lib/TracAPI.jar
java/com.tractrac.clientmodule/pom.xml
| ... | ... | @@ -8,6 +8,6 @@ |
| 8 | 8 | <version>1.0.0-SNAPSHOT</version> |
| 9 | 9 | </parent> |
| 10 | 10 | <artifactId>com.tractrac.clientmodule</artifactId> |
| 11 | - <version>4.0.3</version> |
|
| 11 | + <version>4.0.4</version> |
|
| 12 | 12 | <packaging>eclipse-plugin</packaging> |
| 13 | 13 | </project> |
java/com.tractrac.clientmodule/src/com/tractrac/subscription/app/tracapi/Main.java
| ... | ... | @@ -62,8 +62,8 @@ public class Main { |
| 62 | 62 | race |
| 63 | 63 | ); |
| 64 | 64 | raceSubscriber.subscribeConnectionStatus(listener); |
| 65 | - raceSubscriber.subscribePositionedItemPositions(listener); |
|
| 66 | - //raceSubscriber.subscribePositions(listener); |
|
| 65 | + //raceSubscriber.subscribePositionedItemPositions(listener); |
|
| 66 | + raceSubscriber.subscribePositions(listener); |
|
| 67 | 67 | //raceSubscriber.subscribePositionsSnapped(listener); |
| 68 | 68 | raceSubscriber.subscribeControlPassings(listener); |
| 69 | 69 | //raceSubscriber.subscribeCompetitorSensorData(listener); |
wiki/howto/eventmanagers/linking-race-videos.md
| ... | ... | @@ -59,4 +59,4 @@ This articel provides a short guidance of how to synchronize races with their Yo |
| 59 | 59 | 8. start again from step 3. of chaper 1 |
| 60 | 60 | |
| 61 | 61 |  |
| 62 | -**Figure 4: Link all races covered by the video** |
|
| ... | ... | \ No newline at end of file |
| 0 | +**Figure 4: Link all races covered by the video** |
wiki/info/landscape/amazon-ec2.md
| ... | ... | @@ -153,7 +153,7 @@ Any geo-blocking Web ACL that shall automatically be associated with ALBs that a |
| 153 | 153 | ``` |
| 154 | 154 | Note that in order to run this command you have to have valid credentials for the AWS region you're targeting with the request. Also consider using the ``--region`` argument if you're trying to tag a Web ACL in a region other than your AWS CLI's default region. Check your ``~/.aws/config`` file. Also see ``configuration/environments_scripts/repo/usr/local/bin/awsmfalogon.sh`` for logging on to the AWS CLI. |
| 155 | 155 | |
| 156 | -### MongoDB Replica Setsn |
|
| 156 | +### MongoDB Replica Sets |
|
| 157 | 157 | |
| 158 | 158 | There are currently three MongoDB replica sets: |
| 159 | 159 |
wiki/info/landscape/aws-automation.md
| ... | ... | @@ -328,7 +328,7 @@ The user can assign values to that variable that are then used as default propos |
| 328 | 328 | 7. Configure target group health check |
| 329 | 329 | 8. Register instance within target group |
| 330 | 330 | 9. Create new rule within https listener that points to the correct target group |
| 331 | -10. Append „Use Event-SSL [domain] [eventId] 127.0.0.1 8888“ or „Use Home-SSL [domain] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 331 | +10. Append „Use Event-SSL \[domain\] \[eventId\] 127.0.0.1 8888“ or „Use Home-SSL \[domain\] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 332 | 332 | |
| 333 | 333 | #### SAP instance on a shared EC2 instance |
| 334 | 334 | |
| ... | ... | @@ -349,7 +349,7 @@ The user can assign values to that variable that are then used as default propos |
| 349 | 349 | 15. Create target group with name „S-hared-instanceshortname“ |
| 350 | 350 | 16. Configuration of the target group health check with the server port of the sap instance |
| 351 | 351 | 17. Create new rule within https listener that points to the correct target group |
| 352 | -18. Append „Use Event-SSL [domain] [eventId] 127.0.0.1 8888“ or „Use Home-SSL [domain] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 352 | +18. Append „Use Event-SSL \[domain\] \[eventId\] 127.0.0.1 8888“ or „Use Home-SSL \[domain\] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 353 | 353 | 19. Check apache configuration with "apachectl configtest" and reload with "sudo service httpd reload“ |
| 354 | 354 | |
| 355 | 355 | #### SAP instance on a dedicated EC2 instance as a master |
wiki/info/landscape/operating-system-upgrade.md
| ... | ... | @@ -0,0 +1,131 @@ |
| 1 | +# Operating System Upgrade Across Landscape |
|
| 2 | + |
|
| 3 | +[[_TOC_]] |
|
| 4 | + |
|
| 5 | +Mainly for security reasons we strive to keep the operating systems on which our EC2 instances are running up to date. This includes running the latest Linux kernels and having all packages updated to their latest versions as per the Amazon Linux or other Linux versions used. While doing so, we aim to keep service interruptions to a minimum and in particular keep services available at least in read-only mode also during upgrades. |
|
| 6 | + |
|
| 7 | +We distinguish between in-place upgrades without the need to re-boot, in-place upgrade requiring a reboot (e.g., due to Linux kernel updates), and upgrades that replace EC2 instances by new EC2 instances. The latter case can be sub-divided into cases where an incremental image upgrade can be used to produce a new version of an Amazon Machine Image (AMI) used for that instance type, and cases where a new from-scratch AMI set-up will be required. Also, the procedures to use depend on the type of service run on the instance that requires an upgrade. |
|
| 8 | + |
|
| 9 | +## Approaches for Operating System Updates |
|
| 10 | + |
|
| 11 | +### Using AdminConsole Landscape Management Panel |
|
| 12 | + |
|
| 13 | +The AdminConsole offers the Landscape Management panel (see, e.g., [https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:](https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:)) with a table entitled "Amazon Machine Images (AMIs)." It shows the different AMIs in use, among them the ``sailing-analytics-server``, the ``mongodb-server`` and the ``disposable-reverse-proxy`` images. Each of them have an "Upgrade" action icon in the "Actions" column that can be used to launch an instance off the image and then apply the steps necessary to upgrade the image to the latest version of kernel, all packages, and Java VM (if installed), then creates a new version of the AMI. |
|
| 14 | + |
|
| 15 | +See below for how to proceed with the upgraded images for the different image types. |
|
| 16 | + |
|
| 17 | +### Log On with SSH and Use Package Manager for Live Upgrade |
|
| 18 | + |
|
| 19 | +Instead of or in addition to upgrading the AMIs to new package and kernel versions, you can also log in to a running instance using SSH, and as root (using, e.g., ``sudo``) upgrade packages and kernel in place. Should a reboot be required, however, it depends on the particular instance you have been applying this to. Some instances should not simply be rebooted as this may unnecessarily reduce availability of some services and may not always lead to a clean recovery of all services after the reboot. |
|
| 20 | + |
|
| 21 | +For example, when rebooting an instance that runs one or more primary application processes for which replica processes run on other instances, inconsistencies between primary and replicas may result by a brute-force restart of the primary in some cases. See below for cleaner ways to do this. |
|
| 22 | + |
|
| 23 | +#### Amazon Linux |
|
| 24 | + |
|
| 25 | +We use Amazon Linux as the default for most instance types and hence most AMIs, particularly those for running the Sailing Analytics application, the MongoDB instances, the reverse proxy instances, and MariaDB for our Bugzilla service. |
|
| 26 | + |
|
| 27 | +Amazon Linux 2023 uses ``dnf`` as its package manager. An operating system upgrade is performed by running, as ``root`` (e.g., by logging in as ``ec2-user`` and then using ``sudo``): |
|
| 28 | +``` |
|
| 29 | + dnf --releasever=latest upgrade |
|
| 30 | +``` |
|
| 31 | +This will upgrade all packages installed as well as the kernel. When run interactively, upgrade requiring a reboot will be displayed in the update list in red color. For scripted use, consider the ``needs-restarting`` command, delivering an exit status of ``1`` if a reboot is required. |
|
| 32 | + |
|
| 33 | +#### Debian |
|
| 34 | + |
|
| 35 | +Our use of Debian is currently restricted to running RabbitMQ which is a lot harder to install and configure on Amazon Linux. |
|
| 36 | + |
|
| 37 | +Debian uses ``apt`` as its package manager. Its default login user differs from Amazon Linux, where it is ``ec2-user`` and is called ``admin`` instead. Like the ``ec2-user`` on Amazon Linux, ``admin`` is eligible to use ``sudo`` to run commands with root privileges. |
|
| 38 | + |
|
| 39 | +Executing an update with apt works like this: |
|
| 40 | +``` |
|
| 41 | + apt-get update |
|
| 42 | + apt-get upgrade |
|
| 43 | +``` |
|
| 44 | +If this creates a file ``/var/run/reboot-required`` then the instance must be rebooted for all changes to take effect. |
|
| 45 | + |
|
| 46 | +## Upgrading the Different Instance Types |
|
| 47 | + |
|
| 48 | +### ``security-service.sapsailing.com`` Primary |
|
| 49 | + |
|
| 50 | +The corresponding ``security_service`` replica set usually has a single instance running only the primary application service. It offers a few ``Replicable``s that all other replica sets (except ``DEV``) replicate, such as the ``SecurityService`` and ``SharedSailingData`` services. It acts as a hub in particular for user, group, role and permission management. Other instances have their replicated versions of the service and can make decisions locally, sign in/up and authenticate users and manage their sessions locally. Replication through the ``security_service`` replica set serves the purpose of letting users roam about the landscape. Temporary outages of the ``security_service`` replica set will delay replication of these aspects across the landscape. However, transactions will not be lost but will be queued and applied when the service becomes available again. |
|
| 51 | + |
|
| 52 | +With this in mind, a restart of either the Java VM (in order to upgrade the application to a new version) or even a reboot of the EC2 instance, both typically done in less than 60s, will rarely cause effects noticeable to users. Therefore, we typically afford to upgrade the instance running the single primary process for the ``security_service`` replica set "in place:" |
|
| 53 | + |
|
| 54 | +- log on with ssh as ``ec2-user`` |
|
| 55 | +- run ``dnf --releasever=latest upgrade`` |
|
| 56 | +- if a reboot is required, reboot the instance |
|
| 57 | + |
|
| 58 | +It is useful to wait with the reboot until at least no known Sailing Analytics process start-up is happening which is in the middle of obtaining an initial load from the ``security_service`` replica set because this would be aborted and hence fail upon the reboot. Other than that, existing replicas will synchronize with the rebooted instance and the freshly started service once available again. |
|
| 59 | + |
|
| 60 | +Should you find good reasons against an in-place upgrade, make sure you have an upgraded ``sailing-analytics-server`` AMI, remove the running instance from the ``S-security-service`` and ``S-security-service-m`` target groups, launch a new instance off the upgraded AMI with the user data copied from the running instance, with only the ``INSTALL_FROM_RELEASE`` parameter upgrade to the latest release: |
|
| 61 | +``` |
|
| 62 | +INSTALL_FROM_RELEASE=main-202502181141 |
|
| 63 | +SERVER_NAME=security_service |
|
| 64 | +USE_ENVIRONMENT=security-service-master |
|
| 65 | +``` |
|
| 66 | + |
|
| 67 | +Then add the new instance to the ``S-security-service`` and ``S-security-service-m`` target groups and terminate the old instance. |
|
| 68 | + |
|
| 69 | +### ``DEV`` |
|
| 70 | + |
|
| 71 | +The ``DEV`` replica set is for testing only. Other than that, the instance runs our Hudson CI environment. Both are not expected to be highly available. Therefore, the same in-place update as for the ``security_service`` replica set is possible. For a clean Hudson shut-down, consider using [this link](https://hudson.sapsailing.com/quietDown). |
|
| 72 | + |
|
| 73 | +### ``ARCHIVE`` |
|
| 74 | + |
|
| 75 | +Make sure you have an up-to-date ``sailing-analytics-server`` AMI. Then, see [[Upgrading ARCHIVE server|wiki/info/landscape/archive-server-upgrade]] for how to launch a new ARCHIVE candidate with that new AMI and how to switch to it once the loading of all races has finished successfully. |
|
| 76 | + |
|
| 77 | +### ``my`` |
|
| 78 | + |
|
| 79 | +You can try an [in-place upgrade](#log-on-with-ssh-and-use-package-manager-for-live-upgrade) for these. Should this, however, require a reboot, you should then apply the following procedure: |
|
| 80 | + |
|
| 81 | +To start with, make sure you have an up-to-date ``sailing-analytics-server`` AMI (see above). Also make sure the auto-scaling group for the ``my`` replica set is set to use this latest AMI for any replicas launched by the auto-scaling group. |
|
| 82 | + |
|
| 83 | +The ``my`` replica set is special in comparison to most other replica sets. It runs its primary process on a dedicated instance and requires an instance type with at least 500GB of swap space. A good default is an ``i3.2xlarge`` instance type. The application settings, as of this writing, require 350GB of heap size, indicated by ``MEMORY="350000m"`` in the user data section for the instance. |
|
| 84 | + |
|
| 85 | +In order to move the ``my`` primary process to a new instance with a new operating system, use the AdminConsole's Landscape Management panel, and there the "Move master process to another instance" action. Make sure to select an appropricate ``i3....`` instance type with sufficient swap space, *not* the default ``C5_2_XLARGE`` suggestion. Explicitly enter the amount of memory you'd like to assign to the process, such as "350000" into the "Memory (MB)" field of the pop-up dialog, then confirm using the "OK" button. |
|
| 86 | + |
|
| 87 | +This will detach all running replicas (usually exactly one) from the primary process, remove the primary process from the ``S-my`` and ``S-my-m`` target groups, then stop and remove the primary process, which will also lead to the instance being terminated as this was the last (only) application process running on it. Then, a new instance off the latest AMI will be launched, deploying and starting a new primary process for the ``my`` replica set. Once this has loaded all contents from the DB and reports a healthy status, an explicit "Upgrade Replica" is launched which uses the explicit primary instance's IP address instead of the DNS host name to obtain an initial load. This works around the fact that so far the new primary hasn't been added to any target groups yet and hence isn't reachable under the ``my.sapsailing.com `` domain name. |
|
| 88 | + |
|
| 89 | +When the upgrade replica has reported a healthy status, the primary is added to the ``S-my`` and ``S-my-m`` target groups, and the upgrade replica is added to the ``S-my`` target group. Then, the old auto-replica which is expected to have been launched using the auto-scaling group will be terminated, causing the launching of a new instance to which a ``my`` replica is deployed and started. Once the auto-replica is healthy, the upgrade replica will be terminated which removes it from the ``S-my`` target group. |
|
| 90 | + |
|
| 91 | +### Sailing Analytics Multi-Servers |
|
| 92 | + |
|
| 93 | +You can try an [in-place upgrade](#log-on-with-ssh-and-use-package-manager-for-live-upgrade) for these. Should this, however, require a reboot, you should then apply the following procedure: |
|
| 94 | + |
|
| 95 | +To start with, make sure you have an up-to-date ``sailing-analytics-server`` AMI (see above). Also make sure that all auto-scaling groups for the application replica sets are set to use this latest AMI for any replicas launched by the auto-scaling group. (This can be achieved, e.g., using the AdminConsole's Landscape Management panel, and there the "Update machine image for auto-scaling replicas" button above the replica sets table.) |
|
| 96 | + |
|
| 97 | +Then, sort the replica sets table by the "Master Instance ID" column and identify the instances configured as "Multi-Server." Should this not be obvious, compare with the instances in the AWS EC2 console instance list named "SL Multi-Server". Click the "Move all application processes away from this replica set's master host to a new host" button. This will launch a new instance and move all application processes away from the old host, one by one. All replication aspects are handled automatically. The duration of migration varies depending on the content volumes hosted by the respective replica set. An empty replica sets migrates in a few minutes. Large replica sets may take an hour or more to migrate. The AdminConsole process may run into a timeout, but don't worry, the migration continues all the way to the end regardess of the web UI timeout. |
|
| 98 | + |
|
| 99 | +Still, should something take suspiciously long, maybe check the server logs of the server you used the Landscape Management panel on (usually ``security-service.sapsailing.com``). The ``logs/sailing.log.0`` file may show details of what went wrong or is taking long. Sometimes it may be the loading of one or more races that fails and doesn't let the instance report a healthy status. In such cases, a manual restart of that process may help, cd'ing into its folder and running ``./stop; ./start`` explicitly. |
|
| 100 | + |
|
| 101 | +### MariaDB |
|
| 102 | + |
|
| 103 | +This is a clear candidate for an in-place upgrade. Should a reboot be required, just reboot. It only takes about 10s, and for Bugzilla as system used mostly internally we can afford a 10s unavailability period. |
|
| 104 | + |
|
| 105 | +### RabbitMQ |
|
| 106 | + |
|
| 107 | +This is Debian-based. Try to go for an in-place upgrade. Should a reboot be required, ideally choose a time outside of major events and ongoing instance upgrades as those will require the RabbitMQ service to succeed. |
|
| 108 | + |
|
| 109 | +### MongoDB Replica Sets |
|
| 110 | + |
|
| 111 | +We currently have three MongoDB instances running in our EC2 landscape: ``[dbserver|mongo0|mongo1].internal.sapsailing.com``. The first hosts three ``mongod`` processes, for three separate replica sets: the single primary of the ``archive`` and the ``slow`` replica sets, and a hidden replica of the ``live`` replica set. The two other instance have primary/secondary ``mongod`` processes for the ``live`` replica set. Try an in-place upgrade first. If that doesn't require a reboot, you're done. |
|
| 112 | + |
|
| 113 | +If a reboot is required after an in-place upgrade, be gentle in how you carry out those reboots. The ``dbserver`` instance can be rebooted as long as no ARCHIVE server start-up is currently going on. During an ARCHIVE server start-up, failure to reach the database may lead to an incomplete state in the new ARCHIVE candidate which may require you to start over with the anyhow very time-consuming ARCHIVE start-up. The ``slow`` replica set and the hidden ``live`` replica, however, pose no obstacles regarding a reboot. |
|
| 114 | + |
|
| 115 | +For ``mongo0`` and ``mongo``, log on as ``ec2-user`` using SSH and use ``mongosh`` to see whether that instance is currently PRIMARY or SECONDARY. Reboot the SECONDARY first. When that has completed, SSH into the PRIMARY and in ``mongosh`` issue the command ``rs.stepDown()`` so that the PRIMARY becomes a SECONDARY, and the other instance that previously was a SECONDARY takes over as the new PRIMARY. With this courtesy, ongoing writing transactions will not even have to go through a re-try as you reboot the now SECONDAY instance. |
|
| 116 | + |
|
| 117 | +Should you choose to work with an upgraded ``mongodb-server`` AMI and the AdminConsole's Landscape Management panel, use the "Scale in/out" action on the respective MongoDB replica set to add new instances launched off the new AMI, then, once healthy, scale in to delete the old instances. This way you can handle the ``mongo0`` and ``mongo1`` instances. You should, however, have to adjust the DNS records in Route53 for ``mongo[01].internal.sapsailing.com`` to reflect the new instances because despite all tags-based resource discovery there are still some older configuration and environments files around that bootstrap new application instances by explicitly referring to ``mongo0.internal.sapsailing.com`` and ``mongo1.internal.sapsailing.com`` as their MongoDB instances to use. |
|
| 118 | + |
|
| 119 | +Should you need to upgrade the central ``dbserver.internal.sapsailing.com`` instance without an in-place upgrade, use the ``configuration/environments_scripts/central_mongo_setup/setup-central-mongo-instance.sh`` script to produce a new instance. When it has launched the new instance, it prints detailed instructions to its standard output for how to unmount the data volumes from the old and mount them to the new instance, as well as which DNS actions to take in Route53 and how to name and tag the new instance. |
|
| 120 | + |
|
| 121 | +### Central Reverse Proxy |
|
| 122 | + |
|
| 123 | +The central reverse proxy, currently running as ``sapsailing.com``, can typically be upgraded in-place. Should a re-boot be required, launch a disposable reverse proxy in the same availability zone as the central reverse proxy first, using the AdminConsole's Landscape Management panel with its "Reverse proxies" table and the corresponding "Add" button. Once that new disposable reverse proxy is shown as healthy in the corresponding ``CentralWebServerHTTP-Dyn`` target group you can reboot the central reverse proxy. It will lead to less than 30s of downtime regarding [https://bugzilla.sapsailing.com](https://bugzilla.sapsailing.com) and [https://wiki.sapsailing.com](https://wiki.sapsailing.com), as well as our self-hosted Git repository which is still used for back-up and cross-synchronization with the checked-out workspace for our Wiki. |
|
| 124 | + |
|
| 125 | +Should an in-place upgrade not be what you want, look into the ``configuration/environments_scripts/central_reverse_proxy`` folder with its setup scripts. They automate most parts of providing a new central reverse proxy instance that has been set up with the latest Amazon Linux from scratch. You will have to carry out a few steps manually, e.g., in the AWS Console, and the scripts will tell you in their standard output what these are. |
|
| 126 | + |
|
| 127 | +When done, terminate the additional disposable reverse proxy that you launched in the central reverse proxy's availability zone. |
|
| 128 | + |
|
| 129 | +### Disposable Reverse Proxy |
|
| 130 | + |
|
| 131 | +Make sure you have an upgraded ``disposable-reverse-proxy`` AMI, then use the AdminConsole Landscape Management panel and its "Reverse proxies" section to launch one or more disposable reverse proxies with the "Add" button. The default instance type suggested is usually a good fit. Make sure to launch one per availability zone in which you'd like to replace an old reverse proxy. When your new reverse proxy is healthy (this includes an error code 503 which only indicates that the reverse proxy is not in the same availability zone as the currently active production ARCHIVE server), terminate the corresponding old reverse proxy. |
|
| ... | ... | \ No newline at end of file |
wiki/info/mobile/sailinsight-privacy-policy.md
| ... | ... | @@ -0,0 +1,77 @@ |
| 1 | +# Sail Insight Privacy Policy |
|
| 2 | + |
|
| 3 | +Sail Insight (or the “App”) is a mobile application that allows its users to manage, participate, and watch regattas. The App is licensed for publication by SAP S.E. to d-labs (datenschutz@d-labs.com, Jörn Hartwig, D‑LABS GmbH, Marlene-Dietrich-Allee 15, D-14482 Potsdam). |
|
| 4 | + |
|
| 5 | +This policy aims to help you better understand the App privacy processes and your rights in connection with your personal data. To sign up and use the App, you have to carefully read this policy and provide your consent for us to use your personal data in line with this policy. |
|
| 6 | + |
|
| 7 | +The term “personal data” or “personal information” used in this policy means any information relating to any natural person, who can be identified, directly or indirectly, in particular by reference to such information. |
|
| 8 | + |
|
| 9 | +## WHAT INFORMATION DOES THE APP COLLECT? |
|
| 10 | +The App collects and stores different types of personal data: |
|
| 11 | + |
|
| 12 | +- Data that is provided directly by you |
|
| 13 | +- Data produced during your usage of the App |
|
| 14 | +- Data received from the partners of the App and the third-parties |
|
| 15 | + |
|
| 16 | +User profile: The App collects data when you create or update your account. This may include name, email, and password. |
|
| 17 | + |
|
| 18 | +Data about boats: You may provide the App with the data about your boat (type, registration number, boat rating, scoring, scoring group) or upload it from an external website. |
|
| 19 | + |
|
| 20 | +Location data: Once you grant the App access to your geolocation (and only in-between the timepoints when the user selects “start tracking” and “stop tracking), it may collect location data from your mobile device. The location data is collected when the app is in the foreground and when it’s running in the background. If you don’t grant the App access to the geolocation data, this may affect some functionality of the App. |
|
| 21 | + |
|
| 22 | +Usage data: The App collects data about how you and other users interact with the application. This includes data such as regattas that you participated in, classes, marks, courses, races you’ve created. It may also include specific technical data such as your access dates and times, app features, or pages viewed, app crashes. |
|
| 23 | + |
|
| 24 | +Device data: the App may collect data about the devices used to access the application, operating systems and versions, software, preferred languages, and other system settings of your device. |
|
| 25 | + |
|
| 26 | +## HOW DO WE USE THE INFORMATION? |
|
| 27 | +Initiate, participate, and watch regattas. The App collects and uses personal data to allow you to use the functionality of the application, personalize, maintain, and improve it. |
|
| 28 | + |
|
| 29 | +Marketing. The App may use your personal data for marketing purposes, which may include communication with the user about newsletters, upcoming events, new functionality, promotions. |
|
| 30 | + |
|
| 31 | +Other communications. The App may inform you about changes in the policies, EULA services, or send additional information about the application that is not intended for marketing purposes. |
|
| 32 | + |
|
| 33 | +Research and development. We may use the data we collect for development, testing, research, to improve the user experience. |
|
| 34 | + |
|
| 35 | +## GROUNDS FOR PROCESSING |
|
| 36 | +The App collects and uses personal data based on lawful grounds, that generally include processing personal data to provide you and other users with services and features or/and upon your direct consent. |
|
| 37 | + |
|
| 38 | +## HOW LONG DO WE KEEP YOUR DATA? |
|
| 39 | +We may store and use anonymized data for our Research and Development activities and public regatta statistics. Request for deletion of any personal data shall be sent to support@sapsailing.com. |
|
| 40 | + |
|
| 41 | +## THIRD PARTIES THAT HAVE ACCESS TO YOUR PERSONAL DATA |
|
| 42 | +The App may share your personal information with its partners and other third parties. |
|
| 43 | + |
|
| 44 | +The App has specific agreements with third parties who have access to your personal data. Such agreements include sufficient measures (1) to safeguard your personal data, your rights, freedoms, legitimate interest with regard to such information, (2) to prevent abuse or unlawful access or transfer of your personal data. |
|
| 45 | + |
|
| 46 | +## WHO MAY HAVE ACCESS TO YOUR DATA? |
|
| 47 | +Additionally, we may disclose specific types of personal data to third parties in the following cases: |
|
| 48 | + |
|
| 49 | +- To the purchaser or seller of our business; |
|
| 50 | +- To the extent that we are required to do so by law; |
|
| 51 | +- In the enforcement and/or court procedure and/or when it is required to disclose to a governmental agency in response to a valid court order or subpoena or similar requirement of the court or other regulatory bodies of other countries; |
|
| 52 | +- To establish, exercise, or defend the legal rights of our business, employees, and authorized legal representatives. |
|
| 53 | + |
|
| 54 | +## WHERE DO WE STORE YOUR PERSONAL INFORMATION? |
|
| 55 | +Your personal data is being stored on AWS servers. You can read more about AWS GDPR policies here https://aws.amazon.com/compliance/gdpr-center/. |
|
| 56 | + |
|
| 57 | +The App is exercising and implementing all the sufficient and suitable technical and organizational safeguards and measures needed to provide secure processing and storage of your data in accordance with GDPR and trying to ensure that the third parties that receive your personal data also adhere to the GDPR. |
|
| 58 | + |
|
| 59 | +## YOUR RIGHTS |
|
| 60 | +Given that this version of the App is a beta release that includes separate specific functionality for limited distribution among selected users functionality that allows exercising the rights of the data subjects established by GDPR is not realised in full. The subsequent versions of the application as soon as they are available will include the full functionality that will ensure GDPR compliance of the application as listed below. |
|
| 61 | + |
|
| 62 | +- To request to amend/renew the inaccurate and/or incomplete personal data concerning you; |
|
| 63 | +- To delete your personal data, partially or fully |
|
| 64 | +- To restrict the processing of your personal data; |
|
| 65 | +- To object to the processing of your personal data for profiling to the extent that it is related to direct marketing; |
|
| 66 | +- To request to familiarize and access the personal data that was collected. |
|
| 67 | +- You may submit to us a request to exercise any of the abovementioned rights. We will review your request within 30 calendar days and notify you of the results of its consideration. |
|
| 68 | +- For any requests connected with the rights listed above please contact the person that invited you to test the application. |
|
| 69 | + |
|
| 70 | +## LINKS TO THE SITES OF THE THIRD PARTIES |
|
| 71 | +The App may, from time to time, contain links to websites of third parties. Please note that we are not responsible for the content, terms of use and/or service as well as for the privacy policies of these websites. |
|
| 72 | + |
|
| 73 | +## MODIFICATIONS AND AMENDMENTS |
|
| 74 | +We may amend this privacy policy at any time by posting a new version and sending you a separate notification via email. It will become effective at the time it will be published. If you continue to use the App, it will be regarded as an acceptance of the amended policy. |
|
| 75 | + |
|
| 76 | +## CONTACT |
|
| 77 | +If you have any complaints about the policy and your personal data, please contact us at support@sapsailing.com |