1e0ebdd5abf107bc9565551103662a2f7461496b
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
| ... | ... | @@ -1418,8 +1418,4 @@ public class MockedTrackedRace implements DynamicTrackedRace { |
| 1418 | 1418 | WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { |
| 1419 | 1419 | return null; |
| 1420 | 1420 | } |
| 1421 | - |
|
| 1422 | - @Override |
|
| 1423 | - public void updateManeuvers(Competitor competitor, Iterable<Maneuver> maneuvers) { |
|
| 1424 | - } |
|
| 1425 | 1421 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverhash/ManeuverRaceFingerprint.java
| ... | ... | @@ -1,24 +1,12 @@ |
| 1 | 1 | package com.sap.sailing.domain.maneuverhash; |
| 2 | 2 | |
| 3 | - |
|
| 4 | -import org.json.simple.JSONObject; |
|
| 5 | - |
|
| 6 | -import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 7 | - |
|
| 8 | -public interface ManeuverRaceFingerprint { |
|
| 9 | - |
|
| 10 | - /** |
|
| 11 | - * Returns a {@link JSONObject} of the hash values. |
|
| 12 | - */ |
|
| 13 | - JSONObject toJson(); |
|
| 14 | - |
|
| 15 | - /** |
|
| 16 | - * Incrementally computes the composite fingerprint of the {@code trackedRace} and compares to this fingerprint |
|
| 17 | - * component by component. The fingerprint components are computed in ascending order of computational complexity, |
|
| 18 | - * trying to fail early / fast.<p> |
|
| 19 | - * |
|
| 20 | - * @return {@code true} if the {@code trackedRace} produces a fingerprint equal to this one if passed to |
|
| 21 | - * {@link ManeuverRaceFingerprintFactory#createFingerprint(TrackedRace)} |
|
| 22 | - */ |
|
| 23 | - boolean matches(TrackedRace trackedRace); |
|
| 3 | +import com.sap.sailing.domain.base.Competitor; |
|
| 4 | +import com.sap.sailing.domain.tracking.Maneuver; |
|
| 5 | +import com.sap.sailing.domain.tracking.RaceFingerprint; |
|
| 6 | + |
|
| 7 | +/** |
|
| 8 | + * Contains fingerprint data that encodes the state of the race relevant for computing all {@link Maneuver}s |
|
| 9 | + * for all {@link Competitor}s. |
|
| 10 | + */ |
|
| 11 | +public interface ManeuverRaceFingerprint extends RaceFingerprint { |
|
| 24 | 12 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverhash/ManeuverRaceFingerprintFactory.java
| ... | ... | @@ -1,13 +1,8 @@ |
| 1 | 1 | package com.sap.sailing.domain.maneuverhash; |
| 2 | 2 | |
| 3 | -import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 4 | 3 | import com.sap.sailing.domain.maneuverhash.impl.ManeuverRaceFingerprintFactoryImpl; |
| 5 | -import org.json.simple.JSONObject; |
|
| 4 | +import com.sap.sailing.domain.tracking.RaceFingerprintFactory; |
|
| 6 | 5 | |
| 7 | -public interface ManeuverRaceFingerprintFactory { |
|
| 6 | +public interface ManeuverRaceFingerprintFactory extends RaceFingerprintFactory<ManeuverRaceFingerprint> { |
|
| 8 | 7 | ManeuverRaceFingerprintFactory INSTANCE = new ManeuverRaceFingerprintFactoryImpl(); |
| 9 | - |
|
| 10 | - ManeuverRaceFingerprint createFingerprint (TrackedRace TrackedRace); |
|
| 11 | - |
|
| 12 | - ManeuverRaceFingerprint fromJson (JSONObject json); |
|
| 13 | 8 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverhash/impl/ManeuverRaceFingerprintImpl.java
| ... | ... | @@ -4,228 +4,56 @@ import java.util.Set; |
| 4 | 4 | |
| 5 | 5 | import org.json.simple.JSONObject; |
| 6 | 6 | |
| 7 | -import com.sap.sailing.domain.abstractlog.race.RaceLog; |
|
| 8 | -import com.sap.sailing.domain.abstractlog.race.analyzing.impl.MarkPassingDataFinder; |
|
| 9 | -import com.sap.sailing.domain.base.Competitor; |
|
| 10 | -import com.sap.sailing.domain.base.Mark; |
|
| 11 | -import com.sap.sailing.domain.base.Waypoint; |
|
| 12 | 7 | import com.sap.sailing.domain.common.Wind; |
| 13 | 8 | import com.sap.sailing.domain.common.WindSource; |
| 14 | -import com.sap.sailing.domain.common.tracking.GPSFix; |
|
| 15 | -import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 16 | 9 | import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; |
| 17 | 10 | import com.sap.sailing.domain.maneuverhash.ManeuverRaceFingerprint; |
| 18 | -import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
|
| 19 | -import com.sap.sailing.domain.tracking.GPSFixTrack; |
|
| 11 | +import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintImpl; |
|
| 20 | 12 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 21 | 13 | import com.sap.sailing.domain.tracking.WindTrack; |
| 22 | -import com.sap.sse.common.TimePoint; |
|
| 23 | 14 | import com.sap.sse.common.Util; |
| 24 | -import com.sap.sse.common.Util.Pair; |
|
| 25 | -import com.sap.sse.common.Util.Triple; |
|
| 26 | 15 | |
| 27 | -public class ManeuverRaceFingerprintImpl implements ManeuverRaceFingerprint { |
|
| 28 | - private final int calculatorVersion; |
|
| 29 | - private final int competitorHash; |
|
| 30 | - private final TimePoint startOfTracking; |
|
| 31 | - private final TimePoint startTimeReceived; |
|
| 32 | - private final TimePoint endOfTracking; |
|
| 33 | - private final TimePoint startTimeFromRaceLog; |
|
| 34 | - private final TimePoint finishTimeFromRaceLog; |
|
| 35 | - private final int fixedAndSuppressedMarkPassingsFromRaceLogHash; |
|
| 36 | - private final int waypointsHash; |
|
| 37 | - private final int numberOfGPSFixes; |
|
| 38 | - private final int gpsFixesHash; |
|
| 16 | +public class ManeuverRaceFingerprintImpl extends MarkPassingRaceFingerprintImpl implements ManeuverRaceFingerprint { |
|
| 39 | 17 | private final int windHash; |
| 40 | 18 | private final int detectorVersion; |
| 41 | 19 | |
| 42 | 20 | private static enum JSON_FIELDS { |
| 43 | - COMPETITOR_HASH, START_OF_TRACKING_AS_MILLIS, END_OF_TRACKING_AS_MILLIS, START_TIME_RECEIVED_AS_MILLIS, |
|
| 44 | - START_TIME_FROM_RACE_LOG_AS_MILLIS, FINISH_TIME_FROM_RACE_LOG_AS_MILLIS, WAYPOINTS_HASH, NUMBEROFGPSFIXES, |
|
| 45 | - GPSFIXES_HASH, RACE_ID, CALCULATOR_VERSION, FIXED_AND_SUPPRESSED_MARK_PASSINGS_FROM_RACE_LOG_HASH, WIND_HASH, DETECTOR_VERSION |
|
| 21 | + WIND_HASH, DETECTOR_VERSION |
|
| 46 | 22 | }; |
| 47 | 23 | |
| 48 | 24 | public ManeuverRaceFingerprintImpl(TrackedRace trackedRace) { |
| 25 | + super(trackedRace); |
|
| 49 | 26 | this.detectorVersion = ManeuverDetectorImpl.DETECTOR_VERSION; |
| 50 | - this.calculatorVersion = MarkPassingCalculator.CALCULATOR_VERSION; |
|
| 51 | - this.competitorHash = calculateHashForCompetitors(trackedRace); |
|
| 52 | - this.startOfTracking = trackedRace.getStartOfTracking(); |
|
| 53 | - this.endOfTracking = trackedRace.getEndOfTracking(); |
|
| 54 | - this.startTimeReceived = trackedRace.getStartTimeReceived(); |
|
| 55 | - final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace |
|
| 56 | - .getStartAndFinishedTimeFromRaceLogs(); |
|
| 57 | - this.startTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null |
|
| 58 | - : startAndFinishedTimeFromRaceLogs.getA(); |
|
| 59 | - this.finishTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null |
|
| 60 | - : startAndFinishedTimeFromRaceLogs.getB(); |
|
| 61 | - this.waypointsHash = calculateHashForWaypoints(trackedRace); |
|
| 62 | - this.numberOfGPSFixes = calculateHashForNumberOfGPSFixes(trackedRace); |
|
| 63 | - this.gpsFixesHash = calculateHashForGPSFixes(trackedRace); |
|
| 64 | - this.fixedAndSuppressedMarkPassingsFromRaceLogHash = calculateFixedAndSuppressedMarkPassingsFromRaceLogHash(trackedRace); |
|
| 65 | 27 | this.windHash = calculateWindHash(trackedRace); |
| 66 | 28 | } |
| 67 | 29 | |
| 68 | - private int calculateFixedAndSuppressedMarkPassingsFromRaceLogHash(TrackedRace trackedRace) { |
|
| 69 | - int result = 1023; |
|
| 70 | - for (final RaceLog raceLog : trackedRace.getAttachedRaceLogs()) { |
|
| 71 | - for (final Triple<Competitor, Integer, TimePoint> triple : new MarkPassingDataFinder(raceLog).analyze()) { |
|
| 72 | - result ^= triple.getA().getId().hashCode(); |
|
| 73 | - result ^= triple.getB(); |
|
| 74 | - if (triple.getC() != null) { |
|
| 75 | - result ^= triple.getC().hashCode(); |
|
| 76 | - } |
|
| 77 | - } |
|
| 78 | - } |
|
| 79 | - return result; |
|
| 80 | - } |
|
| 81 | - |
|
| 82 | 30 | public ManeuverRaceFingerprintImpl(JSONObject json) { |
| 31 | + super(json); |
|
| 83 | 32 | this.detectorVersion = ((Number) json.get(JSON_FIELDS.DETECTOR_VERSION.name())).intValue(); |
| 84 | - this.calculatorVersion = ((Number) json.get(JSON_FIELDS.CALCULATOR_VERSION.name())).intValue(); |
|
| 85 | - this.competitorHash = ((Number) json.get(JSON_FIELDS.COMPETITOR_HASH.name())).intValue(); |
|
| 86 | - this.startOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name())); |
|
| 87 | - this.endOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name())); |
|
| 88 | - this.startTimeReceived = TimePoint.of((Long) json.get(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name())); |
|
| 89 | - this.startTimeFromRaceLog = TimePoint |
|
| 90 | - .of((Long) json.get(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name())); |
|
| 91 | - this.finishTimeFromRaceLog = TimePoint |
|
| 92 | - .of((Long) json.get(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name())); |
|
| 93 | - this.waypointsHash = ((Number) json.get(JSON_FIELDS.WAYPOINTS_HASH.name())).intValue(); |
|
| 94 | - this.numberOfGPSFixes = ((Number) json.get(JSON_FIELDS.NUMBEROFGPSFIXES.name())).intValue(); |
|
| 95 | - this.gpsFixesHash = ((Number) json.get(JSON_FIELDS.GPSFIXES_HASH.name())).intValue(); |
|
| 96 | - this.fixedAndSuppressedMarkPassingsFromRaceLogHash = ((Number) json.get(JSON_FIELDS.FIXED_AND_SUPPRESSED_MARK_PASSINGS_FROM_RACE_LOG_HASH.name())).intValue(); |
|
| 97 | 33 | this.windHash = ((Number) json.get(JSON_FIELDS.WIND_HASH.name())).intValue(); |
| 98 | 34 | } |
| 99 | 35 | |
| 100 | 36 | @Override |
| 101 | 37 | public JSONObject toJson() { |
| 102 | - JSONObject result = new JSONObject(); |
|
| 38 | + final JSONObject result = super.toJson(); |
|
| 103 | 39 | result.put(JSON_FIELDS.DETECTOR_VERSION.name(), detectorVersion); |
| 104 | - result.put(JSON_FIELDS.CALCULATOR_VERSION.name(), calculatorVersion); |
|
| 105 | - result.put(JSON_FIELDS.COMPETITOR_HASH.name(), competitorHash); |
|
| 106 | - result.put(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name(), |
|
| 107 | - startOfTracking == null ? null : startOfTracking.asMillis()); |
|
| 108 | - result.put(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name(), |
|
| 109 | - endOfTracking == null ? null : endOfTracking.asMillis()); |
|
| 110 | - result.put(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name(), |
|
| 111 | - startTimeReceived == null ? null : startTimeReceived.asMillis()); |
|
| 112 | - result.put(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name(), |
|
| 113 | - startTimeFromRaceLog == null ? null : startTimeFromRaceLog.asMillis()); |
|
| 114 | - result.put(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name(), |
|
| 115 | - finishTimeFromRaceLog == null ? null : finishTimeFromRaceLog.asMillis()); |
|
| 116 | - result.put(JSON_FIELDS.WAYPOINTS_HASH.name(), waypointsHash); |
|
| 117 | - result.put(JSON_FIELDS.NUMBEROFGPSFIXES.name(), numberOfGPSFixes); |
|
| 118 | - result.put(JSON_FIELDS.GPSFIXES_HASH.name(), gpsFixesHash); |
|
| 119 | - result.put(JSON_FIELDS.FIXED_AND_SUPPRESSED_MARK_PASSINGS_FROM_RACE_LOG_HASH.name(), fixedAndSuppressedMarkPassingsFromRaceLogHash); |
|
| 120 | 40 | result.put(JSON_FIELDS.WIND_HASH.name(), windHash); |
| 121 | 41 | return result; |
| 122 | 42 | } |
| 123 | 43 | |
| 124 | 44 | @Override |
| 125 | 45 | public boolean matches(TrackedRace trackedRace) { |
| 126 | - final boolean result; |
|
| 127 | - if (!Util.equalsWithNull(calculatorVersion, MarkPassingCalculator.CALCULATOR_VERSION)) { |
|
| 128 | - result = false; |
|
| 129 | - }else if (!Util.equalsWithNull(detectorVersion, ManeuverDetectorImpl.DETECTOR_VERSION)) { |
|
| 130 | - result = false; |
|
| 131 | - } else if (!Util.equalsWithNull(startOfTracking, trackedRace.getStartOfTracking())) { |
|
| 132 | - result = false; |
|
| 133 | - } else if (!Util.equalsWithNull(endOfTracking, trackedRace.getEndOfTracking())) { |
|
| 134 | - result = false; |
|
| 135 | - } else if (!Util.equalsWithNull(startTimeReceived, trackedRace.getStartTimeReceived())) { |
|
| 136 | - result = false; |
|
| 137 | - } else { |
|
| 138 | - final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace |
|
| 139 | - .getStartAndFinishedTimeFromRaceLogs(); |
|
| 140 | - if (!Util.equalsWithNull(startTimeFromRaceLog, |
|
| 141 | - startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getA())) { |
|
| 142 | - result = false; |
|
| 143 | - } else if (!Util.equalsWithNull(finishTimeFromRaceLog, |
|
| 144 | - startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getB())) { |
|
| 145 | - result = false; |
|
| 146 | - } else if (waypointsHash != calculateHashForWaypoints(trackedRace)) { |
|
| 147 | - result = false; |
|
| 148 | - } else if (competitorHash != calculateHashForCompetitors(trackedRace)) { |
|
| 149 | - result = false; |
|
| 150 | - } else if (numberOfGPSFixes != calculateHashForNumberOfGPSFixes(trackedRace)) { |
|
| 151 | - result = false; |
|
| 152 | - } else if (fixedAndSuppressedMarkPassingsFromRaceLogHash != calculateFixedAndSuppressedMarkPassingsFromRaceLogHash(trackedRace)) { |
|
| 153 | - result = false; |
|
| 154 | - } else if (gpsFixesHash != calculateHashForGPSFixes(trackedRace)) { |
|
| 46 | + boolean result = super.matches(trackedRace); |
|
| 47 | + if (result) { |
|
| 48 | + if (!Util.equalsWithNull(detectorVersion, ManeuverDetectorImpl.DETECTOR_VERSION)) { |
|
| 155 | 49 | result = false; |
| 156 | 50 | } else if (windHash != calculateWindHash(trackedRace)) { |
| 157 | 51 | result = false; |
| 158 | - }else { |
|
| 159 | - result = true; |
|
| 160 | 52 | } |
| 161 | 53 | } |
| 162 | 54 | return result; |
| 163 | 55 | } |
| 164 | 56 | |
| 165 | - private int calculateHashForCompetitors(TrackedRace trackedRace) { |
|
| 166 | - int hashForCompetitors = 1023; |
|
| 167 | - for (Competitor c : trackedRace.getRace().getCompetitors()) { |
|
| 168 | - hashForCompetitors = hashForCompetitors ^ c.getId().hashCode(); |
|
| 169 | - } |
|
| 170 | - return hashForCompetitors; |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - private int calculateHashForNumberOfGPSFixes(TrackedRace trackedRace) { |
|
| 174 | - int count = 0; |
|
| 175 | - for (Mark m : trackedRace.getMarks()) { |
|
| 176 | - count += trackedRace.getTrack(m).size(); |
|
| 177 | - } |
|
| 178 | - for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { |
|
| 179 | - count += trackedRace.getTrack(competitor).size(); |
|
| 180 | - } |
|
| 181 | - return count; |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - private int calculateHashForGPSFixes(TrackedRace trackedRace) { |
|
| 185 | - int res = 511; |
|
| 186 | - for (Mark m : trackedRace.getMarks()) { |
|
| 187 | - final GPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getTrack(m); |
|
| 188 | - markTrack.lockForRead(); |
|
| 189 | - try { |
|
| 190 | - for (GPSFix gf : markTrack.getRawFixes()) { |
|
| 191 | - res = res ^ gf.getTimePoint().hashCode(); |
|
| 192 | - res = res ^ gf.getPosition().hashCode(); |
|
| 193 | - } |
|
| 194 | - } finally { |
|
| 195 | - markTrack.unlockAfterRead(); |
|
| 196 | - } |
|
| 197 | - } |
|
| 198 | - for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { |
|
| 199 | - final GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(competitor); |
|
| 200 | - competitorTrack.lockForRead(); |
|
| 201 | - try { |
|
| 202 | - for (GPSFixMoving gfm : competitorTrack.getRawFixes()) { |
|
| 203 | - res = res ^ gfm.getTimePoint().hashCode(); |
|
| 204 | - res = res ^ gfm.getPosition().hashCode(); |
|
| 205 | - res = res ^ gfm.getSpeed().getBearing().hashCode(); |
|
| 206 | - res = res ^ Double.hashCode(gfm.getSpeed().getKnots()); |
|
| 207 | - } |
|
| 208 | - } finally { |
|
| 209 | - competitorTrack.unlockAfterRead(); |
|
| 210 | - } |
|
| 211 | - } |
|
| 212 | - return res; |
|
| 213 | - } |
|
| 214 | - |
|
| 215 | - private int calculateHashForWaypoints(TrackedRace trackedRace) { |
|
| 216 | - Iterable<Waypoint> waypoints = trackedRace.getRace().getCourse().getWaypoints(); |
|
| 217 | - int res = 0; |
|
| 218 | - for (Waypoint p : waypoints) { |
|
| 219 | - Iterable<Mark> marks = p.getMarks(); |
|
| 220 | - for (Mark m : marks) { |
|
| 221 | - res = res ^ m.getId().hashCode(); |
|
| 222 | - } |
|
| 223 | - res = res ^ p.getPassingInstructions().name().hashCode(); |
|
| 224 | - res = (res << 5) - res; // we want to detect changes in order |
|
| 225 | - } |
|
| 226 | - return res; |
|
| 227 | - } |
|
| 228 | - |
|
| 229 | 57 | private int calculateWindHash(TrackedRace trackedRace) { |
| 230 | 58 | Set<WindSource> windSoures = trackedRace.getWindSources(); |
| 231 | 59 | int res = 0; |
| ... | ... | @@ -254,19 +82,8 @@ public class ManeuverRaceFingerprintImpl implements ManeuverRaceFingerprint { |
| 254 | 82 | @Override |
| 255 | 83 | public int hashCode() { |
| 256 | 84 | final int prime = 31; |
| 257 | - int result = 1; |
|
| 258 | - result = prime * result + calculatorVersion; |
|
| 85 | + int result = super.hashCode(); |
|
| 259 | 86 | result = prime * result + detectorVersion; |
| 260 | - result = prime * result + competitorHash; |
|
| 261 | - result = prime * result + ((endOfTracking == null) ? 0 : endOfTracking.hashCode()); |
|
| 262 | - result = prime * result + ((finishTimeFromRaceLog == null) ? 0 : finishTimeFromRaceLog.hashCode()); |
|
| 263 | - result = prime * result + gpsFixesHash; |
|
| 264 | - result = prime * result + numberOfGPSFixes; |
|
| 265 | - result = prime * result + ((startOfTracking == null) ? 0 : startOfTracking.hashCode()); |
|
| 266 | - result = prime * result + ((startTimeFromRaceLog == null) ? 0 : startTimeFromRaceLog.hashCode()); |
|
| 267 | - result = prime * result + ((startTimeReceived == null) ? 0 : startTimeReceived.hashCode()); |
|
| 268 | - result = prime * result + waypointsHash; |
|
| 269 | - result = prime * result + fixedAndSuppressedMarkPassingsFromRaceLogHash; |
|
| 270 | 87 | result = prime * result + windHash; |
| 271 | 88 | return result; |
| 272 | 89 | } |
| ... | ... | @@ -279,46 +96,11 @@ public class ManeuverRaceFingerprintImpl implements ManeuverRaceFingerprint { |
| 279 | 96 | return false; |
| 280 | 97 | if (getClass() != obj.getClass()) |
| 281 | 98 | return false; |
| 282 | - ManeuverRaceFingerprintImpl other = (ManeuverRaceFingerprintImpl) obj; |
|
| 283 | - if (calculatorVersion != other.calculatorVersion) |
|
| 99 | + if (!super.equals(obj)) |
|
| 284 | 100 | return false; |
| 101 | + ManeuverRaceFingerprintImpl other = (ManeuverRaceFingerprintImpl) obj; |
|
| 285 | 102 | if (detectorVersion != other.detectorVersion) |
| 286 | 103 | return false; |
| 287 | - if (competitorHash != other.competitorHash) |
|
| 288 | - return false; |
|
| 289 | - if (endOfTracking == null) { |
|
| 290 | - if (other.endOfTracking != null) |
|
| 291 | - return false; |
|
| 292 | - } else if (!endOfTracking.equals(other.endOfTracking)) |
|
| 293 | - return false; |
|
| 294 | - if (finishTimeFromRaceLog == null) { |
|
| 295 | - if (other.finishTimeFromRaceLog != null) |
|
| 296 | - return false; |
|
| 297 | - } else if (!finishTimeFromRaceLog.equals(other.finishTimeFromRaceLog)) |
|
| 298 | - return false; |
|
| 299 | - if (gpsFixesHash != other.gpsFixesHash) |
|
| 300 | - return false; |
|
| 301 | - if (numberOfGPSFixes != other.numberOfGPSFixes) |
|
| 302 | - return false; |
|
| 303 | - if (startOfTracking == null) { |
|
| 304 | - if (other.startOfTracking != null) |
|
| 305 | - return false; |
|
| 306 | - } else if (!startOfTracking.equals(other.startOfTracking)) |
|
| 307 | - return false; |
|
| 308 | - if (startTimeFromRaceLog == null) { |
|
| 309 | - if (other.startTimeFromRaceLog != null) |
|
| 310 | - return false; |
|
| 311 | - } else if (!startTimeFromRaceLog.equals(other.startTimeFromRaceLog)) |
|
| 312 | - return false; |
|
| 313 | - if (startTimeReceived == null) { |
|
| 314 | - if (other.startTimeReceived != null) |
|
| 315 | - return false; |
|
| 316 | - } else if (!startTimeReceived.equals(other.startTimeReceived)) |
|
| 317 | - return false; |
|
| 318 | - if (waypointsHash != other.waypointsHash) |
|
| 319 | - return false; |
|
| 320 | - if (fixedAndSuppressedMarkPassingsFromRaceLogHash != other.fixedAndSuppressedMarkPassingsFromRaceLogHash) |
|
| 321 | - return false; |
|
| 322 | 104 | if (windHash != other.windHash) |
| 323 | 105 | return false; |
| 324 | 106 | return true; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverhash/impl/ManeuversFromDatabase.java
| ... | ... | @@ -8,20 +8,28 @@ import java.util.logging.Logger; |
| 8 | 8 | import com.sap.sailing.domain.base.Competitor; |
| 9 | 9 | import com.sap.sailing.domain.maneuverhash.ManeuverCache; |
| 10 | 10 | import com.sap.sailing.domain.tracking.Maneuver; |
| 11 | +import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 11 | 12 | import com.sap.sse.util.SmartFutureCache.EmptyUpdateInterval; |
| 12 | 13 | |
| 14 | +/** |
|
| 15 | + * Stores a {@link TrackedRace}'s {@link Maneuver}s after they were loaded successfully from the persistent store. |
|
| 16 | + * This happens when a race was considered equal regarding its "fingerprint" to a version loaded previously with |
|
| 17 | + * all maneuvers computed and stored persistently. This saves the computational effort to compute the maneuvers |
|
| 18 | + * again (persistent caching).<p> |
|
| 19 | + * |
|
| 20 | + * This class collaborates with {@link ManeuverCacheDelegate}. |
|
| 21 | + */ |
|
| 13 | 22 | public class ManeuversFromDatabase implements ManeuverCache<Competitor, List<Maneuver>, EmptyUpdateInterval> { |
| 14 | - |
|
| 23 | + boolean suspended; |
|
| 24 | + private static final Logger logger = Logger.getLogger(ManeuversFromDatabase.class.getName()); |
|
| 25 | + private final Map<Competitor, List<Maneuver>> maneuvers; |
|
| 26 | + |
|
| 15 | 27 | public ManeuversFromDatabase( |
| 16 | 28 | Map<Competitor, List<Maneuver>> maneuvers) { |
| 17 | 29 | super(); |
| 18 | 30 | this.maneuvers = maneuvers; |
| 19 | 31 | } |
| 20 | 32 | |
| 21 | - boolean suspended; |
|
| 22 | - private static final Logger logger = Logger.getLogger(ManeuversFromDatabase.class.getName()); |
|
| 23 | - private final Map<Competitor, List<Maneuver>> maneuvers; |
|
| 24 | - |
|
| 25 | 33 | public void resume() { |
| 26 | 34 | logger.log(Level.WARNING, "Method should never be called"); |
| 27 | 35 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java
| ... | ... | @@ -461,8 +461,7 @@ public class MarkPassingCalculator { |
| 461 | 461 | fixesForCompetitor.addAll(competitorEntry.getValue()); |
| 462 | 462 | } |
| 463 | 463 | if (!newMarkFixes.isEmpty()) { |
| 464 | - // FIXME bug 2745 use new mark fixes to invalidate chooser's mark position and mutual mark/waypoint |
|
| 465 | - // distance cache |
|
| 464 | + // FIXME bug 2745 use new mark fixes to invalidate chooser's mark position and mutual mark/waypoint distance cache |
|
| 466 | 465 | for (Entry<Competitor, List<GPSFixMoving>> fixesAffectedByNewMarkFixes : finder |
| 467 | 466 | .calculateFixesAffectedByNewMarkFixes(newMarkFixes).entrySet()) { |
| 468 | 467 | Collection<GPSFixMoving> fixes = combinedCompetitorFixesFinderConsidersAffected |
| ... | ... | @@ -614,7 +613,7 @@ public class MarkPassingCalculator { |
| 614 | 613 | try { |
| 615 | 614 | latchForRunningListenRun.await(); |
| 616 | 615 | final Map<Competitor, Map<Waypoint, MarkPassing>> markPassings = race.getMarkPassings(/* waitForLatestUpdates */ true); |
| 617 | - // Bei Live-Rennen Speicherung unnötig |
|
| 616 | + // TODO bug6182: do we need to store when we can assume that the race is still live? How to find out reliably? |
|
| 618 | 617 | markPassingRaceFingerprintRegistry.storeMarkPassings(race.getRaceIdentifier(), |
| 619 | 618 | MarkPassingRaceFingerprintFactory.INSTANCE.createFingerprint(race), |
| 620 | 619 | markPassings, race.getRace().getCourse()); |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassinghash/MarkPassingRaceFingerprint.java
| ... | ... | @@ -5,24 +5,28 @@ import org.json.simple.JSONObject; |
| 5 | 5 | import com.sap.sailing.domain.base.Course; |
| 6 | 6 | import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
| 7 | 7 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 8 | +import com.sap.sailing.domain.tracking.RaceFingerprint; |
|
| 8 | 9 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 9 | 10 | |
| 10 | 11 | /** |
| 11 | 12 | * An instance of this class represents a composite fingerprint of those components of a {@link TrackedRace} that are |
| 12 | - * relevant for computing the |
|
| 13 | + * relevant for computing |
|
| 13 | 14 | * {@link TrackedRace#getMarkPassing(com.sap.sailing.domain.base.Competitor, com.sap.sailing.domain.base.Waypoint) mark |
| 14 | - * passings} in the {@link MarkPassingCalculator}. It can be {@link #matches(TrackedRace) matched} against a {@link TrackedRace} |
|
| 15 | - * instance to see whether the {@link TrackedRace} will produce a set of {@link MarkPassing}s equal to that of the {@link TrackedRace} |
|
| 16 | - * from which this fingerprint was produced.<p> |
|
| 15 | + * passings} in the {@link MarkPassingCalculator}. It can be {@link #matches(TrackedRace) matched} against a |
|
| 16 | + * {@link TrackedRace} instance to see whether the {@link TrackedRace} will produce a set of {@link MarkPassing}s equal |
|
| 17 | + * to that of the {@link TrackedRace} from which this fingerprint was produced. |
|
| 18 | + * <p> |
|
| 17 | 19 | * |
| 18 | - * To produce a fingerprint from a {@link TrackedRace} or from a JSON representation use {@link MarkPassingRaceFingerprintFactory}.<p> |
|
| 20 | + * To produce a fingerprint from a {@link TrackedRace} or from a JSON representation use |
|
| 21 | + * {@link MarkPassingRaceFingerprintFactory}. |
|
| 22 | + * <p> |
|
| 19 | 23 | * |
| 20 | 24 | * The {@link #equals(Object)} and {@link #hashCode()} methods are defined based on the contents of this fingerprint. |
| 21 | 25 | * |
| 22 | 26 | * @author Fabian Kallenbach (i550803) |
| 23 | 27 | * @author Axel Uhl (d043530) |
| 24 | 28 | */ |
| 25 | -public interface MarkPassingRaceFingerprint { |
|
| 29 | +public interface MarkPassingRaceFingerprint extends RaceFingerprint { |
|
| 26 | 30 | /** |
| 27 | 31 | * Returns a {@link JSONObject} of the hash values. |
| 28 | 32 | */ |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassinghash/MarkPassingRaceFingerprintFactory.java
| ... | ... | @@ -1,26 +1,13 @@ |
| 1 | 1 | package com.sap.sailing.domain.markpassinghash; |
| 2 | 2 | |
| 3 | -import org.json.simple.JSONObject; |
|
| 4 | - |
|
| 5 | 3 | import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintFactoryImpl; |
| 6 | -import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 4 | +import com.sap.sailing.domain.tracking.RaceFingerprintFactory; |
|
| 7 | 5 | |
| 8 | 6 | /** |
| 9 | 7 | * Factory for the creation of a {@link MarkPassingRaceFingerprint}. |
| 10 | 8 | * |
| 11 | 9 | * @author Fabian Kallenbach (i550803) |
| 12 | 10 | */ |
| 13 | -public interface MarkPassingRaceFingerprintFactory { |
|
| 11 | +public interface MarkPassingRaceFingerprintFactory extends RaceFingerprintFactory<MarkPassingRaceFingerprint> { |
|
| 14 | 12 | MarkPassingRaceFingerprintFactory INSTANCE = new MarkPassingRaceFingerprintFactoryImpl(); |
| 15 | - |
|
| 16 | - /** |
|
| 17 | - * Creates a {@link MarkPassingRaceFingerprint} out of a given {@link TrackedRace}. |
|
| 18 | - */ |
|
| 19 | - MarkPassingRaceFingerprint createFingerprint(TrackedRace trackedRace); |
|
| 20 | - |
|
| 21 | - /** |
|
| 22 | - * Creates a {@link MarkPassingRaceFingerprint} out of a given {@link JSONObject}, as produced by |
|
| 23 | - * {@link MarkPassingRaceFingerprint#toJson()}. |
|
| 24 | - */ |
|
| 25 | - MarkPassingRaceFingerprint fromJson(JSONObject json); |
|
| 26 | 13 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java
| ... | ... | @@ -131,7 +131,7 @@ public interface DynamicTrackedRace extends TrackedRace { |
| 131 | 131 | * for the finish line that are affected will be {@link #updateMarkPassings(Competitor, Iterable) updated}. |
| 132 | 132 | */ |
| 133 | 133 | void updateMarkPassingsAfterRaceLogChanges(); |
| 134 | - |
|
| 134 | + |
|
| 135 | 135 | /** |
| 136 | 136 | * Sets the start time as received from the tracking infrastructure. This isn't necessarily |
| 137 | 137 | * what {@link #getStart()} will deliver which assumes that the time announced here may be |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceFingerprint.java
| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | +package com.sap.sailing.domain.tracking; |
|
| 2 | + |
|
| 3 | +import org.json.simple.JSONObject; |
|
| 4 | + |
|
| 5 | +import com.sap.sailing.domain.base.Course; |
|
| 6 | +import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
|
| 7 | +import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory; |
|
| 8 | + |
|
| 9 | +/** |
|
| 10 | + * An instance of this class represents a composite fingerprint of those components of a {@link TrackedRace} that are |
|
| 11 | + * relevant for computing some metric or value that causes significant computational effort and hence lends itself well |
|
| 12 | + * for persistent caching, such as mark passings or maneuvers. It can be {@link #matches(TrackedRace) matched} against a |
|
| 13 | + * {@link TrackedRace} instance to see whether the {@link TrackedRace} will produce a metric or value equal to that of |
|
| 14 | + * the {@link TrackedRace} from which this fingerprint was produced. |
|
| 15 | + * <p> |
|
| 16 | + * |
|
| 17 | + * To produce a fingerprint from a {@link TrackedRace} or from a JSON representation use a |
|
| 18 | + * {@link RaceFingerprintFactory}. |
|
| 19 | + * <p> |
|
| 20 | + * |
|
| 21 | + * The {@link #equals(Object)} and {@link #hashCode()} methods are defined based on the contents of this fingerprint. |
|
| 22 | + * |
|
| 23 | + * @author Fabian Kallenbach (i550803) |
|
| 24 | + * @author Axel Uhl (d043530) |
|
| 25 | + */ |
|
| 26 | +public interface RaceFingerprint { |
|
| 27 | + /** |
|
| 28 | + * Returns a {@link JSONObject} of the hash values. |
|
| 29 | + */ |
|
| 30 | + JSONObject toJson(); |
|
| 31 | + |
|
| 32 | + /** |
|
| 33 | + * Incrementally computes the composite fingerprint of the {@code trackedRace} and compares to this fingerprint |
|
| 34 | + * component by component. The fingerprint components are computed in ascending order of computational complexity, |
|
| 35 | + * trying to fail early / fast.<p> |
|
| 36 | + * |
|
| 37 | + * The implementation may require to obtain the race's {@link Course#lockForRead() read lock}. Note that conversely |
|
| 38 | + * updates to the course that happen under the course's write lock will trigger listeners which may synchronize |
|
| 39 | + * on certain objects, such as the {@link MarkPassingCalculator}. Therefore, should this method be called |
|
| 40 | + * while holding an object monitor ("synchronized") that a course update listener may also require, make |
|
| 41 | + * sure to first obtain at least the course read lock before invoking this method. See also bug 5803. |
|
| 42 | + * |
|
| 43 | + * @return {@code true} if the {@code trackedRace} produces a fingerprint equal to this one if passed to |
|
| 44 | + * {@link MarkPassingRaceFingerprintFactory#createFingerprint(TrackedRace)} |
|
| 45 | + */ |
|
| 46 | + boolean matches(TrackedRace trackedRace); |
|
| 47 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceFingerprintFactory.java
| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | +package com.sap.sailing.domain.tracking; |
|
| 2 | + |
|
| 3 | +import org.json.simple.JSONObject; |
|
| 4 | + |
|
| 5 | +import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint; |
|
| 6 | + |
|
| 7 | +/** |
|
| 8 | + * Factory for the creation of a {@link MarkPassingRaceFingerprint}. |
|
| 9 | + * |
|
| 10 | + * @author Fabian Kallenbach (i550803) |
|
| 11 | + */ |
|
| 12 | +public interface RaceFingerprintFactory<RFP extends RaceFingerprint> { |
|
| 13 | + /** |
|
| 14 | + * Creates a {@link RaceFingerprint} out of a given {@link TrackedRace}. |
|
| 15 | + */ |
|
| 16 | + RFP createFingerprint(TrackedRace trackedRace); |
|
| 17 | + |
|
| 18 | + /** |
|
| 19 | + * Creates a {@link RaceFingerprint} out of a given {@link JSONObject}, as produced by |
|
| 20 | + * {@link RaceFingerprint#toJson()}. |
|
| 21 | + */ |
|
| 22 | + RFP fromJson(JSONObject json); |
|
| 23 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java
| ... | ... | @@ -43,9 +43,11 @@ public interface RaceTrackingConnectivityParameters extends Serializable { |
| 43 | 43 | * tracker. |
| 44 | 44 | */ |
| 45 | 45 | RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, |
| 46 | - RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds, |
|
| 47 | - RaceTrackingHandler raceTrackingHandler, MarkPassingRaceFingerprintRegistry markPassingRaceFingerprintRegistry, ManeuverRaceFingerprintRegistry maneuverRaceFingerprintRegistry) throws Exception; |
|
| 48 | - |
|
| 46 | + RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, |
|
| 47 | + long timeoutInMilliseconds, RaceTrackingHandler raceTrackingHandler, |
|
| 48 | + MarkPassingRaceFingerprintRegistry markPassingRaceFingerprintRegistry, |
|
| 49 | + ManeuverRaceFingerprintRegistry maneuverRaceFingerprintRegistry) throws Exception; |
|
| 50 | + |
|
| 49 | 51 | /** |
| 50 | 52 | * Starts a {@link RaceTracker}, associating the resulting races with the {@link Regatta} passed as argument instead |
| 51 | 53 | * of using the tracker's domain factory to obtain a default {@link Regatta} object for the tracking parameters. |
| ... | ... | @@ -58,9 +60,11 @@ public interface RaceTrackingConnectivityParameters extends Serializable { |
| 58 | 60 | * {@link TrackedRegattaRegistry#removeRace(Regatta, com.sap.sailing.domain.base.RaceDefinition)}. |
| 59 | 61 | */ |
| 60 | 62 | RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, |
| 61 | - RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, long timeoutInMilliseconds, |
|
| 62 | - RaceTrackingHandler raceTrackingHandler, MarkPassingRaceFingerprintRegistry markPassingRaceFingerprintRegistry, ManeuverRaceFingerprintRegistry maneuverRaceFingerprintRegistry) throws Exception; |
|
| 63 | - |
|
| 63 | + RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, |
|
| 64 | + long timeoutInMilliseconds, RaceTrackingHandler raceTrackingHandler, |
|
| 65 | + MarkPassingRaceFingerprintRegistry markPassingRaceFingerprintRegistry, |
|
| 66 | + ManeuverRaceFingerprintRegistry maneuverRaceFingerprintRegistry) throws Exception; |
|
| 67 | + |
|
| 64 | 68 | /** |
| 65 | 69 | * Deliver an ID object equal to that of the {@link RaceTracker#getID()} delivered by the {@link RaceTracker} |
| 66 | 70 | * that will be created from these parameters by calling {@link #createRaceTracker(TrackedRegattaRegistry)}. |