mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-27 14:27:15 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
15fe949b4b
36 files changed
+549
-204
No files matched your search
+1
-1
@@ -1289,7 +1289,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
logger.info("Removing persistent competitor info for competitor "+competitor.getName()+" with ID "+competitor.getId());
|
||||
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
|
||||
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
|
||||
collection.remove(query);
|
||||
collection.remove(query, WriteConcern.SAFE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-14
@@ -409,20 +409,17 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
}
|
||||
|
||||
private void onCourseDesignChangedEvent(RaceLogCourseDesignChangedEvent event) {
|
||||
if (trackedRace == null)
|
||||
return;
|
||||
|
||||
CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog()).analyze();
|
||||
List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>();
|
||||
|
||||
for (Waypoint waypoint : base.getWaypoints()) {
|
||||
update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions()));
|
||||
}
|
||||
|
||||
try {
|
||||
trackedRace.getRace().getCourse().update(update, params.getDomainFactory());
|
||||
} catch (PatchFailedException e) {
|
||||
logger.log(Level.WARNING, "Could not update course for race " + trackedRace.getRace().getName());
|
||||
if (trackedRace != null) {
|
||||
CourseBase base = new LastPublishedCourseDesignFinder(params.getRaceLog()).analyze();
|
||||
List<Util.Pair<ControlPoint, PassingInstruction>> update = new ArrayList<>();
|
||||
for (Waypoint waypoint : base.getWaypoints()) {
|
||||
update.add(new Util.Pair<>(waypoint.getControlPoint(), waypoint.getPassingInstructions()));
|
||||
}
|
||||
try {
|
||||
trackedRace.getRace().getCourse().update(update, params.getDomainFactory());
|
||||
} catch (PatchFailedException e) {
|
||||
logger.log(Level.WARNING, "Could not update course for race " + trackedRace.getRace().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class WeakRaceLogChangedVisitor extends AbstractRaceLogChangedVisitor {
|
||||
while (true) {
|
||||
try {
|
||||
Reference<?> ref = queue.remove();
|
||||
WeakRaceLogChangedVisitor visitor = referenceToVisitor.get(ref);
|
||||
WeakRaceLogChangedVisitor visitor = referenceToVisitor.remove(ref);
|
||||
visitor.removeListener();
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.SEVERE, "Error trying to clean weak reference", e);
|
||||
|
||||
+2
-7
@@ -19,7 +19,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsInFinishingPhase
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsIndividualRecallDisplayedAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogChangedVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.WeakRaceLogChangedVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.RaceStateEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.RaceStateEventScheduler;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
|
||||
@@ -79,12 +79,10 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
if (configuration == null) {
|
||||
throw new IllegalArgumentException("configuration must not be null");
|
||||
}
|
||||
|
||||
this.raceLog = raceLog;
|
||||
this.author = author;
|
||||
this.factory = factory;
|
||||
this.configuration = configuration;
|
||||
|
||||
this.changedListeners = createChangedListenerContainer();
|
||||
this.isRecallDisplayedAnalyzer = new IsIndividualRecallDisplayedAnalyzer(raceLog);
|
||||
this.recallDisplayedFinder = new IndividualRecallDisplayedFinder(raceLog);
|
||||
@@ -92,10 +90,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
this.finishingTimeFinder = new FinishingTimeFinder(raceLog);
|
||||
this.finishedTimeFinder = new FinishedTimeFinder(raceLog);
|
||||
this.startTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
|
||||
|
||||
this.raceLogListener = new RaceLogChangedVisitor(this);
|
||||
this.raceLog.addListener(raceLogListener);
|
||||
|
||||
this.raceLogListener = new WeakRaceLogChangedVisitor(this.raceLog, this);
|
||||
this.cachedIsIndividualRecallDisplayed = false;
|
||||
}
|
||||
|
||||
|
||||
+16
-1
@@ -2,8 +2,23 @@ package com.sap.sailing.domain.base;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface Leg extends Serializable {
|
||||
/**
|
||||
* When two legs are compared, their natural order is by the index of their {@link #getFrom() start waypoint} in the
|
||||
* {@link Course} to which they belong. A leg closer to the beginning of the course ranks "less" than one that is closer
|
||||
* to the end of the course.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface Leg extends Serializable, Comparable<Leg> {
|
||||
Waypoint getFrom();
|
||||
|
||||
Waypoint getTo();
|
||||
|
||||
/**
|
||||
* The index of this leg's {@link #getFrom() start waypoint} in the sequence of the
|
||||
* {@link Course}'s {@link Course#getWaypoints waypoints}. The first waypoint in
|
||||
* the course has index 0.
|
||||
*/
|
||||
int getZeroBasedIndexOfStartWaypoint();
|
||||
}
|
||||
-2
@@ -23,7 +23,6 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
|
||||
@Override
|
||||
public List<Leg> getLegs() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -72,7 +71,6 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
|
||||
|
||||
@Override
|
||||
public Leg getFirstLeg() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package com.sap.sailing.domain.ranking;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.RankingMetrics;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.test.TrackBasedTest;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
* A test for the case described in bug 2976: when a competitor has skipped a mark passing but then got one for a
|
||||
* later waypoint, that competitor may still be the one "farthest ahead." Still, we seem to have observed a case where
|
||||
* under these circumstances the competitor was not considered "farthest ahead" and instead another with a contiguous
|
||||
* mark passing list was preferred.<p>
|
||||
*
|
||||
* This test case establishes such a situation: two competitors, one with contiguous mark passings for the first two
|
||||
* waypoints but not having sailed as far as the other in the second leg; the other competitor skipped the first mark
|
||||
* passing, has one for the second waypoint and leads by windward distance in the second leg.<p>
|
||||
*
|
||||
* Since the problem occurred with the {@link OneDesignRankingMetric}, we'll use that one for the set-up here.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class TestFarthestAheadWithSkippedMarkPassing {
|
||||
private RankingMetric rankingMetric;
|
||||
private DynamicTrackedRace trackedRace;
|
||||
private Competitor c1, c2;
|
||||
private Waypoint start;
|
||||
private Waypoint windward;
|
||||
private Waypoint finish;
|
||||
|
||||
private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) {
|
||||
c1 = TrackBasedTest.createCompetitor("SlowBoat");
|
||||
c2 = TrackBasedTest.createCompetitor("FastBoat");
|
||||
trackedRace = createTrackedRace(Arrays.asList(c1, c2));
|
||||
rankingMetric = trackedRace.getRankingMetric();
|
||||
assertEquals(120, trackedRace.getCourseLength().getNauticalMiles(), 0.02);
|
||||
assertSame(RankingMetrics.ONE_DESIGN, trackedRace.getTrackedRegatta().getRegatta().getRankingMetricType());
|
||||
}
|
||||
|
||||
private DynamicTrackedRace createTrackedRace(Iterable<Competitor> competitors) {
|
||||
final TimePoint timePointForFixes = MillisecondsTimePoint.now();
|
||||
BoatClassImpl boatClass = new BoatClassImpl("Some Handicap Boat Class", /* typicallyStartsUpwind */ true);
|
||||
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
|
||||
RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null,
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* courseArea */ null, OneDesignRankingMetric::new);
|
||||
TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta);
|
||||
List<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
// create a two-lap upwind/downwind course:
|
||||
MarkImpl left = new MarkImpl("Left lee gate buoy");
|
||||
MarkImpl right = new MarkImpl("Right lee gate buoy");
|
||||
ControlPoint leeGate = new ControlPointWithTwoMarksImpl(left, right, "Lee Gate");
|
||||
Mark windwardMark = new MarkImpl("Windward mark");
|
||||
start = new WaypointImpl(leeGate);
|
||||
waypoints.add(start);
|
||||
windward = new WaypointImpl(windwardMark);
|
||||
waypoints.add(windward);
|
||||
finish = new WaypointImpl(leeGate);
|
||||
waypoints.add(finish);
|
||||
Course course = new CourseImpl("Test Course", waypoints);
|
||||
RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors);
|
||||
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
|
||||
EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 0,
|
||||
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000,
|
||||
/* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false,
|
||||
tr->new OneDesignRankingMetric(tr), mock(RaceLogResolver.class));
|
||||
// in this simplified artificial course, the top mark is exactly north of the right leeward gate
|
||||
DegreePosition topPosition = new DegreePosition(1, 0);
|
||||
trackedRace.getOrCreateTrack(left).addGPSFix(new GPSFixImpl(new DegreePosition(0, -0.000001), timePointForFixes));
|
||||
trackedRace.getOrCreateTrack(right).addGPSFix(new GPSFixImpl(new DegreePosition(0, 0.000001), timePointForFixes));
|
||||
trackedRace.getOrCreateTrack(windwardMark).addGPSFix(new GPSFixImpl(topPosition, timePointForFixes));
|
||||
trackedRace.recordWind(new WindImpl(topPosition, timePointForFixes, new KnotSpeedWithBearingImpl(
|
||||
/* speedInKnots */14.7, new DegreeBearingImpl(180))), new WindSourceImpl(WindSourceType.WEB));
|
||||
return trackedRace;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A competitor having only the start mark passing shall be ranked farthest ahead if the only other competitor
|
||||
* has no start mark passing.
|
||||
*/
|
||||
@Test
|
||||
public void testWithOneMarkPassingOnly() {
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> 0.0);
|
||||
final TimePoint startOfRace = MillisecondsTimePoint.now();
|
||||
final TimePoint middleOfFirstLeg = startOfRace.plus(Duration.ONE_HOUR.times(3));
|
||||
trackedRace.updateMarkPassings(
|
||||
c1,
|
||||
Arrays.<MarkPassing> asList(new MarkPassingImpl(startOfRace, start, c1)));
|
||||
trackedRace.getTrack(c1).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(45))));
|
||||
trackedRace.getTrack(c2).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(315))));
|
||||
trackedRace.getTrack(c1).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.5, 0), middleOfFirstLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(45))));
|
||||
trackedRace.getTrack(c2).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.5, 0), middleOfFirstLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(315))));
|
||||
// Both boats have climbed half of the first upwind beat; c1 is rated the faster boat (2.0), c2 has time-on-time factor 1.0.
|
||||
// Therefore, c2 is expected to lead after applying the corrections.
|
||||
final RankingInfo rankingInfo = rankingMetric.getRankingInfo(middleOfFirstLeg);
|
||||
final Competitor farthestAhead = rankingInfo.getCompetitorFarthestAhead();
|
||||
assertSame(c1, farthestAhead);
|
||||
Comparator<Competitor> comparator = rankingMetric.getRaceRankingComparator(middleOfFirstLeg);
|
||||
assertEquals(-1, comparator.compare(c1, c2)); // c1 is "less" than c2; better competitors rank less
|
||||
}
|
||||
|
||||
/**
|
||||
* C2 skipped the start mark passing but got a valid passing for the next waypoint. In the second leg, C2 leads
|
||||
* by windward distance and should therefore be listed as the competitor farthest ahead by the ranking metric.
|
||||
*/
|
||||
@Test
|
||||
public void testWithTwoMarkPassingsForC1AndSkippedStartForC2() {
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> 0.0);
|
||||
final TimePoint startOfRace = MillisecondsTimePoint.now();
|
||||
final TimePoint endOfFirstLeg = startOfRace.plus(Duration.ONE_HOUR.times(3));
|
||||
final TimePoint middleOfSecondLeg = endOfFirstLeg.plus(Duration.ONE_MINUTE.times(10));
|
||||
trackedRace.updateMarkPassings(
|
||||
c1,
|
||||
Arrays.<MarkPassing> asList(new MarkPassingImpl(startOfRace, start, c1), new MarkPassingImpl(endOfFirstLeg, windward, c1)));
|
||||
trackedRace.updateMarkPassings(
|
||||
c2,
|
||||
Arrays.<MarkPassing> asList(/* skipping start waypoint */ new MarkPassingImpl(endOfFirstLeg, windward, c2)));
|
||||
// they both start at the start line
|
||||
trackedRace.getTrack(c1).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(45))));
|
||||
trackedRace.getTrack(c2).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(315))));
|
||||
// both round the windward mark at the same time
|
||||
trackedRace.getTrack(c1).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(1.0, 0), endOfFirstLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(45))));
|
||||
trackedRace.getTrack(c2).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(1.0, 0), endOfFirstLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(315))));
|
||||
// c2 is further down the wind already at the middle of the second leg:
|
||||
trackedRace.getTrack(c1).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.5, 0), middleOfSecondLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(180))));
|
||||
trackedRace.getTrack(c2).add(
|
||||
new GPSFixMovingImpl(new DegreePosition(0.2, 0), middleOfSecondLeg, new KnotSpeedWithBearingImpl(12,
|
||||
new DegreeBearingImpl(180))));
|
||||
// Assert that c2 is the boat farthest ahead
|
||||
final RankingInfo rankingInfo = rankingMetric.getRankingInfo(middleOfSecondLeg);
|
||||
assertSame(c2, rankingInfo.getCompetitorFarthestAhead());
|
||||
Comparator<Competitor> comparator = rankingMetric.getRaceRankingComparator(middleOfSecondLeg);
|
||||
assertEquals(1, comparator.compare(c1, c2)); // c1 is "less" than c2; better competitors rank less
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -477,11 +477,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedRace(TrackedRace trackedRace) {
|
||||
}
|
||||
@@ -536,6 +531,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
@Override
|
||||
public void unlockTrackedRacesAfterWrite() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRaceListener(RaceListener listener) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,4 +28,14 @@ public class LegImpl implements Leg {
|
||||
public String toString() {
|
||||
return getFrom() + " -> " + getTo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZeroBasedIndexOfStartWaypoint() {
|
||||
return indexOfStartWaypoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Leg o) {
|
||||
return getZeroBasedIndexOfStartWaypoint() - o.getZeroBasedIndexOfStartWaypoint();
|
||||
}
|
||||
}
|
||||
@@ -54,11 +54,11 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
*/
|
||||
public interface Entry {
|
||||
int getTrackedRank();
|
||||
Double getNetPoints() throws NoWindException;
|
||||
Double getNetPointsUncorrected() throws NoWindException;
|
||||
Double getTotalPoints() throws NoWindException;
|
||||
Double getNetPoints();
|
||||
Double getNetPointsUncorrected();
|
||||
Double getTotalPoints();
|
||||
MaxPointsReason getMaxPointsReason();
|
||||
boolean isDiscarded() throws NoWindException;
|
||||
boolean isDiscarded();
|
||||
/**
|
||||
* Tells if the net points have been corrected by a {@link ScoreCorrection}
|
||||
*/
|
||||
|
||||
+17
-23
@@ -219,7 +219,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
return fleet;
|
||||
}
|
||||
@Override
|
||||
public Double getNetPointsUncorrected() throws NoWindException {
|
||||
public Double getNetPointsUncorrected() {
|
||||
try {
|
||||
return netPointsUncorrectedProvider.call();
|
||||
} catch (Exception e) {
|
||||
@@ -1068,7 +1068,10 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
public Distance getTotalDistanceTraveled(Competitor competitor, TimePoint timePoint) {
|
||||
Distance result = null;
|
||||
for (TrackedRace trackedRace : getTrackedRaces()) {
|
||||
if (Util.contains(trackedRace.getRace().getCompetitors(), competitor)) {
|
||||
TimePoint startOfRace;
|
||||
if (Util.contains(trackedRace.getRace().getCompetitors(), competitor) &&
|
||||
(startOfRace=trackedRace.getStartOfRace()) != null &&
|
||||
!startOfRace.after(timePoint)) {
|
||||
Distance distanceSailedInRace = trackedRace.getDistanceTraveled(competitor, timePoint);
|
||||
if (distanceSailedInRace != null) {
|
||||
if (result == null) {
|
||||
@@ -1137,10 +1140,10 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
} else {
|
||||
suppressedCompetitors.remove(competitor);
|
||||
}
|
||||
getScoreCorrection().notifyListenersAboutIsSuppressedChange(competitor, suppressed);
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(suppressedCompetitorsLock);
|
||||
}
|
||||
getScoreCorrection().notifyListenersAboutIsSuppressedChange(competitor, suppressed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1277,21 +1280,12 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
final Set<RaceColumn> discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint);
|
||||
for (final RaceColumn raceColumn : this.getRaceColumns()) {
|
||||
RunnableFuture<LeaderboardEntryDTO> future = new FutureTask<LeaderboardEntryDTO>(() -> {
|
||||
try {
|
||||
Entry entry = AbstractSimpleLeaderboardImpl.this.getEntry(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
return getLeaderboardEntryDTO(entry, raceColumn, competitor, timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails != null
|
||||
&& namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory,
|
||||
fillNetPointsUncorrected, cache);
|
||||
} catch (NoWindException e) {
|
||||
logger.info("Exception trying to compute leaderboard entry for competitor "
|
||||
+ competitor.getName() + " in race column " + raceColumn.getName() + ": "
|
||||
+ e.getMessage());
|
||||
logger.throwing(AbstractSimpleLeaderboardImpl.class.getName(),
|
||||
"computeLeaderboardByName.future.call()", e);
|
||||
throw new NoWindError(e);
|
||||
}
|
||||
Entry entry = AbstractSimpleLeaderboardImpl.this.getEntry(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
return getLeaderboardEntryDTO(entry, raceColumn, competitor, timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails != null
|
||||
&& namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory,
|
||||
fillNetPointsUncorrected, cache);
|
||||
});
|
||||
executor.execute(future);
|
||||
futuresForColumnName.put(raceColumn.getName(), future);
|
||||
@@ -1362,7 +1356,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
private LeaderboardEntryDTO getLeaderboardEntryDTO(Entry entry, RaceColumn raceColumn, Competitor competitor,
|
||||
TimePoint timePoint, boolean addLegDetails, boolean waitForLatestAnalyses,
|
||||
Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache, DomainFactory baseDomainFactory,
|
||||
boolean fillNetPointsUncorrected, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
boolean fillNetPointsUncorrected, WindLegTypeAndLegBearingCache cache) {
|
||||
LeaderboardEntryDTO entryDTO = new LeaderboardEntryDTO();
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
|
||||
entryDTO.race = trackedRace == null ? null : trackedRace.getRaceIdentifier();
|
||||
@@ -1538,7 +1532,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
*/
|
||||
private RaceDetails getRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws InterruptedException, ExecutionException {
|
||||
final RaceDetails raceDetails;
|
||||
if (trackedRace.getEndOfTracking() != null && trackedRace.getEndOfTracking().compareTo(timePoint) < 0) {
|
||||
raceDetails = getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(trackedRace, competitor, legRanksCache, rankingInfo, cache);
|
||||
@@ -1550,7 +1544,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private RaceDetails getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(final TrackedRace trackedRace,
|
||||
final Competitor competitor, final Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, final WindLegTypeAndLegBearingCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
RankingInfo rankingInfo, final WindLegTypeAndLegBearingCache cache) throws InterruptedException, ExecutionException {
|
||||
final com.sap.sse.common.Util.Pair<TrackedRace, Competitor> key = new com.sap.sse.common.Util.Pair<TrackedRace, Competitor>(trackedRace, competitor);
|
||||
RunnableFuture<RaceDetails> raceDetails;
|
||||
synchronized (raceDetailsAtEndOfTrackingCache) {
|
||||
@@ -1582,7 +1576,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private RaceDetails calculateRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
WindLegTypeAndLegBearingCache cache, final RankingInfo rankingInfo) throws NoWindException {
|
||||
WindLegTypeAndLegBearingCache cache, final RankingInfo rankingInfo) {
|
||||
final List<LegEntryDTO> legDetails = new ArrayList<LegEntryDTO>();
|
||||
final Course course = trackedRace.getRace().getCourse();
|
||||
course.lockForRead(); // hold back any course re-configurations while looping over the legs
|
||||
@@ -1630,7 +1624,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private LegEntryDTO createLegEntry(TrackedLegOfCompetitor trackedLeg, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) {
|
||||
LegEntryDTO result;
|
||||
final Duration time = trackedLeg.getTime(timePoint);
|
||||
if (trackedLeg == null || time == null) {
|
||||
|
||||
+2
-2
@@ -195,7 +195,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* <code>timePoint</code>, returns the VMG value for this time point. If the competitor has already finished the
|
||||
* leg, the VMG at the time when the competitor finished the leg is returned.
|
||||
*/
|
||||
Speed getVelocityMadeGood(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
Speed getVelocityMadeGood(TimePoint timePoint, WindPositionMode windPositionMode);
|
||||
|
||||
/**
|
||||
* Same as {@link #getVelocityMadeGood(TimePoint, WindPositionMode)}, only that a cache for wind data and leg type and bearing
|
||||
@@ -212,7 +212,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
/**
|
||||
* Same as {@link #getEstimatedTimeToNextMark(TimePoint, WindPositionMode)}, only that a cache for leg type calculation is passed.
|
||||
*/
|
||||
Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
/**
|
||||
* Returns <code>null</code> in case this leg's competitor hasn't started the leg yet. If in the leg at
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.tracking;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
@@ -55,8 +54,6 @@ public interface TrackedRegatta extends Serializable {
|
||||
|
||||
void unlockTrackedRacesAfterWrite();
|
||||
|
||||
Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass);
|
||||
|
||||
/**
|
||||
* Creates a {@link TrackedRace} based on the parameter specified and {@link #addTrackedRace(TrackedRace) adds} it
|
||||
* to this tracked regatta. Afterwards, calling {@link #getTrackedRace(RaceDefinition) getTrackedRace(raceDefinition)}
|
||||
@@ -93,6 +90,8 @@ public interface TrackedRegatta extends Serializable {
|
||||
*/
|
||||
void addRaceListener(RaceListener listener);
|
||||
|
||||
void removeRaceListener(RaceListener listener);
|
||||
|
||||
int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
void removeTrackedRace(RaceDefinition raceDefinition);
|
||||
|
||||
+32
-15
@@ -2,11 +2,14 @@ package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.SortedSet;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -35,6 +38,7 @@ import com.sap.sse.common.TimePoint;
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractRaceRankComparator<C extends Comparable<C>> implements Comparator<Competitor> {
|
||||
private static final Logger logger = Logger.getLogger(AbstractRaceRankComparator.class.getName());
|
||||
private final TrackedRace trackedRace;
|
||||
private final TimePoint timePoint;
|
||||
private final DummyMarkPassingWithTimePointOnly markPassingWithTimePoint;
|
||||
@@ -59,6 +63,7 @@ public abstract class AbstractRaceRankComparator<C extends Comparable<C>> implem
|
||||
if (o1 == o2) {
|
||||
result = 0;
|
||||
} else {
|
||||
final Course course = trackedRace.getRace().getCourse();
|
||||
NavigableSet<MarkPassing> o1MarkPassings = trackedRace.getMarkPassings(o1);
|
||||
NavigableSet<MarkPassing> o1MarkPassingsBeforeTimePoint;
|
||||
MarkPassing o1LastMarkPassingBeforeTimePoint = null;
|
||||
@@ -66,6 +71,7 @@ public abstract class AbstractRaceRankComparator<C extends Comparable<C>> implem
|
||||
TrackedLegOfCompetitor o1Leg;
|
||||
trackedRace.lockForRead(o1MarkPassings);
|
||||
try {
|
||||
// FIXME for the XMLExportTest.testExportingESSNice2014 the "inclusive" parameter doesn't seem to work properly; problem is caused by the MarkPassingByTimeComparator falling back to competitor ID if times are equal
|
||||
o1MarkPassingsBeforeTimePoint = o1MarkPassings.headSet(
|
||||
markPassingWithTimePoint, /* inclusive */true);
|
||||
o1MarkPassingsBeforeTimePointSize = o1MarkPassingsBeforeTimePoint.size();
|
||||
@@ -92,32 +98,43 @@ public abstract class AbstractRaceRankComparator<C extends Comparable<C>> implem
|
||||
} finally {
|
||||
trackedRace.unlockAfterRead(o2MarkPassings);
|
||||
}
|
||||
result = o2MarkPassingsBeforeTimePointSize - o1MarkPassingsBeforeTimePointSize; // inverted: more legs means
|
||||
// smaller rank
|
||||
final List<Leg> legs = course.getLegs();
|
||||
result = o1LastMarkPassingBeforeTimePoint == null
|
||||
? o2LastMarkPassingBeforeTimePoint == null ? 0 // both haven't started yet
|
||||
: 1 // only o2 has started; o1 is worse ("greater") than o2
|
||||
: o2LastMarkPassingBeforeTimePoint == null ? -1 // only o1 has started; o1 is better ("less") than o2
|
||||
/* both have started; any difference */ : course.getIndexOfWaypoint(o2LastMarkPassingBeforeTimePoint.getWaypoint()) -
|
||||
/* in last waypoint passed? */ course.getIndexOfWaypoint(o1LastMarkPassingBeforeTimePoint.getWaypoint());
|
||||
if (result == 0 && o1MarkPassingsBeforeTimePointSize > 0) {
|
||||
assert o2LastMarkPassingBeforeTimePoint != null;
|
||||
// Competitors are on same leg and both have already started the first leg.
|
||||
// TrackedLegOfCompetitor comparison also correctly uses finish times for a leg
|
||||
// in case we have the final leg, so both competitors finished the race.
|
||||
if (o1Leg == null) {
|
||||
// both must already have finished race; sort by race finish time: earlier time means smaller
|
||||
// (better) rank
|
||||
assert o2Leg == null;
|
||||
result = o1LastMarkPassingBeforeTimePoint.getTimePoint().compareTo(
|
||||
o2LastMarkPassingBeforeTimePoint.getTimePoint());
|
||||
} else {
|
||||
if (o2Leg == null) {
|
||||
result = 1; // o1Leg != null, so o1 has started leg already, o2 hasn't
|
||||
assert o2Leg != null; // otherwise, o1Leg!=null && o2Leg==null, but then o1 would have a finish mark passing and o2 not
|
||||
if (o1Leg.getLeg() != o2Leg.getLeg()) {
|
||||
logger.finest("Warning: competitors "+o1+" and "+o2+" in different legs ("+
|
||||
o1Leg.getLeg()+" and "+o2Leg.getLeg()+", respectively) although based on their last mark passings before "+
|
||||
timePoint+" ("+o1LastMarkPassingBeforeTimePoint+" and "+o2LastMarkPassingBeforeTimePoint+", respectively) "+
|
||||
"they should be in the same leg");
|
||||
// strange: both have the same number of mark passings but are in different legs; something is
|
||||
// broken, but we can only try our best:
|
||||
logger.finest("Warning: competitors "+o1+" and "+o2+" in different legs ("+
|
||||
o1Leg.getLeg()+" and "+o2Leg.getLeg()+", respectively) although based on their last mark passings before "+
|
||||
timePoint+" ("+o1LastMarkPassingBeforeTimePoint+" and "+o2LastMarkPassingBeforeTimePoint+", respectively) "+
|
||||
"they should be in the same leg");
|
||||
result = legs.indexOf(o1Leg.getLeg()) - legs.indexOf(o2Leg.getLeg());
|
||||
} else {
|
||||
if (o1Leg.getLeg() != o2Leg.getLeg()) {
|
||||
// strange: both have the same number of mark passings but are in different legs; something is
|
||||
// broken, but we can only try our best:
|
||||
result = trackedRace.getRace().getCourse().getLegs().indexOf(o1Leg.getLeg()) -
|
||||
trackedRace.getRace().getCourse().getLegs().indexOf(o2Leg.getLeg());
|
||||
} else {
|
||||
// competitors are in same leg; compare their windward distance to go
|
||||
final C wwdtgO1 = getComparisonValueForSameLeg(o1);
|
||||
final C wwdtgO2 = getComparisonValueForSameLeg(o2);
|
||||
result = wwdtgO1==null?wwdtgO2==null?0:-1:wwdtgO2==null?1:(lessIsBetter?1:-1)*wwdtgO1.compareTo(wwdtgO2);
|
||||
}
|
||||
// competitors are in same leg; compare their windward distance to go
|
||||
final C wwdtgO1 = getComparisonValueForSameLeg(o1);
|
||||
final C wwdtgO2 = getComparisonValueForSameLeg(o2);
|
||||
result = wwdtgO1==null?wwdtgO2==null?0:-1:wwdtgO2==null?1:(lessIsBetter?1:-1)*wwdtgO1.compareTo(wwdtgO2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-21
@@ -6,35 +6,24 @@ import java.util.Comparator;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
|
||||
/**
|
||||
* Compares two {@link MarkPassing} objects, using their {@link MarkPassing#getTimePoint() time point} as the primary
|
||||
* ordering criterion in ascending order. If two mark passings have equal time points, the competitor IDs' string
|
||||
* representations are compared as the secondary ordering criterion to ensure that two competitors passing a mark at the
|
||||
* same time are still identified as not being equal.
|
||||
* Compares two {@link MarkPassing} objects, using their {@link MarkPassing#getTimePoint() time point} as the only
|
||||
* ordering criterion in ascending order. If two mark passings have equal time points, they are considered equal,
|
||||
* regardless the competitor. Don't use this comparator for collections in which mark passings for multiple competitors
|
||||
* may be managed and shall be treated as different. Use {@link MarkPassingsByTimeAndCompetitorIdComparator} instead.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class MarkPassingByTimeComparator implements Comparator<MarkPassing>, Serializable {
|
||||
private static final long serialVersionUID = 5758820139503482888L;
|
||||
public static MarkPassingByTimeComparator INSTANCE = new MarkPassingByTimeComparator();
|
||||
public static final MarkPassingByTimeComparator INSTANCE = new MarkPassingByTimeComparator();
|
||||
|
||||
@Override
|
||||
public int compare(MarkPassing o1, MarkPassing o2) {
|
||||
int result = o1 == null ? o2 == null ? 0 : -1 : o2 == null ? 1 : o1.getTimePoint() == null ? (o2.getTimePoint() == null ? 0 : -1) : o2.getTimePoint() == null ? 1 : o1.getTimePoint().compareTo(o2.getTimePoint());
|
||||
if (result == 0 && o1 != null) {
|
||||
assert o2 != null;
|
||||
Object c1ID = o1.getCompetitor().getId();
|
||||
Object c2ID = o2.getCompetitor().getId();
|
||||
// shortcut comparison and don't use expensive toString() in case IDs are comparable by nature
|
||||
if (c1ID instanceof Comparable<?> && c1ID.getClass() == c2ID.getClass()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Comparable<Object> c1OID = (Comparable<Object>) c1ID;
|
||||
result = c1OID.compareTo(o2.getCompetitor().getId());
|
||||
} else {
|
||||
result = o1.getCompetitor().getId().toString().compareTo(o2.getCompetitor().getId().toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return o1 == null ?
|
||||
o2 == null ? 0 : -1
|
||||
: o2 == null ? 1 : o1.getTimePoint() == null ?
|
||||
(o2.getTimePoint() == null ? 0 : -1)
|
||||
: o2.getTimePoint() == null ? 1 : o1.getTimePoint().compareTo(o2.getTimePoint());
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
|
||||
/**
|
||||
* Compares two {@link MarkPassing} objects, using their {@link MarkPassing#getTimePoint() time point} as the primary
|
||||
* ordering criterion in ascending order. If two mark passings have equal time points, the competitor IDs' string
|
||||
* representations are compared as the secondary ordering criterion to ensure that two competitors passing a mark at the
|
||||
* same time are still identified as not being equal.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class MarkPassingsByTimeAndCompetitorIdComparator extends MarkPassingByTimeComparator {
|
||||
private static final long serialVersionUID = 5977491869367296660L;
|
||||
public static final MarkPassingsByTimeAndCompetitorIdComparator INSTANCE = new MarkPassingsByTimeAndCompetitorIdComparator();
|
||||
|
||||
@Override
|
||||
public int compare(MarkPassing o1, MarkPassing o2) {
|
||||
int result = super.compare(o1, o2);
|
||||
if (result == 0 && o1 != null) {
|
||||
assert o2 != null;
|
||||
Object c1ID = o1.getCompetitor().getId();
|
||||
Object c2ID = o2.getCompetitor().getId();
|
||||
// shortcut comparison and don't use expensive toString() in case IDs are comparable by nature
|
||||
if (c1ID instanceof Comparable<?> && c1ID.getClass() == c2ID.getClass()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Comparable<Object> c1OID = (Comparable<Object>) c1ID;
|
||||
result = c1OID.compareTo(o2.getCompetitor().getId());
|
||||
} else {
|
||||
result = o1.getCompetitor().getId().toString().compareTo(o2.getCompetitor().getId().toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+13
-9
@@ -284,11 +284,13 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
// as fallback in the absence of wind information, project to leg bearing
|
||||
projectToBearing = cache.getLegBearing(getTrackedLeg(), at);
|
||||
}
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
if (speed.getBearing() != null && projectToBearing != null) {
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -338,10 +340,12 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
final Position endPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(to, /* extrapolate */false);
|
||||
if (endPos != null) {
|
||||
Distance d = getTrackedLeg().getAbsoluteWindwardDistance(
|
||||
getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(start.getTimePoint(), false),
|
||||
endPos, to, WindPositionMode.EXACT, cache);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
final Position startPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(start.getTimePoint(), false);
|
||||
if (startPos != null) {
|
||||
Distance d = getTrackedLeg().getAbsoluteWindwardDistance(startPos, endPos, to,
|
||||
WindPositionMode.EXACT, cache);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -613,7 +617,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode) {
|
||||
return getVelocityMadeGood(at, windPositionMode, new LeaderboardDTOCalculationReuseCache(at));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -452,7 +452,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
markPassingsForWaypoint = new ConcurrentHashMap<Waypoint, NavigableSet<MarkPassing>>();
|
||||
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
|
||||
markPassingsForWaypoint.put(waypoint, new ConcurrentSkipListSet<MarkPassing>(
|
||||
MarkPassingByTimeComparator.INSTANCE));
|
||||
MarkPassingsByTimeAndCompetitorIdComparator.INSTANCE));
|
||||
}
|
||||
markPassingsTimes = new ArrayList<com.sap.sse.common.Util.Pair<Waypoint, com.sap.sse.common.Util.Pair<TimePoint, TimePoint>>>();
|
||||
loadingFromWindStoreState = LoadingFromStoresState.NOT_STARTED;
|
||||
@@ -697,7 +697,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
protected NavigableSet<MarkPassing> createMarkPassingsCollectionForWaypoint(Waypoint waypoint) {
|
||||
final ConcurrentSkipListSet<MarkPassing> result = new ConcurrentSkipListSet<MarkPassing>(
|
||||
MarkPassingByTimeComparator.INSTANCE);
|
||||
MarkPassingsByTimeAndCompetitorIdComparator.INSTANCE);
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
try {
|
||||
markPassingsForWaypoint.put(waypoint, result);
|
||||
|
||||
+44
-38
@@ -5,17 +5,14 @@ import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
@@ -51,21 +48,19 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
*/
|
||||
private final Map<RaceDefinition, TrackedRace> trackedRaces;
|
||||
|
||||
private final Map<BoatClass, Collection<TrackedRace>> trackedRacesByBoatClass;
|
||||
private transient Set<RaceListener> raceListeners;
|
||||
private transient ConcurrentHashMap<RaceListener, RaceListener> raceListeners;
|
||||
|
||||
public TrackedRegattaImpl(Regatta regatta) {
|
||||
super();
|
||||
trackedRacesLock = new NamedReentrantReadWriteLock("trackeRaces lock for tracked regatta "+regatta.getName(), /* fair */ false);
|
||||
this.regatta = regatta;
|
||||
this.trackedRaces = new HashMap<RaceDefinition, TrackedRace>();
|
||||
this.trackedRacesByBoatClass = new HashMap<BoatClass, Collection<TrackedRace>>();
|
||||
raceListeners = new HashSet<RaceListener>();
|
||||
raceListeners = new ConcurrentHashMap<RaceListener, RaceListener>();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
ois.defaultReadObject();
|
||||
this.raceListeners = new HashSet<RaceListener>();
|
||||
this.raceListeners = new ConcurrentHashMap<RaceListener, RaceListener>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,7 +99,6 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
TrackedRegattaImpl result = new TrackedRegattaImpl(this.regatta);
|
||||
result.trackedRaces.putAll(this.trackedRaces);
|
||||
result.trackedRacesByBoatClass.putAll(this.trackedRacesByBoatClass);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -116,19 +110,11 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
logger.info("adding tracked race for "+trackedRace.getRace()+" to tracked regatta "+getRegatta().getName()+
|
||||
" with regatta hash code "+getRegatta().hashCode());
|
||||
oldTrackedRace = trackedRaces.put(trackedRace.getRace(), trackedRace);
|
||||
if (oldTrackedRace != trackedRace) {
|
||||
Collection<TrackedRace> coll = trackedRacesByBoatClass.get(trackedRace.getRace().getBoatClass());
|
||||
if (coll == null) {
|
||||
coll = new ArrayList<TrackedRace>();
|
||||
trackedRacesByBoatClass.put(trackedRace.getRace().getBoatClass(), coll);
|
||||
}
|
||||
coll.add(trackedRace);
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(trackedRacesLock);
|
||||
}
|
||||
if (oldTrackedRace != trackedRace) {
|
||||
for (RaceListener listener : raceListeners) {
|
||||
for (RaceListener listener : raceListeners.keySet()) {
|
||||
listener.raceAdded(trackedRace);
|
||||
}
|
||||
}
|
||||
@@ -149,14 +135,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
LockUtil.lockForWrite(trackedRacesLock);
|
||||
try {
|
||||
trackedRaces.remove(trackedRace.getRace());
|
||||
Collection<TrackedRace> trbbc = trackedRacesByBoatClass.get(trackedRace.getRace().getBoatClass());
|
||||
if (trbbc != null) {
|
||||
trbbc.remove(trackedRace);
|
||||
if (trbbc.isEmpty()) {
|
||||
trackedRacesByBoatClass.remove(trackedRace.getRace().getBoatClass());
|
||||
}
|
||||
}
|
||||
for (RaceListener listener : raceListeners) {
|
||||
for (RaceListener listener : raceListeners.keySet()) {
|
||||
listener.raceRemoved(trackedRace);
|
||||
}
|
||||
} finally {
|
||||
@@ -177,23 +156,40 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
return trackedRaces.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass) {
|
||||
return trackedRacesByBoatClass.get(boatClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRace getTrackedRace(RaceDefinition race) {
|
||||
boolean interrupted = false;
|
||||
lockTrackedRacesForRead();
|
||||
try {
|
||||
TrackedRace result = trackedRaces.get(race);
|
||||
while (!interrupted && result == null) {
|
||||
if (!interrupted && result == null) {
|
||||
final Object mutex = new Object();
|
||||
final RaceListener listener = new RaceListener() {
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {}
|
||||
|
||||
@Override
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
synchronized (mutex) {
|
||||
mutex.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
addRaceListener(listener);
|
||||
try {
|
||||
trackedRaces.wait();
|
||||
result = trackedRaces.get(race);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
synchronized (mutex) {
|
||||
result = trackedRaces.get(race);
|
||||
while (!interrupted && result == null) {
|
||||
try {
|
||||
mutex.wait();
|
||||
result = trackedRaces.get(race);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
removeRaceListener(listener);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -217,7 +213,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
final List<TrackedRace> trackedRacesCopy = new ArrayList<>();
|
||||
lockTrackedRacesForRead();
|
||||
try {
|
||||
raceListeners.add(listener);
|
||||
raceListeners.put(listener, listener);
|
||||
Util.addAll(getTrackedRaces(), trackedRacesCopy);
|
||||
} finally {
|
||||
unlockTrackedRacesAfterRead();
|
||||
@@ -227,6 +223,16 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRaceListener(RaceListener listener) {
|
||||
lockTrackedRacesForRead();
|
||||
try {
|
||||
raceListeners.remove(listener);
|
||||
} finally {
|
||||
unlockTrackedRacesAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
|
||||
int result = 0;
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
<plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sailing.kiworesultimport" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.manage2sail.resultimport" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.news" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="6" />
|
||||
<plugin id="com.sap.sailing.news" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.polars" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="3" />
|
||||
@@ -63,7 +63,7 @@
|
||||
<plugin id="com.sap.sailing.xrr.resultimport" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sse.filestorage" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sse.mail" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sse.replication" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sse.replication" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sse.security.ui" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sse.security.userstore.mongodb" autoStart="true" startLevel="3" />
|
||||
<plugin id="org.apache.felix.shell" autoStart="true" startLevel="0" />
|
||||
|
||||
+4
-6
@@ -11,7 +11,6 @@ import java.util.Set;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
@@ -274,11 +273,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedRace(TrackedRace trackedRace) {
|
||||
}
|
||||
@@ -333,6 +327,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
@Override
|
||||
public void unlockTrackedRacesAfterWrite() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRaceListener(RaceListener listener) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public interface BoatClassImageResources extends ClientBundle {
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/470.png")
|
||||
ImageResource _470Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.png")
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.jpg")
|
||||
ImageResource _49erFXIcon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER.png")
|
||||
|
||||
+1
-1
@@ -3132,7 +3132,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
StrippedLeaderboardDTO leaderboardDTO = createStrippedLeaderboardDTO(leaderboard, withGeoLocationData, withStatisticalData);
|
||||
groupDTO.leaderboards.add(leaderboardDTO);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Catched exception while reading data for leaderboard " + leaderboard.getName(), e);
|
||||
logger.log(Level.SEVERE, "Caught exception while reading data for leaderboard " + leaderboard.getName(), e);
|
||||
}
|
||||
}
|
||||
Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard();
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB |
+14
-5
@@ -8,6 +8,7 @@ import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.DB;
|
||||
@@ -16,6 +17,7 @@ import com.mongodb.MongoException;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
|
||||
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
@@ -25,11 +27,18 @@ import com.sap.sailing.domain.test.AbstractLeaderboardTest;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
|
||||
private DomainFactory domainFactory;
|
||||
|
||||
public StoreCompetitorTest() throws UnknownHostException, MongoException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// clear the domainFactory competitor store for a clean start:
|
||||
domainFactory = new DomainFactoryImpl();
|
||||
}
|
||||
|
||||
private void dropCompetitorCollection() {
|
||||
DB db = getMongoService().getDB();
|
||||
DBCollection competitorCollection = db.getCollection(CollectionNames.COMPETITORS.name());
|
||||
@@ -45,7 +54,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
String competitorName2 = "Hasso Plattner";
|
||||
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor(competitorName1);
|
||||
@@ -72,7 +81,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndUpdateCompetitorWithUUIDAsId() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID());
|
||||
@@ -86,7 +95,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndRemoveCompetitor() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso");
|
||||
@@ -100,7 +109,7 @@ public class StoreCompetitorTest extends AbstractMongoDBTest {
|
||||
@Test
|
||||
public void testStoreAndRemoveCompetitorWithUUIDAsId() {
|
||||
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), domainFactory);
|
||||
dropCompetitorCollection();
|
||||
|
||||
DynamicCompetitor c = AbstractLeaderboardTest.createCompetitor("Hasso", UUID.randomUUID());
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ public class RegattaJsonSerializer implements JsonSerializer<Regatta> {
|
||||
public static final String FIELD_SERIES = "series";
|
||||
public static final String FIELD_COMPETITORS = "competitors";
|
||||
public static final String FIELD_TRACKED_RACES = "trackedRaces";
|
||||
public static final String FIELD_COURSE_AREA_ID = "courseAreaId";
|
||||
|
||||
private final JsonSerializer<Series> seriesSerializer;
|
||||
private final JsonSerializer<Competitor> competitorSerializer;
|
||||
@@ -38,6 +39,7 @@ public class RegattaJsonSerializer implements JsonSerializer<Regatta> {
|
||||
result.put(FIELD_END_DATE, regatta.getEndDate() != null ? regatta.getEndDate().asMillis() : null);
|
||||
result.put(FIELD_SCORINGSYSTEM, regatta.getScoringScheme().getType().name());
|
||||
result.put(FIELD_BOATCLASS, regatta.getBoatClass() != null ? regatta.getBoatClass().getName(): null);
|
||||
result.put(FIELD_COURSE_AREA_ID, regatta.getDefaultCourseArea() != null ? regatta.getDefaultCourseArea().getId().toString(): null);
|
||||
|
||||
if(seriesSerializer != null) {
|
||||
JSONArray seriesJson = new JSONArray();
|
||||
|
||||
+4
-8
@@ -287,14 +287,10 @@ public class RegattaAndRaceDataJsonGetServlet extends AbstractJsonHttpServlet {
|
||||
jsonCompetitorInLeg.put("distanceTraveledOverGroundInMeters",
|
||||
distanceTraveled.getMeters());
|
||||
}
|
||||
try {
|
||||
Speed velocityMadeGood = trackedLegOfCompetitor.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
|
||||
if (velocityMadeGood != null) {
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInKnots", velocityMadeGood.getKnots());
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInMetersPerSecond", velocityMadeGood.getMetersPerSecond());
|
||||
}
|
||||
} catch (NoWindException e) {
|
||||
// well, we don't know the wind direction... then no VMG will be shown...
|
||||
Speed velocityMadeGood = trackedLegOfCompetitor.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
|
||||
if (velocityMadeGood != null) {
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInKnots", velocityMadeGood.getKnots());
|
||||
jsonCompetitorInLeg.put("velocityMadeGoodInMetersPerSecond", velocityMadeGood.getMetersPerSecond());
|
||||
}
|
||||
Speed averageVelocityMadeGood = trackedLegOfCompetitor
|
||||
.getAverageVelocityMadeGood(timePoint);
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
|
||||
<booleanAttribute key="append.args" value="true"/>
|
||||
<booleanAttribute key="automaticAdd" value="false"/>
|
||||
<booleanAttribute key="automaticValidate" value="false"/>
|
||||
<stringAttribute key="bootstrap" value=""/>
|
||||
<stringAttribute key="checked" value="[NONE]"/>
|
||||
<booleanAttribute key="clearConfig" value="false"/>
|
||||
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
|
||||
<booleanAttribute key="default" value="true"/>
|
||||
<booleanAttribute key="default_auto_start" value="true"/>
|
||||
<intAttribute key="default_start_level" value="4"/>
|
||||
<booleanAttribute key="includeOptional" value="true"/>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2011 -Xmx6000m -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar -Dmongo.dbName=replica -Dreplicate.on.start=com.sap.sailing.server.impl.RacingEventServiceImpl,com.sap.sse.security.impl.SecurityServiceImpl,com.sap.sse.filestorage.impl.FileStorageManagementServiceImpl,com.sap.sse.mail.impl.MailServiceImpl -Dreplicate.master.queue.host=127.0.0.1 -Dreplicate.master.queue.port=5672 -Dreplicate.master.servlet.port=8888 -Dreplicate.master.servlet.host=127.0.0.1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<booleanAttribute key="show_selected_only" value="false"/>
|
||||
<stringAttribute key="target_bundles" value="com.fasterxml.jackson.core.jackson-annotations@default:default,com.fasterxml.jackson.core.jackson-core@default:default,com.fasterxml.jackson.core.jackson-databind@default:default,com.fasterxml.jackson.jaxrs.jackson-jaxrs-base@default:default,com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider@default:default,com.rabbitmq.client@default:default,com.sun.jersey.contribs.jersey-multipart@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,jackson-core-lgpl@default:default,jackson-jaxrs@default:default,jackson-mapper-lgpl@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.commons.beanutils@default:default,org.apache.commons.codec@default:default,org.apache.commons.collections@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.commons.math@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.servicemix.bundles.aws-java-sdk@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.servicemix.bundles.zxing@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@3:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.jdom@default:default,org.jvnet.mimepull@default:default,org.mongodb.mongo-java-driver@default:default,org.objectweb.asm@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
<booleanAttribute key="useCustomFeatures" value="false"/>
|
||||
<booleanAttribute key="useDefaultConfigArea" value="false"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.googlecode.mgwt@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.dashboards.gwt@5:true,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.news@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.polars@4:true,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@4:true,com.sap.sailing.server.gateway.serialization.shared.android@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@4:true,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt.theme@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mail@4:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@4:true,com.sap.sse.security.common@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.sap.sse.shared.android@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
|
||||
<stringAttribute key="yk-options" value=" additional-options2=onexit\=snapshot "/>
|
||||
</launchConfiguration>
|
||||
@@ -30,14 +30,24 @@ public interface ReplicationService {
|
||||
*/
|
||||
ReplicationMasterDescriptor getReplicatingFromMaster();
|
||||
|
||||
/**
|
||||
* Same as {@link #startToReplicateFrom(ReplicationMasterDescriptor, Iterable)}, where the list of replicables
|
||||
* is determined by asking this service's {@link ReplicablesProvider} which replicables are currently available.
|
||||
* Note that this is not a good idea during the OSGi start-up phase, particularly if there is a bundle that
|
||||
* starts after this bundle because then that {@link Replicable} will not yet be registered and won't become part
|
||||
* of replication.
|
||||
*/
|
||||
void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Performs a servlet request to the remote master server's {@link ReplicationServlet}, first registering this
|
||||
* replica, ensuring the message queue for replicating operations is created, then subscribing to the master's
|
||||
* replication message queue and asking the servlet for the stream containing the initial load which will then be
|
||||
* used to {@link Replicable#initiallyFillFrom(java.io.ObjectInputStream) replace} the current {@link Replicable}'s
|
||||
* used to {@link Replicable#initiallyFillFrom(java.io.ObjectInputStream) replace} the {@link Replicable replicables}'
|
||||
* state.
|
||||
*/
|
||||
void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException;
|
||||
void startToReplicateFrom(ReplicationMasterDescriptor master, Iterable<Replicable<?, ?>> replicables) throws IOException,
|
||||
ClassNotFoundException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Registers a replica with this master instance. The <code>replica</code> will be considered in the result of
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.sap.sse.replication.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import com.sap.sse.replication.Replicable;
|
||||
import com.sap.sse.replication.ReplicablesProvider;
|
||||
import com.sap.sse.replication.ReplicationService;
|
||||
|
||||
@@ -129,12 +132,16 @@ public class Activator implements BundleActivator {
|
||||
new Thread("ServiceTracker waiting for Replicables "+Arrays.toString(replicableIdsAsStrings)) {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("Waiting for Replicables " + replicableIdsAsStrings + " before firing up replication automatically...");
|
||||
logger.info("Waiting for Replicables " + Arrays.toString(replicableIdsAsStrings) +
|
||||
" before firing up replication automatically...");
|
||||
final List<Replicable<?, ?>> replicables = new ArrayList<>();
|
||||
for (String replicableIdAsString : replicableIdsAsStrings) {
|
||||
replicablesProvider.getReplicable(replicableIdAsString, /* wait */true);
|
||||
Replicable<?, ?> replicable = replicablesProvider.getReplicable(replicableIdAsString, /* wait */true);
|
||||
logger.info("Obtained Replicable " + replicableIdAsString);
|
||||
replicables.add(replicable);
|
||||
}
|
||||
logger.info("Configuration requested automatic replication. Starting it up...");
|
||||
logger.info("Configuration requested automatic replication for replicables "+
|
||||
Arrays.toString(replicableIdsAsStrings)+". Starting it up...");
|
||||
String replicateFromExchangeName = System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_EXCHANGE_NAME);
|
||||
if (replicateFromExchangeName == null) {
|
||||
replicateFromExchangeName = masterExchangeName;
|
||||
@@ -147,8 +154,8 @@ public class Activator implements BundleActivator {
|
||||
System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_SERVLET_HOST),
|
||||
Integer.valueOf(System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_SERVLET_PORT).trim()));
|
||||
try {
|
||||
// TODO pass on the replicables to wait for
|
||||
serverReplicationMasterService.startToReplicateFrom(master);
|
||||
// TODO pass on the replicables to wait for; failure to do so can cause the symptoms of bug 3015
|
||||
serverReplicationMasterService.startToReplicateFrom(master, replicables);
|
||||
logger.info("Automatic replication has been started.");
|
||||
} catch (ClassNotFoundException | IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+11
-5
@@ -476,7 +476,14 @@ public class ReplicationServiceImpl implements ReplicationService {
|
||||
*/
|
||||
@Override
|
||||
public void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException {
|
||||
logger.info("Starting to replicate from "+master);
|
||||
final Iterable<Replicable<?, ?>> replicables = getReplicables();
|
||||
startToReplicateFrom(master, replicables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startToReplicateFrom(ReplicationMasterDescriptor master, Iterable<Replicable<?, ?>> replicables)
|
||||
throws IOException, ClassNotFoundException, InterruptedException {
|
||||
logger.info("Starting to replicate from " + master);
|
||||
try {
|
||||
registerReplicaWithMaster(master);
|
||||
} catch (Exception ex) {
|
||||
@@ -497,14 +504,13 @@ public class ReplicationServiceImpl implements ReplicationService {
|
||||
throw ex;
|
||||
}
|
||||
logger.info("Connection to exchange successful.");
|
||||
final Iterable<Replicable<?, ?>> replicables = replicablesProvider.getReplicables();
|
||||
URL initialLoadURL = master.getInitialLoadURL(replicables);
|
||||
logger.info("Initial load URL is "+initialLoadURL);
|
||||
// start receiving messages already now, but start in suspended mode
|
||||
replicator = new ReplicationReceiver(master, replicablesProvider, /* startSuspended */true, consumer);
|
||||
// clear Replicable state here, before starting to receive and de-serialized operations which builds up
|
||||
// new state, e.g., in competitor store
|
||||
for (Replicable<?, ?> r : getReplicables()) {
|
||||
for (Replicable<?, ?> r : replicables) {
|
||||
r.clearReplicaState();
|
||||
r.startedReplicatingFrom(master);
|
||||
}
|
||||
@@ -517,7 +523,7 @@ public class ReplicationServiceImpl implements ReplicationService {
|
||||
RabbitInputStreamProvider rabbitInputStreamProvider = new RabbitInputStreamProvider(master.createChannel(), queueName);
|
||||
try {
|
||||
final GZIPInputStream gzipInputStream = new GZIPInputStream(rabbitInputStreamProvider.getInputStream());
|
||||
for (Replicable<?, ?> replicable : getReplicables()) {
|
||||
for (Replicable<?, ?> replicable : replicables) { // absolutely make sure to use the same sequence of replicables as for URL (bug 3015)
|
||||
logger.info("Starting to receive initial load for "+replicable.getId());
|
||||
try {
|
||||
replicable.initiallyFillFrom(gzipInputStream);
|
||||
@@ -527,9 +533,9 @@ public class ReplicationServiceImpl implements ReplicationService {
|
||||
}
|
||||
logger.info("Done receiving initial load for "+replicable.getId());
|
||||
}
|
||||
} finally {
|
||||
logger.info("Resuming replicator to apply queues");
|
||||
replicator.setSuspended(false); // apply queued operations
|
||||
} finally {
|
||||
// delete initial load queue
|
||||
DeleteOk deleteOk = consumer.getChannel().queueDelete(queueName);
|
||||
logger.info("Deleted queue " + queueName + " used for initial load: " + deleteOk.toString());
|
||||
|
||||
@@ -246,12 +246,16 @@ if [[ $OPERATION == "auto-install" ]]; then
|
||||
activate_user_data
|
||||
# then download and install environment and append to env.sh
|
||||
install_environment
|
||||
# finally, append user data to env.sh as it shall take precedence over the installed environment's defaults
|
||||
append_user_data_to_envsh
|
||||
# finally, append user data to env.sh as it shall take precedence over the installed environment's defaults
|
||||
append_user_data_to_envsh
|
||||
|
||||
# make sure to reload data
|
||||
source `pwd`/env.sh
|
||||
|
||||
# Append Apache macro invocation for /internal-server-status based on mod_status and INSTANCE_DNS to /etc/httpd/conf.d/001-events.conf
|
||||
APACHE_CONFIG_DIR=/etc/httpd/conf.d
|
||||
echo "Appending macro usage for /internal-server-status URL for mod_status based Apache monitoring to $APACHE_CONFIG_DIR/001-events.conf"
|
||||
echo "Use Status $INSTANCE_DNS internal-server-status" >>$APACHE_CONFIG_DIR/001-events.conf
|
||||
|
||||
if [[ $INSTALL_FROM_RELEASE == "" ]] && [[ $BUILD_BEFORE_START != "True" ]]; then
|
||||
echo "I could not find any option telling me to download a release or to build! Possible cause: Your environment contains empty values for these variables!"
|
||||
|
||||
Reference in new issue
Block a user