Merge branch 'master' into ubilabs--tracking_app--develop

This commit is contained in:
Bennet Brunsen committed 2015-06-25 09:48:13 +02:00
commit 742c71c637
41 files changed
+552 -249

No files matched your search

+25 -24
View File
@@ -660,30 +660,31 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
if [[ ${PIPESTATUS[0]} != 0 ]]; then if [[ ${PIPESTATUS[0]} != 0 ]]; then
exit 103 exit 103
fi fi
adb emu kill # TODO find a way that the emulator test is stable in hudson
echo "Downloading image (sys-img-${ANDROID_ABI}-android-${TEST_API})..." | tee -a $START_DIR/build.log # adb emu kill
echo yes | "$ANDROID" update sdk $ANDROID_OPTIONS --filter sys-img-${ANDROID_ABI}-android-${TEST_API} --no-ui --force --all > /dev/null # echo "Downloading image (sys-img-${ANDROID_ABI}-android-${TEST_API})..." | tee -a $START_DIR/build.log
echo no | "$ANDROID" create avd --name ${AVD_NAME} --target android-${TEST_API} --abi ${ANDROID_ABI} --force -p ${START_DIR}/emulator # echo yes | "$ANDROID" update sdk $ANDROID_OPTIONS --filter sys-img-${ANDROID_ABI}-android-${TEST_API} --no-ui --force --all > /dev/null
echo "Starting emulator..." | tee -a $START_DIR/build.log # echo no | "$ANDROID" create avd --name ${AVD_NAME} --target android-${TEST_API} --abi ${ANDROID_ABI} --force -p ${START_DIR}/emulator
emulator -avd ${AVD_NAME} -no-skin -no-audio -no-window & # echo "Starting emulator..." | tee -a $START_DIR/build.log
echo "Waiting for startup..." | tee -a $START_DIR/build.log # emulator -avd ${AVD_NAME} -no-skin -no-audio -no-window &
adb wait-for-device # echo "Waiting for startup..." | tee -a $START_DIR/build.log
sleep 60 # adb wait-for-device
$PROJECT_HOME/configuration/androidWaitForEmulator.sh # sleep 60
if [[ $? != 0 ]]; then # $PROJECT_HOME/configuration/androidWaitForEmulator.sh
adb emu kill # if [[ $? != 0 ]]; then
"$ANDROID" delete avd --name ${AVD_NAME} # adb emu kill
exit 102 # "$ANDROID" delete avd --name ${AVD_NAME}
fi # exit 102
adb shell input keyevent 82 & # fi
./gradlew deviceCheck connectedCheck | tee -a $START_DIR/build.log # adb shell input keyevent 82 &
if [[ ${PIPESTATUS[0]} != 0 ]]; then # ./gradlew deviceCheck connectedCheck | tee -a $START_DIR/build.log
adb emu kill # if [[ ${PIPESTATUS[0]} != 0 ]]; then
"$ANDROID" delete avd --name ${AVD_NAME} # adb emu kill
exit 101 # "$ANDROID" delete avd --name ${AVD_NAME}
fi # exit 101
adb emu kill # fi
"$ANDROID" delete avd --name ${AVD_NAME} # adb emu kill
# "$ANDROID" delete avd --name ${AVD_NAME}
fi fi
else else
echo "INFO: Deactivating mobile modules" echo "INFO: Deactivating mobile modules"
@@ -8,7 +8,6 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl; import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.AbstractTimePoint;
/** /**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid * A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
@@ -42,7 +41,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
private final double latDeg; private final double latDeg;
private final double lngDeg; private final double lngDeg;
private final long timePointAsMillis; private final TimePoint timePoint;
/** /**
* Tells if in the containing {@link DynamicGPSFixTrackImpl} this fix is considered valid. This cache * Tells if in the containing {@link DynamicGPSFixTrackImpl} this fix is considered valid. This cache
@@ -78,15 +77,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
} }
} }
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
private class CompactEstimatedSpeedBearing extends AbstractBearing { private class CompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L; private static final long serialVersionUID = 8549231429037883121L;
@@ -113,7 +103,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
public CompactGPSFixImpl(Position position, TimePoint timePoint) { public CompactGPSFixImpl(Position position, TimePoint timePoint) {
latDeg = position.getLatDeg(); latDeg = position.getLatDeg();
lngDeg = position.getLngDeg(); lngDeg = position.getLngDeg();
timePointAsMillis = timePoint==null?-1:timePoint.asMillis(); this.timePoint = timePoint;
} }
public CompactGPSFixImpl(GPSFix gpsFix) { public CompactGPSFixImpl(GPSFix gpsFix) {
@@ -132,7 +122,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
@Override @Override
public TimePoint getTimePoint() { public TimePoint getTimePoint() {
return new CompactTimePoint(); return timePoint;
} }
@Override @Override
@@ -0,0 +1,38 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sse.common.TimePoint;
public class IsIndividualRecallDisplayedAnalyzer extends RaceLogAnalyzer<Boolean> {
private final IndividualRecallFinder displayedFinder;
private final IndividualRecallFinder removedFinder;
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog) {
this(raceLog, null);
}
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog, TimePoint at) {
this(raceLog, new IndividualRecallDisplayedFinder(raceLog, at), new IndividualRecallRemovedFinder(raceLog, at), at);
}
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog, IndividualRecallDisplayedFinder displayedFinder,
IndividualRecallRemovedFinder removedFinder, TimePoint at) {
super(raceLog);
this.displayedFinder = displayedFinder;
this.removedFinder = removedFinder;
}
@Override
protected Boolean performAnalysis() {
TimePoint displayedAt = displayedFinder.analyze();
TimePoint removedAt = removedFinder.analyze();
if (displayedAt != null) {
if (removedAt == null || displayedAt.after(removedAt)) {
return true;
}
}
return false;
}
}
@@ -30,4 +30,9 @@ public class RaceLogProtestStartTimeEventImpl extends RaceLogEventImpl implement
return protestStartTime; return protestStartTime;
} }
@Override
public String getShortInfo() {
return "protestStartTime=" + protestStartTime;
}
} }
@@ -30,7 +30,7 @@ public class SimpleRaceLogIdentifierImpl implements SimpleRaceLogIdentifier {
@Override @Override
public String toString() { public String toString() {
return "SimpleRaceLogIdentifier [regattaLikeParentName=" + regattaLikeParentName + ", raceCloumnName=" + raceColumnName return "SimpleRaceLogIdentifier [regattaLikeParentName=" + regattaLikeParentName + ", raceColumnName=" + raceColumnName
+ ", fleetName=" + fleetName + "]"; + ", fleetName=" + fleetName + "]";
} }
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IndividualRecallDi
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IndividualRecallRemovedFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IndividualRecallRemovedFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsFinishedAnalyzer; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsFinishedAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsInFinishingPhaseAnalyzer; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.IsInFinishingPhaseAnalyzer;
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.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder; 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.RaceLogChangedVisitor;
@@ -28,9 +29,7 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProce
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureChangedListener; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureChangedListener;
import com.sap.sailing.domain.base.configuration.RacingProcedureConfiguration; import com.sap.sailing.domain.base.configuration.RacingProcedureConfiguration;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
/** /**
* Base class for all your {@link RacingProcedure}s. * Base class for all your {@link RacingProcedure}s.
@@ -42,10 +41,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener implements RacingProcedure, public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener implements RacingProcedure,
RaceLogChangedListener { RaceLogChangedListener {
/** private final static long individualRecallRemovalTimeout = 4 * 60 * 1000; // minutes * seconds * milliseconds
* The time since race start after which the X-ray flag has to go down
*/
private final static Duration individualRecallRemovalTimeout = Duration.ONE_MINUTE.times(4);
private RaceStateEventScheduler scheduler; private RaceStateEventScheduler scheduler;
@@ -55,35 +51,41 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
private final RacingProcedureConfiguration configuration; private final RacingProcedureConfiguration configuration;
private final RacingProcedureChangedListeners<? extends RacingProcedureChangedListener> changedListeners; private final RacingProcedureChangedListeners<? extends RacingProcedureChangedListener> changedListeners;
private final IsIndividualRecallDisplayedAnalyzer isRecallDisplayedAnalyzer;
private final IndividualRecallDisplayedFinder recallDisplayedFinder; private final IndividualRecallDisplayedFinder recallDisplayedFinder;
private final IndividualRecallRemovedFinder recallRemovedFinder; private final IndividualRecallRemovedFinder recallRemovedFinder;
private final FinishingTimeFinder finishingTimeFinder; private final FinishingTimeFinder finishingTimeFinder;
private final FinishedTimeFinder finishedTimeFinder; private final FinishedTimeFinder finishedTimeFinder;
private final RaceLogEventVisitor raceLogListener; private final RaceLogEventVisitor raceLogListener;
private final StartTimeFinder startTimeFinder;
private boolean cachedIsIndividualRecallDisplayed; private boolean cachedIsIndividualRecallDisplayed;
/** /**
* When calling me, call {@link BaseRacingProcedure#update()} afterwards! * When calling me, call {@link BaseRacingProcedure#update()} afterwards!
* @param raceLogResolver TODO *
* @param raceLogResolver
* is required for a {@link StartTimeFinder} in order to make individual recall timeouts depend on start
* time rather than the time the individual recall was issued
*/ */
public BaseRacingProcedure(RaceLog raceLog, AbstractLogEventAuthor author, RaceLogEventFactory factory, public BaseRacingProcedure(RaceLog raceLog, AbstractLogEventAuthor author, RaceLogEventFactory factory,
RacingProcedureConfiguration configuration, RaceLogResolver raceLogResolver) { RacingProcedureConfiguration configuration, RaceLogResolver raceLogResolver) {
if (configuration == null) { if (configuration == null) {
throw new IllegalArgumentException("configuration must not be null"); throw new IllegalArgumentException("configuration must not be null");
} }
this.raceLog = raceLog; this.raceLog = raceLog;
this.author = author; this.author = author;
this.factory = factory; this.factory = factory;
this.configuration = configuration; this.configuration = configuration;
this.changedListeners = createChangedListenerContainer(); this.changedListeners = createChangedListenerContainer();
this.isRecallDisplayedAnalyzer = new IsIndividualRecallDisplayedAnalyzer(raceLog);
this.recallDisplayedFinder = new IndividualRecallDisplayedFinder(raceLog); this.recallDisplayedFinder = new IndividualRecallDisplayedFinder(raceLog);
this.recallRemovedFinder = new IndividualRecallRemovedFinder(raceLog); this.recallRemovedFinder = new IndividualRecallRemovedFinder(raceLog);
this.finishingTimeFinder = new FinishingTimeFinder(raceLog); this.finishingTimeFinder = new FinishingTimeFinder(raceLog);
this.finishedTimeFinder = new FinishedTimeFinder(raceLog); this.finishedTimeFinder = new FinishedTimeFinder(raceLog);
this.startTimeFinder = new StartTimeFinder(raceLogResolver, raceLog); // Use the following in order to initialize a start time finder once it's needed for start time-dependent individual recall timeouts
// this.startTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
this.raceLogListener = new RaceLogChangedVisitor(this); this.raceLogListener = new RaceLogChangedVisitor(this);
this.raceLog.addListener(raceLogListener); this.raceLog.addListener(raceLogListener);
@@ -131,7 +133,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
break; break;
case RUNNING: case RUNNING:
if (isIndividualRecallDisplayed()) { if (isIndividualRecallDisplayed()) {
rescheduleIndividualRecallTimeout(); rescheduleIndividualRecallTimeout(recallDisplayedFinder.analyze());
} }
break; break;
default: default:
@@ -139,10 +141,6 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
} }
} }
private TimePoint getStartTime() {
return startTimeFinder.analyze().getStartTime();
}
@Override @Override
public boolean isIndividualRecallDisplayed() { public boolean isIndividualRecallDisplayed() {
return cachedIsIndividualRecallDisplayed; return cachedIsIndividualRecallDisplayed;
@@ -150,10 +148,10 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
@Override @Override
public boolean isIndividualRecallDisplayed(TimePoint at) { public boolean isIndividualRecallDisplayed(TimePoint at) {
final TimePoint individualRecallDisplayedTime = getIndividualRecallDisplayedTime(); if (hasIndividualRecall()) {
final TimePoint individualRecallRemovalTime = getIndividualRecallRemovalTime(); return new IsIndividualRecallDisplayedAnalyzer(getRaceLog(), at).analyze();
return (hasIndividualRecall() && individualRecallDisplayedTime != null && !individualRecallDisplayedTime.after(at) && }
individualRecallRemovalTime != null && !individualRecallRemovalTime.after(at)); return false;
} }
@Override @Override
@@ -166,20 +164,17 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
@Override @Override
public TimePoint getIndividualRecallRemovalTime() { public TimePoint getIndividualRecallRemovalTime() {
final TimePoint result; TimePoint displayed = getIndividualRecallDisplayedTime();
final TimePoint raceStartTime = getStartTime(); if (hasIndividualRecall()) {
final TimePoint removedEvent; TimePoint removedEvent = recallRemovedFinder.analyze();
if (hasIndividualRecall() && (removedEvent = recallRemovedFinder.analyze()) != null && (raceStartTime == null || removedEvent.after(raceStartTime))) { if (removedEvent != null && (displayed == null || removedEvent.after(displayed))) {
result = removedEvent; return removedEvent;
} else {
final TimePoint individualRecallDisplayedTime = getIndividualRecallDisplayedTime();
if (individualRecallDisplayedTime != null && raceStartTime != null) {
result = raceStartTime.plus(individualRecallRemovalTimeout);
} else {
result = null;
} }
} }
return result; if (displayed != null) {
return displayed.plus(individualRecallRemovalTimeout);
}
return null;
} }
@Override @Override
@@ -217,21 +212,18 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
} }
protected void update() { protected void update() {
// TODO an X-ray up event with a time point in the future will not be recognized here because at the current time the flag is not displayed and no other race log event will trigger the update; we'd need to schedule another state event; as long as clocks are set correctly, additional transmission time will make this unlikely boolean isRecallDisplayed = isRecallDisplayedAnalyzer.analyze();
boolean isRecallDisplayed = isIndividualRecallDisplayed(MillisecondsTimePoint.now());
if (cachedIsIndividualRecallDisplayed != isRecallDisplayed) { if (cachedIsIndividualRecallDisplayed != isRecallDisplayed) {
cachedIsIndividualRecallDisplayed = isRecallDisplayed; cachedIsIndividualRecallDisplayed = isRecallDisplayed;
if (!cachedIsIndividualRecallDisplayed) { if (cachedIsIndividualRecallDisplayed) {
// individual recall was removed; no need to schedule an event for removal changedListeners.onIndividualRecallDisplayed(this);
rescheduleIndividualRecallTimeout(recallDisplayedFinder.analyze());
} else {
changedListeners.onIndividualRecallRemoved(this); changedListeners.onIndividualRecallRemoved(this);
unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT); unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT);
} }
} }
// (re-)schedule the individual recall removal in any case; the removal time may have changed
if (cachedIsIndividualRecallDisplayed) {
changedListeners.onIndividualRecallDisplayed(this);
rescheduleIndividualRecallTimeout();
}
// always call listeners for changed flag, as this does not only affect recall, but also // always call listeners for changed flag, as this does not only affect recall, but also
// changes start procedure, for which some text elements need to be updated // changes start procedure, for which some text elements need to be updated
@@ -239,9 +231,9 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
changedListeners.onActiveFlagsChanged(this); changedListeners.onActiveFlagsChanged(this);
} }
private void rescheduleIndividualRecallTimeout() { private void rescheduleIndividualRecallTimeout(TimePoint displayTime) {
unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT); unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT);
scheduleStateEvents(new RaceStateEventImpl(getIndividualRecallRemovalTime(), RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT)); scheduleStateEvents(new RaceStateEventImpl(displayTime.plus(individualRecallRemovalTimeout), RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT));
} }
protected void scheduleStateEvents(RaceStateEvent stateEvent) { protected void scheduleStateEvents(RaceStateEvent stateEvent) {
@@ -61,7 +61,7 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg {
private DynamicTrackedRace trackedRace; private DynamicTrackedRace trackedRace;
private Competitor c1, c2; private Competitor c1, c2;
private void setUp(Function<Competitor, Double> timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) { private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) {
c1 = TrackBasedTest.createCompetitor("FastBoat"); c1 = TrackBasedTest.createCompetitor("FastBoat");
c2 = TrackBasedTest.createCompetitor("SlowBoat"); c2 = TrackBasedTest.createCompetitor("SlowBoat");
trackedRace = createTrackedRace(Arrays.asList(c1, c2), timeOnTimeFactors, timeOnDistanceFactors); trackedRace = createTrackedRace(Arrays.asList(c1, c2), timeOnTimeFactors, timeOnDistanceFactors);
@@ -70,7 +70,7 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg {
assertSame(RankingMetrics.TIME_ON_TIME_AND_DISTANCE, trackedRace.getTrackedRegatta().getRegatta().getRankingMetricType()); assertSame(RankingMetrics.TIME_ON_TIME_AND_DISTANCE, trackedRace.getTrackedRegatta().getRegatta().getRankingMetricType());
} }
private DynamicTrackedRace createTrackedRace(Iterable<Competitor> competitors, Function<Competitor, Double> timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) { private DynamicTrackedRace createTrackedRace(Iterable<Competitor> competitors, TimeOnTimeFactorMapping timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) {
final TimePoint timePointForFixes = MillisecondsTimePoint.now(); final TimePoint timePointForFixes = MillisecondsTimePoint.now();
BoatClassImpl boatClass = new BoatClassImpl("Some Handicap Boat Class", /* typicallyStartsUpwind */ true); BoatClassImpl boatClass = new BoatClassImpl("Some Handicap Boat Class", /* typicallyStartsUpwind */ true);
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
@@ -0,0 +1,47 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
import java.util.UUID;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.TimeOnTimeAndDistanceRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
public class SerializeRankingMetricTest extends AbstractSerializationTest {
@Test
public void testTimeOnTimeAndDistanceRankingMetricSerialization() throws ClassNotFoundException, IOException {
final BoatClass _49er = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
final TrackedRace trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(
new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "Regatta", /* boatClass */ _49er,
/* startDate */ null, /* endDate */ null, /* trackedRegattaRegistry */ null,
new LowPoint(), UUID.randomUUID(), /* courseArea */ null)),
new RaceDefinitionImpl("Race", new CourseImpl("Course", Collections.<Waypoint>emptyList()) ,
/* boatClass */ _49er, Collections.<Competitor>emptySet()),
Collections.<Sideline>emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 0l, 0l, 0l,
false, TimeOnTimeAndDistanceRankingMetric::new, null);
RankingMetric clone = cloneBySerialization(trackedRace.getRankingMetric(), DomainFactory.INSTANCE);
assertNotNull(clone);
assertTrue(clone instanceof TimeOnTimeAndDistanceRankingMetric);
}
}
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Util;
import com.sap.sse.common.WithID; import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.TimeRangeImpl; import com.sap.sse.common.impl.TimeRangeImpl;
@@ -255,7 +256,15 @@ public class TopLevelMasterData implements Serializable {
} }
public void setMasterDataExportFlagOnRaceColumns(boolean flagValue) { public void setMasterDataExportFlagOnRaceColumns(boolean flagValue) {
for (LeaderboardGroup group : leaderboardGroups) { // collect all leaderboard groups for all events that will be touched during serialization
final Set<LeaderboardGroup> allLeaderboardGroups = new HashSet<>();
allLeaderboardGroups.addAll(leaderboardGroups);
for (Entry<LeaderboardGroup, Set<Event>> i : eventForLeaderboardGroup.entrySet()) {
for (Event e : i.getValue()) {
Util.addAll(e.getLeaderboardGroups(), allLeaderboardGroups);
}
}
for (LeaderboardGroup group : allLeaderboardGroups) {
for (Leaderboard leaderboard : group.getLeaderboards()) { for (Leaderboard leaderboard : group.getLeaderboards()) {
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
raceColumn.setMasterDataExportOngoingThreadFlag(true); raceColumn.setMasterDataExportOngoingThreadFlag(true);
@@ -228,5 +228,19 @@ public interface PolarDataService {
void insertExistingFixes(TrackedRace trackedRace); void insertExistingFixes(TrackedRace trackedRace);
/**
*
* @param boatClass When polars of this boat class change, the listener will be notified. May not be null.
* @param listener may not be null
*/
void registerListener(BoatClass boatClass, PolarsChangedListener listener);
/**
*
* @param boatClass
* @param listener
*/
void unregisterListener(BoatClass boatClass, PolarsChangedListener listener);
} }
@@ -0,0 +1,10 @@
package com.sap.sailing.domain.polars;
public interface PolarsChangedListener {
/**
* Is called when the polars of the boat class you registered the listener for changed
*/
void polarsChanged();
}
@@ -10,6 +10,7 @@ import java.util.function.Supplier;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import com.sap.sailing.domain.base.Competitor; 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.base.Leg;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl;
@@ -24,6 +25,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache; import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode; import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sailing.domain.tracking.impl.AbstractRaceRankComparator;
import com.sap.sse.common.Duration; import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
@@ -281,17 +283,20 @@ public abstract class AbstractRankingMetric implements RankingMetric {
/** /**
* Constructs a comparator based on the results of * Constructs a comparator based on the results of
* {@link #getWindwardDistanceTraveled(Competitor, TimePoint, WindLegTypeAndLegBearingCache)} where competitors are * {@link #getWindwardDistanceTraveled(Competitor, TimePoint, WindLegTypeAndLegBearingCache)} where competitors are
* "less" than other competitors ("better") if they have a greater windward distance traveled. * "less" than other competitors ("better") if they are in a later leg or, if in the same leg, have a greater
* windward distance traveled. If both competitors have already finished the race, the finishing time is compared.
*/ */
private Comparator<Competitor> getWindwardDistanceTraveledComparator(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) { private Comparator<Competitor> getWindwardDistanceTraveledComparator(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) {
final Map<Competitor, Distance> windwardDistanceTraveledPerCompetitor = new HashMap<>(); final Map<Competitor, Distance> windwardDistanceTraveledPerCompetitor = new HashMap<>();
for (final Competitor competitor : getCompetitors()) { for (final Competitor competitor : getCompetitors()) {
windwardDistanceTraveledPerCompetitor.put(competitor, getWindwardDistanceTraveled(competitor, timePoint, cache)); windwardDistanceTraveledPerCompetitor.put(competitor, getWindwardDistanceTraveled(competitor, timePoint, cache));
} }
final Comparator<Distance> nullsFirstDistanceComparator = Comparator.nullsFirst(Comparator.naturalOrder()); return new AbstractRaceRankComparator<Distance>(trackedRace, timePoint, /* lessIsBetter */ false) {
return (c1, c2) -> nullsFirstDistanceComparator.compare( @Override
windwardDistanceTraveledPerCompetitor.get(c2), protected Distance getComparisonValueForSameLeg(Competitor competitor) {
windwardDistanceTraveledPerCompetitor.get(c1)); return windwardDistanceTraveledPerCompetitor.get(competitor);
}
};
} }
/** /**
@@ -508,8 +513,8 @@ public abstract class AbstractRankingMetric implements RankingMetric {
if (legTo.hasFinishedLeg(timePoint)) { if (legTo.hasFinishedLeg(timePoint)) {
// calculate actual time it takes who to reach the end of the leg starting at timePoint: // calculate actual time it takes who to reach the end of the leg starting at timePoint:
final TimePoint whosLegFinishTime = legWho.getFinishTime(); final TimePoint whosLegFinishTime = legWho.getFinishTime();
if (whosLegFinishTime != null) { if (whosLegFinishTime != null && !whosLegFinishTime.after(timePoint)) {
// who's leg finishing time is known; we don't need to extrapolate // who's leg finishing time is known and is already reached at timePoint; we don't need to extrapolate
toEndOfLegOrTo = timePoint.until(whosLegFinishTime); toEndOfLegOrTo = timePoint.until(whosLegFinishTime);
} else { } else {
assert getWindwardDistanceTraveled(legTo.getCompetitor(), legTo.hasFinishedLeg(timePoint)?legTo.getFinishTime():timePoint, cache).compareTo( assert getWindwardDistanceTraveled(legTo.getCompetitor(), legTo.hasFinishedLeg(timePoint)?legTo.getFinishTime():timePoint, cache).compareTo(
@@ -601,6 +606,9 @@ public abstract class AbstractRankingMetric implements RankingMetric {
} else { } else {
Distance d = Distance.NULL; Distance d = Distance.NULL;
boolean count = false; // start counting only once the "from" waypoint has been found boolean count = false; // start counting only once the "from" waypoint has been found
final Course course = getTrackedRace().getRace().getCourse();
course.lockForRead();
try {
for (final TrackedLeg trackedLeg : getTrackedRace().getTrackedLegs()) { for (final TrackedLeg trackedLeg : getTrackedRace().getTrackedLegs()) {
count = count || trackedLeg.getLeg().getFrom() == from; count = count || trackedLeg.getLeg().getFrom() == from;
if (count) { if (count) {
@@ -629,6 +637,9 @@ public abstract class AbstractRankingMetric implements RankingMetric {
} }
} }
} }
} finally {
course.unlockAfterRead();
}
result = d; result = d;
} }
return result; return result;
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.ranking;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.common.Duration;
import com.sap.sse.util.SerializableFunction;
@FunctionalInterface
public interface TimeOnDistanceAllowancePerNauticalMileMap extends SerializableFunction<Competitor, Duration> {
}
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.ranking;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
@@ -52,9 +51,9 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric {
public final static RankingMetricConstructor CONSTRUCTOR = TimeOnTimeAndDistanceRankingMetric::new; public final static RankingMetricConstructor CONSTRUCTOR = TimeOnTimeAndDistanceRankingMetric::new;
private final Function<Competitor, Double> timeOnTimeFactor; private final TimeOnTimeFactorMapping timeOnTimeFactor;
private final Function<Competitor, Duration> timeOnDistanceFactorNauticalMile; private final TimeOnDistanceAllowancePerNauticalMileMap timeOnDistanceFactorNauticalMile;
/** /**
* The regular constructor that can also be used as <code>TimeOnTimeAndDistanceRankingMetric::new</code> * The regular constructor that can also be used as <code>TimeOnTimeAndDistanceRankingMetric::new</code>
@@ -72,8 +71,8 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric {
* its regatta, handicap mappings can be passed directly and overrule anything defined for the competitor or * its regatta, handicap mappings can be passed directly and overrule anything defined for the competitor or
* on the regatta. * on the regatta.
*/ */
public TimeOnTimeAndDistanceRankingMetric(final TrackedRace trackedRace, Function<Competitor, Double> timeOnTimeFactor, public TimeOnTimeAndDistanceRankingMetric(final TrackedRace trackedRace, TimeOnTimeFactorMapping timeOnTimeFactor,
Function<Competitor, Duration> timeOnDistanceFactorInSecondsPerNauticalMile) { TimeOnDistanceAllowancePerNauticalMileMap timeOnDistanceFactorInSecondsPerNauticalMile) {
super(trackedRace); super(trackedRace);
this.timeOnTimeFactor = timeOnTimeFactor; this.timeOnTimeFactor = timeOnTimeFactor;
this.timeOnDistanceFactorNauticalMile = timeOnDistanceFactorInSecondsPerNauticalMile; this.timeOnDistanceFactorNauticalMile = timeOnDistanceFactorInSecondsPerNauticalMile;
@@ -0,0 +1,8 @@
package com.sap.sailing.domain.ranking;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.util.SerializableFunction;
@FunctionalInterface
public interface TimeOnTimeFactorMapping extends SerializableFunction<Competitor, Double> {
}
@@ -0,0 +1,129 @@
package com.sap.sailing.domain.tracking.impl;
import java.util.Collections;
import java.util.Comparator;
import java.util.NavigableSet;
import java.util.SortedSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
/**
* Compares two competitors by their ranking in the overall race for a given time point. Competitors who haven't started
* the first leg are all equally ranked last. Competitors in different legs are ranked by inverse leg index: the higher
* the number of the leg the lesser (better) the rank. Competitors in the same leg are ranked by their windward distance
* to go in that leg, requiring wind data or estimates to be available for the given time point. The wind is estimated at
* the middle of the leg for consistent ordering, so the same wind data will be used for comparing all competitors in the
* same leg at a given point in time.
* <p>
*
* This comparator assumes that the race's {@link Course} remains unchanged from the time this comparator's constructor
* is invoked and the last call to {@link #compare(Competitor, Competitor)} on this comparator. This should be obvious
* because course changes have an effect on the race's leg structure and therefore on the foundations for ranking.
* Clients should use {@link Course#lockForRead()} and {@link Course#unlockAfterRead()} to frame the construction and
* use of this comparator.<p>
*
* Two different competitors may end up being ranked equal by this comparator. So take care and don't use this
* comparator class when inserting into an {@link SortedSet} when you want something like a ranking. It may overwrite
* existing entries. Use {@link Collections#sort(java.util.List, Comparator)} instead.
*
* @author Axel Uhl (d043530)
*
*/
public abstract class AbstractRaceRankComparator<C extends Comparable<C>> implements Comparator<Competitor> {
private final TrackedRace trackedRace;
private final TimePoint timePoint;
private final DummyMarkPassingWithTimePointOnly markPassingWithTimePoint;
private final boolean lessIsBetter;
/**
* @param lessIsBetter
* tells whether lesser return values for {@link #getComparisonValueForSameLeg(Competitor)} denote a
* better competitor ranking or not.
*/
public AbstractRaceRankComparator(TrackedRace trackedRace, TimePoint timePoint, boolean lessIsBetter) {
super();
this.trackedRace = trackedRace;
this.timePoint = timePoint;
this.markPassingWithTimePoint = new DummyMarkPassingWithTimePointOnly(timePoint);
this.lessIsBetter = lessIsBetter;
}
@Override
public int compare(Competitor o1, Competitor o2) {
int result;
if (o1 == o2) {
result = 0;
} else {
NavigableSet<MarkPassing> o1MarkPassings = trackedRace.getMarkPassings(o1);
NavigableSet<MarkPassing> o1MarkPassingsBeforeTimePoint;
MarkPassing o1LastMarkPassingBeforeTimePoint = null;
int o1MarkPassingsBeforeTimePointSize;
TrackedLegOfCompetitor o1Leg;
trackedRace.lockForRead(o1MarkPassings);
try {
o1MarkPassingsBeforeTimePoint = o1MarkPassings.headSet(
markPassingWithTimePoint, /* inclusive */true);
o1MarkPassingsBeforeTimePointSize = o1MarkPassingsBeforeTimePoint.size();
if (o1MarkPassingsBeforeTimePointSize > 0) {
o1LastMarkPassingBeforeTimePoint = o1MarkPassingsBeforeTimePoint.last();
}
o1Leg = trackedRace.getCurrentLeg(o1, timePoint);
} finally {
trackedRace.unlockAfterRead(o1MarkPassings);
}
NavigableSet<MarkPassing> o2MarkPassings = trackedRace.getMarkPassings(o2);
NavigableSet<MarkPassing> o2MarkPassingsBeforeTimePoint;
MarkPassing o2LastMarkPassingBeforeTimePoint = null;
int o2MarkPassingsBeforeTimePointSize;
TrackedLegOfCompetitor o2Leg;
trackedRace.lockForRead(o2MarkPassings);
try {
o2MarkPassingsBeforeTimePoint = o2MarkPassings.headSet(markPassingWithTimePoint, /* inclusive */true);
o2MarkPassingsBeforeTimePointSize = o2MarkPassingsBeforeTimePoint.size();
if (o2MarkPassingsBeforeTimePointSize > 0) {
o2LastMarkPassingBeforeTimePoint = o2MarkPassingsBeforeTimePoint.last();
}
o2Leg = trackedRace.getCurrentLeg(o2, timePoint);
} finally {
trackedRace.unlockAfterRead(o2MarkPassings);
}
result = o2MarkPassingsBeforeTimePointSize - o1MarkPassingsBeforeTimePointSize; // inverted: more legs means
// smaller rank
if (result == 0 && o1MarkPassingsBeforeTimePointSize > 0) {
// 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
result = o1LastMarkPassingBeforeTimePoint.getTimePoint().compareTo(
o2LastMarkPassingBeforeTimePoint.getTimePoint());
} else {
if (o2Leg == null) {
result = 1; // o1Leg != null, so o1 has started leg already, o2 hasn't
} 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);
}
}
}
}
}
return result;
}
protected abstract C getComparisonValueForSameLeg(Competitor competitor);
}
@@ -4,13 +4,11 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.NavigableSet;
import java.util.SortedSet; import java.util.SortedSet;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache; import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
@@ -39,17 +37,11 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public class RaceRankComparator implements Comparator<Competitor> { public class RaceRankComparator extends AbstractRaceRankComparator<Distance> {
private final TrackedRace trackedRace;
private final TimePoint timePoint;
private final DummyMarkPassingWithTimePointOnly markPassingWithTimePoint;
private final Map<Competitor, Distance> windwardDistanceToGoInLegCache; private final Map<Competitor, Distance> windwardDistanceToGoInLegCache;
public RaceRankComparator(TrackedRace trackedRace, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { public RaceRankComparator(TrackedRace trackedRace, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
super(); super(trackedRace, timePoint, /* lessIsBetter */ true);
this.trackedRace = trackedRace;
this.timePoint = timePoint;
this.markPassingWithTimePoint = new DummyMarkPassingWithTimePointOnly(timePoint);
this.windwardDistanceToGoInLegCache = new HashMap<Competitor, Distance>(); this.windwardDistanceToGoInLegCache = new HashMap<Competitor, Distance>();
for (Competitor competitor : trackedRace.getRace().getCompetitors()) { for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
final TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, timePoint); final TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, timePoint);
@@ -61,74 +53,7 @@ public class RaceRankComparator implements Comparator<Competitor> {
} }
@Override @Override
public int compare(Competitor o1, Competitor o2) { protected Distance getComparisonValueForSameLeg(Competitor o1) {
int result; return windwardDistanceToGoInLegCache.get(o1);
if (o1 == o2) {
result = 0;
} else {
NavigableSet<MarkPassing> o1MarkPassings = trackedRace.getMarkPassings(o1);
NavigableSet<MarkPassing> o1MarkPassingsBeforeTimePoint;
MarkPassing o1LastMarkPassingBeforeTimePoint = null;
int o1MarkPassingsBeforeTimePointSize;
TrackedLegOfCompetitor o1Leg;
trackedRace.lockForRead(o1MarkPassings);
try {
o1MarkPassingsBeforeTimePoint = o1MarkPassings.headSet(
markPassingWithTimePoint, /* inclusive */true);
o1MarkPassingsBeforeTimePointSize = o1MarkPassingsBeforeTimePoint.size();
if (o1MarkPassingsBeforeTimePointSize > 0) {
o1LastMarkPassingBeforeTimePoint = o1MarkPassingsBeforeTimePoint.last();
}
o1Leg = trackedRace.getCurrentLeg(o1, timePoint);
} finally {
trackedRace.unlockAfterRead(o1MarkPassings);
}
NavigableSet<MarkPassing> o2MarkPassings = trackedRace.getMarkPassings(o2);
NavigableSet<MarkPassing> o2MarkPassingsBeforeTimePoint;
MarkPassing o2LastMarkPassingBeforeTimePoint = null;
int o2MarkPassingsBeforeTimePointSize;
TrackedLegOfCompetitor o2Leg;
trackedRace.lockForRead(o2MarkPassings);
try {
o2MarkPassingsBeforeTimePoint = o2MarkPassings.headSet(markPassingWithTimePoint, /* inclusive */true);
o2MarkPassingsBeforeTimePointSize = o2MarkPassingsBeforeTimePoint.size();
if (o2MarkPassingsBeforeTimePointSize > 0) {
o2LastMarkPassingBeforeTimePoint = o2MarkPassingsBeforeTimePoint.last();
}
o2Leg = trackedRace.getCurrentLeg(o2, timePoint);
} finally {
trackedRace.unlockAfterRead(o2MarkPassings);
}
result = o2MarkPassingsBeforeTimePointSize - o1MarkPassingsBeforeTimePointSize; // inverted: more legs means
// smaller rank
if (result == 0 && o1MarkPassingsBeforeTimePointSize > 0) {
// 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
result = o1LastMarkPassingBeforeTimePoint.getTimePoint().compareTo(
o2LastMarkPassingBeforeTimePoint.getTimePoint());
} else {
if (o2Leg == null) {
result = 1; // o1Leg != null, so o1 has started leg already, o2 hasn't
} 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 Distance wwdtgO1 = windwardDistanceToGoInLegCache.get(o1);
final Distance wwdtgO2 = windwardDistanceToGoInLegCache.get(o2);
result = wwdtgO1==null?wwdtgO2==null?0:-1:wwdtgO2==null?1:wwdtgO1.compareTo(wwdtgO2);
}
}
}
}
}
return result;
} }
} }
File mode changed.
View File
File mode changed.
@@ -189,7 +189,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements
StructureImportManagementPanel structureImportUrlsManagementPanel = new StructureImportManagementPanel(sailingService, this, getStringMessages(), this, eventManagementPanel); StructureImportManagementPanel structureImportUrlsManagementPanel = new StructureImportManagementPanel(sailingService, this, getStringMessages(), this, eventManagementPanel);
panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel<StructureImportManagementPanel>(structureImportUrlsManagementPanel), panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel<StructureImportManagementPanel>(structureImportUrlsManagementPanel),
getStringMessages().structureImportUrls(), Permission.MANAGE_STRUCTURE_IMPORT_URLS); getStringMessages().manage2Sail() + " " + getStringMessages().regattaStructureImport(), Permission.MANAGE_STRUCTURE_IMPORT_URLS);
/* ADVANCED */ /* ADVANCED */
@@ -10,7 +10,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
@@ -18,16 +17,18 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CaptionPanel;
import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.sap.sailing.domain.common.RankingMetrics; import com.sap.sailing.domain.common.RankingMetrics;
@@ -49,7 +50,7 @@ import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
public class StructureImportManagementPanel extends FlowPanel implements RegattaStructureProvider { public class StructureImportManagementPanel extends SimplePanel implements RegattaStructureProvider {
private final SailingServiceAsync sailingService; private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter; private final ErrorReporter errorReporter;
private final StringMessages stringMessages; private final StringMessages stringMessages;
@@ -59,7 +60,7 @@ public class StructureImportManagementPanel extends FlowPanel implements Regatta
private final BusyIndicator busyIndicator; private final BusyIndicator busyIndicator;
private TextBox jsonURLTextBox; private TextBox jsonURLTextBox;
private TextBox eventIDTextBox; private TextBox eventIDTextBox;
private CaptionPanel regattaImportPanel;
private Button listRegattasButton; private Button listRegattasButton;
private Button importDetailsButton; private Button importDetailsButton;
private VerticalPanel editSeriesPanel; private VerticalPanel editSeriesPanel;
@@ -105,7 +106,12 @@ public class StructureImportManagementPanel extends FlowPanel implements Regatta
this.regattaListComposite = new StructureImportListComposite(this.sailingService, new RegattaSelectionModel( this.regattaListComposite = new StructureImportListComposite(this.sailingService, new RegattaSelectionModel(
true), this.regattaRefresher, this, this.errorReporter, this.stringMessages); true), this.regattaRefresher, this, this.errorReporter, this.stringMessages);
regattaListComposite.ensureDebugId("RegattaListComposite"); regattaListComposite.ensureDebugId("RegattaListComposite");
createUI();
VerticalPanel mainPanel = new VerticalPanel();
setWidget(mainPanel);
mainPanel.setWidth("100%");
createUI(mainPanel);
regattaListComposite.addSelectionChangeHandler(new Handler() { regattaListComposite.addSelectionChangeHandler(new Handler() {
@Override @Override
public void onSelectionChange(SelectionChangeEvent event) { public void onSelectionChange(SelectionChangeEvent event) {
@@ -116,12 +122,10 @@ public class StructureImportManagementPanel extends FlowPanel implements Regatta
regattaStructureGrid = new FlexTable(); regattaStructureGrid = new FlexTable();
} }
private void createUI() { private void createUI(VerticalPanel mainPanel) {
final Panel progressPanel = new FlowPanel(); final Panel progressPanel = new FlowPanel();
progressPanel.add(busyIndicator); progressPanel.add(busyIndicator);
Grid URLgrid = new Grid(2, 2);
Label eventIDLabel = new Label(stringMessages.event() + ":");
Label jsonURLLabel = new Label(stringMessages.jsonUrl() + ":");
eventIDTextBox = new TextBox(); eventIDTextBox = new TextBox();
eventIDTextBox.addChangeHandler(new ChangeHandler() { eventIDTextBox.addChangeHandler(new ChangeHandler() {
@Override @Override
@@ -161,33 +165,39 @@ public class StructureImportManagementPanel extends FlowPanel implements Regatta
listRegattasAndTheirStructures(); listRegattasAndTheirStructures();
} }
}); });
VerticalPanel vp = new VerticalPanel();
HorizontalPanel buttonPanel = new HorizontalPanel(); Grid eventURLGrid = new Grid(3, 2);
URLgrid.setWidget(0, 0, eventIDLabel); eventURLGrid.setWidget(0, 0, new Label(stringMessages.manage2Sail() + " " + stringMessages.event() + " " + stringMessages.id() + ":"));
URLgrid.setWidget(0, 1, eventIDTextBox); eventURLGrid.setWidget(0, 1, eventIDTextBox);
URLgrid.setWidget(1, 0, jsonURLLabel); eventURLGrid.setWidget(1, 0, new Label(stringMessages.jsonUrl() + ":"));
URLgrid.setWidget(1, 1, jsonURLTextBox); eventURLGrid.setWidget(1, 1, jsonURLTextBox);
vp.add(URLgrid); eventURLGrid.setWidget(2, 1, listRegattasButton);
vp.add(buttonPanel);
buttonPanel.add(listRegattasButton); mainPanel.add(progressPanel);
buttonPanel.add(importDetailsButton); mainPanel.add(eventURLGrid);
Grid grid = new Grid(1, 1);
vp.add(grid); regattaImportPanel = new CaptionPanel(stringMessages.manage2Sail() + " " + stringMessages.event());
mainPanel.add(regattaImportPanel);
VerticalPanel regattaImportContentPanel = new VerticalPanel();
regattaImportPanel.setContentWidget(regattaImportContentPanel);
grid.setWidget(0, 0, regattaListComposite);
grid.getRowFormatter().setVerticalAlign(0, HasVerticalAlignment.ALIGN_TOP);
grid.getColumnFormatter().getElement(0/** 1 */).getStyle().setPaddingTop(2.0, Unit.EM);
editSeriesPanel = new VerticalPanel(); editSeriesPanel = new VerticalPanel();
add(progressPanel);
add(vp); HorizontalPanel hPanel = new HorizontalPanel();
add(editSeriesPanel); hPanel.setSpacing(10);
hPanel.add(regattaListComposite);
hPanel.setCellWidth(regattaListComposite, "50%");
hPanel.add(editSeriesPanel);
hPanel.setCellWidth(editSeriesPanel, "50%");
regattaImportContentPanel.add(hPanel);
regattaImportContentPanel.add(importDetailsButton);
} }
/** /**
* Adds an event selector and a "create new event" button to the {@link #editSeriesPanel} in its first row * Adds an event selector and a "create new event" button to the {@link #editSeriesPanel} in its first row
*/ */
private void createEventSelectionAndCreateEventButtonUI() { private Widget createEventSelectionAndCreateEventButtonUI() {
Grid grid = new Grid(1, 2); Grid grid = new Grid(1, 2);
sailingEventsListBox = new ListBox(); sailingEventsListBox = new ListBox();
sailingEventsListBox.setMultipleSelect(false); sailingEventsListBox.setMultipleSelect(false);
@@ -223,7 +233,7 @@ public class StructureImportManagementPanel extends FlowPanel implements Regatta
} }
} }
}); });
return grid;
} }
private void openEventCreateDialog() { private void openEventCreateDialog() {
@@ -11,6 +11,7 @@ import com.sap.sse.gwt.client.DefaultRule_1_0n;
public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, com.sap.sse.gwt.adminconsole.StringMessages { public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, com.sap.sse.gwt.adminconsole.StringMessages {
public static final StringMessages INSTANCE = GWT.create(StringMessages.class); public static final StringMessages INSTANCE = GWT.create(StringMessages.class);
String manage2Sail();
String tracTracEvents(); String tracTracEvents();
String swissTimingEvents(); String swissTimingEvents();
String trackedBefore(); String trackedBefore();
@@ -1164,7 +1165,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
@Override @Override
String errorCommunicatingWithServer(); String errorCommunicatingWithServer();
String userManagement(); String userManagement();
String structureImportUrls(); String regattaStructureImport();
String filteredBy(); String filteredBy();
String filterBy(); String filterBy();
String editSeries(); String editSeries();
@@ -1,3 +1,4 @@
manage2Sail=Manage2Sail
tracTracEvents=TracTrac Events tracTracEvents=TracTrac Events
swissTimingEvents=SwissTiming Events swissTimingEvents=SwissTiming Events
trackedBefore=History of tracked events trackedBefore=History of tracked events
@@ -1134,7 +1135,7 @@ remoteProcedureCall=Remote Procedure Call
serverReplies=Server replies serverReplies=Server replies
errorCommunicatingWithServer=Error communicating with server errorCommunicatingWithServer=Error communicating with server
userManagement=User Management userManagement=User Management
structureImportUrls=Regatta Structure Import regattaStructureImport=Regatta Structure Import
filteredBy=filtered by filteredBy=filtered by
editSeries=Edit Series editSeries=Edit Series
pleaseSelectAtLeastOneRegatta=Please select at least one regatta pleaseSelectAtLeastOneRegatta=Please select at least one regatta
@@ -1,3 +1,4 @@
manage2Sail=Manage2Sail
createNewEvent=Create New EventtracTracEvents=TracTrac Events createNewEvent=Create New EventtracTracEvents=TracTrac Events
swissTimingEvents=SwissTiming Events swissTimingEvents=SwissTiming Events
trackedBefore=Historie verfolgter Events trackedBefore=Historie verfolgter Events
@@ -1128,7 +1129,7 @@ remoteProcedureCall=Entfernter Prozeduraufruf
serverReplies=Server antwortet serverReplies=Server antwortet
errorCommunicatingWithServer=Fehler bei der Kommunikation mit dem Server errorCommunicatingWithServer=Fehler bei der Kommunikation mit dem Server
userManagement=Benutzerverwaltung userManagement=Benutzerverwaltung
structureImportUrls=Regattastruktur-Import regattaStructureImport=Regattastruktur-Import
editSeries=Serie bearbeiten editSeries=Serie bearbeiten
filteredBy=gefiltert nach filteredBy=gefiltert nach
pleaseSelectAtLeastOneRegatta=Bitte wenigstens eine Regatta auswählen pleaseSelectAtLeastOneRegatta=Bitte wenigstens eine Regatta auswählen
@@ -1,4 +1,5 @@
# If you want new properties translated then sent this over GMR (Mark Angell <mangell@gmr.com>) # If you want new properties translated then sent this over GMR (Mark Angell <mangell@gmr.com>)
manage2Sail=Manage2Sail
tracTracEvents=События Трак Трак tracTracEvents=События Трак Трак
swissTimingEvents=События Swiss Timing swissTimingEvents=События Swiss Timing
trackedBefore=Ранее Отслеженные trackedBefore=Ранее Отслеженные
@@ -1076,7 +1077,7 @@ remoteProcedureCall=Remote Procedure Call
serverReplies=Server replies serverReplies=Server replies
errorCommunicatingWithServer=Error communicating with server errorCommunicatingWithServer=Error communicating with server
userManagement=User Management userManagement=User Management
structureImportUrls=Regatta structure import regattaStructureImport=Regatta structure import
editSeries=Edit Series editSeries=Edit Series
filteredBy=filtered by filteredBy=filtered by
pleaseSelectAtLeastOneRegatta=Please select at least one regatta pleaseSelectAtLeastOneRegatta=Please select at least one regatta
@@ -1,3 +1,4 @@
manage2Sail=Manage2Sail
enableRaceFilter=Enable filter enableRaceFilter=Enable filter
finishLine=终点线 finishLine=终点线
raceLabel=比赛轮次 raceLabel=比赛轮次
@@ -460,7 +461,7 @@ createFlexibleLeaderboard=Create flexible leaderboard
unknown=Unknown unknown=Unknown
furtherSignalsAshore=Further signals ashore furtherSignalsAshore=Further signals ashore
pleaseSelectAtLeastOneRegatta=Please select at least one regatta pleaseSelectAtLeastOneRegatta=Please select at least one regatta
structureImportUrls=Regatta Structure Import regattaStructureImport=Regatta Structure Import
raceAbandoned=放弃 raceAbandoned=放弃
sheetName=Sheet name sheetName=Sheet name
rrs26Start=RRS26 start rrs26Start=RRS26 start
@@ -56,6 +56,7 @@ public class RaceContext {
private final RaceLog raceLog; private final RaceLog raceLog;
private final ReadonlyRaceState state; private final ReadonlyRaceState state;
private final Event event; private final Event event;
private final long TIME_BEFORE_START_TO_SHOW_RACES_AS_LIVE = 15 * 60 * 1000; // 15 min
private final long TIME_TO_SHOW_CANCELED_RACES_AS_LIVE = 5 * 60 * 1000; // 5 min private final long TIME_TO_SHOW_CANCELED_RACES_AS_LIVE = 5 * 60 * 1000; // 5 min
public RaceContext(Event event, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, RaceLogResolver raceLogResolver) { public RaceContext(Event event, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, RaceLogResolver raceLogResolver) {
@@ -308,7 +309,7 @@ public class RaceContext {
result = trackedRace.isLive(now); result = trackedRace.isLive(now);
} else { } else {
// no data from tracking but maybe a manual setting of the start and finish time // no data from tracking but maybe a manual setting of the start and finish time
TimePoint startOfLivePeriod = startTime.minus(TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS); TimePoint startOfLivePeriod = startTime.minus(TIME_BEFORE_START_TO_SHOW_RACES_AS_LIVE);
TimePoint endOfLivePeriod = finishTime != null ? finishTime.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS) : null; TimePoint endOfLivePeriod = finishTime != null ? finishTime.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS) : null;
if(now.after(startOfLivePeriod) && (endOfLivePeriod == null || now.before(endOfLivePeriod))) { if(now.after(startOfLivePeriod) && (endOfLivePeriod == null || now.before(endOfLivePeriod))) {
result = true; result = true;
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.news.EventNewsItem; import com.sap.sailing.news.EventNewsItem;
import com.sap.sailing.news.EventNewsProvider; import com.sap.sailing.news.EventNewsProvider;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
@@ -28,11 +29,10 @@ public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider {
Iterable<LeaderboardGroup> leaderboardGroups = event.getLeaderboardGroups(); Iterable<LeaderboardGroup> leaderboardGroups = event.getLeaderboardGroups();
for(LeaderboardGroup leaderboardGroup: leaderboardGroups) { for(LeaderboardGroup leaderboardGroup: leaderboardGroups) {
for(Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) { for(Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) {
TimePoint timePointOfLatestModification = leaderboard.getTimePointOfLatestModification(); SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if(timePointOfLatestModification == null) { if(scoreCorrection != null) {
continue; TimePoint timePointOfLatestModification = scoreCorrection.getTimePointOfLastCorrectionsValidity();
} if(timePointOfLatestModification != null) {
String displayName = leaderboard.getDisplayName() != null ?leaderboard.getDisplayName() :leaderboard.getName(); String displayName = leaderboard.getDisplayName() != null ?leaderboard.getDisplayName() :leaderboard.getName();
String boatClassName= null; String boatClassName= null;
if(leaderboard instanceof RegattaLeaderboard) { if(leaderboard instanceof RegattaLeaderboard) {
@@ -44,6 +44,8 @@ public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider {
result.add(new LeaderboardUpdateNewsItem(event.getId(), timePointOfLatestModification.asDate(), leaderboard.getName(), displayName, boatClassName)); result.add(new LeaderboardUpdateNewsItem(event.getId(), timePointOfLatestModification.asDate(), leaderboard.getName(), displayName, boatClassName));
} }
} }
}
}
Collections.sort(result); Collections.sort(result);
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarsChangedListener;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.aggregation.PolarFixAggregator;
import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval; import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval;
@@ -206,4 +207,14 @@ public class PolarDataServiceImpl implements PolarDataService {
} }
} }
@Override
public void registerListener(BoatClass boatClass, PolarsChangedListener listener) {
polarDataMiner.registerListener(boatClass, listener);
}
@Override
public void unregisterListener(BoatClass boatClass, PolarsChangedListener listener) {
polarDataMiner.unregisterListener(boatClass, listener);
}
} }
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.math.analysis.polynomials.PolynomialFunction; import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarsChangedListener;
import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.factories.GroupKeyFactory;
@@ -26,6 +28,12 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
private final Map<GroupKey, AngleAndSpeedRegression> regressions = new HashMap<>(); private final Map<GroupKey, AngleAndSpeedRegression> regressions = new HashMap<>();
private ConcurrentHashMap<BoatClass, Set<PolarsChangedListener>> listeners;
public CubicRegressionPerCourseProcessor(ConcurrentHashMap<BoatClass, Set<PolarsChangedListener>> listeners) {
this.listeners = listeners;
}
@Override @Override
public boolean canProcessElements() { public boolean canProcessElements() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
@@ -46,6 +54,12 @@ public class CubicRegressionPerCourseProcessor implements Processor<GroupedDataE
} }
} }
regression.addData(fix.getWind(), fix.getAbsoluteAngleToTheWind(), fix.getBoatSpeed()); regression.addData(fix.getWind(), fix.getAbsoluteAngleToTheWind(), fix.getBoatSpeed());
Set<PolarsChangedListener> listenersForBoatClass = listeners.get(fix.getBoatClass());
if (listenersForBoatClass != null) {
for (PolarsChangedListener listener : listenersForBoatClass) {
listener.polarsChanged();
}
}
} }
} }
@@ -3,11 +3,13 @@ package com.sap.sailing.polars.mining;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.RejectedExecutionHandler;
@@ -40,6 +42,7 @@ import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance; import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarsChangedListener;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.impl.CubicEquation; import com.sap.sailing.polars.impl.CubicEquation;
import com.sap.sse.datamining.components.FilterCriterion; import com.sap.sse.datamining.components.FilterCriterion;
@@ -65,6 +68,8 @@ public class PolarDataMiner {
private static final Logger logger = Logger.getLogger(PolarDataMiner.class.getSimpleName()); private static final Logger logger = Logger.getLogger(PolarDataMiner.class.getSimpleName());
private final ConcurrentHashMap<BoatClass, Set<PolarsChangedListener>> listeners = new ConcurrentHashMap<>();
private ThreadPoolExecutor createExecutor() { private ThreadPoolExecutor createExecutor() {
return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(EXECUTOR_QUEUE_SIZE), new RejectedExecutionHandler() { new LinkedBlockingQueue<Runnable>(EXECUTOR_QUEUE_SIZE), new RejectedExecutionHandler() {
@@ -135,7 +140,7 @@ public class PolarDataMiner {
GPSFixMovingWithPolarContext.class, executor, movingAverageGrouperResultReceivers, parameterizedDimensions); GPSFixMovingWithPolarContext.class, executor, movingAverageGrouperResultReceivers, parameterizedDimensions);
cubicRegressionPerCourseProcessor = new CubicRegressionPerCourseProcessor(); cubicRegressionPerCourseProcessor = new CubicRegressionPerCourseProcessor(listeners);
Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> regressionPerCourseGrouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>(); Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> regressionPerCourseGrouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>();
regressionPerCourseGrouperResultReceivers.add(cubicRegressionPerCourseProcessor); regressionPerCourseGrouperResultReceivers.add(cubicRegressionPerCourseProcessor);
@@ -147,7 +152,7 @@ public class PolarDataMiner {
GPSFixMovingWithPolarContext.class, executor, regressionPerCourseGrouperResultReceivers, parameterizedDimensionsForCubicRegression); GPSFixMovingWithPolarContext.class, executor, regressionPerCourseGrouperResultReceivers, parameterizedDimensionsForCubicRegression);
ClusterGroup<Bearing> angleClusterGroup = createAngleClusterGroup(); ClusterGroup<Bearing> angleClusterGroup = createAngleClusterGroup();
speedRegressionPerAngleClusterProcessor = new SpeedRegressionPerAngleClusterProcessor(angleClusterGroup); speedRegressionPerAngleClusterProcessor = new SpeedRegressionPerAngleClusterProcessor(angleClusterGroup, listeners);
Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> regressionPerAngleClusterGrouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>(); Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> regressionPerAngleClusterGrouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>();
regressionPerAngleClusterGrouperResultReceivers.add(speedRegressionPerAngleClusterProcessor); regressionPerAngleClusterGrouperResultReceivers.add(speedRegressionPerAngleClusterProcessor);
@@ -436,4 +441,21 @@ public class PolarDataMiner {
} }
} }
} }
public void registerListener(BoatClass boatClass, PolarsChangedListener listener) {
Set<PolarsChangedListener> listenersForBoatClass = listeners.get(boatClass);
if (listenersForBoatClass == null) {
Map<PolarsChangedListener, Boolean> mapForConcurrency = new ConcurrentHashMap<>();
listenersForBoatClass = Collections.newSetFromMap(mapForConcurrency);
listeners.put(boatClass, listenersForBoatClass);
}
listenersForBoatClass.add(listener);
}
public void unregisterListener(BoatClass boatClass, PolarsChangedListener listener) {
Set<PolarsChangedListener> listenersForBoatClass = listeners.get(boatClass);
if (listenersForBoatClass != null) {
listenersForBoatClass.remove(listener);
}
}
} }
@@ -2,6 +2,8 @@ package com.sap.sailing.polars.mining;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.math.analysis.polynomials.PolynomialFunction; import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
@@ -14,6 +16,7 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarsChangedListener;
import com.sap.sailing.polars.regression.IncrementalLeastSquares; import com.sap.sailing.polars.regression.IncrementalLeastSquares;
import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl; import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl;
import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.AdditionalResultDataBuilder;
@@ -34,8 +37,11 @@ public class SpeedRegressionPerAngleClusterProcessor implements Processor<Groupe
private final ClusterGroup<Bearing> angleClusterGroup; private final ClusterGroup<Bearing> angleClusterGroup;
public SpeedRegressionPerAngleClusterProcessor(ClusterGroup<Bearing> angleClusterGroup) { private final ConcurrentHashMap<BoatClass, Set<PolarsChangedListener>> listeners;
public SpeedRegressionPerAngleClusterProcessor(ClusterGroup<Bearing> angleClusterGroup, ConcurrentHashMap<BoatClass, Set<PolarsChangedListener>> listeners) {
this.angleClusterGroup = angleClusterGroup; this.angleClusterGroup = angleClusterGroup;
this.listeners = listeners;
} }
@Override @Override
@@ -64,6 +70,12 @@ public class SpeedRegressionPerAngleClusterProcessor implements Processor<Groupe
} }
GPSFixMovingWithPolarContext fix = element.getDataEntry(); GPSFixMovingWithPolarContext fix = element.getDataEntry();
regression.addData(fix.getWind().getObject().getKnots(), fix.getBoatSpeed().getObject().getKnots()); regression.addData(fix.getWind().getObject().getKnots(), fix.getBoatSpeed().getObject().getKnots());
Set<PolarsChangedListener> listenersForBoatClass = listeners.get(fix.getBoatClass());
if (listenersForBoatClass != null) {
for (PolarsChangedListener listener : listenersForBoatClass) {
listener.polarsChanged();
}
}
} }
/** /**
@@ -27,6 +27,6 @@
<booleanAttribute key="tracing" value="false"/> <booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/> <booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" 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@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="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="&#13;&#10;additional-options2=onexit\=snapshot&#13;&#10;"/> <stringAttribute key="yk-options" value="&#13;&#10;additional-options2=onexit\=snapshot&#13;&#10;"/>
</launchConfiguration> </launchConfiguration>
@@ -16,10 +16,10 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/> <listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute> </listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre1.8.0_20"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sapjvm_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.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.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y -ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=2010 -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 -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Digtimi.client.id=7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6 -Digtimi.client.secret=aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a -Digtimi.client.redirecturi=http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback -DspatialWind=true -Dmongo.host=172.31.25.253 -Dmongo.port=10200 -Dmongo.dbName=LOAD_TEST"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y -ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=2010 -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 -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Digtimi.client.id=7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6 -Digtimi.client.secret=aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a -Digtimi.client.redirecturi=http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/> <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/> <stringAttribute key="pde.version" value="3.3"/>
<booleanAttribute key="show_selected_only" value="false"/> <booleanAttribute key="show_selected_only" value="false"/>
@@ -88,6 +88,7 @@ import com.sap.sailing.domain.base.impl.EventImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl; import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl;
import com.sap.sailing.domain.common.DataImportProgress; import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
@@ -2374,13 +2375,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
public void serializeForInitialReplicationInternal(ObjectOutputStream oos) throws IOException { public void serializeForInitialReplicationInternal(ObjectOutputStream oos) throws IOException {
StringBuffer logoutput = new StringBuffer(); StringBuffer logoutput = new StringBuffer();
logger.info("Serializing events...");
oos.writeObject(eventsById);
logoutput.append("\nSerialized " + eventsById.size() + " events\n");
for (Event event : eventsById.values()) {
logoutput.append(String.format("%3s\n", event.toString()));
}
logger.info("Serializing regattas..."); logger.info("Serializing regattas...");
oos.writeObject(regattasByName); oos.writeObject(regattasByName);
logoutput.append("Serialized " + regattasByName.size() + " regattas\n"); logoutput.append("Serialized " + regattasByName.size() + " regattas\n");
@@ -2388,6 +2382,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
logoutput.append(String.format("%3s\n", regatta.toString())); logoutput.append(String.format("%3s\n", regatta.toString()));
} }
logger.info("Serializing events...");
oos.writeObject(eventsById);
logoutput.append("\nSerialized " + eventsById.size() + " events\n");
for (Event event : eventsById.values()) {
logoutput.append(String.format("%3s\n", event.toString()));
}
logger.info("Serializing regattas observed..."); logger.info("Serializing regattas observed...");
oos.writeObject(regattasObservedForDefaultLeaderboard); oos.writeObject(regattasObservedForDefaultLeaderboard);
logger.info("Serializing regatta tracking cache..."); logger.info("Serializing regatta tracking cache...");
@@ -2441,13 +2442,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
// de-serialization; this will cause all classes to be visible that this bundle // de-serialization; this will cause all classes to be visible that this bundle
// (com.sap.sailing.server) can see // (com.sap.sailing.server) can see
StringBuffer logoutput = new StringBuffer(); StringBuffer logoutput = new StringBuffer();
logger.info("Reading all events...");
eventsById.putAll((Map<Serializable, Event>) ois.readObject());
logoutput.append("\nReceived " + eventsById.size() + " NEW events\n");
for (Event event : eventsById.values()) {
logoutput.append(String.format("%3s\n", event.toString()));
}
logger.info("Reading all regattas..."); logger.info("Reading all regattas...");
regattasByName.putAll((Map<String, Regatta>) ois.readObject()); regattasByName.putAll((Map<String, Regatta>) ois.readObject());
logoutput.append("Received " + regattasByName.size() + " NEW regattas\n"); logoutput.append("Received " + regattasByName.size() + " NEW regattas\n");
@@ -2455,6 +2449,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
logoutput.append(String.format("%3s\n", regatta.toString())); logoutput.append(String.format("%3s\n", regatta.toString()));
} }
logger.info("Reading all events...");
eventsById.putAll((Map<Serializable, Event>) ois.readObject());
logoutput.append("\nReceived " + eventsById.size() + " NEW events\n");
for (Event event : eventsById.values()) {
logoutput.append(String.format("%3s\n", event.toString()));
}
// it is important that the leaderboards and tracked regattas are cleared before auto-linking to // it is important that the leaderboards and tracked regattas are cleared before auto-linking to
// old leaderboards takes place which then don't match the new ones // old leaderboards takes place which then don't match the new ones
logger.info("Reading all dynamic tracked regattas..."); logger.info("Reading all dynamic tracked regattas...");
@@ -2504,6 +2505,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
logger.info("Reading competitors..."); logger.info("Reading competitors...");
for (Competitor competitor : ((CompetitorStore) ois.readObject()).getCompetitors()) { for (Competitor competitor : ((CompetitorStore) ois.readObject()).getCompetitors()) {
DynamicCompetitor dynamicCompetitor = (DynamicCompetitor) competitor; DynamicCompetitor dynamicCompetitor = (DynamicCompetitor) competitor;
// the following should actually be redundant because during de-serialization the Competitor objects,
// whose classes implement IsManagedByCache, should already have been got/created from/in the
// competitor store
competitorStore.getOrCreateCompetitor(dynamicCompetitor.getId(), dynamicCompetitor.getName(), competitorStore.getOrCreateCompetitor(dynamicCompetitor.getId(), dynamicCompetitor.getName(),
dynamicCompetitor.getColor(), dynamicCompetitor.getEmail(), dynamicCompetitor.getFlagImage(), dynamicCompetitor.getColor(), dynamicCompetitor.getEmail(), dynamicCompetitor.getFlagImage(),
dynamicCompetitor.getTeam(), dynamicCompetitor.getBoat(), dynamicCompetitor.getTimeOnTimeFactor(), dynamicCompetitor.getTeam(), dynamicCompetitor.getBoat(), dynamicCompetitor.getTimeOnTimeFactor(),
@@ -3218,9 +3222,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
public Iterable<Competitor> getCompetitorInOrderOfWindwardDistanceTraveledFarthestFirst(TrackedRace trackedRace, TimePoint timePoint) { public Iterable<Competitor> getCompetitorInOrderOfWindwardDistanceTraveledFarthestFirst(TrackedRace trackedRace, TimePoint timePoint) {
final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint);
final List<Competitor> result = new ArrayList<>(); final List<Competitor> result = new ArrayList<>();
Util.addAll(trackedRace.getRace().getCompetitors(), result); final Map<Competitor, Distance> windwardDistanceSailedPerCompetitor = new HashMap<>();
result.sort((c1, c2) -> rankingInfo.getCompetitorRankingInfo().apply(c2).getWindwardDistanceSailed().compareTo( for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
rankingInfo.getCompetitorRankingInfo().apply(c1).getWindwardDistanceSailed())); result.add(competitor);
windwardDistanceSailedPerCompetitor.put(competitor, rankingInfo.getCompetitorRankingInfo().apply(competitor).getWindwardDistanceSailed());
}
result.sort((c1, c2) -> windwardDistanceSailedPerCompetitor.get(c2).compareTo(
windwardDistanceSailedPerCompetitor.get(c1)));
return result; return result;
} }
@@ -379,11 +379,15 @@ public class SimulationServiceImpl implements SimulationService {
// determine legtype upwind/downwind/reaching // determine legtype upwind/downwind/reaching
LegType legType = null; LegType legType = null;
if (startTimePoint != null) {
try { try {
legType = trackedRace.getTrackedLeg(leg).getLegType(startTimePoint); legType = trackedRace.getTrackedLeg(leg).getLegType(startTimePoint);
} catch (NoWindException e) { } catch (NoWindException e) {
return null; return null;
} }
} else {
return null;
}
// get windfield // get windfield
WindFieldGenerator windField = new WindFieldTrackedRaceImpl(trackedRace); WindFieldGenerator windField = new WindFieldTrackedRaceImpl(trackedRace);
@@ -816,15 +816,15 @@ public class PolarDiagramBase implements PolarDiagram, Serializable {
@Override @Override
public int compare(Bearing o1, Bearing o2) { public int compare(Bearing o1, Bearing o2) {
Double d1 = o1.getDegrees(); double d1 = o1.getDegrees();
if (d1 < 0) { if (d1 < 0) {
d1 = 360 + d1; d1 = 360 + d1;
} }
Double d2 = o2.getDegrees(); double d2 = o2.getDegrees();
if (d2 < 0) { if (d2 < 0) {
d2 = 360 + d2; d2 = 360 + d2;
} }
return d1.compareTo(d2); return d1 < d2 ? -1 : d1 == d2 ? 0 : 1;
} }
}; };
@@ -0,0 +1,8 @@
package com.sap.sse.util;
import java.io.Serializable;
import java.util.function.Function;
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
}
@@ -286,7 +286,7 @@ public class MessageSendingService extends Service implements MessageSendingList
int resendMillis = PrefUtils.getInt(this, R.string.preference_messageResendIntervalMillis_key, int resendMillis = PrefUtils.getInt(this, R.string.preference_messageResendIntervalMillis_key,
R.integer.preference_messageResendIntervalMillis_default); R.integer.preference_messageResendIntervalMillis_default);
if (!success) { if (!success) {
ExLog.i(this, "MS", "success"); ExLog.i(this, "MS", "!success");
reportApiConnectivity(APIConnectivity.transmissionError); reportApiConnectivity(APIConnectivity.transmissionError);
ExLog.w(this, TAG, "Error while posting intent to server. Will persist intent..."); ExLog.w(this, TAG, "Error while posting intent to server. Will persist intent...");
try { try {
@@ -302,7 +302,7 @@ public class MessageSendingService extends Service implements MessageSendingList
reportUnsentGPSFixesCount(); reportUnsentGPSFixesCount();
serviceLogger.onMessageSentFailed(); serviceLogger.onMessageSentFailed();
} else { } else {
ExLog.i(this, "MS", "!success"); ExLog.i(this, "MS", "success");
reportApiConnectivity(APIConnectivity.transmissionSuccess); reportApiConnectivity(APIConnectivity.transmissionSuccess);
ExLog.i(this, TAG, "Message successfully sent."); ExLog.i(this, TAG, "Message successfully sent.");
if (persistenceManager.areIntentsDelayed()) { if (persistenceManager.areIntentsDelayed()) {
@@ -1,5 +1,7 @@
package com.sap.sailing.android.buoy.positioning.app.ui.fragments; package com.sap.sailing.android.buoy.positioning.app.ui.fragments;
import java.text.DecimalFormat;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
import android.location.LocationListener; import android.location.LocationListener;
@@ -23,14 +25,15 @@ import com.sap.sailing.android.buoy.positioning.app.util.PingHelper;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkInfo; import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkInfo;
import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkPingInfo; import com.sap.sailing.android.buoy.positioning.app.valueobjects.MarkPingInfo;
import com.sap.sailing.android.shared.data.LeaderboardInfo; import com.sap.sailing.android.shared.data.LeaderboardInfo;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.ui.customviews.OpenSansButton; import com.sap.sailing.android.shared.ui.customviews.OpenSansButton;
import com.sap.sailing.android.shared.ui.customviews.OpenSansTextView; import com.sap.sailing.android.shared.ui.customviews.OpenSansTextView;
import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView; import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.ui.fragments.BaseFragment; import com.sap.sailing.android.ui.fragments.BaseFragment;
import java.text.DecimalFormat;
public class BuoyFragment extends BaseFragment implements LocationListener { public class BuoyFragment extends BaseFragment implements LocationListener {
private static final int GPS_MIN_DISTANCE = 1;
private static final int GPS_MIN_TIME = 1000;
private OpenSansTextView markHeaderTextView; private OpenSansTextView markHeaderTextView;
private OpenSansTextView latitudeTextView; private OpenSansTextView latitudeTextView;
private OpenSansTextView longitudeTextView; private OpenSansTextView longitudeTextView;
@@ -80,7 +83,7 @@ public class BuoyFragment extends BaseFragment implements LocationListener {
signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal.toInt()); signalQualityIndicatorView.setSignalQuality(GPSQuality.noSignal.toInt());
if (mark != null) { if (mark != null) {
markHeaderTextView.setText(mark.getName()); markHeaderTextView.setText(mark.getName());
setUpTextUI(null); setUpTextUI(lastKnownLocation);
GoogleMap map = mapFragment.getMap(); GoogleMap map = mapFragment.getMap();
configureMap(map); configureMap(map);
updateMap(); updateMap();
@@ -124,6 +127,11 @@ public class BuoyFragment extends BaseFragment implements LocationListener {
accuracyText += " (" + String.format(accuracyString, accuracyFormatter.format(markPing.getAccuracy())) accuracyText += " (" + String.format(accuracyString, accuracyFormatter.format(markPing.getAccuracy()))
+ ")"; + ")";
} }
ExLog.w(getActivity(), getTag(), "Setting latitude to: "+latitudeText);
ExLog.w(getActivity(), getTag(), "Setting longitude to: "+longitudeText);
ExLog.w(getActivity(), getTag(), "Setting accuracy to: "+accuracyText);
latitudeTextView.setText(latitudeText); latitudeTextView.setText(latitudeText);
longitudeTextView.setText(longitudeText); longitudeTextView.setText(longitudeText);
accuracyTextView.setText(accuracyText); accuracyTextView.setText(accuracyText);
@@ -179,7 +187,13 @@ public class BuoyFragment extends BaseFragment implements LocationListener {
private void initLocationProvider() { private void initLocationProvider() {
locationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE); locationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
locationManager.removeUpdates(this); locationManager.removeUpdates(this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10, this); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_MIN_TIME, GPS_MIN_DISTANCE, this);
Location initialLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (initialLocation != null) {
onLocationChanged(initialLocation);
}
} }
public void updateMap() { public void updateMap() {
+8 -3
View File
@@ -3,22 +3,27 @@
## Server Landscape ## Server Landscape
We run four servers for the event: We run four servers for the event:
- on EC2: **Master 1**, **Master 2**, **Replica 1** and **Replica 2** with an **ELB** in front, - on EC2: **Master 1**, **Master 2**, **Master 3**, **Replica 1** and **Replica 2** with an **ELB** in front,
- ELB canonical name is KW2015-553831863.eu-west-1.elb.amazonaws.com - ELB canonical name is KW2015-553831863.eu-west-1.elb.amazonaws.com
- locally: **Cube-Server** - locally: **Cube-Server**
for fast and available switching. The DB they use is called `KW2015`, and so is the replication channel. The git branch we use to merge the features we want to deploy to the event servers is called `kielerwoche2015` and the git tag used to build a release for it is called `kielerwoche2015-release`. See http://hudson.sapsailing.com/job/SAPSailingAnalytics-kielerwoche2015/ and http://hudson.sapsailing.com/job/SAPSailingAnalytics-kielerwoche2015-release/, respectively, for their Hudson jobs. for fast and available switching. The DB they use is called `KW2015`, and so is the replication channel. The git branch we use to merge the features we want to deploy to the event servers is called `kielerwoche2015` and the git tag used to build a release for it is called `kielerwoche2015-release`. See http://hudson.sapsailing.com/job/SAPSailingAnalytics-kielerwoche2015/ and http://hudson.sapsailing.com/job/SAPSailingAnalytics-kielerwoche2015-release/, respectively, for their Hudson jobs.
### Master 1 Server ### Master 1 Server (32GB)
- Admin Console at http://ec2-54-154-151-10.eu-west-1.compute.amazonaws.com:8888/gwt/AdminConsole.html - Admin Console at http://ec2-54-154-151-10.eu-west-1.compute.amazonaws.com:8888/gwt/AdminConsole.html
- External IP: 54.154.151.10 - External IP: 54.154.151.10
- Internal IP: 172.31.24.187 - Internal IP: 172.31.24.187
### Master 2 Server ### Master 2 Server (32GB)
- Admin Console at http://ec2-54-76-169-213.eu-west-1.compute.amazonaws.com:8888/gwt/AdminConsole.html - Admin Console at http://ec2-54-76-169-213.eu-west-1.compute.amazonaws.com:8888/gwt/AdminConsole.html
- External IP: 54.76.169.213 - External IP: 54.76.169.213
- Internal IP: 172.31.30.73 - Internal IP: 172.31.30.73
### Master 3 Server (64GB)
- Admin Console at http://ec2-54-171-148-10.eu-west-1.compute.amazonaws.com:8888/gwt/AdminConsole.html
- External IP: 54.171.148.10
- Internal IP: 172.31.26.119
### "Current" Master ### "Current" Master
Whichever master is current, we map the main Apache URL http://kielerwoche2015-master.sapsailing.com to it. This is also the URL to use for Tablet and Smartphone configurations. Whichever master is current, we map the main Apache URL http://kielerwoche2015-master.sapsailing.com to it. This is also the URL to use for Tablet and Smartphone configurations.