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
![Figure 4: Link all races covered by the video](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/linking%20race%20videos/multi_sync.png)
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