Merge branch 'master' into polars

This commit is contained in:
Frederik Petersen committed 2013-10-01 10:19:58 +02:00
commit f15cd53ee4
251 files changed
+4971 -1860

No files matched your search

+38 -12
View File
@@ -61,6 +61,7 @@ HAS_OVERWRITTEN_TARGET=0
TARGET_SERVER_NAME=$active_branch TARGET_SERVER_NAME=$active_branch
gwtcompile=1 gwtcompile=1
onegwtpermutationonly=0
testing=1 testing=1
clean="clean" clean="clean"
offline=0 offline=0
@@ -68,9 +69,10 @@ proxy=0
extra='' extra=''
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "buildAndUpdateProduct [-g -t -o -c -m <config> -n <package> -l <port>] [build|install|all|hot-deploy|remote-deploy]" echo "buildAndUpdateProduct [-b -g -t -o -c -m <config> -n <package> -l <port>] [build|install|all|hot-deploy|remote-deploy]"
echo "" echo ""
echo "-g Disable GWT compile, no gwt files will be generated, old ones will be preserved." echo "-g Disable GWT compile, no gwt files will be generated, old ones will be preserved."
echo "-b Build GWT permutation only for one browser and English language."
echo "-t Disable tests" echo "-t Disable tests"
echo "-o Enable offline mode (does not work for tycho surefire plugin)" echo "-o Enable offline mode (does not work for tycho surefire plugin)"
echo "-c Disable cleaning (use only if you are sure that no java file has changed)" echo "-c Disable cleaning (use only if you are sure that no java file has changed)"
@@ -104,12 +106,13 @@ echo PROJECT_HOME is $PROJECT_HOME
echo SERVERS_HOME is $SERVERS_HOME echo SERVERS_HOME is $SERVERS_HOME
echo BRANCH is $active_branch echo BRANCH is $active_branch
options=':gtocpm:n:l:s:w:' options=':bgtocpm:n:l:s:w:'
while getopts $options option while getopts $options option
do do
case $option in case $option in
g) gwtcompile=0;; g) gwtcompile=0;;
t) testing=0;; t) testing=0;;
b) onegwtpermutationonly=1;;
o) offline=1;; o) offline=1;;
c) clean="";; c) clean="";;
p) proxy=1;; p) proxy=1;;
@@ -146,15 +149,15 @@ if [[ "$@" == "hot-deploy" ]]; then
exit exit
fi fi
if [[ $HAS_OVERWRITTEN_TARGET -eq 1 ]]; then
active_branch=$TARGET_SERVER_NAME
fi
if [ ! -d $SERVERS_HOME/$active_branch/plugins ]; then if [ ! -d $SERVERS_HOME/$active_branch/plugins ]; then
echo "Could not find target directory $SERVERS_HOME/$active_branch/plugins!" echo "Could not find target directory $SERVERS_HOME/$active_branch/plugins!"
exit exit
fi fi
if [[ $HAS_OVERWRITTEN_TARGET -eq 1 ]]; then
active_branch=$TARGET_SERVER_NAME
fi
# locate old bundle # locate old bundle
BUNDLE_COUNT=`find $SERVERS_HOME/$active_branch/plugins -maxdepth 1 -name "${OSGI_BUNDLE_NAME}_*.jar" | wc -l` BUNDLE_COUNT=`find $SERVERS_HOME/$active_branch/plugins -maxdepth 1 -name "${OSGI_BUNDLE_NAME}_*.jar" | wc -l`
OLD_BUNDLE=`find $SERVERS_HOME/$active_branch/plugins -maxdepth 1 -name "${OSGI_BUNDLE_NAME}_*.jar"` OLD_BUNDLE=`find $SERVERS_HOME/$active_branch/plugins -maxdepth 1 -name "${OSGI_BUNDLE_NAME}_*.jar"`
@@ -270,6 +273,21 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
if [ $gwtcompile -eq 1 ]; then if [ $gwtcompile -eq 1 ]; then
echo "INFO: Compiling GWT (rm -rf com.sap.sailing.gwt.ui/com.sap.sailing.*)" echo "INFO: Compiling GWT (rm -rf com.sap.sailing.gwt.ui/com.sap.sailing.*)"
rm -rf com.sap.sailing.gwt.ui/com.sap.sailing.* rm -rf com.sap.sailing.gwt.ui/com.sap.sailing.*
if [ $onegwtpermutationonly -eq 1 ]; then
echo "INFO: Patching .gwt.xml files such that only one GWT permutation needs to be compiled"
for i in com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/*.gwt.xml; do
cp $i $i.bak
cat $i | sed -e 's/^[ ]*<extend-property name="locale" values="de"\/>[ ]*$/<!-- <extend-property name="locale" values="de"\/> --> <set-property name="user.agent" value="safari" \/>/' >$i.sed
mv $i.sed $i
done
else
echo "INFO: Patching .gwt.xml files such that all GWT permutations are compiled"
for i in com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/*.gwt.xml; do
cp $i $i.bak
cat $i | sed -e 's/<!-- <extend-property name="locale" values="de"\/> --> <set-property name="user.agent" value="safari" \/>/<extend-property name="locale" values="de"\/>/' >$i.sed
mv $i.sed $i
done
fi
else else
echo "INFO: GWT Compilation disabled" echo "INFO: GWT Compilation disabled"
extra="-Pdebug.no-gwt-compile" extra="-Pdebug.no-gwt-compile"
@@ -298,6 +316,14 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
echo "Using following command: mvn $extra -fae -s $MAVEN_SETTINGS $clean install" echo "Using following command: mvn $extra -fae -s $MAVEN_SETTINGS $clean install"
mvn $extra -fae -s $MAVEN_SETTINGS $clean install 2>&1 | tee $START_DIR/build.log mvn $extra -fae -s $MAVEN_SETTINGS $clean install 2>&1 | tee $START_DIR/build.log
if [ $gwtcompile -eq 1 ]; then
# Now move back the backup .gwt.xml files before they were (maybe) patched
for i in com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/*.gwt.xml; do
echo "INFO: restoring backup copy of $i after it was patched before"
mv -v $i.bak $i
done
fi
echo "Build complete. Do not forget to install product..." echo "Build complete. Do not forget to install product..."
fi fi
@@ -344,11 +370,14 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
rm -rf $ACDIR/org.eclipse.* rm -rf $ACDIR/org.eclipse.*
rm -rf $ACDIR/configuration/org.eclipse.* rm -rf $ACDIR/configuration/org.eclipse.*
# always overwrite start and stop scripts as they
# should never contain any custom logic
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/status $ACDIR/
if [ ! -f "$ACDIR/env.sh" ]; then if [ ! -f "$ACDIR/env.sh" ]; then
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/ cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/status $ACDIR/
fi fi
if [ ! -f $ACDIR/no-overwrite ]; then if [ ! -f $ACDIR/no-overwrite ]; then
@@ -363,9 +392,6 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/ cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/ cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/status $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/ cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
cp -v $PROJECT_HOME/java/target/http2udpmirror $ACDIR cp -v $PROJECT_HOME/java/target/http2udpmirror $ACDIR
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6 -1
View File
@@ -18,16 +18,21 @@ Like businesses, sailors need the latest information to make strategic decisions
* Information for Developers * Information for Developers
* [[Cook Book|wiki/cook-book]] * [[Cook Book|wiki/cook-book]]
* [[OnBoarding Information|wiki/onboarding]] * [[OnBoarding Information|wiki/onboarding]]
* [[UI Tests with Selenium|wiki/selenium-ui-tests]]
* [[Typical Development Scenarios|wiki/typical-development-scenarios]] * [[Typical Development Scenarios|wiki/typical-development-scenarios]]
* [[Server Replication|wiki/server-replication]] * [[Server Replication|wiki/server-replication]]
* [[Configure Races on Server|wiki/typical-development-scenarios]] * [[Configure Races on Server|wiki/typical-development-scenarios]]
* [[Development Environment|wiki/development-environment]] * [[Development Environment|wiki/development-environment]]
* [[Working with GWT Locally|wiki/local-gwt]]
* [[Mobile Development|wiki/mobile-development]] * [[Mobile Development|wiki/mobile-development]]
* General Information * General Information
* [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]] * [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]]
* [[Sailing Domain Algorithms|wiki/sailing-domain-algorithms]] * [[Sailing Domain Algorithms|wiki/sailing-domain-algorithms]]
* [[Inventory|wiki/inventar-liste]] * [[Inventory|wiki/inventar-liste]]
* [[Smartphone Tracking|wiki/smartphone-tracking]] * [[Smartphone Tracking|wiki/smartphone-tracking]]
* [[Racecommittee App|wiki/racecommittee-app]]
* [[Web Services API|wiki/sailing-webservices]]
* [[Theses (Bachelor, Master, ...)|wiki/theses]]
* Planning and Event Information * Planning and Event Information
* [[Project Planning (bigger development)|wiki/planning]] * [[Project Planning (bigger development)|wiki/planning]]
* [[General Event Planning|wiki/general-event-planning]] * [[General Event Planning|wiki/general-event-planning]]
@@ -40,4 +45,4 @@ Like businesses, sailors need the latest information to make strategic decisions
* [Maven Repository Browser](http://maven.sapsailing.com/maven/) * [Maven Repository Browser](http://maven.sapsailing.com/maven/)
* [Main Sailing Website](http://www.sapsailing.com) * [Main Sailing Website](http://www.sapsailing.com)
* [Visitor Statistics](http://analysis.sapsailing.com/) * [Visitor Statistics](http://analysis.sapsailing.com/)
* [Traffic and CPU for app01](http://mrtg.sapsailing.com/) * [Traffic and CPU for app01](http://mrtg.sapsailing.com/)
-2
View File
@@ -2,8 +2,6 @@
<classpath> <classpath>
<classpathentry exported="true" kind="lib" path="lib/gwt-dev.jar"/> <classpathentry exported="true" kind="lib" path="lib/gwt-dev.jar"/>
<classpathentry exported="true" kind="lib" path="lib/gwt-user.jar"/> <classpathentry exported="true" kind="lib" path="lib/gwt-user.jar"/>
<classpathentry exported="true" kind="lib" path="lib/validation-api-1.0.0.GA-sources.jar"/>
<classpathentry exported="true" kind="lib" path="lib/validation-api-1.0.0.GA.jar"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
@@ -7,10 +7,7 @@ Bundle-Vendor: GOOGLE
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ClassPath: lib/gwt-dev.jar, Bundle-ClassPath: lib/gwt-dev.jar,
lib/gwt-user.jar, lib/gwt-user.jar,
lib/validation-api-1.0.0.GA-sources.jar,
lib/validation-api-1.0.0.GA.jar,
. .
Eclipse-BuddyPolicy: registered
Export-Package: com.gargoylesoftware.htmlunit, Export-Package: com.gargoylesoftware.htmlunit,
com.gargoylesoftware.htmlunit.attachment, com.gargoylesoftware.htmlunit.attachment,
com.gargoylesoftware.htmlunit.gae, com.gargoylesoftware.htmlunit.gae,
@@ -601,4 +598,6 @@ Export-Package: com.gargoylesoftware.htmlunit,
org.mortbay.util, org.mortbay.util,
org.mortbay.util.ajax, org.mortbay.util.ajax,
org.mortbay.xml org.mortbay.xml
Require-Bundle: javax.servlet;bundle-version="2.5.0";visibility:=reexport Require-Bundle: javax.validation;bundle-version="1.0.0"
Import-Package: javax.servlet;version="2.5.0",
javax.servlet.http;version="2.5.0"
+1 -3
View File
@@ -1,6 +1,4 @@
bin.includes = META-INF/,\ bin.includes = META-INF/,\
.,\ .,\
lib/gwt-dev.jar,\ lib/gwt-dev.jar,\
lib/gwt-user.jar,\ lib/gwt-user.jar
lib/validation-api-1.0.0.GA-sources.jar,\
lib/validation-api-1.0.0.GA.jar
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.google.gwt.user.osgi</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -6,8 +6,7 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.declination Fragment-Host: com.sap.sailing.declination
Require-Bundle: org.junit4;bundle-version="4.8.2"
Import-Package: com.sap.sailing.domain.common, Import-Package: com.sap.sailing.domain.common,
com.sap.sailing.domain.common.impl, com.sap.sailing.domain.common.impl,
com.sap.sailing.domain.common.quadtree, com.sap.sailing.domain.common.quadtree
junit.framework;version="4.8.1",
org.junit;version="4.8.1"
@@ -59,6 +59,9 @@ public class NOAAImporter {
URL url = new URL(QUERY_URL+"?lon1="+position.getLngDeg()+"&lat1="+position.getLatDeg()+"&startYear=" + calendar.get(Calendar.YEAR) + "&startMonth=" URL url = new URL(QUERY_URL+"?lon1="+position.getLngDeg()+"&lat1="+position.getLatDeg()+"&startYear=" + calendar.get(Calendar.YEAR) + "&startMonth="
+ (calendar.get(Calendar.MONTH) + 1) + "&startDay=" + calendar.get(Calendar.DAY_OF_MONTH)+"&resultFormat=xml"); + (calendar.get(Calendar.MONTH) + 1) + "&startDay=" + calendar.get(Calendar.DAY_OF_MONTH)+"&resultFormat=xml");
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
conn.setRequestProperty("Accept-Language", "en-US,en;q=0.8");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36");
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
final InputStream inputStream = conn.getInputStream(); final InputStream inputStream = conn.getInputStream();
Document doc = builder.parse(inputStream); Document doc = builder.parse(inputStream);
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.common.impl; package com.sap.sailing.domain.common.impl;
import java.text.SimpleDateFormat;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
@@ -9,8 +8,6 @@ import com.sap.sailing.domain.common.TimePoint;
public abstract class AbstractTimePoint implements TimePoint { public abstract class AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = 8825508619301420378L; private static final long serialVersionUID = 8825508619301420378L;
public static SimpleDateFormat TIMEPOINT_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static Comparator<TimePoint> TIMEPOINT_COMPARATOR = new SerializableComparator<TimePoint>() { public static Comparator<TimePoint> TIMEPOINT_COMPARATOR = new SerializableComparator<TimePoint>() {
private static final long serialVersionUID = 7644726881387366025L; private static final long serialVersionUID = 7644726881387366025L;
@@ -68,7 +65,7 @@ public abstract class AbstractTimePoint implements TimePoint {
@Override @Override
public String toString() { public String toString() {
return TIMEPOINT_FORMATTER.format(asDate()); return asDate().toString();
} }
/** /**
@@ -82,7 +82,6 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -238,16 +237,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
result = leaderboardRegistry.getLeaderboardByName(leaderboardName); result = leaderboardRegistry.getLeaderboardByName(leaderboardName);
} }
if (result == null) { if (result == null) {
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS); ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name()); String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name());
if (groupForMetaLeaderboard != null) { if (groupForMetaLeaderboard != null) {
result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, loadScoringScheme(dbLeaderboard), resultDiscardingRule); result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, loadScoringScheme(dbLeaderboard), resultDiscardingRule);
groupForMetaLeaderboard.setOverallLeaderboard(result); groupForMetaLeaderboard.setOverallLeaderboard(result);
} else if (regattaName == null) { } else if (regattaName == null) {
result = loadFlexibleLeaderboard(dbLeaderboard, scoreCorrection, resultDiscardingRule); result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule);
} else { } else {
result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard, scoreCorrection, resultDiscardingRule, regattaRegistry); result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard, resultDiscardingRule, regattaRegistry);
} }
if (result != null) { if (result != null) {
final Leaderboard finalResult = result; final Leaderboard finalResult = result;
@@ -263,7 +261,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} }
} }
}); });
needsMigration[0] = loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, scoreCorrection) || needsMigration[0]; needsMigration[0] = loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, result.getScoreCorrection()) || needsMigration[0];
needsMigration[0] = loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections) || needsMigration[0]; needsMigration[0] = loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections) || needsMigration[0];
loadColumnFactors(dbLeaderboard, result); loadColumnFactors(dbLeaderboard, result);
// add the leaderboard to the registry // add the leaderboard to the registry
@@ -338,20 +336,20 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
* @return <code>null</code> if the regatta cannot be resolved; otherwise the leaderboard for the regatta specified * @return <code>null</code> if the regatta cannot be resolved; otherwise the leaderboard for the regatta specified
*/ */
private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName, DBObject dbLeaderboard, private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName, DBObject dbLeaderboard,
SettableScoreCorrection scoreCorrection, ThresholdBasedResultDiscardingRule resultDiscardingRule, RegattaRegistry regattaRegistry) { ThresholdBasedResultDiscardingRule resultDiscardingRule, RegattaRegistry regattaRegistry) {
RegattaLeaderboard result = null; RegattaLeaderboard result = null;
Regatta regatta = regattaRegistry.getRegatta(new RegattaName(regattaName)); Regatta regatta = regattaRegistry.getRegatta(new RegattaName(regattaName));
if (regatta == null) { if (regatta == null) {
logger.info("Couldn't find regatta "+regattaName+" for corresponding regatta leaderboard. Not loading regatta leaderboard."); logger.info("Couldn't find regatta "+regattaName+" for corresponding regatta leaderboard. Not loading regatta leaderboard.");
} else { } else {
result = new RegattaLeaderboardImpl(regatta, scoreCorrection, resultDiscardingRule); result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule);
result.setName(leaderboardName); result.setName(leaderboardName);
} }
return result; return result;
} }
private FlexibleLeaderboard loadFlexibleLeaderboard(DBObject dbLeaderboard, private FlexibleLeaderboard loadFlexibleLeaderboard(DBObject dbLeaderboard,
SettableScoreCorrection scoreCorrection, ThresholdBasedResultDiscardingRule resultDiscardingRule) { ThresholdBasedResultDiscardingRule resultDiscardingRule) {
final FlexibleLeaderboardImpl result; final FlexibleLeaderboardImpl result;
BasicDBList dbRaceColumns = (BasicDBList) dbLeaderboard.get(FieldNames.LEADERBOARD_COLUMNS.name()); BasicDBList dbRaceColumns = (BasicDBList) dbLeaderboard.get(FieldNames.LEADERBOARD_COLUMNS.name());
if (dbRaceColumns == null) { if (dbRaceColumns == null) {
@@ -372,7 +370,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} }
result = new FlexibleLeaderboardImpl(raceLogStore, (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME result = new FlexibleLeaderboardImpl(raceLogStore, (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME
.name()), scoreCorrection, resultDiscardingRule, scoringScheme, courseArea); .name()), resultDiscardingRule, scoringScheme, courseArea);
// For a FlexibleLeaderboard, fleets are owned by the leaderboard's RaceColumn objects. We need to manage // For a FlexibleLeaderboard, fleets are owned by the leaderboard's RaceColumn objects. We need to manage
// them here: // them here:
Map<String, Fleet> fleetsByName = new HashMap<String, Fleet>(); Map<String, Fleet> fleetsByName = new HashMap<String, Fleet>();
@@ -1009,11 +1007,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(identifier.getIdentifier())); query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(identifier.getIdentifier()));
loadRaceLogEvents(result, query); loadRaceLogEvents(result, query);
// query for obsolete events // query for events with deprecated identifier format
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(String.format("%s.%s.%s", query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getDeprecatedIdentifier()));
identifier.getTemplate().getParentObjectName(),
identifier.getRaceColumnName(),
identifier.getFleetName())));
loadRaceLogEvents(result, query); loadRaceLogEvents(result, query);
} catch (Throwable t) { } catch (Throwable t) {
// something went wrong during DB access; report, then use empty new race log // something went wrong during DB access; report, then use empty new race log
@@ -1028,7 +1023,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
for (DBObject o : raceLog.find(query)) { for (DBObject o : raceLog.find(query)) {
RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name())); RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name()));
if (raceLogEvent != null) { if (raceLogEvent != null) {
result.add(raceLogEvent); result.load(raceLogEvent);
} }
} }
} }
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
@@ -280,14 +281,16 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
} }
private void storeScoreCorrections(Leaderboard leaderboard, BasicDBObject dbScoreCorrections) { private void storeScoreCorrections(Leaderboard leaderboard, BasicDBObject dbScoreCorrections) {
TimePoint now = MillisecondsTimePoint.now();
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection(); SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
BasicDBList dbCorrectionForRace = new BasicDBList(); BasicDBList dbCorrectionForRace = new BasicDBList();
for (Competitor competitor : leaderboard.getCompetitors()) { for (Competitor competitor : leaderboard.getCompetitors()) {
if (scoreCorrection.isScoreCorrected(competitor, raceColumn)) { // TODO bug 655: make score corrections time dependent
if (scoreCorrection.isScoreCorrected(competitor, raceColumn, now)) {
BasicDBObject dbCorrectionForCompetitor = new BasicDBObject(); BasicDBObject dbCorrectionForCompetitor = new BasicDBObject();
dbCorrectionForCompetitor.put(FieldNames.COMPETITOR_ID.name(), competitor.getId()); dbCorrectionForCompetitor.put(FieldNames.COMPETITOR_ID.name(), competitor.getId());
MaxPointsReason maxPointsReason = scoreCorrection.getMaxPointsReason(competitor, raceColumn); MaxPointsReason maxPointsReason = scoreCorrection.getMaxPointsReason(competitor, raceColumn, now);
if (maxPointsReason != MaxPointsReason.NONE) { if (maxPointsReason != MaxPointsReason.NONE) {
dbCorrectionForCompetitor.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name(), dbCorrectionForCompetitor.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name(),
maxPointsReason.name()); maxPointsReason.name());
@@ -751,7 +754,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningListChangedEvent.class.getSimpleName()); result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningListChangedEvent.class.getSimpleName());
result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningListChangedEvent.getPositionedCompetitors())); result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningListChangedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()));
return result; return result;
} }
@@ -762,7 +765,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningConfirmedEvent.class.getSimpleName()); result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogFinishPositioningConfirmedEvent.class.getSimpleName());
result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningConfirmedEvent.getPositionedCompetitors())); result.put(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name(), storePositionedCompetitors(finishPositioningConfirmedEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons()));
return result; return result;
} }
@@ -29,9 +29,9 @@ public class ConfirmedFinishPositioningListFinderTest extends
@Override @Override
protected List<Triple<Serializable, String, MaxPointsReason>> setupTargetEventsForPassAwareTests(int passId) { protected List<Triple<Serializable, String, MaxPointsReason>> setupTargetEventsForPassAwareTests(int passId) {
RaceLogFinishPositioningConfirmedEvent event = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1, passId); RaceLogFinishPositioningConfirmedEvent event = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1, passId);
when(event.getPositionedCompetitors()).thenReturn(mock(List.class)); when(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(List.class));
raceLog.add(event); raceLog.add(event);
return event.getPositionedCompetitors(); return event.getPositionedCompetitorsIDsNamesMaxPointsReasons();
} }
@Test @Test
@@ -45,11 +45,11 @@ public class ConfirmedFinishPositioningListFinderTest extends
public void testMostRecent() { public void testMostRecent() {
RaceLogFinishPositioningConfirmedEvent event1 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1); RaceLogFinishPositioningConfirmedEvent event1 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1);
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 2); RaceLogFinishPositioningConfirmedEvent event2 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 2);
when(event2.getPositionedCompetitors()).thenReturn(mock(List.class)); when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(List.class));
raceLog.add(event1); raceLog.add(event1);
raceLog.add(event2); raceLog.add(event2);
assertEquals(event2.getPositionedCompetitors(), analyzer.analyze()); assertEquals(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons(), analyzer.analyze());
} }
} }
@@ -66,4 +66,13 @@ public interface RaceLog extends Track<RaceLogEvent>, WithID {
void addAllListeners(HashSet<RaceLogEventVisitor> listeners); void addAllListeners(HashSet<RaceLogEventVisitor> listeners);
Iterable<RaceLogEventVisitor> getAllListeners(); Iterable<RaceLogEventVisitor> getAllListeners();
/**
* Like {@link #add(RaceLogEvent)}, only that no events are triggered. Use this method only when loading a race log,
* e.g., from a replication or master data import or when loading from the database.
*
* @return <code>true</code> if the event was actually added which is the case if there was no equal event contained
* in this race log yet
*/
boolean load(RaceLogEvent event);
} }
@@ -8,5 +8,9 @@ import com.sap.sailing.domain.common.impl.Util.Triple;
public abstract interface RaceLogFinishPositioningEvent extends RaceLogEvent { public abstract interface RaceLogFinishPositioningEvent extends RaceLogEvent {
List<Triple<Serializable, String, MaxPointsReason>> getPositionedCompetitors(); /**
* @return a triple holding the competitor ID, the competitor name and the {@link MaxPointReason} documenting the
* score for the competitor
*/
List<Triple<Serializable, String, MaxPointsReason>> getPositionedCompetitorsIDsNamesMaxPointsReasons();
} }
@@ -12,7 +12,7 @@ public class AbortingFlagFinder extends RaceLogAnalyzer<RaceLogFlagEvent> {
} }
@Override @Override
protected RaceLogFlagEvent performAnalyzation() { protected RaceLogFlagEvent performAnalysis() {
if (raceLog.getCurrentPassId() <= RaceLog.DefaultPassId) { if (raceLog.getCurrentPassId() <= RaceLog.DefaultPassId) {
return null; return null;
} }
@@ -16,11 +16,11 @@ public class ConfirmedFinishPositioningListFinder extends RaceLogAnalyzer<List<T
} }
@Override @Override
protected List<Triple<Serializable, String, MaxPointsReason>> performAnalyzation() { protected List<Triple<Serializable, String, MaxPointsReason>> performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogFinishPositioningConfirmedEvent) { if (event instanceof RaceLogFinishPositioningConfirmedEvent) {
RaceLogFinishPositioningConfirmedEvent finishPositioningEvent = (RaceLogFinishPositioningConfirmedEvent) event; RaceLogFinishPositioningConfirmedEvent finishPositioningEvent = (RaceLogFinishPositioningConfirmedEvent) event;
return finishPositioningEvent.getPositionedCompetitors(); return finishPositioningEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons();
} }
} }
@@ -16,11 +16,11 @@ public class FinishPositioningListFinder extends RaceLogAnalyzer<List<Triple<Ser
} }
@Override @Override
protected List<Triple<Serializable, String, MaxPointsReason>> performAnalyzation() { protected List<Triple<Serializable, String, MaxPointsReason>> performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogFinishPositioningListChangedEvent) { if (event instanceof RaceLogFinishPositioningListChangedEvent) {
RaceLogFinishPositioningListChangedEvent finishPositioningEvent = (RaceLogFinishPositioningListChangedEvent) event; RaceLogFinishPositioningListChangedEvent finishPositioningEvent = (RaceLogFinishPositioningListChangedEvent) event;
return finishPositioningEvent.getPositionedCompetitors(); return finishPositioningEvent.getPositionedCompetitorsIDsNamesMaxPointsReasons();
} }
} }
@@ -13,7 +13,7 @@ public class FinishedTimeFinder extends RaceLogAnalyzer<TimePoint> {
} }
@Override @Override
protected TimePoint performAnalyzation() { protected TimePoint performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogRaceStatusEvent) { if (event instanceof RaceLogRaceStatusEvent) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event; RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
@@ -13,7 +13,7 @@ public class FinishingTimeFinder extends RaceLogAnalyzer<TimePoint> {
} }
@Override @Override
protected TimePoint performAnalyzation() { protected TimePoint performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogRaceStatusEvent) { if (event instanceof RaceLogRaceStatusEvent) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event; RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
@@ -11,7 +11,7 @@ public class GateLineOpeningTimeFinder extends RaceLogAnalyzer<Long> {
} }
@Override @Override
protected Long performAnalyzation() { protected Long performAnalysis() {
for (RaceLogEvent event : getPassEvents()) { for (RaceLogEvent event : getPassEvents()) {
if (event instanceof RaceLogGateLineOpeningTimeEvent) { if (event instanceof RaceLogGateLineOpeningTimeEvent) {
return ((RaceLogGateLineOpeningTimeEvent) event).getGateLineOpeningTime(); return ((RaceLogGateLineOpeningTimeEvent) event).getGateLineOpeningTime();
@@ -14,7 +14,7 @@ public abstract class IndividualRecallFinder extends RaceLogAnalyzer<TimePoint>
protected abstract boolean isRelevant(RaceLogFlagEvent flagEvent); protected abstract boolean isRelevant(RaceLogFlagEvent flagEvent);
@Override @Override
protected TimePoint performAnalyzation() { protected TimePoint performAnalysis() {
for (RaceLogEvent event : getPassEvents()) { for (RaceLogEvent event : getPassEvents()) {
if (event instanceof RaceLogFlagEvent) { if (event instanceof RaceLogFlagEvent) {
RaceLogFlagEvent flagEvent = (RaceLogFlagEvent) event; RaceLogFlagEvent flagEvent = (RaceLogFlagEvent) event;
@@ -25,7 +25,7 @@ public class LastFlagsFinder extends RaceLogAnalyzer<List<RaceLogFlagEvent>> {
} }
@Override @Override
protected List<RaceLogFlagEvent> performAnalyzation() { protected List<RaceLogFlagEvent> performAnalysis() {
Iterator<RaceLogEvent> iterator = getPassEventsDescending().iterator(); Iterator<RaceLogEvent> iterator = getPassEventsDescending().iterator();
RaceLogFlagEvent flagEvent = getNextFlagEvent(iterator); RaceLogFlagEvent flagEvent = getNextFlagEvent(iterator);
@@ -13,7 +13,7 @@ public class LastPublishedCourseDesignFinder extends RaceLogAnalyzer<CourseBase>
} }
@Override @Override
protected CourseBase performAnalyzation() { protected CourseBase performAnalysis() {
for (RaceLogEvent event : getAllEventsDescending()) { for (RaceLogEvent event : getAllEventsDescending()) {
if (event instanceof RaceLogCourseDesignChangedEvent) { if (event instanceof RaceLogCourseDesignChangedEvent) {
RaceLogCourseDesignChangedEvent courseDesignEvent = (RaceLogCourseDesignChangedEvent) event; RaceLogCourseDesignChangedEvent courseDesignEvent = (RaceLogCourseDesignChangedEvent) event;
@@ -12,7 +12,7 @@ public class LastWindFixFinder extends RaceLogAnalyzer<Wind> {
} }
@Override @Override
protected Wind performAnalyzation() { protected Wind performAnalysis() {
for (RaceLogEvent event : getAllEventsDescending()) { for (RaceLogEvent event : getAllEventsDescending()) {
if (event instanceof RaceLogWindFixEvent) { if (event instanceof RaceLogWindFixEvent) {
RaceLogWindFixEvent windFixEvent = (RaceLogWindFixEvent) event; RaceLogWindFixEvent windFixEvent = (RaceLogWindFixEvent) event;
@@ -11,7 +11,7 @@ public class PathfinderFinder extends RaceLogAnalyzer<String> {
} }
@Override @Override
protected String performAnalyzation() { protected String performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogPathfinderEvent) { if (event instanceof RaceLogPathfinderEvent) {
return ((RaceLogPathfinderEvent) event).getPathfinderId(); return ((RaceLogPathfinderEvent) event).getPathfinderId();
@@ -12,7 +12,7 @@ public class ProtestStartTimeFinder extends RaceLogAnalyzer<TimePoint> {
} }
@Override @Override
protected TimePoint performAnalyzation() { protected TimePoint performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogProtestStartTimeEvent) { if (event instanceof RaceLogProtestStartTimeEvent) {
return ((RaceLogProtestStartTimeEvent) event).getProtestStartTime(); return ((RaceLogProtestStartTimeEvent) event).getProtestStartTime();
@@ -30,7 +30,7 @@ public class RRS26StartModeFlagFinder extends RaceLogAnalyzer<Flags> {
} }
@Override @Override
protected Flags performAnalyzation() { protected Flags performAnalysis() {
StartProcedureType type = procedureAnalyzer.analyze(); StartProcedureType type = procedureAnalyzer.analyze();
if (StartProcedureType.RRS26.equals(type)) { if (StartProcedureType.RRS26.equals(type)) {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
@@ -24,13 +24,13 @@ public abstract class RaceLogAnalyzer<ResultType> {
public ResultType analyze() { public ResultType analyze() {
raceLog.lockForRead(); raceLog.lockForRead();
try { try {
return performAnalyzation(); return performAnalysis();
} finally { } finally {
raceLog.unlockAfterRead(); raceLog.unlockAfterRead();
} }
} }
protected abstract ResultType performAnalyzation(); protected abstract ResultType performAnalysis();
protected Iterable<RaceLogEvent> getPassEvents() { protected Iterable<RaceLogEvent> getPassEvents() {
return raceLog.getFixes(); return raceLog.getFixes();
@@ -12,7 +12,7 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
} }
@Override @Override
protected RaceLogRaceStatus performAnalyzation() { protected RaceLogRaceStatus performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogRaceStatusEvent) { if (event instanceof RaceLogRaceStatusEvent) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event; RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
@@ -12,7 +12,7 @@ public class StartProcedureTypeAnalyzer extends RaceLogAnalyzer<StartProcedureTy
} }
@Override @Override
protected StartProcedureType performAnalyzation() { protected StartProcedureType performAnalysis() {
for (RaceLogEvent event : getAllEventsDescending()) { for (RaceLogEvent event : getAllEventsDescending()) {
if (event instanceof RaceLogStartProcedureChangedEvent) { if (event instanceof RaceLogStartProcedureChangedEvent) {
RaceLogStartProcedureChangedEvent startProcedureEvent = (RaceLogStartProcedureChangedEvent) event; RaceLogStartProcedureChangedEvent startProcedureEvent = (RaceLogStartProcedureChangedEvent) event;
@@ -12,7 +12,7 @@ public class StartTimeFinder extends RaceLogAnalyzer<TimePoint> {
} }
@Override @Override
protected TimePoint performAnalyzation() { protected TimePoint performAnalysis() {
for (RaceLogEvent event : getPassEventsDescending()) { for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogStartTimeEvent) { if (event instanceof RaceLogStartTimeEvent) {
return ((RaceLogStartTimeEvent) event).getStartTime(); return ((RaceLogStartTimeEvent) event).getStartTime();
@@ -15,7 +15,7 @@ public class WindFixesFinder extends RaceLogAnalyzer<List<Wind>> {
} }
@Override @Override
protected List<Wind> performAnalyzation() { protected List<Wind> performAnalysis() {
final List<Wind> windFixes = new ArrayList<Wind>(); final List<Wind> windFixes = new ArrayList<Wind>();
for (RaceLogEvent event : getAllEventsDescending()) { for (RaceLogEvent event : getAllEventsDescending()) {
if (event instanceof RaceLogWindFixEvent) { if (event instanceof RaceLogWindFixEvent) {
@@ -22,7 +22,7 @@ public abstract class RaceLogFinishPositioningEventImpl extends RaceLogEventImpl
} }
@Override @Override
public List<Triple<Serializable, String, MaxPointsReason>> getPositionedCompetitors() { public List<Triple<Serializable, String, MaxPointsReason>> getPositionedCompetitorsIDsNamesMaxPointsReasons() {
return positionedCompetitors; return positionedCompetitors;
} }
@@ -88,6 +88,24 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
} }
return isAdded; return isAdded;
} }
@Override
public boolean load(RaceLogEvent event) {
boolean isAdded = false;
lockForWrite();
try {
isAdded = getInternalRawFixes().add(event);
} finally {
unlockAfterWrite();
}
if (isAdded) {
logger.finer(String.format("%s (%s) was loaded into log.", event, event.getClass().getName()));
setCurrentPassId(Math.max(event.getPassId(), this.currentPassId));
} else {
logger.warning(String.format("%s (%s) was not loaded into log. Ignoring", event, event.getClass().getName()));
}
return isAdded;
}
protected void notifyListenersAboutReceive(RaceLogEvent event) { protected void notifyListenersAboutReceive(RaceLogEvent event) {
synchronized (listeners) { synchronized (listeners) {
@@ -185,6 +185,26 @@ public class SwissTimingAdapterPersistenceImpl implements SwissTimingAdapterPers
return null; return null;
} }
@Override
public boolean hasRaceStartlist(String raceID) {
DBCollection races = database.getCollection(CollectionNames.RACES_MESSAGES.name());
BasicDBObject query = new BasicDBObject();
query.append(FieldNames.RACE_ID.name(), raceID);
query.append(FieldNames.MESSAGE_COMMAND.name(), MessageType.STL.name());
DBObject object = races.findOne(query);
return object != null;
}
@Override
public boolean hasRaceCourse(String raceID) {
DBCollection races = database.getCollection(CollectionNames.RACES_MESSAGES.name());
BasicDBObject query = new BasicDBObject();
query.append(FieldNames.RACE_ID.name(), raceID);
query.append(FieldNames.MESSAGE_COMMAND.name(), MessageType.CCG.name());
DBObject object = races.findOne(query);
return object != null;
}
@Override @Override
public Iterable<Race> getRaces() { public Iterable<Race> getRaces() {
DBCollection races = database.getCollection(CollectionNames.RACES_MASTERDATA.name()); DBCollection races = database.getCollection(CollectionNames.RACES_MASTERDATA.name());
@@ -16,9 +16,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.persistence, com.sap.sailing.domain.persistence,
com.sap.sailing.domain.tractracadapter, com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.swisstimingadapter.persistence;bundle-version="1.0.0", com.sap.sailing.domain.swisstimingadapter.persistence;bundle-version="1.0.0",
org.junit4;bundle-version="4.8.2",
org.hamcrest.core;bundle-version="1.1.0" org.hamcrest.core;bundle-version="1.1.0"
Bundle-ClassPath: resources/, Bundle-ClassPath: resources/,
. .
Import-Package: com.sap.sailing.server, Import-Package: com.sap.sailing.server
junit.framework;version="4.8.1",
org.junit;version="4.8.1"
@@ -47,14 +47,6 @@ public class DomainFactoryTest {
assertThat(raceType.getRaceCode(), is(OlympicRaceCode._470_WOMEN)); assertThat(raceType.getRaceCode(), is(OlympicRaceCode._470_WOMEN));
} }
@Test
public void testGet470WomenBoatClass2() throws Exception {
String raceID = "SAW005_xxx";
RaceType raceType = swissTimingDomainFactory.getRaceTypeFromRaceID(raceID );
assertThat(raceType, is(notNullValue()));
assertThat(raceType.getRaceCode(), is(OlympicRaceCode._470_WOMEN));
}
@Test @Test
public void testDontGetWrongBoatClass() throws Exception { public void testDontGetWrongBoatClass() throws Exception {
String raceID = "ABC"; String raceID = "ABC";
@@ -208,4 +208,16 @@ public class OverlappingStoredAndReceivedMessagesTest implements RaceSpecificMes
return null; return null;
} }
@Override
public boolean hasRaceStartlist(String raceID) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasRaceCourse(String raceID) {
// TODO Auto-generated method stub
return false;
}
} }
@@ -45,7 +45,7 @@ public class SimpleDomainFactoryTest {
@Test @Test
public void testExtractBoatClassFromRaceIDWithEventID() { public void testExtractBoatClassFromRaceIDWithEventID() {
String raceID = "QINSWC12013;SAW005901"; String raceID = "QINSWC12013_SAW005901";
RaceType raceType = DomainFactory.INSTANCE.getRaceTypeFromRaceID(raceID); RaceType raceType = DomainFactory.INSTANCE.getRaceTypeFromRaceID(raceID);
assertEquals("470", raceType.getBoatClass().getName()); assertEquals("470", raceType.getBoatClass().getName());
} }
@@ -10,4 +10,5 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.udpconnector, com.sap.sailing.udpconnector,
com.googlecode.java-diff-utils;bundle-version="1.3.0", com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sailing.domain.common com.sap.sailing.domain.common
Export-Package: com.sap.sailing.domain.swisstimingadapter Export-Package: com.sap.sailing.domain.swisstimingadapter,
com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test"
@@ -7,11 +7,15 @@ public class RaceRecord {
private String raceID; private String raceID;
private String description; private String description;
private Date startTime; private Date startTime;
private boolean hasCourse;
private boolean hasStartlist;
public RaceRecord(String raceID, String description, Date startTime) { public RaceRecord(String raceID, String description, Date startTime, boolean hasCourse, boolean hasStartlist) {
this.raceID = raceID; this.raceID = raceID;
this.description = description; this.description = description;
this.startTime = startTime; this.startTime = startTime;
this.hasCourse = hasCourse;
this.hasStartlist = hasStartlist;
} }
public String getRaceID() { public String getRaceID() {
@@ -26,4 +30,11 @@ public class RaceRecord {
return startTime; return startTime;
} }
public boolean hasCourse() {
return hasCourse;
}
public boolean hasStartlist() {
return hasStartlist;
}
} }
@@ -21,4 +21,8 @@ public interface RaceSpecificMessageLoader {
Iterable<Race> getRaces(); Iterable<Race> getRaces();
Race getRace(String raceID); Race getRace(String raceID);
boolean hasRaceStartlist(String raceID);
boolean hasRaceCourse(String raceID);
} }
@@ -26,12 +26,12 @@ public interface RaceType {
_470_MEN("SAM005", "470", Gender.male), _470_MEN("SAM005", "470", Gender.male),
STAR("SAM007", "Star", Gender.male), STAR("SAM007", "Star", Gender.male),
_49ER("SAM009", "49er", Gender.male), _49ER("SAM009", "49er", Gender.male),
_49ERFX("SAW009", "49erFX", Gender.female),
ELLIOTT6M("SAW007", "Elliott 6M", Gender.female), ELLIOTT6M("SAW007", "Elliott 6M", Gender.female),
RSX_WOMEN("SAW102", "RS:X", Gender.female), RSX_WOMEN("SAW102", "RS:X", Gender.female),
RSX_MEN("SAM102", "RS:X", Gender.male), RSX_MEN("SAM102", "RS:X", Gender.male),
LASER_RADIAL("SAW103", "Laser Radial", Gender.female), NACRA17("SAX017", "Nacra 17", Gender.mixed),
_49ERFX_TODO("xxxxx_TODO_xxxxx", "49er FX", Gender.female), LASER_RADIAL("SAW103", "Laser Radial", Gender.female);
NACRA17_TOTO("xxxxx_TODO_xxxxx", "Nacra 17", Gender.mixed);
enum Gender { enum Gender {
male, female, mixed, unspecified; male, female, mixed, unspecified;
@@ -19,8 +19,12 @@ public interface SailMasterConnector {
Course getCourse(String raceID) throws UnknownHostException, IOException, InterruptedException; Course getCourse(String raceID) throws UnknownHostException, IOException, InterruptedException;
boolean hasCourse(String raceID);
StartList getStartList(String raceID) throws UnknownHostException, IOException, InterruptedException; StartList getStartList(String raceID) throws UnknownHostException, IOException, InterruptedException;
boolean hasStartlist(String raceID);
TimePoint getStartTime(String raceID) throws UnknownHostException, IOException, ParseException, InterruptedException; TimePoint getStartTime(String raceID) throws UnknownHostException, IOException, ParseException, InterruptedException;
Distance getDistanceToMark(String raceID, int markIndex, String boatID) throws UnknownHostException, IOException, InterruptedException; Distance getDistanceToMark(String raceID, int markIndex, String boatID) throws UnknownHostException, IOException, InterruptedException;
@@ -206,7 +206,7 @@ public class DomainFactoryImpl implements DomainFactory {
public RaceType getRaceTypeFromRaceID(String raceID) { public RaceType getRaceTypeFromRaceID(String raceID) {
final RaceType result; final RaceType result;
if (raceID != null && raceID.length() >= 6) { if (raceID != null && raceID.length() >= 6) {
final String[] optionalEventIDAndMandatoryRaceID = raceID.split(";"); final String[] optionalEventIDAndMandatoryRaceID = raceID.split("_");
final String swissTimingRaceCode = optionalEventIDAndMandatoryRaceID[optionalEventIDAndMandatoryRaceID.length-1].substring(0, 6).toUpperCase(); final String swissTimingRaceCode = optionalEventIDAndMandatoryRaceID[optionalEventIDAndMandatoryRaceID.length-1].substring(0, 6).toUpperCase();
RaceType raceType = raceTypeByID.get(swissTimingRaceCode); RaceType raceType = raceTypeByID.get(swissTimingRaceCode);
if (raceType == null) { if (raceType == null) {
@@ -606,6 +606,24 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
return result; return result;
} }
@Override
public boolean hasCourse(String raceID) {
boolean result = false;
if (messageLoader != null) {
result = messageLoader.hasRaceCourse(raceID);
}
return result;
}
@Override
public boolean hasStartlist(String raceID) {
boolean result = false;
if (messageLoader != null) {
result = messageLoader.hasRaceStartlist(raceID);
}
return result;
}
private List<Race> parseAvailableRacesMessage(SailMasterMessage availableRacesMessage) { private List<Race> parseAvailableRacesMessage(SailMasterMessage availableRacesMessage) {
assertMessageType(MessageType.RAC, availableRacesMessage); assertMessageType(MessageType.RAC, availableRacesMessage);
int count = Integer.valueOf(availableRacesMessage.getSections()[1]); int count = Integer.valueOf(availableRacesMessage.getSections()[1]);
@@ -7,6 +7,6 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.domain.swisstimingreplayadapter Fragment-Host: com.sap.sailing.domain.swisstimingreplayadapter
Import-Package: org.json.simple, Import-Package: org.json.simple,
org.json.simple.parser, org.json.simple.parser
org.junit;version="4.8.2" Require-Bundle: com.sap.sailing.domain.test,
Require-Bundle: com.sap.sailing.domain.test org.junit4;bundle-version="4.8.2"
@@ -14,10 +14,8 @@ Require-Bundle: com.sap.sailing.domain,
org.mockito.mockito-core;bundle-version="1.9.5", org.mockito.mockito-core;bundle-version="1.9.5",
com.sap.sailing.domain.test, com.sap.sailing.domain.test,
org.hamcrest;bundle-version="1.1.0", org.hamcrest;bundle-version="1.1.0",
org.objenesis;bundle-version="1.3.0" org.objenesis;bundle-version="1.3.0",
org.junit4;bundle-version="4.8.2",
org.json.simple;bundle-version="1.1.0"
Export-Package: com.sap.sailing.domain.test, Export-Package: com.sap.sailing.domain.test,
com.sap.sailing.domain.test.mock com.sap.sailing.domain.test.mock
Import-Package: junit.framework;version="4.8.1",
org.json.simple,
org.json.simple.parser,
org.junit;version="4.8.1"
@@ -23,7 +23,6 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType; import com.sap.sailing.domain.tractracadapter.ReceiverType;
@@ -38,8 +37,8 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest {
@Test @Test
public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException { public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException {
leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(), leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }),
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
MillisecondsTimePoint now = MillisecondsTimePoint.now(); MillisecondsTimePoint now = MillisecondsTimePoint.now();
loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2 loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2
@@ -40,7 +40,6 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -102,7 +101,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test @Test
public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() { public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null); new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc"; final String columnName = "abc";
@@ -115,7 +114,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test @Test
public void testRepeatedLeaderboardDTOCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException { public void testRepeatedLeaderboardDTOCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null); new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc"; final String columnName = "abc";
@@ -174,8 +173,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
r.addCompetitor(c3); // this makes maxPoints==4 r.addCompetitor(c3); // this makes maxPoints==4
testRaces.add(r); // hash set should take care of more or less randomly permuting the races testRaces.add(r); // hash set should take care of more or less randomly permuting the races
} }
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null); new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -190,7 +188,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
bestScoringRaceColumn = raceColumn; bestScoringRaceColumn = raceColumn;
} }
} }
scoreCorrection.setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DSQ); leaderboard.getScoreCorrection().setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DSQ);
// assert that best scoring but now disqualified race gets max net points because of disqualification: // assert that best scoring but now disqualified race gets max net points because of disqualification:
assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1, assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1,
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001);
@@ -208,8 +206,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
competitor, /* rank */ 1, /* started */true); competitor, /* rank */ 1, /* started */true);
testRace.addCompetitor(c2); testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4 testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null); new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet); leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
@@ -237,14 +234,13 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
competitor, /* rank */ 1, /* started */true); competitor, /* rank */ 1, /* started */true);
testRace.addCompetitor(c2); testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4 testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null); new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet); RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
raceColumnsInLeaderboard.put(testRace, r1); raceColumnsInLeaderboard.put(testRace, r1);
RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */ false, defaultFleet); RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */ false, defaultFleet);
scoreCorrection.setMaxPointsReason(competitor, r2, MaxPointsReason.DND); // non-discardable disqualification leaderboard.getScoreCorrection().setMaxPointsReason(competitor, r2, MaxPointsReason.DND); // non-discardable disqualification
// assert that max points were given before discarding... // assert that max points were given before discarding...
assertEquals(4.0, leaderboard.getEntry(competitor, r2, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); assertEquals(4.0, leaderboard.getEntry(competitor, r2, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001);
// ...and after... // ...and after...
@@ -265,8 +261,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
r.addCompetitor(c3); // this makes maxPoints==4 r.addCompetitor(c3); // this makes maxPoints==4
testRaces.add(r); // hash set should take care of more or less randomly permuting the races testRaces.add(r); // hash set should take care of more or less randomly permuting the races
} }
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null); new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -281,7 +276,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
bestScoringRaceColumn = raceColumn; bestScoringRaceColumn = raceColumn;
} }
} }
scoreCorrection.setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DND); leaderboard.getScoreCorrection().setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DND);
// assert that best scoring but now disqualified race gets max net points because of disqualification: // assert that best scoring but now disqualified race gets max net points because of disqualification:
assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1, assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1,
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001); leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001);
@@ -293,7 +288,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold, protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold,
int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException { int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException {
setupRaces(numberOfStartedRaces, numberOfNotStartedRaces); setupRaces(numberOfStartedRaces, numberOfNotStartedRaces);
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl( FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(
new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowPoint(), null); new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null); Fleet defaultFleet = leaderboard.getFleet(null);
int i=0; int i=0;
@@ -44,9 +44,9 @@ import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassing;
@@ -59,9 +59,8 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
private ArrayList<Series> series; private ArrayList<Series> series;
private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) { private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
ScoreCorrectionImpl scoreCorrections = new ScoreCorrectionImpl();
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds); ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules); return new RegattaLeaderboardImpl(regatta, discardingRules);
} }
@Test @Test
@@ -800,24 +799,24 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
Competitor[] f6 = new Competitor[] { c[3], c[0] }; Competitor[] f6 = new Competitor[] { c[3], c[0] };
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new HighPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f1)), "R1", /* medalRace */ false, leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f1)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f2)), "R2", /* medalRace */ false, leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f2)), "R2", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
assertTrue(leaderboard1.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare( assertTrue(leaderboard1.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard1.getTotalPoints(c[0], later), leaderboard1.getTotalPoints(c[3], later)) < 0); // c0 better than c3 leaderboard1.getTotalPoints(c[0], later), leaderboard1.getTotalPoints(c[3], later)) < 0); // c0 better than c3
FlexibleLeaderboard leaderboard2 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard2 = new FlexibleLeaderboardImpl("Leaderboard 3", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new HighPoint(), null);
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f3)), "R1", /* medalRace */ false, leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f3)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f4)), "R2", /* medalRace */ false, leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f4)), "R2", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
assertTrue(leaderboard2.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare( assertTrue(leaderboard2.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard2.getTotalPoints(c[3], later), leaderboard2.getTotalPoints(c[0], later)) < 0); // c3 better than c0 leaderboard2.getTotalPoints(c[3], later), leaderboard2.getTotalPoints(c[0], later)) < 0); // c3 better than c0
FlexibleLeaderboard leaderboard3 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(), FlexibleLeaderboard leaderboard3 = new FlexibleLeaderboardImpl("Leaderboard 3", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null); new HighPoint(), null);
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f5)), "R1", /* medalRace */ false, leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f5)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null)); leaderboard1.getFleet(null));
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f6)), "R2", /* medalRace */ false, leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f6)), "R2", /* medalRace */ false,
@@ -838,6 +837,100 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(29 /* two wins, one second */, leaderboardGroup.getOverallLeaderboard().getTotalPoints(c[3], later), 0.000000001); assertEquals(29 /* two wins, one second */, leaderboardGroup.getOverallLeaderboard().getTotalPoints(c[3], later), 0.000000001);
} }
@Test
public void testApplicationOfScoreCorrectionsInRacesWithNoTrackedRaceAfterLastTrackedRaceWithoutMarkPassings() throws NoWindException {
// Let c0 lead the series, c3 trail it, c1 and c2 are one-time competitors which are then suppressed in the
// overall leaderboard, expecting c3 to rank second overall, after c0 ranking first, with c1 and c2 not appearing
// in the overall leaderboard's sorted competitor list
Competitor[] c = createCompetitors(4).toArray(new Competitor[0]);
Competitor[] f1 = new Competitor[] { c[2], c[0], c[1], c[3] };
final TimePoint endOfR1 = MillisecondsTimePoint.now();
final TimePoint withinR1 = endOfR1.minus(1000);
final TimePoint startOfR1 = withinR1.minus(10000);
final TimePoint beforeStartOfR1 = startOfR1.minus(10000);
final TimePoint afterEndOfR1 = endOfR1.plus(1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new LowPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(f1)) {
private static final long serialVersionUID = 8705622361027154428L;
public TimePoint getEndOfRace() {
return endOfR1;
}
}, "R1", /* medalRace */ false,
leaderboard1.getFleet(null));
RaceColumn r2 = leaderboard1.addRaceColumn("R2", /* medalRace */ false, new Fleet[] { new FleetImpl("Default Fleet") });
leaderboard1.getScoreCorrection().correctScore(c[0], r2, 123.);
assertEquals(2. + 123., leaderboard1.getTotalPoints(c[0], afterEndOfR1), 0.00000001); // correction expected to apply after end of last tracked race before the corrected column
assertEquals(2. + 0., leaderboard1.getTotalPoints(c[0], withinR1), 0.00000001); // correction expected NOT to apply before end of last tracked race before the corrected column
assertEquals(0., leaderboard1.getTotalPoints(c[0], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
}
@Test
public void testApplicationOfScoreCorrectionsInRacesWithNoTrackedRaceAfterLastTrackedRaceWithMarkPassings() throws NoWindException {
// Let c0 lead the series, c3 trail it, c1 and c2 are one-time competitors which are then suppressed in the
// overall leaderboard, expecting c3 to rank second overall, after c0 ranking first, with c1 and c2 not appearing
// in the overall leaderboard's sorted competitor list
Competitor[] c = createCompetitors(4).toArray(new Competitor[0]);
Competitor[] f1 = new Competitor[] { c[2], c[0], c[1], c[3] };
final TimePoint endOfR1 = MillisecondsTimePoint.now();
final TimePoint withinR1 = endOfR1.minus(1000);
final TimePoint startOfR1 = withinR1.minus(10000);
final TimePoint beforeStartOfR1 = startOfR1.minus(10000);
final TimePoint afterEndOfR1 = endOfR1.plus(1000);
final Waypoint start = new WaypointImpl(new GateImpl(new MarkImpl("Start Pin End"), new MarkImpl("Start Committee Boat"), "Start"));
final Waypoint finish = new WaypointImpl(new GateImpl(new MarkImpl("Finish Pin End"), new MarkImpl("Finish Committee Boat"), "Finish"));
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new LowPoint(), null);
final MockedTrackedRaceWithStartTimeAndRanks trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(f1)) {
private static final long serialVersionUID = 8705622361027154428L;
@Override
public TimePoint getEndOfRace() {
return endOfR1;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<MarkPassing>(new TimedComparator());
result.add(new MarkPassingImpl(startOfR1, start, competitor));
result.add(new MarkPassingImpl(endOfR1, finish, competitor));
return result;
}
};
trackedRace.getRace().getCourse().addWaypoint(0, start);
trackedRace.getRace().getCourse().addWaypoint(1, finish);
RaceColumn r1 = leaderboard1.addRace(trackedRace, "R1", /* medalRace */ false, leaderboard1.getFleet(null));
RaceColumn r2 = leaderboard1.addRaceColumn("R2", /* medalRace */ false, new Fleet[] { new FleetImpl("Default Fleet") });
leaderboard1.getScoreCorrection().correctScore(c[0], r2, 123.);
assertEquals(2. + 123., leaderboard1.getTotalPoints(c[0], afterEndOfR1), 0.00000001); // correction expected to apply after end of last tracked race before the corrected column
assertEquals(2. + 0., leaderboard1.getTotalPoints(c[0], withinR1), 0.00000001); // correction expected NOT to apply before end of last tracked race before the corrected column
assertEquals(0., leaderboard1.getTotalPoints(c[0], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
leaderboard1.getScoreCorrection().setMaxPointsReason(c[1], r2, MaxPointsReason.DNS);
assertEquals(3. + 5., leaderboard1.getTotalPoints(c[1], afterEndOfR1), 0.00000001); // DNS is applied at race start (not known), so at least after the end of R1
assertEquals(3. + 0., leaderboard1.getTotalPoints(c[1], withinR1), 0.00000001); // correction expected NOT to apply before end of last tracked race before the corrected column
assertEquals(0., leaderboard1.getTotalPoints(c[1], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
leaderboard1.getScoreCorrection().setMaxPointsReason(c[2], r2, MaxPointsReason.DNF);
assertEquals(1. + 5., leaderboard1.getTotalPoints(c[2], afterEndOfR1), 0.00000001); // DNF is applied after R2 finish (not known), so at least after the end of R1
assertEquals(1. + 0., leaderboard1.getTotalPoints(c[2], withinR1), 0.00000001); // correction expected NOT to apply before end of last tracked race before the corrected column
assertEquals(0., leaderboard1.getTotalPoints(c[2], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
leaderboard1.getScoreCorrection().setMaxPointsReason(c[3], r1, MaxPointsReason.DNF);
assertEquals(0., leaderboard1.getTotalPoints(c[3], beforeStartOfR1), 0.00000001); // DNF does not apply before the start
assertEquals(4., leaderboard1.getTotalPoints(c[3], withinR1), 0.00000001); // correction expected NOT to apply before end of race
assertEquals(5., leaderboard1.getTotalPoints(c[3], afterEndOfR1), 0.00000001); // after race is finished, DNF applies
leaderboard1.getScoreCorrection().setMaxPointsReason(c[3], r1, MaxPointsReason.DNS);
assertEquals(0., leaderboard1.getTotalPoints(c[3], beforeStartOfR1), 0.00000001); // DNS does not apply before the start
assertEquals(5., leaderboard1.getTotalPoints(c[3], withinR1), 0.00000001); // correction expected to apply after the start
assertEquals(5., leaderboard1.getTotalPoints(c[3], afterEndOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
leaderboard1.getScoreCorrection().setMaxPointsReason(c[3], r1, null);
leaderboard1.getScoreCorrection().correctScore(c[3], r1, 123.);
assertEquals(4., leaderboard1.getTotalPoints(c[3], withinR1), 0.00000001); // correction expected NOT to apply before end of last tracked race before the corrected column
assertEquals(123., leaderboard1.getTotalPoints(c[3], afterEndOfR1), 0.00000001); // correction is applied after R1 finish
assertEquals(0., leaderboard1.getTotalPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
}
private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, Competitor[]... competitorLists) { private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, Competitor[]... competitorLists) {
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
@@ -16,7 +16,6 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.test.mock.MockedTrackedRace; import com.sap.sailing.domain.test.mock.MockedTrackedRace;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -48,8 +47,7 @@ public class RaceLogAttachOnTrackedRaceTest {
@Before @Before
public void setUp() { public void setUp() {
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl(); leaderboard = new FlexibleLeaderboardImpl("", null, new LowPoint(), null);
leaderboard = new FlexibleLeaderboardImpl("", scoreCorrection, null, new LowPoint(), null);
} }
@Test @Test
@@ -11,7 +11,7 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.common, com.sap.sailing.domain.common,
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0" com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0"
Export-Package: com.sap.sailing.domain.tractracadapter, Export-Package: com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.swisstimingadapter.test" com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.server.test"
Bundle-ActivationPolicy: lazy Bundle-ActivationPolicy: lazy
Import-Package: org.json.simple, Import-Package: org.json.simple,
org.json.simple.parser org.json.simple.parser
@@ -179,25 +179,26 @@ public interface DomainFactory {
/** /**
* Creates a {@link RaceDefinition} from a TracTrac {@link Race} and a domain {@link Course} definition. The * Creates a {@link RaceDefinition} from a TracTrac {@link Race} and a domain {@link Course} definition. The
* resulting {@link RaceDefinition} is added to the {@link com.sap.sailing.domain.base.Regatta} to which * resulting {@link RaceDefinition} is added to the {@link com.sap.sailing.domain.base.Regatta} to which
* <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race cache. * <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race
* The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on the race * cache. The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on
* cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the tracked race * the race cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the
* has been created and the {@link RaceDefinition} was * tracked race has been created and the {@link RaceDefinition} was
* {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added} to the domain event. This ensures that * {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added} to the domain event. This ensures that
* waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non- <code>null</code> tracked race * waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non-<code>null</code> tracked race
* already immediately after the notification was sent, and that the {@link RaceDefinition} is already * already immediately after the notification was sent, and that the {@link RaceDefinition} is already
* {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its containing * {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its containing
* {@link com.sap.sailing.domain.base.Regatta}. * {@link com.sap.sailing.domain.base.Regatta}.
* @param raceName TODO *
* @param competitors TODO
* @param boatClass TODO
* @param raceDefinitionSetToUpdate * @param raceDefinitionSetToUpdate
* if not <code>null</code>, after creating the {@link TrackedRace}, the {@link RaceDefinition} is * if not <code>null</code>, after creating the {@link TrackedRace}, the {@link RaceDefinition} is
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that object. * {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that
* object.
*/ */
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, UUID raceId, DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass, Course course, Iterable<Sideline> sidelines, String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass,
WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword); Course course, Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword);
/** /**
* The record may be for a single mark or a gate. If for a gate, the * The record may be for a single mark or a gate. If for a gate, the
@@ -469,7 +469,7 @@ public class DomainFactoryImpl implements DomainFactory {
} }
@Override @Override
public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, UUID raceId, public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<Competitor> competitors, BoatClass boatClass, Course course, String raceName, Iterable<Competitor> competitors, BoatClass boatClass, Course course,
Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis, Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
@@ -479,36 +479,38 @@ public class DomainFactoryImpl implements DomainFactory {
if (raceDefinition == null) { if (raceDefinition == null) {
logger.info("Creating RaceDefinitionImpl for race "+raceName); logger.info("Creating RaceDefinitionImpl for race "+raceName);
raceDefinition = new RaceDefinitionImpl(raceName, course, boatClass, competitors, raceId); raceDefinition = new RaceDefinitionImpl(raceName, course, boatClass, competitors, raceId);
} else {
logger.info("Already found RaceDefinitionImpl for race "+raceName);
}
DynamicTrackedRace trackedRace = trackedRegatta.getExistingTrackedRace(raceDefinition);
if (trackedRace == null) {
// add to existing regatta only if boat class matches // add to existing regatta only if boat class matches
if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) { if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) {
trackedRegatta.getRegatta().addRace(raceDefinition); trackedRegatta.getRegatta().addRace(raceDefinition);
DynamicTrackedRace trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore, trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore,
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate); delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate);
logger.info("Added race "+raceDefinition+" to regatta "+trackedRegatta.getRegatta()); logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta());
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(tracTracUpdateURI, TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(
tracTracUsername, tracTracPassword, tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
tracTracEventUuid, raceDefinition.getId()); raceDefinition.getId());
trackedRace.addCourseDesignChangedListener(courseDesignHandler); trackedRace.addCourseDesignChangedListener(courseDesignHandler);
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(tracTracUpdateURI, TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(
tracTracUsername, tracTracPassword, tracTracEventUuid, raceDefinition.getId()); tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
raceDefinition.getId());
trackedRace.addStartTimeChangedListener(startTimeHandler); trackedRace.addStartTimeChangedListener(startTimeHandler);
raceCache.put(raceId, raceDefinition);
synchronized (raceCache) { raceCache.notifyAll();
raceCache.put(raceId, raceDefinition);
raceCache.notifyAll();
}
return trackedRace;
} else { } else {
logger.warning("Not adding race "+raceDefinition+" to regatta "+trackedRegatta.getRegatta()+ logger.warning("Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()
" because boat class "+raceDefinition.getBoatClass()+" doesn't match regatta's boat class "+ + " because boat class " + raceDefinition.getBoatClass()
trackedRegatta.getRegatta().getBoatClass()); + " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass());
return null;
} }
} else { } else {
throw new RuntimeException("Race "+raceName+" already exists"); logger.info("Found existing tracked race for race "+raceName+" with ID "+raceId);
} }
return trackedRace;
} }
} }
@@ -29,17 +29,21 @@ public class Simulator {
private static final Logger logger = Logger.getLogger(Simulator.class.getName()); private static final Logger logger = Logger.getLogger(Simulator.class.getName());
private DynamicTrackedRace trackedRace; private DynamicTrackedRace trackedRace;
private WindStore windStore; private final WindStore windStore;
private long advanceInMillis = -1; private long advanceInMillis = -1;
private Timer timer = new Timer("Timer for TracTrac Simulator"); private Timer timer = new Timer("Timer for TracTrac Simulator");
private boolean stopped; private boolean stopped;
public Simulator(WindStore windStore) {
super();
assert windStore != null;
this.windStore = windStore;
}
/** /**
* Creates a wind store which replays the wind store events against a tracked race, correcting the wind fixes with * The wind store returned is an {@link EmptyWindStore}.
* the simulation delay. The wind store returned is an {@link EmptyWindStore}.
*/ */
public WindStore simulatingWindStore(WindStore windStore) { public WindStore simulatingWindStore(WindStore windStore) {
this.windStore = windStore;
return EmptyWindStore.INSTANCE; return EmptyWindStore.INSTANCE;
} }
@@ -63,7 +67,6 @@ public class Simulator {
*/ */
private void startWindPlayer() { private void startWindPlayer() {
assert this.trackedRace != null; assert this.trackedRace != null;
assert this.windStore != null;
for (final Map.Entry<? extends WindSource, ? extends WindTrack> windSourceAndTrack : windStore.loadWindTracks( for (final Map.Entry<? extends WindSource, ? extends WindTrack> windSourceAndTrack : windStore.loadWindTracks(
trackedRace.getTrackedRegatta(), trackedRace, trackedRace.getTrackedRegatta(), trackedRace,
/* millisecondsOverWhichToAverageWind doesn't matter because we only use raw fixes */ 10000).entrySet()) { /* millisecondsOverWhichToAverageWind doesn't matter because we only use raw fixes */ 10000).entrySet()) {
@@ -100,8 +100,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/ */
private final boolean isLiveTracking; private final boolean isLiveTracking;
private final TrackedRegattaRegistry trackedRegattaRegistry;
/** /**
* Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push * Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push
* data from there. Receiving continues until {@link #stop()} is called. * data from there. Receiving continues until {@link #stop()} is called.
@@ -183,7 +181,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
WindStore windStore, String tracTracUsername, String tracTracPassword, TrackedRegattaRegistry trackedRegattaRegistry) WindStore windStore, String tracTracUsername, String tracTracPassword, TrackedRegattaRegistry trackedRegattaRegistry)
throws URISyntaxException, MalformedURLException, FileNotFoundException { throws URISyntaxException, MalformedURLException, FileNotFoundException {
super(); super();
this.trackedRegattaRegistry = trackedRegattaRegistry;
this.tractracEvent = tractracEvent; this.tractracEvent = tractracEvent;
urls = createID(paramURL, liveURI, storedURI); urls = createID(paramURL, liveURI, storedURI);
isLiveTracking = liveURI != null; isLiveTracking = liveURI != null;
@@ -192,7 +189,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.domainFactory = domainFactory; this.domainFactory = domainFactory;
final Simulator simulator; final Simulator simulator;
if (simulateWithStartTimeNow) { if (simulateWithStartTimeNow) {
simulator = new Simulator(); simulator = new Simulator(windStore);
} else { } else {
simulator = null; simulator = null;
} }
@@ -225,8 +222,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
// in this case, create a default regatta based on the TracTrac event data // in this case, create a default regatta based on the TracTrac event data
this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, tractracEvent, trackedRegattaRegistry) : effectiveRegatta; this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, tractracEvent, trackedRegattaRegistry) : effectiveRegatta;
trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(this.regatta); trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(this.regatta);
// Read event data from configuration file
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI, delayToLiveInMillis, tracTracUsername, tracTracPassword);
receivers = new HashSet<Receiver>(); receivers = new HashSet<Receiver>();
Set<TypeController> typeControllers = new HashSet<TypeController>(); Set<TypeController> typeControllers = new HashSet<TypeController>();
for (Receiver receiver : domainFactory.getUpdateReceivers(getTrackedRegatta(), tractracEvent, startOfTracking, for (Receiver receiver : domainFactory.getUpdateReceivers(getTrackedRegatta(), tractracEvent, startOfTracking,
@@ -237,6 +232,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
} }
} }
addListenersForStoredDataAndStartController(typeControllers); addListenersForStoredDataAndStartController(typeControllers);
// Read event data from configuration file
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI, delayToLiveInMillis, tracTracUsername, tracTracPassword);
} }
@Override @Override
@@ -529,6 +526,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
} else { } else {
logger.info("Joined TracTrac IO thread for race(s) "+getRaces()); logger.info("Joined TracTrac IO thread for race(s) "+getRaces());
} }
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, /* will be ignored */ 1.0);
updateStatusOfTrackedRaces();
} }
protected DataController getController() { protected DataController getController() {
@@ -548,10 +547,17 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
@Override @Override
public void stopped() { public void stopped() {
logger.info("stopped TracTrac tracking for "+getRaces()); logger.info("stopped TracTrac tracking for "+getRaces());
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, 1.0); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1.0);
updateStatusOfTrackedRaces(); updateStatusOfTrackedRaces();
// don't stop the tracker (see bug 1517) as it seems that the storedData... callbacks are unreliable, and
// we have seen many more fixes been transmitted after having received stopped()
} }
/**
* Propagates {@link #lastStatus} to all tracked races to which this tracker writes.
*
* @see #updateStatusOfTrackedRace(DynamicTrackedRace)
*/
private void updateStatusOfTrackedRaces() { private void updateStatusOfTrackedRaces() {
for (RaceDefinition race : getRaces()) { for (RaceDefinition race : getRaces()) {
DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race); DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race);
@@ -561,9 +567,24 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
} }
} }
/**
* Propagates {@link #lastStatus} to <code>trackedRace</code>'s {@link TrackedRace#getStatus() status}. If
* {@link #lastStatus} is a {@link TrackedRaceStatusEnum#FINISHED FINISHED} status, the progress value is taken from
* the tracked race's current status instead of overwriting it with the progress indicated by
* {@link #lastStatus}.
*/
private void updateStatusOfTrackedRace(DynamicTrackedRace trackedRace) { private void updateStatusOfTrackedRace(DynamicTrackedRace trackedRace) {
if (lastStatus != null) { // can't update a race status once it has been set to FINISHED
trackedRace.setStatus(lastStatus); if (lastStatus != null && trackedRace.getStatus() != null && trackedRace.getStatus().getStatus() != TrackedRaceStatusEnum.FINISHED) {
final TrackedRaceStatus status;
if (lastStatus.getStatus() == TrackedRaceStatusEnum.FINISHED) {
// in this case use the tracked race's progress value:
status = new TrackedRaceStatusImpl(lastStatus.getStatus(), trackedRace.getStatus() == null ? 0.0
: trackedRace.getStatus().getLoadingProgress());
} else {
status = lastStatus;
}
trackedRace.setStatus(status);
} }
} }
@@ -580,38 +601,13 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
if (isLiveTracking) { if (isLiveTracking) {
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1);
updateStatusOfTrackedRaces(); updateStatusOfTrackedRaces();
} else {
// ask RacingEventService to cleanly stop and unregister this tracker
// if the race has all data loaded. Doing this asynchronously because
// stopping can take longer and if you're loading many races in parallel
// this can slow down loading extremely
for (final RaceDefinition race : getRaces()) {
DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race);
final Regatta regatta = getRegatta();
if (trackedRace.getStatus().getLoadingProgress() == 1.0) {
Thread raceStopper = new Thread(new Runnable() {
@Override
public void run() {
try {
trackedRegattaRegistry.stopTracking(regatta, race);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to stop tracker for race "+race.getName()+
" in regatta "+getRegatta().getName(), e);
}
}
});
raceStopper.start();
} else {
logger.log(Level.SEVERE, "Not stopping race "+race.getName()+" because it has not all data loaded! Progress: " + trackedRace.getStatus().getLoadingProgress());
}
}
} }
} }
@Override @Override
public void storedDataProgress(float progress) { public void storedDataProgress(float progress) {
logger.info("Stored data progress for race(s) "+getRaces()+": "+progress); logger.info("Stored data progress for race(s) "+getRaces()+": "+progress);
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, progress); lastStatus = new TrackedRaceStatusImpl(progress==1.0 ? TrackedRaceStatusEnum.TRACKING : TrackedRaceStatusEnum.LOADING, progress);
updateStatusOfTrackedRaces(); updateStatusOfTrackedRaces();
} }
@@ -20,6 +20,12 @@ import com.sap.sailing.domain.tracking.TrackedRace;
* *
*/ */
public interface ScoreCorrection extends Serializable { public interface ScoreCorrection extends Serializable {
/**
* A score correction as valid at the time point specified with the query that produced this object.
*
* @author Axel Uhl (d043530)
*
*/
public interface Result { public interface Result {
/** /**
* @return <code>null</code> in case there is no score attributed to the competitor for the race column in * @return <code>null</code> in case there is no score attributed to the competitor for the race column in
@@ -31,6 +37,11 @@ public interface ScoreCorrection extends Serializable {
MaxPointsReason getMaxPointsReason(); MaxPointsReason getMaxPointsReason();
boolean isCorrected(); boolean isCorrected();
/**
* @return the time point for which this result is valid
*/
TimePoint getTimePoint();
} }
/** /**
@@ -39,26 +50,51 @@ public interface ScoreCorrection extends Serializable {
* and {@link MaxPointsReason#NONE} will be listed as the {@link Result#getMaxPointsReason() correction reason}. * and {@link MaxPointsReason#NONE} will be listed as the {@link Result#getMaxPointsReason() correction reason}.
* Note, though, that {@link MaxPointsReason#NONE} can also be the reason for an explicit score correction, e.g., if * Note, though, that {@link MaxPointsReason#NONE} can also be the reason for an explicit score correction, e.g., if
* the tracking results were overruled by the jury. Clients may use * the tracking results were overruled by the jury. Clients may use
* {@link #isScoreCorrected(Competitor, TrackedRace)} to detect the difference. * {@link #isScoreCorrected(Competitor, TrackedRace, TimePoint)} to detect the difference.
*
* @param trackedRankProvider
* can provide the tracked rank if needed; will only be called if there is no score correction for the
* <code>timePoint</code> specified.
* @param timePoint
* the time point for which to get the corrected score; score corrections have a validity time interval.
* Only the last score correction valid at <code>timePoint</code> is considered.
* *
* @param numberOfCompetitorsFetcher * @param numberOfCompetitorsFetcher
* can determine the number of competitors to use as the basis for penalty score calculation * can determine the number of competitors to use as the basis for penalty score calculation
* ("max points") if needed * ("max points") if needed
*/ */
Result getCorrectedScore(Callable<Integer> trackedRank, Competitor competitor, RaceColumn raceColumn, Result getCorrectedScore(Callable<Integer> trackedRankProvider, Competitor competitor, RaceColumn raceColumn,
TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsFetcher, ScoringScheme scoringScheme); TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsFetcher, ScoringScheme scoringScheme);
/**
* @param timePoint
* the time point at which the max points reason is to be valid
*
* @return a non-<code>null</code> result
*/
MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
/** /**
* Note the difference between what this method does and a more naive comparison of uncorrected and corrected score. * Note the difference between what this method does and a more naive comparison of uncorrected and corrected score.
* Should, for some reason, the uncorrected score change later, an existing score correction would still remain in * Should, for some reason, the uncorrected score change later, an existing score correction would still remain in
* place whereas if no score correction exists for the competitor/race combination, the resulting score after * place whereas if no score correction exists for the competitor/race combination, the resulting score after
* "correction" will still be the uncorrected value.<p> * "correction" will still be the uncorrected value.
* <p>
*
* @param timePoint
* the time point for which to check whether the competitor's score was corrected at that time point; in
* other words, this method checks if there is a score correction for <code>competitor</code> in column
* <code>raceColumn</code> that is valid at <code>timePoint</code>.
* *
* @return if an explicit score correction was made for the combination of <code>competitor</code> and * @return if an explicit score correction was made for the combination of <code>competitor</code> and
* <code>raceColumn</code> * <code>raceColumn</code>
*/ */
boolean isScoreCorrected(Competitor competitor, RaceColumn raceColumn); boolean isScoreCorrected(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
/**
* Checks if this score correction object has any score corrections for any competitor valid at any time point for
* the race column specified by <code>raceInLeaderboard</code>.
*/
boolean hasCorrectionFor(RaceColumn raceInLeaderboard); boolean hasCorrectionFor(RaceColumn raceInLeaderboard);
/** /**
@@ -13,17 +13,12 @@ public interface SettableScoreCorrection extends ScoreCorrection {
/** /**
* @param reason * @param reason
* if <code>null</code>, any existing max-points reason is removed; while * if <code>null</code>, any existing max-points reason is removed; while
* {@link #getMaxPointsReason(Competitor, RaceColumn)} will return {@link MaxPointsReason#NONE}, a * {@link #getMaxPointsReason(Competitor, RaceColumn, TimePoint)} will return {@link MaxPointsReason#NONE}, a
* call to {@link #isScoreCorrected(Competitor, RaceColumn)} will return <code>false</code> if no * call to {@link #isScoreCorrected(Competitor, RaceColumn, TimePoint)} will return <code>false</code> if no
* other explicit score correction was made for the <code>competitor</code>. * other explicit score correction was made for the <code>competitor</code>.
*/ */
void setMaxPointsReason(Competitor competitor, RaceColumn raceColumn, MaxPointsReason reason); void setMaxPointsReason(Competitor competitor, RaceColumn raceColumn, MaxPointsReason reason);
/**
* @return a non-<code>null</code> result
*/
MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn);
void correctScore(Competitor competitor, RaceColumn raceColumn, double points); void correctScore(Competitor competitor, RaceColumn raceColumn, double points);
/** /**
@@ -82,8 +82,8 @@ public class LeaderboardDTOCache implements LeaderboardCache {
public LeaderboardDTOCache(boolean waitForLatestAnalyses, Leaderboard leaderboard) { public LeaderboardDTOCache(boolean waitForLatestAnalyses, Leaderboard leaderboard) {
this.leaderboard = leaderboard; this.leaderboard = leaderboard;
this.waitForLatestAnalyses = waitForLatestAnalyses; this.waitForLatestAnalyses = waitForLatestAnalyses;
// if the leaderboard becomes weakly referenced and eventually GCed, then so can the cached results for it // Note: don't use access-based ordering as it turns the get(...) call into a "write" access
this.leaderboardCache = new LinkedHashMap<Pair<TimePoint, Collection<String>>, FutureTask<LeaderboardDTO>>(16, 0.75f, /* access-based eviction */ true) { this.leaderboardCache = new LinkedHashMap<Pair<TimePoint, Collection<String>>, FutureTask<LeaderboardDTO>>(16, 0.75f) {
private static final long serialVersionUID = 7287916997229815039L; private static final long serialVersionUID = 7287916997229815039L;
@Override @Override
protected boolean removeEldestEntry(Map.Entry<Pair<TimePoint, Collection<String>>, FutureTask<LeaderboardDTO>> e) { protected boolean removeEldestEntry(Map.Entry<Pair<TimePoint, Collection<String>>, FutureTask<LeaderboardDTO>> e) {
@@ -14,7 +14,6 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.leaderboard.ScoreCorrection; import com.sap.sailing.domain.leaderboard.ScoreCorrection;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -38,14 +37,11 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
private transient Iterable<Competitor> allCompetitorsCache; private transient Iterable<Competitor> allCompetitorsCache;
/** /**
* @param scoreCorrection must not be <code>null</code>
* @param scoreComparator the comparator to use to compare basic scores, such as net points * @param scoreComparator the comparator to use to compare basic scores, such as net points
* @param name must not be <code>null</code> * @param name must not be <code>null</code>
*/ */
public AbstractLeaderboardImpl(SettableScoreCorrection scoreCorrection, public AbstractLeaderboardImpl(ThresholdBasedResultDiscardingRule resultDiscardingRule) {
ThresholdBasedResultDiscardingRule resultDiscardingRule) { super(resultDiscardingRule);
super(scoreCorrection, resultDiscardingRule);
assert scoreCorrection != null;
} }
@Override @Override
@@ -133,7 +129,7 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
Iterator<Competitor> ci = competitorsFromBestToWorst.iterator(); Iterator<Competitor> ci = competitorsFromBestToWorst.iterator();
while (betterCompetitorRank < rank && ci.hasNext()) { while (betterCompetitorRank < rank && ci.hasNext()) {
Competitor betterTrackedCompetitor = ci.next(); Competitor betterTrackedCompetitor = ci.next();
MaxPointsReason maxPointsReasonForBetterCompetitor = getScoreCorrection().getMaxPointsReason(betterTrackedCompetitor, raceColumn); MaxPointsReason maxPointsReasonForBetterCompetitor = getScoreCorrection().getMaxPointsReason(betterTrackedCompetitor, raceColumn, timePoint);
if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE && if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE &&
maxPointsReasonForBetterCompetitor.isAdvanceCompetitorsTrackedWorse()) { maxPointsReasonForBetterCompetitor.isAdvanceCompetitorsTrackedWorse()) {
correctedRank--; correctedRank--;
@@ -342,9 +342,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} }
} }
public AbstractSimpleLeaderboardImpl(SettableScoreCorrection scoreCorrection, ThresholdBasedResultDiscardingRule resultDiscardingRule) { public AbstractSimpleLeaderboardImpl(ThresholdBasedResultDiscardingRule resultDiscardingRule) {
this.carriedPoints = new HashMap<Competitor, Double>(); this.carriedPoints = new HashMap<Competitor, Double>();
this.scoreCorrection = scoreCorrection; this.scoreCorrection = createScoreCorrection();
this.displayNames = new HashMap<Competitor, String>(); this.displayNames = new HashMap<Competitor, String>();
this.crossLeaderboardResultDiscardingRule = resultDiscardingRule; this.crossLeaderboardResultDiscardingRule = resultDiscardingRule;
this.suppressedCompetitors = new HashSet<Competitor>(); this.suppressedCompetitors = new HashSet<Competitor>();
@@ -354,6 +354,15 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
initTransientFields(); initTransientFields();
} }
/**
* Produces the score correction object to use in this leaderboard. Used by the constructor. Subclasses may override
* this method to create a more specific type of score correction. This implementation produces an object of type
* {@link ScoreCorrectionImpl}.
*/
protected SettableScoreCorrection createScoreCorrection() {
return new ScoreCorrectionImpl(this);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject(); ois.defaultReadObject();
initTransientFields(); initTransientFields();
@@ -459,7 +468,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override @Override
public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
return getScoreCorrection().getMaxPointsReason(competitor, raceColumn); return getScoreCorrection().getMaxPointsReason(competitor, raceColumn, timePoint);
} }
@Override @Override
@@ -615,7 +624,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
TrackedRace trackedRaceForCompetitorInColumn; TrackedRace trackedRaceForCompetitorInColumn;
return getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) && return getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) &&
(getScoreCorrection().isScoreCorrected(competitor, raceColumn) || (getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) ||
((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null && ((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
trackedRaceForCompetitorInColumn.hasStarted(timePoint))); trackedRaceForCompetitorInColumn.hasStarted(timePoint)));
} }
@@ -18,7 +18,6 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.FlexibleRaceColumn; import com.sap.sailing.domain.leaderboard.FlexibleRaceColumn;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
@@ -47,14 +46,14 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
private transient RaceLogStore raceLogStore; private transient RaceLogStore raceLogStore;
private CourseArea courseArea; private CourseArea courseArea;
public FlexibleLeaderboardImpl(String name, SettableScoreCorrection scoreCorrection, public FlexibleLeaderboardImpl(String name, ThresholdBasedResultDiscardingRule resultDiscardingRule,
ThresholdBasedResultDiscardingRule resultDiscardingRule, ScoringScheme scoringScheme, CourseArea courseArea) { ScoringScheme scoringScheme, CourseArea courseArea) {
this(EmptyRaceLogStore.INSTANCE, name, scoreCorrection, resultDiscardingRule, scoringScheme, courseArea); this(EmptyRaceLogStore.INSTANCE, name, resultDiscardingRule, scoringScheme, courseArea);
} }
public FlexibleLeaderboardImpl(RaceLogStore raceLogStore, String name, SettableScoreCorrection scoreCorrection, public FlexibleLeaderboardImpl(RaceLogStore raceLogStore, String name, ThresholdBasedResultDiscardingRule resultDiscardingRule,
ThresholdBasedResultDiscardingRule resultDiscardingRule, ScoringScheme scoringScheme, CourseArea courseArea) { ScoringScheme scoringScheme, CourseArea courseArea) {
super(scoreCorrection, resultDiscardingRule); super(resultDiscardingRule);
this.scoringScheme = scoringScheme; this.scoringScheme = scoringScheme;
if (name == null) { if (name == null) {
throw new IllegalArgumentException("A leaderboard's name must not be null"); throw new IllegalArgumentException("A leaderboard's name must not be null");
@@ -14,7 +14,6 @@ import com.sap.sailing.domain.base.impl.RaceColumnInSeriesImpl;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -31,9 +30,8 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
private static final long serialVersionUID = 2370461218294770084L; private static final long serialVersionUID = 2370461218294770084L;
private final Regatta regatta; private final Regatta regatta;
public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection, public RegattaLeaderboardImpl(Regatta regatta, ThresholdBasedResultDiscardingRule resultDiscardingRule) {
ThresholdBasedResultDiscardingRule resultDiscardingRule) { super(resultDiscardingRule);
super(scoreCorrection, resultDiscardingRule);
this.regatta = regatta; this.regatta = regatta;
regatta.addRaceColumnListener(this); regatta.addRaceColumnListener(this);
} }
@@ -5,6 +5,7 @@ import java.io.ObjectInputStream;
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.NavigableSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@@ -14,10 +15,12 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher; import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener; import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
/** /**
@@ -57,8 +60,11 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
private TimePoint timePointOfLastCorrectionsValidity; private TimePoint timePointOfLastCorrectionsValidity;
private transient Set<ScoreCorrectionListener> scoreCorrectionListeners; private transient Set<ScoreCorrectionListener> scoreCorrectionListeners;
private final Leaderboard leaderboard;
public ScoreCorrectionImpl() { public ScoreCorrectionImpl(Leaderboard leaderboard) {
this.leaderboard = leaderboard;
this.maxPointsReasons = new HashMap<Util.Pair<Competitor, RaceColumn>, MaxPointsReason>(); this.maxPointsReasons = new HashMap<Util.Pair<Competitor, RaceColumn>, MaxPointsReason>();
this.correctedScores = new HashMap<Util.Pair<Competitor, RaceColumn>, Double>(); this.correctedScores = new HashMap<Util.Pair<Competitor, RaceColumn>, Double>();
this.scoreCorrectionListeners = new HashSet<ScoreCorrectionListener>(); this.scoreCorrectionListeners = new HashSet<ScoreCorrectionListener>();
@@ -135,9 +141,10 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
} }
@Override @Override
public boolean isScoreCorrected(Competitor competitor, RaceColumn raceColumn) { public boolean isScoreCorrected(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
Pair<Competitor, RaceColumn> key = raceColumn.getKey(competitor); Pair<Competitor, RaceColumn> key = raceColumn.getKey(competitor);
return correctedScores.containsKey(key) || maxPointsReasons.containsKey(key); return (correctedScores.containsKey(key) && !isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor))
|| (maxPointsReasons.containsKey(key) && isMaxPointsReasonApplicable(maxPointsReasons.get(key), timePoint, raceColumn, competitor));
} }
@Override @Override
@@ -145,31 +152,199 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
Double oldScore = correctedScores.remove(raceColumn.getKey(competitor)); Double oldScore = correctedScores.remove(raceColumn.getKey(competitor));
notifyListeners(competitor, raceColumn, oldScore, null); notifyListeners(competitor, raceColumn, oldScore, null);
} }
/**
* Based on the order of the {@link Leaderboard#getRaceColumns() race columns} in the {@link #getLeaderboard()
* leaderboard to which this score correction object belongs}, tries to determine whether the <code>timePoint</code>
* is before the start of <code>competitor</code>'s race in the race column specified by <code>raceColumn</code>. If
* there is a {@link RaceColumn#getTrackedRace(Competitor) tracked race for the competitor associated with the race
* column}, that race's start time is used for the calculation. Otherwise, if there is a tracked race column prior
* to <code>raceColumn</code> in the leaderboard in which <code>competitor</code> competes, and the competitor
* hasn't finished that race at <code>timePoint</code>, we also know that this must be before the start of
* <code>competitor</code>'s race in <code>raceColumn</code> because we assume that the same competitor can only
* compete in one race at a time within a single leaderboard.
* <p>
*
* In all other cases, <code>false</code> is returned which can either mean that <code>timePoint</code> is certainly
* known to be after the race start of <code>competitor</code> in the race for <code>raceColumn</code>, or we just
* can't tell, e.g., because <code>competitor</code>'s race for <code>raceColumn</code> is not tracked, and
* <code>timePoint</code> is after all prior race column's finishing time for <code>competitor</code>.
* <p>
*
* This method can be used to decide whether to apply a {@link MaxPointsReason#DNC}, {@link MaxPointsReason#DNS} or
* {@link MaxPointsReason#OCS} correction for <code>competitor</code> at <code>timePoint</code> in the race for
* <code>raceColumn</code>, because the correction can be applied at race start but should not be applied any
* earlier than that because it would incorrectly influence the total scores displayed for the competitor during
* earlier races.
*/
private boolean isCertainlyBeforeRaceStart(TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) {
final boolean result;
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
final TimePoint startOfRace;
if (trackedRace != null && (startOfRace = trackedRace.getStartOfRace()) != null) {
result = timePoint.before(startOfRace);
} else {
boolean preResult = false;
for (RaceColumn rc : getLeaderboard().getRaceColumns()) {
if (rc == raceColumn) {
break;
}
TrackedRace rcTrackedRace = rc.getTrackedRace(competitor);
if (rcTrackedRace != null) {
NavigableSet<MarkPassing> markPassings = rcTrackedRace.getMarkPassings(competitor);
if (markPassings != null && !markPassings.isEmpty()) {
MarkPassing lastMarkPassing = markPassings.last();
if (timePoint.before(lastMarkPassing.getTimePoint())) {
preResult = true;
break;
}
} else {
// if available, use the end of the race as indicator for how long competitor may have been in the race
TimePoint endOfRace = rcTrackedRace.getEndOfRace();
if (endOfRace != null && timePoint.before(endOfRace)) {
preResult = true;
break;
}
}
}
}
result = preResult;
}
return result;
}
/**
* Based on the order of the {@link Leaderboard#getRaceColumns() race columns} in the {@link #getLeaderboard()
* leaderboard to which this score correction object belongs}, tries to determine whether the <code>timePoint</code>
* is before the finish or abandoning of <code>competitor</code>'s race in the race column specified by
* <code>raceColumn</code>. If there is a {@link RaceColumn#getTrackedRace(Competitor) tracked race for the
* competitor associated with the race column}, that race's finish time for <code>competitor</code> (if defined) or
* the {@link TrackedRace#getEndOfRace() end of the race} is used for the calculation. Otherwise, if there is a
* tracked race column prior to <code>raceColumn</code> in the leaderboard in which <code>competitor</code>
* competes, and the competitor has finished that race after <code>timePoint</code>, we also know that this must be
* before the end of <code>competitor</code>'s race in <code>raceColumn</code> because we assume that the same
* competitor can only compete in one race at a time within a single leaderboard.
* <p>
*
* In all other cases, <code>false</code> is returned which can either mean that <code>timePoint</code> is certainly
* known to be after the race finish of <code>competitor</code> in the race for <code>raceColumn</code>, or we just
* can't tell, e.g., because <code>competitor</code>'s race for <code>raceColumn</code> is not tracked, and
* <code>timePoint</code> is after all prior race column's finishing time for <code>competitor</code>.
* <p>
*
* This method can be used to decide whether to apply a score correction for <code>competitor</code> at
* <code>timePoint</code> in the race for <code>raceColumn</code>, because the correction should not be applied
* before race end because it would incorrectly influence the total scores displayed for the competitor during
* earlier races.
*/
private boolean isCertainlyBeforeRaceFinish(TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) {
final boolean result;
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
final TimePoint endOfRace;
// endOfRace != null means we at least will have a fallback result even if there are no mark passings for the competitor
if (trackedRace != null && (endOfRace = trackedRace.getEndOfRace()) != null) {
NavigableSet<MarkPassing> markPassings = trackedRace.getMarkPassings(competitor);
final MarkPassing lastMarkPassing;
// count race as finished for the competitor if the finish mark passing exists or the time is after the end of race
if (markPassings != null && !markPassings.isEmpty() &&
(lastMarkPassing = markPassings.last()).getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint()) {
result = timePoint.before(lastMarkPassing.getTimePoint());
} else {
// if available, use the end of the race as indicator for how long competitor may have been in the race
result = timePoint.before(endOfRace);
}
} else {
boolean preResult = false;
for (RaceColumn rc : getLeaderboard().getRaceColumns()) {
if (rc == raceColumn) {
break;
}
TrackedRace rcTrackedRace = rc.getTrackedRace(competitor);
if (rcTrackedRace != null) {
NavigableSet<MarkPassing> markPassings = rcTrackedRace.getMarkPassings(competitor);
if (markPassings != null && !markPassings.isEmpty()) {
MarkPassing lastMarkPassing = markPassings.last();
if (timePoint.before(lastMarkPassing.getTimePoint())) {
preResult = true;
break;
}
} else {
// if available, use the end of the race as indicator for how long competitor may have been in the race
TimePoint rcEndOfRace = rcTrackedRace.getEndOfRace();
if (rcEndOfRace != null) {
if (timePoint.before(rcEndOfRace)) {
preResult = true;
break;
}
} else {
TimePoint rcStartOfRace = rcTrackedRace.getStartOfRace();
if (rcStartOfRace != null && timePoint.before(rcStartOfRace)) {
preResult = true;
break;
}
}
}
}
}
result = preResult;
}
return result;
}
private boolean appliesAtStartOfRace(MaxPointsReason maxPointsReason) {
final boolean result;
switch (maxPointsReason) {
case DNS:
case DNC:
case OCS:
result = true;
break;
default:
result = false;
}
return result;
}
@Override @Override
public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn) { public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
MaxPointsReason result = maxPointsReasons.get(raceColumn.getKey(competitor)); MaxPointsReason result = maxPointsReasons.get(raceColumn.getKey(competitor));
if (result == null) { if (result == null || !isMaxPointsReasonApplicable(result, timePoint, raceColumn, competitor)) {
result = MaxPointsReason.NONE; result = MaxPointsReason.NONE;
} }
return result; return result;
} }
private boolean isMaxPointsReasonApplicable(MaxPointsReason maxPointsReason, TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) {
return (appliesAtStartOfRace(maxPointsReason) && !isCertainlyBeforeRaceStart(timePoint, raceColumn, competitor))
|| !isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor);
}
/** /**
* If the {@link #getMaxPointsReason(Competitor, TrackedRace)} for the <code>competitor</code> for the * If the {@link #getMaxPointsReason(Competitor, TrackedRace, TimePoint)} for the <code>competitor</code> for the
* <code>raceColumn</code>'s tracked race is not {@link MaxPointsReason#NONE}, the * <code>raceColumn</code>'s tracked race is not {@link MaxPointsReason#NONE}, the
* {@link #getMaxPoints(TrackedRace) maximum score} is computed for the competitor. Otherwise, the * {@link #getMaxPoints(TrackedRace) maximum score} is computed for the competitor. Otherwise, the
* <code>uncorrectedScore</code> is returned. * <code>uncorrectedScore</code> is returned.
* <p> * <p>
*
* The current implementation considers <code>timePoint</code> by comparing it to the <code>competitor</code>'s
* times for the tracked race associated for that competitor in <code>raceColumn</code>. If there is no such tracked
* race, any score correction available will be applied unchanged. If a tracked race is attached to the column for
* the competitor's fleet, the score correction is applied if <code>timePoint</code> is after the competitor
* finished or aborted the race. If the <code>timePoint</code> is after the {@link TrackedRace#getStartOfRace() race
* start time}, a score correction for the competitor for that race is considered if it is a
* {@link MaxPointsReason#DNS}, {@link MaxPointsReason#DNC} or {@link MaxPointsReason#OCS} code. Those penalties
* apply already from the start of the race and will cause the penalty score to be applied already during the race
* time interval, so the competitor will be sorted to the end of the leaderboard already during replay.
* <p>
*/ */
@Override @Override
public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor, public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor,
final RaceColumn raceColumn, TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, final RaceColumn raceColumn, final TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
ScoringScheme scoringScheme) { ScoringScheme scoringScheme) {
Double result; Double result;
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn); final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn, timePoint);
if (maxPointsReason == MaxPointsReason.NONE) { if (maxPointsReason == MaxPointsReason.NONE) {
result = getCorrectedNonMaxedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher); result = getCorrectedNonMaxedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint);
} else { } else {
// allow explicit override even when max points reason is specified; calculation may be wrong, // allow explicit override even when max points reason is specified; calculation may be wrong,
// e.g., in case we have an untracked race and the number of competitors is estimated incorrectly // e.g., in case we have an untracked race and the number of competitors is estimated incorrectly
@@ -196,7 +371,12 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
@Override @Override
public boolean isCorrected() { public boolean isCorrected() {
return isScoreCorrected(competitor, raceColumn); return isScoreCorrected(competitor, raceColumn, getTimePoint());
}
@Override
public TimePoint getTimePoint() {
return timePoint;
} }
}; };
} }
@@ -216,21 +396,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
* Under the assumption that the competitor is not assigned the maximum score due to disqualification or other * Under the assumption that the competitor is not assigned the maximum score due to disqualification or other
* reasons, computes the corrected score. If {@link #correctedScores} contains an entry for the * reasons, computes the corrected score. If {@link #correctedScores} contains an entry for the
* <code>competitor</code>'s key, it is used. Otherwise, the <code>uncorrectedScore</code> is returned. * <code>competitor</code>'s key, it is used. Otherwise, the <code>uncorrectedScore</code> is returned.
*
* @param scoringScheme * @param scoringScheme
* used to transform the tracked rank into a score if there is no score correction applied * used to transform the tracked rank into a score if there is no score correction applied
*
* @return <code>null</code> in case the <code>competitor</code> has no score assigned in that race which is the * @return <code>null</code> in case the <code>competitor</code> has no score assigned in that race which is the
* case if the score is not corrected by these score corrections, and the <code>trackedRankProvider</code> delivers 0 * case if the score is not corrected by these score corrections, and the <code>trackedRankProvider</code>
* as the rank, or if the score is not corrected and the scoring scheme cannot find the competitor in any tracked race * delivers 0 as the rank, or if the score is not corrected and the scoring scheme cannot find the
* of the <code>raceColumn</code>, meaning there cannot be a tracked rank for the competitor regardless what * competitor in any tracked race of the <code>raceColumn</code>, meaning there cannot be a tracked rank for
* <code>trackedRankProvider</code> delivers. * the competitor regardless what <code>trackedRankProvider</code> delivers.
*/ */
protected Double getCorrectedNonMaxedScore(final Competitor competitor, final RaceColumn raceColumn, private Double getCorrectedNonMaxedScore(final Competitor competitor, final RaceColumn raceColumn,
Callable<Integer> trackedRankProvider, ScoringScheme scoringScheme, Callable<Integer> trackedRankProvider, ScoringScheme scoringScheme,
final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) { final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor)); Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
Double result; Double result;
if (correctedNonMaxedScore == null) { if (correctedNonMaxedScore == null || isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor)) {
try { try {
int trackedRank = trackedRankProvider.call(); int trackedRank = trackedRankProvider.call();
result = scoringScheme.getScoreForRank(raceColumn, competitor, trackedRank, result = scoringScheme.getScoreForRank(raceColumn, competitor, trackedRank,
@@ -289,4 +469,8 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
this.comment = scoreCorrectionComment; this.comment = scoreCorrectionComment;
} }
protected Leaderboard getLeaderboard() {
return leaderboard;
}
} }
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard; import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener; import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -74,8 +75,7 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
} }
public AbstractMetaLeaderboard(String name, ScoringScheme scoringScheme, ThresholdBasedResultDiscardingRule resultDiscardingRule) { public AbstractMetaLeaderboard(String name, ScoringScheme scoringScheme, ThresholdBasedResultDiscardingRule resultDiscardingRule) {
super(new MetaLeaderboardScoreCorrection(), resultDiscardingRule); super(resultDiscardingRule);
getScoreCorrection().setMetaLeaderboard(this);
metaFleet = new FleetImpl("MetaFleet"); metaFleet = new FleetImpl("MetaFleet");
this.scoringScheme = scoringScheme; this.scoringScheme = scoringScheme;
this.name = name; this.name = name;
@@ -83,6 +83,13 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
scoreCorrectionChangeForwardersByLeaderboard = new WeakHashMap<Leaderboard, ScoreCorrectionListener>(); scoreCorrectionChangeForwardersByLeaderboard = new WeakHashMap<Leaderboard, ScoreCorrectionListener>();
} }
@Override
protected SettableScoreCorrection createScoreCorrection() {
return new MetaLeaderboardScoreCorrection(this);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject(); ois.defaultReadObject();
columnsForLeaderboards = new WeakHashMap<>(); columnsForLeaderboards = new WeakHashMap<>();
@@ -6,18 +6,15 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher; import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
public class MetaLeaderboardScoreCorrection extends ScoreCorrectionImpl { public class MetaLeaderboardScoreCorrection extends ScoreCorrectionImpl {
private static final long serialVersionUID = 3773423384260065869L; private static final long serialVersionUID = 3773423384260065869L;
private AbstractMetaLeaderboard metaLeaderboard;
public MetaLeaderboardScoreCorrection() { public MetaLeaderboardScoreCorrection(MetaLeaderboard leaderboard) {
} super(leaderboard);
protected void setMetaLeaderboard(AbstractMetaLeaderboard metaLeaderboard) {
this.metaLeaderboard = metaLeaderboard;
} }
@Override @Override
@@ -43,11 +40,16 @@ public class MetaLeaderboardScoreCorrection extends ScoreCorrectionImpl {
MaxPointsReason newMaxPointsReason) { MaxPointsReason newMaxPointsReason) {
super.notifyListeners(competitor, oldMaxPointsReason, newMaxPointsReason); super.notifyListeners(competitor, oldMaxPointsReason, newMaxPointsReason);
} }
@Override
protected MetaLeaderboard getLeaderboard() {
return (MetaLeaderboard) super.getLeaderboard();
}
@Override @Override
public TimePoint getTimePointOfLastCorrectionsValidity() { public TimePoint getTimePointOfLastCorrectionsValidity() {
TimePoint result = super.getTimePointOfLastCorrectionsValidity(); TimePoint result = super.getTimePointOfLastCorrectionsValidity();
for (Leaderboard leaderboard : metaLeaderboard.getLeaderboards()) { for (Leaderboard leaderboard : getLeaderboard().getLeaderboards()) {
TimePoint leaderboardLastCorrectionTimePoint = leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity(); TimePoint leaderboardLastCorrectionTimePoint = leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity();
if (result == null || (leaderboardLastCorrectionTimePoint != null && leaderboardLastCorrectionTimePoint.after(result))) { if (result == null || (leaderboardLastCorrectionTimePoint != null && leaderboardLastCorrectionTimePoint.after(result))) {
result = leaderboardLastCorrectionTimePoint; result = leaderboardLastCorrectionTimePoint;
@@ -9,7 +9,6 @@ import com.sap.sailing.domain.base.LeaderboardMasterData;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
public class FlexibleLeaderboardMasterData extends LeaderboardMasterData { public class FlexibleLeaderboardMasterData extends LeaderboardMasterData {
@@ -48,8 +47,8 @@ public class FlexibleLeaderboardMasterData extends LeaderboardMasterData {
@Override @Override
public Leaderboard getLeaderboard() { public Leaderboard getLeaderboard() {
return new FlexibleLeaderboardImpl(getName(), new ScoreCorrectionImpl(), getResultDiscardingRule(), return new FlexibleLeaderboardImpl(getName(), getResultDiscardingRule(), scoringScheme,
scoringScheme, courseArea); courseArea);
} }
public List<RaceColumnMasterData> getRaceColumns() { public List<RaceColumnMasterData> getRaceColumns() {
@@ -10,7 +10,6 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEvent;
public class RegattaLeaderboardMasterData extends LeaderboardMasterData { public class RegattaLeaderboardMasterData extends LeaderboardMasterData {
@@ -40,7 +39,7 @@ public class RegattaLeaderboardMasterData extends LeaderboardMasterData {
if (regatta == null) { if (regatta == null) {
return null; return null;
} }
return new RegattaLeaderboardImpl(regatta, new ScoreCorrectionImpl(), getResultDiscardingRule()); return new RegattaLeaderboardImpl(regatta, getResultDiscardingRule());
} }
} }
@@ -12,6 +12,8 @@ public interface RaceLogIdentifier extends Serializable {
Triple<String, String, String> getIdentifier(); Triple<String, String, String> getIdentifier();
String getDeprecatedIdentifier();
RaceLogIdentifierTemplate getTemplate(); RaceLogIdentifierTemplate getTemplate();
} }
@@ -25,6 +25,14 @@ public class RaceLogIdentifierImpl implements RaceLogIdentifier {
return new Util.Triple<String, String, String>(template.getParentObjectName(), raceColumnName, fleetName); return new Util.Triple<String, String, String>(template.getParentObjectName(), raceColumnName, fleetName);
} }
@Override
public String getDeprecatedIdentifier() {
return String.format("%s.%s.%s",
this.getTemplate().getParentObjectName(),
this.getRaceColumnName(),
this.getFleetName());
}
@Override @Override
public RaceLogIdentifierTemplate getTemplate() { public RaceLogIdentifierTemplate getTemplate() {
return template; return template;
@@ -31,7 +31,7 @@ import com.sap.sailing.domain.tracking.Wind;
public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor { public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
public static final Logger log = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName()); private static final Logger logger = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName());
private DynamicTrackedRace trackedRace; private DynamicTrackedRace trackedRace;
@@ -112,7 +112,7 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
// ... onCourseDesignChangedByRaceCommittee() might be called more than once. // ... onCourseDesignChangedByRaceCommittee() might be called more than once.
trackedRace.onCourseDesignChangedByRaceCommittee(courseDesign); trackedRace.onCourseDesignChangedByRaceCommittee(courseDesign);
} else { } else {
log.info("Could not find any course design update on race log of " + trackedRace.getRace().getName() + "! Not sending out any events."); logger.info("Could not find any course design update on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
} }
} }
@@ -125,7 +125,7 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
/* invoke listeners with received start time, this will also trigger tractrac update */ /* invoke listeners with received start time, this will also trigger tractrac update */
trackedRace.onStartTimeChangedByRaceCommittee(startTime); trackedRace.onStartTimeChangedByRaceCommittee(startTime);
} else { } else {
log.info("Could not find any valid start time on race log of " + trackedRace.getRace().getName() + "! Not sending out any events."); logger.info("Could not find any valid start time on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
} }
} }
@@ -8,7 +8,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.expeditionconnector Fragment-Host: com.sap.sailing.expeditionconnector
Require-Bundle: com.sap.sailing.server, Require-Bundle: com.sap.sailing.server,
com.sap.sailing.domain.test, com.sap.sailing.domain.test,
com.sap.sailing.udpconnector com.sap.sailing.udpconnector,
Import-Package: junit.framework;version="4.8.1", org.junit4;bundle-version="4.8.2"
org.junit;version="4.8.1",
org.osgi.framework;version="1.6.0"
@@ -34,10 +34,10 @@
<plugin id="com.sap.sailing.barbados.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.barbados.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.ess40.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.ess40.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.freg.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.freg.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="4" /> <plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.kiworesultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.kiworesultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.manage2sail.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.manage2sail.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="5" /> <plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="6" />
<plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="3" /> <plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.server" autoStart="true" startLevel="4" /> <plugin id="com.sap.sailing.server" autoStart="true" startLevel="4" />
@@ -27,17 +27,15 @@
<import plugin="org.eclipse.osgi"/> <import plugin="org.eclipse.osgi"/>
<import plugin="org.eclipse.osgi.services"/> <import plugin="org.eclipse.osgi.services"/>
<import plugin="slf4j.api"/> <import plugin="slf4j.api"/>
<import plugin="javax.el"/>
<import plugin="javax.servlet.jsp"/>
<import plugin="javax.servlet.jsp.jstl"/>
<import plugin="org.apache.jasper.glassfish"/>
<import plugin="org.apache.taglibs.standard.glassfish"/>
<import plugin="org.eclipse.jetty.osgi.boot"/> <import plugin="org.eclipse.jetty.osgi.boot"/>
<import plugin="org.eclipse.jetty.websocket.api"/> <import plugin="org.eclipse.jetty.websocket.api"/>
<import plugin="org.eclipse.jetty.websocket.client"/> <import plugin="org.eclipse.jetty.websocket.client"/>
<import plugin="org.eclipse.jetty.websocket.common"/> <import plugin="org.eclipse.jetty.websocket.common"/>
<import plugin="org.eclipse.jetty.websocket.server"/> <import plugin="org.eclipse.jetty.websocket.server"/>
<import plugin="org.eclipse.jetty.websocket.servlet"/> <import plugin="org.eclipse.jetty.websocket.servlet"/>
<import plugin="org.apache.felix.gogo.command"/>
<import plugin="org.apache.felix.gogo.runtime"/>
<import plugin="org.apache.felix.gogo.shell"/>
</requires> </requires>
<plugin <plugin
@@ -47,14 +45,6 @@
version="9.0.4.v20130625" version="9.0.4.v20130625"
unpack="false"/> unpack="false"/>
<plugin
id="org.eclipse.jetty.osgi.boot.jsp"
download-size="0"
install-size="0"
version="9.0.4.v20130625"
fragment="true"
unpack="false"/>
<plugin <plugin
id="jul.to.slf4j" id="jul.to.slf4j"
download-size="0" download-size="0"
@@ -98,4 +88,11 @@
version="0.0.0" version="0.0.0"
unpack="false"/> unpack="false"/>
<plugin
id="org.eclipse.equinox.console"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature> </feature>
+23 -33
View File
@@ -22,20 +22,38 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
<requires> <requires>
<import plugin="com.googlecode.java-diff-utils" version="1.3.0" match="greaterOrEqual"/> <import plugin="com.googlecode.java-diff-utils" version="1.3.0" match="greaterOrEqual"/>
<import plugin="org.eclipse.osgi"/> <import plugin="org.eclipse.osgi" version="3.9.0" match="greaterOrEqual"/>
<import plugin="javax.servlet" version="2.6.0" match="greaterOrEqual"/> <import plugin="javax.servlet" version="2.6.0" match="greaterOrEqual"/>
<import plugin="com.mongodb.driver" version="2.6.2" match="greaterOrEqual"/> <import plugin="com.mongodb.driver" version="2.6.2" match="greaterOrEqual"/>
<import plugin="com.google.gwt.osgi" version="2.4.0" match="greaterOrEqual"/> <import plugin="com.google.gwt.osgi" version="2.5.1" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.domain.common"/>
<import plugin="org.json.simple" version="1.1.0" match="greaterOrEqual"/> <import plugin="org.json.simple" version="1.1.0" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.operationaltransformation"/>
<import plugin="org.moxieapps.gwt.highcharts" version="1.1.4" match="greaterOrEqual"/> <import plugin="org.moxieapps.gwt.highcharts" version="1.1.4" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.geocoding"/>
<import plugin="org.apache.geronimo.specs.geronimo-jms_1.1_spec"/> <import plugin="org.apache.geronimo.specs.geronimo-jms_1.1_spec"/>
<import plugin="com.rabbitmq.client"/> <import plugin="com.rabbitmq.client"/>
<import plugin="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec"/> <import plugin="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec"/>
</requires> </requires>
<plugin
id="com.sap.sailing.geocoding"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.operationaltransformation"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.domain.common"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin <plugin
id="com.sap.sailing.domain" id="com.sap.sailing.domain"
download-size="0" download-size="0"
@@ -85,13 +103,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0" version="0.0.0"
unpack="false"/> unpack="false"/>
<plugin
id="org.json.simple"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin <plugin
id="com.sap.sailing.declination" id="com.sap.sailing.declination"
download-size="0" download-size="0"
@@ -99,13 +110,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0" version="0.0.0"
unpack="false"/> unpack="false"/>
<plugin
id="com.mongodb.driver"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin <plugin
id="com.sap.sailing.mongodb" id="com.sap.sailing.mongodb"
download-size="0" download-size="0"
@@ -176,20 +180,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0" version="0.0.0"
unpack="false"/> unpack="false"/>
<plugin
id="com.google.gwt.osgi"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.googlecode.java-diff-utils"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin <plugin
id="com.sap.sailing.www" id="com.sap.sailing.www"
download-size="0" download-size="0"
@@ -6,6 +6,5 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.geocoding Fragment-Host: com.sap.sailing.geocoding
Import-Package: junit.framework;version="4.8.2", Require-Bundle: org.junit4;bundle-version="4.8.2"
org.json.simple.parser, Import-Package: org.json.simple.parser
org.junit;version="4.8.2"
+1 -1
View File
@@ -3,8 +3,8 @@
<classpathentry kind="src" path="src/main/resources"/> <classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" path="src/main/java"/> <classpathentry kind="src" path="src/main/java"/>
<classpathentry exported="true" kind="lib" path="WEB-INF/classes/"/> <classpathentry exported="true" kind="lib" path="WEB-INF/classes/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="WEB-INF/lib/gwt-servlet.jar" sourcepath="/com.google.gwt.osgi/lib/gwt-user.jar"/> <classpathentry kind="lib" path="WEB-INF/lib/gwt-servlet.jar" sourcepath="/com.google.gwt.osgi/lib/gwt-user.jar"/>
<classpathentry kind="lib" path="WEB-INF/lib/gwt-maps.jar"/> <classpathentry kind="lib" path="WEB-INF/lib/gwt-maps.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
@@ -16,7 +16,6 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.persistence, com.sap.sailing.domain.persistence,
com.sap.sailing.domain.swisstimingadapter.persistence, com.sap.sailing.domain.swisstimingadapter.persistence,
com.sap.sailing.domain.tractracadapter.persistence, com.sap.sailing.domain.tractracadapter.persistence,
com.google.gwt.osgi;bundle-version="2.4.0",
com.sap.sailing.domain.common, com.sap.sailing.domain.common,
com.sap.sailing.resultimport, com.sap.sailing.resultimport,
org.moxieapps.gwt.highcharts;bundle-version="1.5.0", org.moxieapps.gwt.highcharts;bundle-version="1.5.0",
@@ -27,12 +26,14 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.swisstimingreplayadapter, com.sap.sailing.domain.swisstimingreplayadapter,
com.sap.sailing.simulator;bundle-version="1.0.0", com.sap.sailing.simulator;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0", com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
de.csenk.gwt.ws de.csenk.gwt.ws,
com.google.gwt.osgi;bundle-version="2.5.1"
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy Bundle-ActivationPolicy: lazy
Import-Package: javax.jms;version="1.1.0", Import-Package: javax.jms;version="1.1.0",
javax.servlet;version="2.6.0", javax.servlet;version="2.6.0",
javax.servlet.http;version="2.6.0", javax.servlet.http;version="2.6.0",
javax.servlet.descriptor;version="2.6.0",
org.json.simple, org.json.simple,
org.json.simple.parser, org.json.simple.parser,
org.osgi.framework, org.osgi.framework,
+2 -1
View File
@@ -48,7 +48,8 @@
<!-- GWT version detected from dependencyManagement --> <!-- GWT version detected from dependencyManagement -->
<execution> <execution>
<configuration> <configuration>
<extraJvmArgs>-Dgwt.persistentunitcache=false</extraJvmArgs> <extraJvmArgs>-Dgwt.persistentunitcache=false</extraJvmArgs>
<userAgents>chrome</userAgents>
<modules> <modules>
<module>com.sap.sailing.gwt.ui.AdminConsole</module> <module>com.sap.sailing.gwt.ui.AdminConsole</module>
<module>com.sap.sailing.gwt.ui.Leaderboard</module> <module>com.sap.sailing.gwt.ui.Leaderboard</module>
@@ -816,32 +816,36 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
private void selectTrackedRaceInRaceList() { private void selectTrackedRaceInRaceList() {
final String selectedLeaderboardName = getSelectedLeaderboardName(); final String selectedLeaderboardName = getSelectedLeaderboardName();
final RaceColumnDTOAndFleetDTOWithNameBasedEquality selectedRaceColumnAndFleetNameInLeaderboard = getSelectedRaceColumnWithFleet(); if (selectedLeaderboardName != null) {
final String selectedRaceColumnName = selectedRaceColumnAndFleetNameInLeaderboard.getA().getRaceColumnName(); final RaceColumnDTOAndFleetDTOWithNameBasedEquality selectedRaceColumnAndFleetNameInLeaderboard = getSelectedRaceColumnWithFleet();
final String selectedFleetName = selectedRaceColumnAndFleetNameInLeaderboard.getB().getName(); final String selectedRaceColumnName = selectedRaceColumnAndFleetNameInLeaderboard.getA()
sailingService.getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(selectedLeaderboardName, .getRaceColumnName();
selectedRaceColumnName, new AsyncCallback<Map<String, RegattaAndRaceIdentifier>>() { final String selectedFleetName = selectedRaceColumnAndFleetNameInLeaderboard.getB().getName();
@Override sailingService.getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(selectedLeaderboardName,
public void onFailure(Throwable t) { selectedRaceColumnName, new AsyncCallback<Map<String, RegattaAndRaceIdentifier>>() {
errorReporter.reportError("Error trying to determine tracked race linked to race column " @Override
+ selectedRaceColumnName + " in leaderboard " + selectedLeaderboardName + ": " public void onFailure(Throwable t) {
+ t.getMessage()); errorReporter.reportError("Error trying to determine tracked race linked to race column "
} + selectedRaceColumnName + " in leaderboard " + selectedLeaderboardName + ": "
+ t.getMessage());
}
@Override @Override
public void onSuccess(Map<String, RegattaAndRaceIdentifier> regattaAndRaceNamesPerFleet) { public void onSuccess(Map<String, RegattaAndRaceIdentifier> regattaAndRaceNamesPerFleet) {
if (regattaAndRaceNamesPerFleet != null && !regattaAndRaceNamesPerFleet.isEmpty()) { if (regattaAndRaceNamesPerFleet != null && !regattaAndRaceNamesPerFleet.isEmpty()) {
RegattaAndRaceIdentifier raceIdentifier = regattaAndRaceNamesPerFleet.get(selectedFleetName); RegattaAndRaceIdentifier raceIdentifier = regattaAndRaceNamesPerFleet
if (raceIdentifier != null) { .get(selectedFleetName);
selectRaceInList(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName()); if (raceIdentifier != null) {
} else { selectRaceInList(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
trackedRacesListComposite.clearSelection(); } else {
} trackedRacesListComposite.clearSelection();
} else { }
trackedRacesListComposite.clearSelection(); } else {
} trackedRacesListComposite.clearSelection();
} }
}); }
});
}
} }
private void selectRaceInList(String regattaName, String raceName) { private void selectRaceInList(String regattaName, String raceName) {
@@ -46,14 +46,9 @@ import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO; import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
/** /**
* Allows the user to start and stop tracking of events, regattas and races using the TracTrac connector. In particular, * Allows the user to start and stop tracking of races using the SwissTiming connector. In particular,
* previously configured connections can be retrieved from a drop-down list which then pre-populates all connection * previously configured connections can be retrieved from a drop-down list which then pre-populates all connection
* parameters. The user can also choose to enter connection information manually. Using a "hierarchical" entry system * parameters. The user can also choose to enter connection information manually.
* comparable to that of, e.g., the Eclipse CVS connection setup wizard, components entered will be used to
* automatically assemble the full URL which can still be overwritten manually. There is a propagation order across the
* fields. Hostname propagates to JSON URL, Live URI and Stored URI. Port Live Data propagates to Port Stored Data,
* incremented by one. The ports propagate to Live URI and Stored URI, respectively. The event name propagates to the
* JSON URL.
* *
* @author Axel Uhl (D043530) * @author Axel Uhl (D043530)
* *
@@ -166,7 +161,16 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
} }
}; };
TextColumn<SwissTimingRaceRecordDTO> raceStartTrackingColumn = new TextColumn<SwissTimingRaceRecordDTO>() { TextColumn<SwissTimingRaceRecordDTO> raceStateColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
@Override
public String getValue(SwissTimingRaceRecordDTO object) {
String result = object.hasCourse ? "C✓ " : "";
result += object.hasStartlist ? "S✓" : "";
return result;
}
};
TextColumn<SwissTimingRaceRecordDTO> raceStartTimeColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
@Override @Override
public String getValue(SwissTimingRaceRecordDTO object) { public String getValue(SwissTimingRaceRecordDTO object) {
return object.raceStartTime==null?"":dateFormatter.render(object.raceStartTime) + " " + timeFormatter.render(object.raceStartTime); return object.raceStartTime==null?"":dateFormatter.render(object.raceStartTime) + " " + timeFormatter.render(object.raceStartTime);
@@ -176,36 +180,49 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
HorizontalPanel racesSplitPanel = new HorizontalPanel(); HorizontalPanel racesSplitPanel = new HorizontalPanel();
mainPanel.add(racesSplitPanel); mainPanel.add(racesSplitPanel);
CaptionPanel racesCaptionPanel = new CaptionPanel(stringConstants.trackableRaces()); CaptionPanel trackableRacesCaptionPanel = new CaptionPanel(stringConstants.trackableRaces());
racesSplitPanel.add(racesCaptionPanel); racesSplitPanel.add(trackableRacesCaptionPanel);
racesCaptionPanel.setWidth("50%"); trackableRacesCaptionPanel.setWidth("50%");
CaptionPanel trackedRacesCaptionPanel = new CaptionPanel(stringConstants.trackedRaces()); CaptionPanel trackedRacesCaptionPanel = new CaptionPanel(stringConstants.trackedRaces());
racesSplitPanel.add(trackedRacesCaptionPanel); racesSplitPanel.add(trackedRacesCaptionPanel);
trackedRacesCaptionPanel.setWidth("50%"); trackedRacesCaptionPanel.setWidth("50%");
VerticalPanel racesPanel = new VerticalPanel(); VerticalPanel trackableRacesPanel = new VerticalPanel();
racesCaptionPanel.setContentWidget(racesPanel); trackableRacesCaptionPanel.setContentWidget(trackableRacesPanel);
racesCaptionPanel.setStyleName("bold"); trackableRacesCaptionPanel.setStyleName("bold");
VerticalPanel trackedRacesPanel = new VerticalPanel(); VerticalPanel trackedRacesPanel = new VerticalPanel();
trackedRacesPanel.setWidth("100%"); trackedRacesPanel.setWidth("100%");
trackedRacesCaptionPanel.setContentWidget(trackedRacesPanel); trackedRacesCaptionPanel.setContentWidget(trackedRacesPanel);
trackedRacesCaptionPanel.setStyleName("bold"); trackedRacesCaptionPanel.setStyleName("bold");
// text box for filtering the cell table // Regatta selection
HorizontalPanel filterPanel = new HorizontalPanel();
filterPanel.setSpacing(5);
racesPanel.add(filterPanel);
// Regatta
HorizontalPanel regattaPanel = new HorizontalPanel(); HorizontalPanel regattaPanel = new HorizontalPanel();
regattaPanel.setSpacing(5); regattaPanel.setSpacing(5);
Label regattaForTrackingLabel = new Label(stringMessages.regattaUsedForTheTrackedRace()); Label regattaForTrackingLabel = new Label(stringMessages.regattaUsedForTheTrackedRace());
regattaForTrackingLabel.setWordWrap(false); regattaForTrackingLabel.setWordWrap(false);
regattaPanel.add(regattaForTrackingLabel); regattaPanel.add(regattaForTrackingLabel);
regattaPanel.add(getAvailableRegattasListBox()); regattaPanel.add(getAvailableRegattasListBox());
racesPanel.add(regattaPanel); trackableRacesPanel.add(regattaPanel);
Label lblTrackSettings = new Label(stringConstants.trackSettings());
trackableRacesPanel.add(lblTrackSettings);
final CheckBox trackWindCheckbox = new CheckBox(stringConstants.trackWind());
trackWindCheckbox.setWordWrap(false);
trackWindCheckbox.setValue(true);
trackableRacesPanel.add(trackWindCheckbox);
final CheckBox declinationCheckbox = new CheckBox(stringConstants.declinationCheckbox());
declinationCheckbox.setWordWrap(false);
declinationCheckbox.setValue(true);
trackableRacesPanel.add(declinationCheckbox);
// text box for filtering the cell table
HorizontalPanel filterPanel = new HorizontalPanel();
filterPanel.setSpacing(5);
trackableRacesPanel.add(filterPanel);
Label lblFilterEvents = new Label(stringConstants.filterRacesByName() + ":"); Label lblFilterEvents = new Label(stringConstants.filterRacesByName() + ":");
filterPanel.add(lblFilterEvents); filterPanel.add(lblFilterEvents);
@@ -218,51 +235,32 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
fillRaceListFromAvailableRacesApplyingFilter(SwissTimingEventManagementPanel.this.filterEventsTextbox.getText()); fillRaceListFromAvailableRacesApplyingFilter(SwissTimingEventManagementPanel.this.filterEventsTextbox.getText());
} }
}); });
filterPanel.add(filterEventsTextbox); filterPanel.add(filterEventsTextbox);
HorizontalPanel racesHorizontalPanel = new HorizontalPanel();
racesPanel.add(racesHorizontalPanel);
VerticalPanel trackPanel = new VerticalPanel();
trackPanel.setStyleName("paddedPanel");
raceNameColumn.setSortable(true); raceNameColumn.setSortable(true);
raceStartTrackingColumn.setSortable(true); raceStartTimeColumn.setSortable(true);
AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
raceTable = new CellTable<SwissTimingRaceRecordDTO>(/* pageSize */ 10000, tableRes); raceTable = new CellTable<SwissTimingRaceRecordDTO>(/* pageSize */ 10000, tableRes);
raceTable.addColumn(raceNameColumn, stringConstants.name()); raceTable.addColumn(raceNameColumn, stringConstants.name());
raceTable.addColumn(raceDescriptionColumn, stringConstants.description()); raceTable.addColumn(raceDescriptionColumn, stringConstants.description());
raceTable.addColumn(boatClassColumn, stringConstants.boatClass()); raceTable.addColumn(boatClassColumn, stringConstants.boatClass());
raceTable.addColumn(raceStartTrackingColumn, stringConstants.raceStartTimeColumn()); raceTable.addColumn(raceStateColumn, stringConstants.status());
raceTable.addColumn(raceStartTimeColumn, stringConstants.startTime());
raceTable.setWidth("300px"); raceTable.setWidth("300px");
raceTable.setSelectionModel(new MultiSelectionModel<SwissTimingRaceRecordDTO>() {}); raceTable.setSelectionModel(new MultiSelectionModel<SwissTimingRaceRecordDTO>() {});
racesHorizontalPanel.add(raceTable); trackableRacesPanel.add(raceTable);
racesHorizontalPanel.add(trackPanel);
raceList = new ListDataProvider<SwissTimingRaceRecordDTO>(); raceList = new ListDataProvider<SwissTimingRaceRecordDTO>();
raceList.addDataDisplay(raceTable); raceList.addDataDisplay(raceTable);
Handler columnSortHandler = getRaceTableColumnSortHandler(raceList.getList(), raceNameColumn, raceStartTrackingColumn); Handler columnSortHandler = getRaceTableColumnSortHandler(raceList.getList(), raceNameColumn, raceStartTimeColumn);
raceTable.addColumnSortHandler(columnSortHandler); raceTable.addColumnSortHandler(columnSortHandler);
Label lblTrackSettings = new Label(stringConstants.trackSettings());
trackPanel.add(lblTrackSettings);
final CheckBox trackWindCheckbox = new CheckBox(stringConstants.trackWind());
trackWindCheckbox.setWordWrap(false);
trackWindCheckbox.setValue(true);
trackPanel.add(trackWindCheckbox);
final CheckBox declinationCheckbox = new CheckBox(stringConstants.declinationCheckbox());
declinationCheckbox.setWordWrap(false);
declinationCheckbox.setValue(true);
trackPanel.add(declinationCheckbox);
trackedRacesPanel.add(trackedRacesListComposite); trackedRacesPanel.add(trackedRacesListComposite);
HorizontalPanel racesButtonPanel = new HorizontalPanel(); HorizontalPanel racesButtonPanel = new HorizontalPanel();
racesPanel.add(racesButtonPanel); trackableRacesPanel.add(racesButtonPanel);
Button btnTrack = new Button(stringConstants.startTracking()); Button btnTrack = new Button(stringConstants.startTracking());
racesButtonPanel.add(btnTrack); racesButtonPanel.add(btnTrack);
@@ -273,7 +271,6 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
trackSelectedRaces(trackWindCheckbox.getValue(), declinationCheckbox.getValue()); trackSelectedRaces(trackWindCheckbox.getValue(), declinationCheckbox.getValue());
} }
}); });
} }
private ListHandler<SwissTimingRaceRecordDTO> getRaceTableColumnSortHandler(List<SwissTimingRaceRecordDTO> raceRecords, private ListHandler<SwissTimingRaceRecordDTO> getRaceTableColumnSortHandler(List<SwissTimingRaceRecordDTO> raceRecords,
@@ -65,6 +65,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
private StringMessages stringMessages; private StringMessages stringMessages;
private final ErrorReporter errorReporter; private final ErrorReporter errorReporter;
private final UserDTO user; private final UserDTO user;
private boolean autoSelectMedia;
private MediaPlayer activeAudioPlayer; private MediaPlayer activeAudioPlayer;
private Date currentRaceTime; private Date currentRaceTime;
@@ -73,7 +74,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
public MediaSelector(RegattaAndRaceIdentifier selectedRaceIdentifier, RaceTimesInfoProvider raceTimesInfoProvider, public MediaSelector(RegattaAndRaceIdentifier selectedRaceIdentifier, RaceTimesInfoProvider raceTimesInfoProvider,
Timer raceTimer, MediaServiceAsync mediaService, StringMessages stringMessages, Timer raceTimer, MediaServiceAsync mediaService, StringMessages stringMessages,
ErrorReporter errorReporter, UserDTO user) { ErrorReporter errorReporter, UserDTO user, boolean autoSelectMedia) {
this.raceIdentifier = selectedRaceIdentifier; this.raceIdentifier = selectedRaceIdentifier;
this.raceTimesInfoProvider = raceTimesInfoProvider; this.raceTimesInfoProvider = raceTimesInfoProvider;
this.raceTimer = raceTimer; this.raceTimer = raceTimer;
@@ -81,6 +82,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
this.stringMessages = stringMessages; this.stringMessages = stringMessages;
this.errorReporter = errorReporter; this.errorReporter = errorReporter;
this.user = user; this.user = user;
this.autoSelectMedia = autoSelectMedia;
Window.addCloseHandler(this); Window.addCloseHandler(this);
Window.addWindowClosingHandler(this); Window.addWindowClosingHandler(this);
@@ -120,7 +122,6 @@ public class MediaSelector implements PlayStateListener, TimeListener,
} }
}); });
setWidgetsVisible(false); setWidgetsVisible(false);
} }
@@ -249,12 +250,12 @@ public class MediaSelector implements PlayStateListener, TimeListener,
} }
private void startPlaying() { private void startPlaying() {
if (activeAudioPlayer != null) { if ((activeAudioPlayer != null) && activeAudioPlayer.isCoveringCurrentRaceTime()) {
activeAudioPlayer.playMedia(); activeAudioPlayer.playMedia();
} }
for (MediaPlayer player : videoPlayers.values()) { for (MediaPlayer videoPlayer : videoPlayers.values()) {
if (player.isMediaPaused()) { if (videoPlayer.isMediaPaused() && videoPlayer.isCoveringCurrentRaceTime()) {
player.playMedia(); videoPlayer.playMedia();
} }
} }
} }
@@ -286,6 +287,11 @@ public class MediaSelector implements PlayStateListener, TimeListener,
setStatus(mediaTrack); setStatus(mediaTrack);
} }
setWidgetsVisible((this.mediaTracks.size() > 0) || (this.user != null)); setWidgetsVisible((this.mediaTracks.size() > 0) || (this.user != null));
toggleMediaButton.setValue(autoSelectMedia);
if (autoSelectMedia) {
playDefault();
}
} }
private void setWidgetsVisible(boolean isVisible) { private void setWidgetsVisible(boolean isVisible) {
@@ -452,7 +458,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
private void ensurePlayState(final MediaPlayer mediaPlayer) { private void ensurePlayState(final MediaPlayer mediaPlayer) {
switch (this.currentPlayState) { switch (this.currentPlayState) {
case Playing: case Playing:
if (mediaPlayer.isMediaPaused()) { if (mediaPlayer.isMediaPaused() && mediaPlayer.isCoveringCurrentRaceTime()) {
mediaPlayer.playMedia(); mediaPlayer.playMedia();
} }
break; break;
@@ -7,7 +7,7 @@ import com.sap.sailing.domain.common.media.MediaTrack;
public abstract class AbstractMediaPlayer implements MediaPlayer { public abstract class AbstractMediaPlayer implements MediaPlayer {
private static final int TOLERATED_LAG_IN_MILLISECONDS = 2000; private static final int TOLERATED_LAG_IN_MILLISECONDS = 4000;
private final MediaTrack mediaTrack; private final MediaTrack mediaTrack;
private long raceTimeInMillis; private long raceTimeInMillis;
@@ -15,7 +15,7 @@ public abstract class AbstractMediaPlayer implements MediaPlayer {
this.mediaTrack = mediaTrack; this.mediaTrack = mediaTrack;
} }
public MediaTrack getMediaTrack() { public MediaTrack getMediaTrack() {
return mediaTrack; return mediaTrack;
} }
@@ -37,6 +37,12 @@ public abstract class AbstractMediaPlayer implements MediaPlayer {
return Math.round(getCurrentMediaTime() * 1000); return Math.round(getCurrentMediaTime() * 1000);
} }
@Override
public boolean isCoveringCurrentRaceTime() {
double mediaTime = (raceTimeInMillis - mediaTrack.startTime.getTime()) / 1000d;
return (mediaTime >= 0) && (mediaTime <= getDuration());
}
protected void alignTime() { protected void alignTime() {
long mediaStartTimeInMillis = mediaTrack.startTime.getTime(); long mediaStartTimeInMillis = mediaTrack.startTime.getTime();
long mediaTimeInMillis = mediaStartTimeInMillis + getCurrentMediaTimeMillis(); long mediaTimeInMillis = mediaStartTimeInMillis + getCurrentMediaTimeMillis();
@@ -31,4 +31,6 @@ public interface MediaPlayer {
void close(); void close();
boolean isCoveringCurrentRaceTime();
} }
@@ -82,8 +82,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default*/); boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default*/);
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/); String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false); final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false);
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false);
raceboardViewConfig = new RaceBoardViewConfiguration(viewMode, activeCompetitorsFilterSetName, showLeaderboard, raceboardViewConfig = new RaceBoardViewConfiguration(viewMode, activeCompetitorsFilterSetName, showLeaderboard,
showWindChart, showCompetitorsChart, canReplayWhileLiveIsPossible); showWindChart, showCompetitorsChart, canReplayWhileLiveIsPossible, autoSelectMedia);
final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>(); final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>();
final ParallelExecutionCallback<List<RegattaDTO>> getRegattasCallback = new ParallelExecutionCallback<List<RegattaDTO>>(); final ParallelExecutionCallback<List<RegattaDTO>> getRegattasCallback = new ParallelExecutionCallback<List<RegattaDTO>>();
@@ -221,7 +222,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
} }
FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel);
FlowPanel timePanel = createTimePanel(raceBoardPanel); FlowPanel timePanel = createTimePanel(raceBoardPanel);
p.addNorth(logoAndTitlePanel, 68); p.addNorth(logoAndTitlePanel, 68);
p.addNorth(toolbarPanel, 40); p.addNorth(toolbarPanel, 40);
p.addSouth(timePanel, 90); p.addSouth(timePanel, 90);
p.add(raceBoardPanel); p.add(raceBoardPanel);
@@ -160,7 +160,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
toolbarPanel = new FlowPanel(); toolbarPanel = new FlowPanel();
toolbarPanel.setWidth("100%"); toolbarPanel.setWidth("100%");
mainPanel.add(toolbarPanel);
componentControlsPanel = new FlowPanel(); componentControlsPanel = new FlowPanel();
componentControlsPanel.addStyleName("raceBoardNavigation"); componentControlsPanel.addStyleName("raceBoardNavigation");
toolbarPanel.add(componentControlsPanel); toolbarPanel.add(componentControlsPanel);
@@ -181,7 +180,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
} }
CompetitorsFilterSets loadedCompetitorsFilterSets = loadCompetitorsFilterSets(); CompetitorsFilterSets loadedCompetitorsFilterSets = loadCompetitorsFilterSets();
if(loadedCompetitorsFilterSets != null) { if (loadedCompetitorsFilterSets != null) {
competitorsFilterSets = loadedCompetitorsFilterSets; competitorsFilterSets = loadedCompetitorsFilterSets;
insertSelectedCompetitorsFilter(competitorsFilterSets); insertSelectedCompetitorsFilter(competitorsFilterSets);
} else { } else {
@@ -190,9 +189,9 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
} }
// in case the URL configuration contains the name of a competitors filter set we try to activate it // in case the URL configuration contains the name of a competitors filter set we try to activate it
if(raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) { if (raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) {
for(FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet: competitorsFilterSets.getFilterSets()) { for (FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet: competitorsFilterSets.getFilterSets()) {
if(filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) { if (filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) {
competitorsFilterSets.setActiveFilterSet(filterSet); competitorsFilterSets.setActiveFilterSet(filterSet);
break; break;
} }
@@ -277,7 +276,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
} }
private void addMediaSelectorToNavigationMenu() { private void addMediaSelectorToNavigationMenu() {
MediaSelector mediaSelector = new MediaSelector(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, this.user); MediaSelector mediaSelector = new MediaSelector(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, this.user, getConfiguration().isAutoSelectMedia());
// raceTimesInfoProvider.addRaceTimesInfoProviderListener(mediaSelector); // raceTimesInfoProvider.addRaceTimesInfoProviderListener(mediaSelector);
timer.addPlayStateListener(mediaSelector); timer.addPlayStateListener(mediaSelector);
timer.addTimeListener(mediaSelector); timer.addTimeListener(mediaSelector);
@@ -461,7 +460,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
private <SettingsType> void addComponentToNavigationMenu(final ComponentViewer componentViewer, private <SettingsType> void addComponentToNavigationMenu(final ComponentViewer componentViewer,
final Component<SettingsType> component, boolean isToogleCheckboxEnabled) { final Component<SettingsType> component, boolean isToogleCheckboxEnabled) {
final CheckBox checkBox= new CheckBox(component.getLocalizedShortName()); final CheckBox checkBox = new CheckBox(component.getLocalizedShortName());
checkBox.getElement().getStyle().setFloat(Style.Float.LEFT); checkBox.getElement().getStyle().setFloat(Style.Float.LEFT);
checkBox.setEnabled(isToogleCheckboxEnabled); checkBox.setEnabled(isToogleCheckboxEnabled);
@@ -489,7 +488,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
componentControlsPanel.add(checkBox); componentControlsPanel.add(checkBox);
if(component.hasSettings()) { if (component.hasSettings()) {
Button settingsButton = new Button(""); Button settingsButton = new Button("");
settingsButton.addClickHandler(new ClickHandler() { settingsButton.addClickHandler(new ClickHandler() {
@Override @Override
@@ -12,14 +12,16 @@ public class RaceBoardViewConfiguration {
private ViewModes viewMode; private ViewModes viewMode;
private String activeCompetitorsFilterSetName; private String activeCompetitorsFilterSetName;
private boolean canReplayDuringLiveRaces; private boolean canReplayDuringLiveRaces;
private final boolean autoSelectMedia;
public static final String PARAM_VIEW_MODE = "viewMode"; public static final String PARAM_VIEW_MODE = "viewMode";
public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard"; public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard";
public static final String PARAM_VIEW_SHOW_NAVIGATION_PANEL = "viewShowNavigationPanel"; public static final String PARAM_VIEW_SHOW_NAVIGATION_PANEL = "viewShowNavigationPanel";
public static final String PARAM_VIEW_SHOW_WINDCHART = "viewShowWindChart"; public static final String PARAM_VIEW_SHOW_WINDCHART = "viewShowWindChart";
public static final String PARAM_VIEW_SHOW_COMPETITORSCHART = "viewShowCompetitorsChart"; public static final String PARAM_VIEW_SHOW_COMPETITORSCHART = "viewShowCompetitorsChart";
public static final String PARAM_VIEW_COMPETITOR_FILTER = "viewCompetitorFilter"; public static final String PARAM_VIEW_COMPETITOR_FILTER = "viewCompetitorFilter";
public static final String PARAM_CAN_REPLAY_DURING_LIVE_RACES = "canReplayDuringLiveRaces"; public static final String PARAM_CAN_REPLAY_DURING_LIVE_RACES = "canReplayDuringLiveRaces";
public static final String PARAM_AUTOSELECT_MEDIA = "autoSelectMedia";
public static enum ViewModes { ONESCREEN }; public static enum ViewModes { ONESCREEN };
@@ -28,16 +30,18 @@ public class RaceBoardViewConfiguration {
showLeaderboard = true; showLeaderboard = true;
showWindChart = false; showWindChart = false;
showCompetitorsChart = false; showCompetitorsChart = false;
} autoSelectMedia = false;
}
public RaceBoardViewConfiguration(ViewModes viewMode, String activeCompetitorsFilterSetName, public RaceBoardViewConfiguration(ViewModes viewMode, String activeCompetitorsFilterSetName,
boolean showLeaderboard, boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces) { boolean showLeaderboard, boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces, boolean autoSelectMedia) {
this.viewMode = viewMode; this.viewMode = viewMode;
this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName; this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName;
this.showLeaderboard = showLeaderboard; this.showLeaderboard = showLeaderboard;
this.showWindChart = showWindChart; this.showWindChart = showWindChart;
this.showCompetitorsChart = showCompetitorsChart; this.showCompetitorsChart = showCompetitorsChart;
this.canReplayDuringLiveRaces = canReplayDuringLiveRaces; this.canReplayDuringLiveRaces = canReplayDuringLiveRaces;
this.autoSelectMedia = autoSelectMedia;
} }
public boolean isShowLeaderboard() { public boolean isShowLeaderboard() {
@@ -63,4 +67,9 @@ public class RaceBoardViewConfiguration {
public boolean isCanReplayDuringLiveRaces() { public boolean isCanReplayDuringLiveRaces() {
return canReplayDuringLiveRaces; return canReplayDuringLiveRaces;
} }
public boolean isAutoSelectMedia() {
return autoSelectMedia;
}
} }
@@ -1250,13 +1250,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
GPSFixMoving fix = fixIter.next(); GPSFixMoving fix = fixIter.next();
while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 || while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 ||
(fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) { (fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) {
Tack tack = trackedRace.getTack(competitor, fix.getTimePoint()); Tack tack;
try {
tack = trackedRace.getTack(competitor, fix.getTimePoint());
} catch (NoWindException nwe) {
tack = null;
}
TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor,
fix.getTimePoint()); fix.getTimePoint());
LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg( LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg(
trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint()); trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint());
Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding); Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding);
WindDTO windDTO = createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding); WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding);
GPSFixDTO fixDTO = createGPSFixDTO(fix, fix.getSpeed(), windDTO, tack, legType, /* extrapolate */ GPSFixDTO fixDTO = createGPSFixDTO(fix, fix.getSpeed(), windDTO, tack, legType, /* extrapolate */
false); false);
fixesForCompetitor.add(fixDTO); fixesForCompetitor.add(fixDTO);
@@ -1266,13 +1271,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
// check if fix was at date and if extrapolation is requested // check if fix was at date and if extrapolation is requested
if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) { if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) {
Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate); Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate);
Tack tack2 = trackedRace.getTack(competitor, toTimePointExcluding); Tack tack2;
try {
tack2 = trackedRace.getTack(competitor, toTimePointExcluding);
} catch (NoWindException nwe) {
tack2 = null;
}
LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace
.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType( .getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType(
fix.getTimePoint()); fix.getTimePoint());
SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding); SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding);
Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); Wind wind2 = trackedRace.getWind(position, toTimePointExcluding);
WindDTO windDTO2 = createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding); WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding);
GPSFixDTO extrapolated = new GPSFixDTO( GPSFixDTO extrapolated = new GPSFixDTO(
toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()), toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()),
position==null?null:new PositionDTO(position.getLatDeg(), position.getLngDeg()), position==null?null:new PositionDTO(position.getLatDeg(), position.getLngDeg()),
@@ -2022,6 +2032,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
BoatClass boatClass = com.sap.sailing.domain.swisstimingadapter.DomainFactory.INSTANCE.getRaceTypeFromRaceID(rr.getRaceID()).getBoatClass(); BoatClass boatClass = com.sap.sailing.domain.swisstimingadapter.DomainFactory.INSTANCE.getRaceTypeFromRaceID(rr.getRaceID()).getBoatClass();
swissTimingRaceRecordDTO.boatClass = boatClass != null ? boatClass.getName() : null; swissTimingRaceRecordDTO.boatClass = boatClass != null ? boatClass.getName() : null;
swissTimingRaceRecordDTO.discipline = rr.getRaceID().length() >= 3 ? rr.getRaceID().substring(2, 3) : null; swissTimingRaceRecordDTO.discipline = rr.getRaceID().length() >= 3 ? rr.getRaceID().substring(2, 3) : null;
swissTimingRaceRecordDTO.hasCourse = rr.hasCourse();
swissTimingRaceRecordDTO.hasStartlist = rr.hasStartlist();
result.add(swissTimingRaceRecordDTO); result.add(swissTimingRaceRecordDTO);
} }
return result; return result;
@@ -13,6 +13,9 @@ public class SwissTimingRaceRecordDTO implements IsSerializable {
public String boatClass; public String boatClass;
public String discipline; public String discipline;
public boolean hasCourse;
public boolean hasStartlist;
public SwissTimingRaceRecordDTO() {} public SwissTimingRaceRecordDTO() {}
public SwissTimingRaceRecordDTO(String id, String description, Date raceStartTime) { public SwissTimingRaceRecordDTO(String id, String description, Date raceStartTime) {
@@ -43,8 +43,9 @@ public class TVEntryPoint extends AbstractEntryPoint {
final boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter( final boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(
RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */); RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */);
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/); String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false);
raceboardViewConfig = new RaceBoardViewConfiguration(RaceBoardViewConfiguration.ViewModes.ONESCREEN, raceboardViewConfig = new RaceBoardViewConfiguration(RaceBoardViewConfiguration.ViewModes.ONESCREEN,
activeCompetitorsFilterSetName, showLeaderboard, showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */ false); activeCompetitorsFilterSetName, showLeaderboard, showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */ false, autoSelectMedia);
sailingService.getLeaderboardNames(new AsyncCallback<List<String>>() { sailingService.getLeaderboardNames(new AsyncCallback<List<String>>() {
@Override @Override
@@ -169,7 +169,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
ScrollPanel leaderboardContentPanel = new ScrollPanel(); ScrollPanel leaderboardContentPanel = new ScrollPanel();
leaderboardContentPanel.add(leaderboardPanel); leaderboardContentPanel.add(leaderboardPanel);
dockPanel.add(leaderboardContentPanel); dockPanel.add(leaderboardContentPanel);
if(logoAndTitlePanel != null) { if (logoAndTitlePanel != null) {
logoAndTitlePanel.setSubTitle(leaderboardName); logoAndTitlePanel.setSubTitle(leaderboardName);
} }
currentLiveRace = null; currentLiveRace = null;
@@ -180,7 +180,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
} }
private void showRaceBoard() { private void showRaceBoard() {
if(activeTvView != TVViews.Raceboard) { if (activeTvView != TVViews.Raceboard) {
clearContentPanels(); clearContentPanels();
RaceBoardPanel raceBoardPanel = createRaceBoardPanel(leaderboardName, currentLiveRace); RaceBoardPanel raceBoardPanel = createRaceBoardPanel(leaderboardName, currentLiveRace);
raceBoardPanel.setSize("100%", "100%"); raceBoardPanel.setSize("100%", "100%");
@@ -17,9 +17,8 @@ Require-Bundle: com.sap.sailing.domain,
org.json.simple;bundle-version="1.1.0", org.json.simple;bundle-version="1.1.0",
com.sap.sailing.domain.swisstimingadapter, com.sap.sailing.domain.swisstimingadapter,
org.objenesis;bundle-version="1.3.0", org.objenesis;bundle-version="1.3.0",
org.junit4;bundle-version="4.8.2",
org.hamcrest;bundle-version="1.1.0", org.hamcrest;bundle-version="1.1.0",
org.mockito.mockito-core;bundle-version="1.9.5" org.mockito.mockito-core;bundle-version="1.9.5"
Import-Package: com.sap.sailing.domain.common.impl, Import-Package: com.sap.sailing.domain.common.impl
junit.framework;version="4.8.2",
org.junit;version="4.8.2"
Loaded 100 of 251 files, more files were not shown because too many files have changed in this diff. Show more