diff --git a/configuration/syncEC2ElbLogs b/configuration/syncEC2ElbLogs
new file mode 100755
index 00000000000..25affdba085
--- /dev/null
+++ b/configuration/syncEC2ElbLogs
@@ -0,0 +1,2 @@
+#!/bin/sh
+/usr/bin/s3cmd -c /root/.s3cfg sync s3://sapsailing-access-logs/elb-access-logs/ /var/log/old/elb-access-logs/ >/var/log/last-elb-access-logs-sync.out 2>/var/log/last-elb-access-logs-sync.err
diff --git a/configuration/unique_ips_per_referrer b/configuration/unique_ips_per_referrer
index d06f2201d0a..518f7f27509 100755
--- a/configuration/unique_ips_per_referrer
+++ b/configuration/unique_ips_per_referrer
@@ -16,6 +16,8 @@ mkdir -p "$STATS"
# A sample line:
# 505Worlds2012.sapsailing.com 66.249.73.53 - - [12/Jan/2014:03:33:11 +0000] "GET /robots.txt HTTP/1.1" 404 238 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
+echo Starting "$0" at `date` on file set "$*"
+
for i in $*; do
grep -q "^$i\$" $VISITED_FILES
if [ "$?" = "0" ]; then
@@ -50,4 +52,4 @@ for month in `cat $MONTHS`; do
done | tee $STATS/unique-ips-days-useragents-per-event-$month
echo "Wrote per-month results to $STATS/unique-ips-days-useragents-per-event-$month"
done
-echo "Done."
+echo "Done at `date`."
diff --git a/configuration/unique_ips_per_referrer_generate_month_results_only b/configuration/unique_ips_per_referrer_generate_month_results_only
new file mode 100755
index 00000000000..dcea8f54211
--- /dev/null
+++ b/configuration/unique_ips_per_referrer_generate_month_results_only
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+CACHE=/var/log/old/cache/unique-ips-per-referrer
+VISITED_FILES=$CACHE/visited
+STATS=$CACHE/stats
+MONTHS=$STATS/months
+
+for month in `cat $MONTHS`; do
+ for i in ${STATS}/*.ips; do
+ echo -n "`basename $i .ips` "
+ cat $i | grep "^[^ ]* [0-9]*/$month .*" | sort -u | wc | awk '{ print $1; }'
+ # TODO consider producing an AWStats configuration file for each virtual host name and splitting all new logs into virtual host name-specific log files
+ done | tee $STATS/unique-ips-days-useragents-per-event-`echo $month | tr / -`
+ echo "Wrote per-month results to $STATS/unique-ips-days-useragents-per-event-$month"
+done
+echo "Done."
diff --git a/configuration/unique_ips_per_referrer_generate_results_only b/configuration/unique_ips_per_referrer_generate_results_only
new file mode 100755
index 00000000000..23e316484ce
--- /dev/null
+++ b/configuration/unique_ips_per_referrer_generate_results_only
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+CACHE=/var/log/old/cache/unique-ips-per-referrer
+VISITED_FILES=$CACHE/visited
+STATS=$CACHE/stats
+MONTHS=$STATS/months
+
+for i in ${STATS}/*.ips; do
+ echo -n "`basename $i .ips` "
+ cat $i | sort -u | wc | awk '{ print $1; }'
+ # TODO consider producing an AWStats configuration file for each virtual host name and splitting all new logs into virtual host name-specific log files
+done | tee $STATS/unique-ips-days-useragents-per-event
+echo "Wrote total results to $STATS/unique-ips-days-useragents-per-event"
+
+
+# Now filter and group by month
+cat ${STATS}/*.ips | awk '{ print $2; }' | sed -e 's/^[0-9]*\///' | sort -u >$MONTHS
+for month in `cat $MONTHS`; do
+ for i in ${STATS}/*.ips; do
+ echo -n "`basename $i .ips` "
+ cat $i | grep "^[^ ]* [0-9]*/$month .*" | sort -u | wc | awk '{ print $1; }'
+ # TODO consider producing an AWStats configuration file for each virtual host name and splitting all new logs into virtual host name-specific log files
+ done | tee $STATS/unique-ips-days-useragents-per-event-`echo $month | tr / -`
+ echo "Wrote per-month results to $STATS/unique-ips-days-useragents-per-event-$month"
+done
+echo "Done."
diff --git a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java b/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java
index 3ecf17a7c01..1df916f6e48 100644
--- a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java
+++ b/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java
@@ -94,8 +94,8 @@ public class TracTrac {
new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()),
new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()),
raceLogStore, regattaLogStore,
- RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* simulateWithStartTimeNow */
- false, /*useTracTracMarkPassings*/ false,
+ RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* offsetToStartTimeOfSimulatedRace */
+ null, /*useTracTracMarkPassings*/ false,
/* tracTracUsername */"", /* tracTracPassword */"", record.getRaceStatus(),
record.getRaceVisibility());
} else {
diff --git a/java/com.sap.sailing.domain.common/pom.xml b/java/com.sap.sailing.domain.common/pom.xml
index 031a6daf0f6..a1ae29ff16f 100755
--- a/java/com.sap.sailing.domain.common/pom.xml
+++ b/java/com.sap.sailing.domain.common/pom.xml
@@ -1,40 +1,40 @@
-
-
- 4.0.0
-
- root
- com.sap.sailing
- 1.0.0-SNAPSHOT
-
- com.sap.sailing.domain.common
- eclipse-plugin
-
-
-
-
- org.eclipse.tycho
- tycho-compiler-plugin
- ${tycho-version}
-
- 1.7
- 1.7
-
-
-
- org.eclipse.tycho
- tycho-source-plugin
- ${tycho-version}
-
-
- plugin-source
- generate-sources
-
- plugin-source
-
-
-
-
-
-
-
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sailing.domain.common
+ eclipse-plugin
+
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-plugin
+ ${tycho-version}
+
+ 1.7
+ 1.7
+
+
+
+ org.eclipse.tycho
+ tycho-source-plugin
+ ${tycho-version}
+
+
+ plugin-source
+ generate-sources
+
+ plugin-source
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractSpeedImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractSpeedImpl.java
index 90b3c7e5116..4b38e380d31 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractSpeedImpl.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractSpeedImpl.java
@@ -21,7 +21,7 @@ public abstract class AbstractSpeedImpl implements Speed {
@Override
public Duration getDuration(Distance distance) {
- return new MillisecondsDurationImpl((long) (1000 * distance.getMeters() / getMetersPerSecond()));
+ return distance == null ? null : new MillisecondsDurationImpl((long) (1000 * distance.getMeters() / getMetersPerSecond()));
}
@Override
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java
index 8a9fa9017a9..ff197288789 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java
@@ -1,430 +1,433 @@
-package com.sap.sailing.domain.common.dto;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import com.sap.sailing.domain.common.LeaderboardType;
-import com.sap.sailing.domain.common.RaceIdentifier;
-import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
-import com.sap.sailing.domain.common.ScoringSchemeType;
-import com.sap.sse.common.Util;
-
-public abstract class AbstractLeaderboardDTO implements Serializable {
- private static final long serialVersionUID = -205106531931903527L;
-
- public String name;
-
- private List races;
- public Map competitorDisplayNames;
- public Map rows;
- public boolean hasCarriedPoints;
- public int[] discardThresholds;
-
- /**
- * Set to the non-null regatta name if this DTO represents a RegattaLeaderboard.
- */
- public String regattaName;
- public String displayName;
- public UUID defaultCourseAreaId;
- public String defaultCourseAreaName;
- public ScoringSchemeType scoringScheme;
- public LeaderboardType type;
-
- private Long delayToLiveInMillisForLatestRace;
-
- public AbstractLeaderboardDTO() {
- races = new ArrayList();
- }
-
- public Set getBoatClasses() {
- Set result = new HashSet();
- if(rows != null) {
- for (CompetitorDTO competitor : rows.keySet()) {
- result.add(competitor.getBoatClass());
- }
- }
- return result;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public String getDisplayName(CompetitorDTO competitor) {
- if (competitorDisplayNames == null || competitorDisplayNames.get(competitor) == null) {
- return competitor.getName();
- } else {
- return competitorDisplayNames.get(competitor);
- }
- }
-
- /**
- * If the race column whose name is specified in raceColumnName has at least one competitor who has valid
- * {@link LeaderboardEntryDTO#legDetails} for that race column, the maximum number of entries of all such competitors in
- * the leg details is returned, telling the number of legs that the race column shall display. Otherwise, -1 is returned.
- * If you specify an non null preselectedRace the leg count is calculated only for that race for the column
- * holding that race. All other columns will be unaffected by preselectedRace and will return their regular
- * leg count. Note that this may lead to a situation where some competitors may have sailed more legs than others because
- * their fleet association may have varied across races.
- *
- * See also bug 2604 and bug 2035.
- */
- public int getLegCount(String raceColumnName, RaceIdentifier preselectedRace) {
- int result = -1;
- final boolean preselectedRaceIsInRaceColumn = isRaceInColumn(preselectedRace, raceColumnName);
- for (LeaderboardRowDTO row : rows.values()) {
- LeaderboardEntryDTO leaderboardEntryDTO = row.fieldsByRaceColumnName.get(raceColumnName);
- if (leaderboardEntryDTO != null && leaderboardEntryDTO.legDetails != null
- // when no race is pre-selected, always use the actual leg count;
- // otherwise, if the pre-selected race is in the column identified by
- // raceColumnName, use the entry only if it's in the pre-selected race;
- // otherwise (a race is pre-selected but a different column's leg count
- // is requested), use the regular leg count as is.
- && (preselectedRace == null || !preselectedRaceIsInRaceColumn || preselectedRace.equals(leaderboardEntryDTO.race))) {
- result = Math.max(result, leaderboardEntryDTO.legDetails.size());
- }
- }
- return result;
- }
-
- private boolean isRaceInColumn(RaceIdentifier preselectedRace, String raceColumnName) {
- RaceColumnDTO raceColumn = getRaceColumnByName(raceColumnName);
- return raceColumn.containsRace(preselectedRace);
- }
-
- /**
- * Tells if the competitor scored (and therefore presumably participated) in a medal race represented
- * in this leaderboard.
- */
- public boolean scoredInMedalRace(CompetitorDTO competitor) {
- LeaderboardRowDTO row = rows.get(competitor);
- for (RaceColumnDTO race : races) {
- if (race.isMedalRace() && row.fieldsByRaceColumnName.get(race.getRaceColumnName()).totalPoints > 0) {
- return true;
- }
- }
- return false;
- }
-
- public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
- Double netPoints = null;
- LeaderboardRowDTO row = rows.get(competitor);
- if (row != null) {
- LeaderboardEntryDTO field = row.fieldsByRaceColumnName.get(nameOfLastRaceSoFar);
- if (field != null && field.netPoints != null) {
- netPoints = field.netPoints;
- }
- }
- return netPoints;
- }
-
- public boolean raceIsTracked(String raceColumnName) {
- for (RaceColumnDTO race : races) {
- if (race.getRaceColumnName().equals(raceColumnName)) {
- for (FleetDTO fleet : race.getFleets()) {
- if (race.isTrackedRace(fleet)) {
- return true;
- }
- }
- }
- }
- return false;
-
- }
-
- public boolean raceIsMedalRace(String raceColumnName) {
- return getRaceColumnByName(raceColumnName).isMedalRace();
- }
-
- /**
- * If the {@link RaceColumnDTO} by the name raceColumnName doesn't exist yet within this leaderboard
- * DTO, it is created, setting is {@link RaceColumnDTO#isValidInTotalScore()} to true. This method
- * ensures that a fleet named fleetName is present. If it's not present yet, it's added to the race
- * column's fleet name list. The trackedRaceIdentifier and race are associated with the
- * column for the fleet identified by fleetName.
- *
- * @param explicitFactor
- * factor by which to multiply the race column's points for the overall score; if null, the
- * default will be determined by whether or not the column is marked as medal race
- * @param effectiveFactor
- * is what you get when you call {@link RaceColumn#getFactor()} on the race column that the resulting
- * {@link RaceColumnDTO} represents
- * @param regattaName
- * must not be null if seriesName is not null; specified
- * separately from trackedRaceIdentifier because a column may belong to regatta/series
- * despite not having a tracked race associated
- * @param seriesName
- * if null, this method will produce a {@link RaceColumnDTO}, otherwise a
- * {@link RaceColumnInSeriesDTO}
- * @param fleetDTO
- * must not be null
- */
- public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor,
- String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace, RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race) {
- assert fleetDTO != null;
- RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName);
- if (raceColumnDTO == null) {
- raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace,
- explicitFactor, regattaName, seriesName);
- races.add(raceColumnDTO);
- }
- raceColumnDTO.setEffectiveFactor(effectiveFactor);
- boolean contains = false;
- for (FleetDTO fleet : raceColumnDTO.getFleets()) {
- if (fleet.getName().equals(fleetDTO.getName())) {
- contains = true;
- break;
- }
- }
- if (!contains) {
- raceColumnDTO.addFleet(fleetDTO);
- }
- raceColumnDTO.setRaceIdentifier(fleetDTO, trackedRaceIdentifier);
- raceColumnDTO.setRace(fleetDTO, race);
- return raceColumnDTO;
- }
-
- public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName, String seriesName) {
- final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName,
- medalRace, /* explicit factor */ null, regattaName, seriesName);
- races.add(raceColumn);
- return raceColumn;
- }
-
- public RaceColumnDTO getRaceColumnByName(String raceColumnName) {
- for (RaceColumnDTO race : races) {
- if (race.getRaceColumnName().equals(raceColumnName)) {
- return race;
- }
- }
- return null;
- }
-
- public int getRaceColumnsCount() {
- return races.size();
- }
-
- public int getRacesCount() {
- int result = 0;
- for (RaceColumnDTO race : getRaceList()) {
- result += race.getFleets().size();
- }
- return result;
- }
-
- public int getTrackedRacesCount() {
- int result = 0;
- for (RaceColumnDTO raceColumn : getRaceList()) {
- for (FleetDTO fleet : raceColumn.getFleets()) {
- RaceDTO race = raceColumn.getRace(fleet);
- if(race != null && race.trackedRace != null && race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
- result++;
- }
- }
- }
- return result;
- }
-
- public List getRaceList() {
- return races;
- }
-
- public void setRaceList(List raceList) {
- this.races = raceList;
- }
-
- public boolean raceListContains(String raceColumnName) {
- return getRaceColumnByName(raceColumnName) != null;
- }
-
- public void moveRaceUp(String raceColumnName) {
- RaceColumnDTO race = getRaceColumnByName(raceColumnName);
- int index = races.indexOf(race);
- index--;
- if (index >= 0) {
- races.remove(index + 1);
- races.add(index, race);
- }
- }
-
- public void moveRaceDown(String raceColumnName) {
- RaceColumnDTO race = getRaceColumnByName(raceColumnName);
- int index = races.indexOf(race);
- if (index != -1) {
- index++;
- if (index < races.size()) {
- races.remove(index - 1);
- races.add(index, race);
- }
- }
- }
-
- public void setIsMedalRace(String raceColumnName, boolean medalRace) {
- getRaceColumnByName(raceColumnName).setMedalRace(medalRace);
- }
-
- /**
- * @return The earliest start date of the races, or null if no start dates of the races are available.
- */
- public Date getStartDate() {
- Date leaderboardStart = null;
- for (RaceColumnDTO race : getRaceList()) {
- for (FleetDTO fleet: race.getFleets()) {
- Date raceStart = race.getStartDate(fleet);
- if (raceStart != null) {
- if (leaderboardStart == null) {
- leaderboardStart = new Date();
- } else {
- leaderboardStart = leaderboardStart.before(raceStart) ? leaderboardStart : raceStart;
- }
- }
- }
- }
- return leaderboardStart;
- }
-
- /**
- * Takes the {@link PlacemarkOrderDTO} of all races in this leaderboard, if the PlacemarkOrderDTO for the race is
- * available, and fills all {@link PlacemarkDTO} in a new PlacemarkOrderDTO.
- * The order of the races in this leaderboard determine the order of the PlacemarkDTOs in the PlacemarkOrderDTO.
- *
- * @return The places of this leaderboard in form of a {@link PlacemarkOrderDTO}, or null if the
- * {@link PlacemarkOrderDTO places} of no race are available
- */
- public PlacemarkOrderDTO getPlaces() {
- PlacemarkOrderDTO leaderboardPlaces = null;
- for (RaceColumnDTO race : getRaceList()) {
- PlacemarkOrderDTO racePlaces = race.getPlaces();
- if (racePlaces != null) {
- if (leaderboardPlaces == null) {
- leaderboardPlaces = new PlacemarkOrderDTO();
- }
- leaderboardPlaces.getPlacemarks().addAll(racePlaces.getPlacemarks());
- }
- }
- return leaderboardPlaces;
- }
-
- /**
- * @return true if the leaderboard contains a race which is live
- */
- public boolean hasLiveRace(long serverTimePointAsMillis) {
- for (RaceColumnDTO race : getRaceList()) {
- for (FleetDTO fleet : race.getFleets()) {
- if (race.isLive(fleet, serverTimePointAsMillis)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public List> getLiveRaces(long serverTimePointAsMillis) {
- List> result = new ArrayList>();
- for (RaceColumnDTO race : getRaceList()) {
- for (FleetDTO fleet : race.getFleets()) {
- if (race.isLive(fleet, serverTimePointAsMillis)) {
- result.add(new Util.Pair(race, fleet));
- }
- }
- }
- return result;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((competitorDisplayNames == null) ? 0 : competitorDisplayNames.hashCode());
- result = prime * result + Arrays.hashCode(discardThresholds);
- result = prime * result + (hasCarriedPoints ? 1231 : 1237);
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- result = prime * result + ((scoringScheme == null) ? 0 : scoringScheme.hashCode());
- if (races == null) {
- result = prime * result;
- } else {
- List raceNames = new ArrayList();
- for (RaceColumnDTO race : races) {
- raceNames.add(race.getName());
- }
- result = prime * result + raceNames.hashCode();
- }
- result = prime * result + ((rows == null) ? 0 : rows.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- AbstractLeaderboardDTO other = (AbstractLeaderboardDTO) obj;
- if (competitorDisplayNames == null) {
- if (other.competitorDisplayNames != null)
- return false;
- } else if (!competitorDisplayNames.equals(other.competitorDisplayNames))
- return false;
- if (!Arrays.equals(discardThresholds, other.discardThresholds))
- return false;
- if (hasCarriedPoints != other.hasCarriedPoints)
- return false;
- if (type != other.type)
- return false;
- if (scoringScheme != other.scoringScheme)
- return false;
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
- if (races == null) {
- if (other.races != null)
- return false;
- } else {
- // compare race column names only, not the contents
- if (races.size() != (other.races==null?0:other.races.size())) {
- return false;
- }
- List raceColumnNames = new ArrayList(races.size());
- List otherRaceColumnNames = new ArrayList(races.size());
- for (RaceColumnDTO race : races) {
- raceColumnNames.add(race.getName());
- }
- if (other.races != null) {
- for (RaceColumnDTO otherRace : other.races) {
- otherRaceColumnNames.add(otherRace.getName());
- }
- }
- if (!raceColumnNames.equals(otherRaceColumnNames))
- return false;
- if (rows == null) {
- if (other.rows != null)
- return false;
- } else if (!rows.equals(other.rows))
- return false;
- }
- return true;
- }
-
- public boolean isDisplayNameSet(CompetitorDTO competitor) {
- return competitorDisplayNames.get(competitor) != null;
- }
-
- public Long getDelayToLiveInMillisForLatestRace() {
- return delayToLiveInMillisForLatestRace;
- }
-
- public void setDelayToLiveInMillisForLatestRace(Long delayToLiveInMillisForLatestRace) {
- this.delayToLiveInMillisForLatestRace = delayToLiveInMillisForLatestRace;
- }
-}
+package com.sap.sailing.domain.common.dto;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import com.sap.sailing.domain.common.LeaderboardType;
+import com.sap.sailing.domain.common.RaceIdentifier;
+import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
+import com.sap.sailing.domain.common.ScoringSchemeType;
+import com.sap.sse.common.Util;
+
+public abstract class AbstractLeaderboardDTO implements Serializable {
+ private static final long serialVersionUID = -205106531931903527L;
+
+ public String name;
+
+ private List races;
+ public Map competitorDisplayNames;
+ public Map rows;
+ public boolean hasCarriedPoints;
+ public int[] discardThresholds;
+
+ /**
+ * Set to the non-null regatta name if this DTO represents a RegattaLeaderboard.
+ */
+ public String regattaName;
+ public String displayName;
+ public UUID defaultCourseAreaId;
+ public String defaultCourseAreaName;
+ public ScoringSchemeType scoringScheme;
+ public LeaderboardType type;
+
+ private Long delayToLiveInMillisForLatestRace;
+
+ public AbstractLeaderboardDTO() {
+ races = new ArrayList();
+ }
+
+ public Set getBoatClasses() {
+ Set result = new HashSet();
+ if(rows != null) {
+ for (CompetitorDTO competitor : rows.keySet()) {
+ result.add(competitor.getBoatClass());
+ }
+ }
+ return result;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getDisplayName(CompetitorDTO competitor) {
+ if (competitorDisplayNames == null || competitorDisplayNames.get(competitor) == null) {
+ return competitor.getName();
+ } else {
+ return competitorDisplayNames.get(competitor);
+ }
+ }
+
+ /**
+ * If the race column whose name is specified in raceColumnName has at least one competitor who has valid
+ * {@link LeaderboardEntryDTO#legDetails} for that race column, the maximum number of entries of all such competitors in
+ * the leg details is returned, telling the number of legs that the race column shall display. Otherwise, -1 is returned.
+ * If you specify an non null preselectedRace the leg count is calculated only for that race for the column
+ * holding that race. All other columns will be unaffected by preselectedRace and will return their regular
+ * leg count. Note that this may lead to a situation where some competitors may have sailed more legs than others because
+ * their fleet association may have varied across races.
+ *
+ * See also bug 2604 and bug 2035.
+ */
+ public int getLegCount(String raceColumnName, RaceIdentifier preselectedRace) {
+ int result = -1;
+ final boolean preselectedRaceIsInRaceColumn = isRaceInColumn(preselectedRace, raceColumnName);
+ for (LeaderboardRowDTO row : rows.values()) {
+ LeaderboardEntryDTO leaderboardEntryDTO = row.fieldsByRaceColumnName.get(raceColumnName);
+ if (leaderboardEntryDTO != null && leaderboardEntryDTO.legDetails != null
+ // when no race is pre-selected, always use the actual leg count;
+ // otherwise, if the pre-selected race is in the column identified by
+ // raceColumnName, use the entry only if it's in the pre-selected race;
+ // otherwise (a race is pre-selected but a different column's leg count
+ // is requested), use the regular leg count as is.
+ && (preselectedRace == null || !preselectedRaceIsInRaceColumn || preselectedRace.equals(leaderboardEntryDTO.race))) {
+ result = Math.max(result, leaderboardEntryDTO.legDetails.size());
+ }
+ }
+ return result;
+ }
+
+ private boolean isRaceInColumn(RaceIdentifier preselectedRace, String raceColumnName) {
+ RaceColumnDTO raceColumn = getRaceColumnByName(raceColumnName);
+ return raceColumn.containsRace(preselectedRace);
+ }
+
+ /**
+ * Tells if the competitor scored (and therefore presumably participated) in a medal race represented
+ * in this leaderboard.
+ */
+ public boolean scoredInMedalRace(CompetitorDTO competitor) {
+ LeaderboardRowDTO row = rows.get(competitor);
+ for (RaceColumnDTO race : races) {
+ if (race.isMedalRace() && row.fieldsByRaceColumnName.get(race.getRaceColumnName()).totalPoints > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
+ Double netPoints = null;
+ LeaderboardRowDTO row = rows.get(competitor);
+ if (row != null) {
+ LeaderboardEntryDTO field = row.fieldsByRaceColumnName.get(nameOfLastRaceSoFar);
+ if (field != null && field.netPoints != null) {
+ netPoints = field.netPoints;
+ }
+ }
+ return netPoints;
+ }
+
+ public boolean raceIsTracked(String raceColumnName) {
+ for (RaceColumnDTO race : races) {
+ if (race.getRaceColumnName().equals(raceColumnName)) {
+ for (FleetDTO fleet : race.getFleets()) {
+ if (race.isTrackedRace(fleet)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ }
+
+ public boolean raceIsMedalRace(String raceColumnName) {
+ return getRaceColumnByName(raceColumnName).isMedalRace();
+ }
+
+ /**
+ * If the {@link RaceColumnDTO} by the name raceColumnName doesn't exist yet within this leaderboard
+ * DTO, it is created, setting is {@link RaceColumnDTO#isValidInTotalScore()} to true. This method
+ * ensures that a fleet named fleetName is present. If it's not present yet, it's added to the race
+ * column's fleet name list. The trackedRaceIdentifier and race are associated with the
+ * column for the fleet identified by fleetName.
+ *
+ * @param explicitFactor
+ * factor by which to multiply the race column's points for the overall score; if null, the
+ * default will be determined by whether or not the column is marked as medal race
+ * @param effectiveFactor
+ * is what you get when you call {@link RaceColumn#getFactor()} on the race column that the resulting
+ * {@link RaceColumnDTO} represents
+ * @param regattaName
+ * must not be null if seriesName is not null; specified
+ * separately from trackedRaceIdentifier because a column may belong to regatta/series
+ * despite not having a tracked race associated
+ * @param seriesName
+ * if null, this method will produce a {@link RaceColumnDTO}, otherwise a
+ * {@link RaceColumnInSeriesDTO}
+ * @param fleetDTO
+ * must not be null
+ */
+ public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor,
+ String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace,
+ RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn) {
+ assert fleetDTO != null;
+ RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName);
+ if (raceColumnDTO == null) {
+ raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace,
+ explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn);
+ races.add(raceColumnDTO);
+ }
+ raceColumnDTO.setEffectiveFactor(effectiveFactor);
+ boolean contains = false;
+ for (FleetDTO fleet : raceColumnDTO.getFleets()) {
+ if (fleet.getName().equals(fleetDTO.getName())) {
+ contains = true;
+ break;
+ }
+ }
+ if (!contains) {
+ raceColumnDTO.addFleet(fleetDTO);
+ }
+ raceColumnDTO.setRaceIdentifier(fleetDTO, trackedRaceIdentifier);
+ raceColumnDTO.setRace(fleetDTO, race);
+ return raceColumnDTO;
+ }
+
+ public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName,
+ String seriesName, boolean isMetaLeaderboardColumn) {
+ final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName,
+ medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn);
+ races.add(raceColumn);
+ return raceColumn;
+ }
+
+ public RaceColumnDTO getRaceColumnByName(String raceColumnName) {
+ for (RaceColumnDTO race : races) {
+ if (race.getRaceColumnName().equals(raceColumnName)) {
+ return race;
+ }
+ }
+ return null;
+ }
+
+ public int getRaceColumnsCount() {
+ return races.size();
+ }
+
+ public int getRacesCount() {
+ int result = 0;
+ for (RaceColumnDTO race : getRaceList()) {
+ result += race.getFleets().size();
+ }
+ return result;
+ }
+
+ public int getTrackedRacesCount() {
+ int result = 0;
+ for (RaceColumnDTO raceColumn : getRaceList()) {
+ for (FleetDTO fleet : raceColumn.getFleets()) {
+ RaceDTO race = raceColumn.getRace(fleet);
+ if(race != null && race.trackedRace != null && race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
+ result++;
+ }
+ }
+ }
+ return result;
+ }
+
+ public List getRaceList() {
+ return races;
+ }
+
+ public void setRaceList(List raceList) {
+ this.races = raceList;
+ }
+
+ public boolean raceListContains(String raceColumnName) {
+ return getRaceColumnByName(raceColumnName) != null;
+ }
+
+ public void moveRaceUp(String raceColumnName) {
+ RaceColumnDTO race = getRaceColumnByName(raceColumnName);
+ int index = races.indexOf(race);
+ index--;
+ if (index >= 0) {
+ races.remove(index + 1);
+ races.add(index, race);
+ }
+ }
+
+ public void moveRaceDown(String raceColumnName) {
+ RaceColumnDTO race = getRaceColumnByName(raceColumnName);
+ int index = races.indexOf(race);
+ if (index != -1) {
+ index++;
+ if (index < races.size()) {
+ races.remove(index - 1);
+ races.add(index, race);
+ }
+ }
+ }
+
+ public void setIsMedalRace(String raceColumnName, boolean medalRace) {
+ getRaceColumnByName(raceColumnName).setMedalRace(medalRace);
+ }
+
+ /**
+ * @return The earliest start date of the races, or null if no start dates of the races are available.
+ */
+ public Date getStartDate() {
+ Date leaderboardStart = null;
+ for (RaceColumnDTO race : getRaceList()) {
+ for (FleetDTO fleet: race.getFleets()) {
+ Date raceStart = race.getStartDate(fleet);
+ if (raceStart != null) {
+ if (leaderboardStart == null) {
+ leaderboardStart = new Date();
+ } else {
+ leaderboardStart = leaderboardStart.before(raceStart) ? leaderboardStart : raceStart;
+ }
+ }
+ }
+ }
+ return leaderboardStart;
+ }
+
+ /**
+ * Takes the {@link PlacemarkOrderDTO} of all races in this leaderboard, if the PlacemarkOrderDTO for the race is
+ * available, and fills all {@link PlacemarkDTO} in a new PlacemarkOrderDTO.
+ * The order of the races in this leaderboard determine the order of the PlacemarkDTOs in the PlacemarkOrderDTO.
+ *
+ * @return The places of this leaderboard in form of a {@link PlacemarkOrderDTO}, or null if the
+ * {@link PlacemarkOrderDTO places} of no race are available
+ */
+ public PlacemarkOrderDTO getPlaces() {
+ PlacemarkOrderDTO leaderboardPlaces = null;
+ for (RaceColumnDTO race : getRaceList()) {
+ PlacemarkOrderDTO racePlaces = race.getPlaces();
+ if (racePlaces != null) {
+ if (leaderboardPlaces == null) {
+ leaderboardPlaces = new PlacemarkOrderDTO();
+ }
+ leaderboardPlaces.getPlacemarks().addAll(racePlaces.getPlacemarks());
+ }
+ }
+ return leaderboardPlaces;
+ }
+
+ /**
+ * @return true if the leaderboard contains a race which is live
+ */
+ public boolean hasLiveRace(long serverTimePointAsMillis) {
+ for (RaceColumnDTO race : getRaceList()) {
+ for (FleetDTO fleet : race.getFleets()) {
+ if (race.isLive(fleet, serverTimePointAsMillis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public List> getLiveRaces(long serverTimePointAsMillis) {
+ List> result = new ArrayList>();
+ for (RaceColumnDTO race : getRaceList()) {
+ for (FleetDTO fleet : race.getFleets()) {
+ if (race.isLive(fleet, serverTimePointAsMillis)) {
+ result.add(new Util.Pair(race, fleet));
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((competitorDisplayNames == null) ? 0 : competitorDisplayNames.hashCode());
+ result = prime * result + Arrays.hashCode(discardThresholds);
+ result = prime * result + (hasCarriedPoints ? 1231 : 1237);
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((scoringScheme == null) ? 0 : scoringScheme.hashCode());
+ if (races == null) {
+ result = prime * result;
+ } else {
+ List raceNames = new ArrayList();
+ for (RaceColumnDTO race : races) {
+ raceNames.add(race.getName());
+ }
+ result = prime * result + raceNames.hashCode();
+ }
+ result = prime * result + ((rows == null) ? 0 : rows.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AbstractLeaderboardDTO other = (AbstractLeaderboardDTO) obj;
+ if (competitorDisplayNames == null) {
+ if (other.competitorDisplayNames != null)
+ return false;
+ } else if (!competitorDisplayNames.equals(other.competitorDisplayNames))
+ return false;
+ if (!Arrays.equals(discardThresholds, other.discardThresholds))
+ return false;
+ if (hasCarriedPoints != other.hasCarriedPoints)
+ return false;
+ if (type != other.type)
+ return false;
+ if (scoringScheme != other.scoringScheme)
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (races == null) {
+ if (other.races != null)
+ return false;
+ } else {
+ // compare race column names only, not the contents
+ if (races.size() != (other.races==null?0:other.races.size())) {
+ return false;
+ }
+ List raceColumnNames = new ArrayList(races.size());
+ List otherRaceColumnNames = new ArrayList(races.size());
+ for (RaceColumnDTO race : races) {
+ raceColumnNames.add(race.getName());
+ }
+ if (other.races != null) {
+ for (RaceColumnDTO otherRace : other.races) {
+ otherRaceColumnNames.add(otherRace.getName());
+ }
+ }
+ if (!raceColumnNames.equals(otherRaceColumnNames))
+ return false;
+ if (rows == null) {
+ if (other.rows != null)
+ return false;
+ } else if (!rows.equals(other.rows))
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isDisplayNameSet(CompetitorDTO competitor) {
+ return competitorDisplayNames.get(competitor) != null;
+ }
+
+ public Long getDelayToLiveInMillisForLatestRace() {
+ return delayToLiveInMillisForLatestRace;
+ }
+
+ public void setDelayToLiveInMillisForLatestRace(Long delayToLiveInMillisForLatestRace) {
+ this.delayToLiveInMillisForLatestRace = delayToLiveInMillisForLatestRace;
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BasicRaceDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BasicRaceDTO.java
new file mode 100644
index 00000000000..8938136783c
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BasicRaceDTO.java
@@ -0,0 +1,112 @@
+package com.sap.sailing.domain.common.dto;
+
+import java.util.Date;
+
+import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
+import com.sap.sailing.domain.common.TimingConstants;
+
+/**
+ * Master data about a single race that is to be transferred to the client. Holds only timing and a bit
+ * of information about the tracked race, if any. See also {@link RaceDTO} for a more comprehensive set
+ * of data about a race to be serialized to a client.
+ *
+ * @author Axel Uhl (d043530)
+ *
+ */
+public class BasicRaceDTO extends NamedDTO {
+ private static final long serialVersionUID = -7884808503795229609L;
+
+ public Date startOfRace;
+ public Date endOfRace;
+ public TrackedRaceDTO trackedRace;
+
+ public BasicRaceDTO() {} // for GWT serialization only
+
+ public BasicRaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace) {
+ super(raceIdentifier.getRaceName());
+ this.trackedRace = trackedRace;
+ }
+
+ /**
+ * @see {@link TrackedRace#isLive} for further explanation.
+ * @param serverTimePointAsMillis
+ * the time point (in server clock time) at which to determine whether the race is/was live
+ * @return true if serverTimePointAsMillis is between (inclusively) the start and end time
+ * point of the "live" interval as defined above.
+ */
+ public boolean isLive(long serverTimePointAsMillis) {
+ final Date startOfLivePeriod;
+ final Date endOfLivePeriod;
+ if (trackedRace == null || !trackedRace.hasGPSData || !trackedRace.hasWindData) {
+ startOfLivePeriod = null;
+ endOfLivePeriod = null;
+ } else {
+ if (startOfRace == null) {
+ startOfLivePeriod = trackedRace.startOfTracking;
+ } else {
+ startOfLivePeriod = new Date(startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
+ }
+ if (endOfRace == null) {
+ if (trackedRace.timePointOfNewestEvent != null) {
+ endOfLivePeriod = new Date(trackedRace.timePointOfNewestEvent.getTime()
+ + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
+ } else {
+ endOfLivePeriod = null;
+ }
+ } else {
+ endOfLivePeriod = new Date(endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
+ }
+ }
+
+ // if an empty timepoint is given then take the start of the race
+ if (serverTimePointAsMillis == 0) {
+ serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
+ }
+
+ // whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
+ // when this DTO was assembled, or there were no GPS or wind data
+ final boolean result =
+ startOfLivePeriod != null &&
+ endOfLivePeriod != null &&
+ startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
+ serverTimePointAsMillis <= endOfLivePeriod.getTime();
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((endOfRace == null) ? 0 : endOfRace.hashCode());
+ result = prime * result + ((startOfRace == null) ? 0 : startOfRace.hashCode());
+ result = prime * result + ((trackedRace == null) ? 0 : trackedRace.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BasicRaceDTO other = (BasicRaceDTO) obj;
+ if (endOfRace == null) {
+ if (other.endOfRace != null)
+ return false;
+ } else if (!endOfRace.equals(other.endOfRace))
+ return false;
+ if (startOfRace == null) {
+ if (other.startOfRace != null)
+ return false;
+ } else if (!startOfRace.equals(other.startOfRace))
+ return false;
+ if (trackedRace == null) {
+ if (other.trackedRace != null)
+ return false;
+ } else if (!trackedRace.equals(other.trackedRace))
+ return false;
+ return true;
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
index 64012fec26c..dd9b5e94981 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardDTO.java
@@ -62,7 +62,6 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
/**
* @param uuidGenerator used to provide the {@link #id ID} for this object (see also {@link #getId()}) and for any clones produced
* from it by the {@link #clone()} operation.
- * @param hasOverallDetails TODO
*/
public LeaderboardDTO(Date timePointOfLastCorrectionsValidity, String comment, ScoringSchemeType scoringScheme, boolean higherScoreIsBetter, UUIDGenerator uuidGenerator, boolean hasOverallDetails) {
initCollections();
@@ -79,7 +78,7 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
this.suppressedCompetitors = new ArrayList();
}
- public LeaderboardDTO(String id) {
+ protected LeaderboardDTO(String id) {
initCollections();
this.id = id;
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java
new file mode 100644
index 00000000000..475718b447b
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/MetaLeaderboardRaceColumnDTO.java
@@ -0,0 +1,49 @@
+package com.sap.sailing.domain.common.dto;
+
+import java.util.ArrayList;
+
+public class MetaLeaderboardRaceColumnDTO extends RaceColumnDTO {
+
+ private static final long serialVersionUID = 6152752963316150432L;
+
+ private ArrayList raceList = new ArrayList();
+
+ public boolean isLive(FleetDTO fleet, long serverTimePointAsMillis) {
+ for (BasicRaceDTO race : raceList) {
+ if (race.isLive(serverTimePointAsMillis)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void addRace(BasicRaceDTO race) {
+ raceList.add(race);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((raceList == null) ? 0 : raceList.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ MetaLeaderboardRaceColumnDTO other = (MetaLeaderboardRaceColumnDTO) obj;
+ if (raceList == null) {
+ if (other.raceList != null)
+ return false;
+ } else if (!raceList.equals(other.raceList))
+ return false;
+ return true;
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java
index 7d8a0efbb67..46083549e93 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceColumnDTOFactory.java
@@ -7,15 +7,17 @@ public class RaceColumnDTOFactory {
* @param regattaName
* must not be null if seriesName is not null
* @param seriesName
- * when null, a regular {@link RaceColumnDTO} will be produced and initialized from the
- * properties passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created.
+ * when null, a regular {@link RaceColumnDTO} or {@link MetaLeaderboardRaceColumnDTO} will
+ * be produced (depending on isMetaLeaderboardColumn) and initialized from the properties
+ * passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created.
*/
- public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor, String regattaName, String seriesName) {
+ public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor,
+ String regattaName, String seriesName, boolean isMetaLeaderboardColumn) {
final RaceColumnDTO raceColumnDTO;
if (seriesName != null) {
raceColumnDTO = new RaceColumnInSeriesDTO(seriesName, regattaName);
} else {
- raceColumnDTO = new RaceColumnDTO();
+ raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO() : new RaceColumnDTO();
}
fillRaceColumnDTO(raceColumnDTO, columnName, isMedal, explicitFactor);
return raceColumnDTO;
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
index eba9caf39b7..6a8e90c617f 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RaceDTO.java
@@ -1,10 +1,7 @@
package com.sap.sailing.domain.common.dto;
-import java.util.Date;
-
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
-import com.sap.sailing.domain.common.TimingConstants;
/**
* Master data about a single race that is to be transferred to the client.
@@ -12,7 +9,7 @@ import com.sap.sailing.domain.common.TimingConstants;
* @author Axel Uhl (d043530)
*
*/
-public class RaceDTO extends NamedDTO {
+public class RaceDTO extends BasicRaceDTO {
private static final long serialVersionUID = 2613189982608149975L;
public enum RaceLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE, TRACKED_BUT_NOT_SCHEDULED };
@@ -24,13 +21,10 @@ public class RaceDTO extends NamedDTO {
*/
public boolean isTracked;
- public Date startOfRace;
- public Date endOfRace;
public RaceStatusDTO status;
public PlacemarkOrderDTO places;
- public TrackedRaceDTO trackedRace;
public TrackedRaceStatisticsDTO trackedRaceStatistics;
private String regattaName;
@@ -43,21 +37,20 @@ public class RaceDTO extends NamedDTO {
}
public RaceDTO(RegattaAndRaceIdentifier raceIdentifier, TrackedRaceDTO trackedRace, boolean isCurrentlyTracked) {
- super(raceIdentifier.getRaceName());
+ super(raceIdentifier, trackedRace);
this.regattaName = raceIdentifier.getRegattaName();
- this.trackedRace = trackedRace;
this.isTracked = isCurrentlyTracked;
}
public RaceLiveState getLiveState(long serverTimePointAsMillis) {
- RaceLiveState result = RaceLiveState.NOT_TRACKED;
- if(trackedRace != null && trackedRace.hasGPSData && trackedRace.hasWindData) {
+ RaceLiveState result = RaceLiveState.NOT_TRACKED;
+ if (trackedRace != null && trackedRace.hasGPSData && trackedRace.hasWindData) {
result = RaceLiveState.TRACKED;
- if(isLive(serverTimePointAsMillis)) {
+ if (isLive(serverTimePointAsMillis)) {
result = RaceLiveState.TRACKED_AND_LIVE;
- if(startOfRace == null) {
+ if (startOfRace == null) {
result = RaceLiveState.TRACKED_BUT_NOT_SCHEDULED;
- }
+ }
}
}
return result;
@@ -71,64 +64,15 @@ public class RaceDTO extends NamedDTO {
return regattaName;
}
- /**
- * @see {@link TrackedRace#isLive} for further explanation.
- * @param serverTimePointAsMillis
- * the time point (in server clock time) at which to determine whether the race is/was live
- * @return true if serverTimePointAsMillis is between (inclusively) the start and end time
- * point of the "live" interval as defined above.
- */
- public boolean isLive(long serverTimePointAsMillis) {
- final Date startOfLivePeriod;
- final Date endOfLivePeriod;
- if (trackedRace == null || !trackedRace.hasGPSData || !trackedRace.hasWindData) {
- startOfLivePeriod = null;
- endOfLivePeriod = null;
- } else {
- if (startOfRace == null) {
- startOfLivePeriod = trackedRace.startOfTracking;
- } else {
- startOfLivePeriod = new Date(startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
- }
- if (endOfRace == null) {
- if (trackedRace.timePointOfNewestEvent != null) {
- endOfLivePeriod = new Date(trackedRace.timePointOfNewestEvent.getTime()
- + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
- } else {
- endOfLivePeriod = null;
- }
- } else {
- endOfLivePeriod = new Date(endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
- }
- }
-
- // if an empty timepoint is given then take the start of the race
- if (serverTimePointAsMillis == 0) {
- serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
- }
-
- // whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
- // when this DTO was assembled, or there were no GPS or wind data
- final boolean result =
- startOfLivePeriod != null &&
- endOfLivePeriod != null &&
- startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
- serverTimePointAsMillis <= endOfLivePeriod.getTime();
- return result;
- }
-
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
- result = prime * result + ((endOfRace == null) ? 0 : endOfRace.hashCode());
result = prime * result + (isTracked ? 1231 : 1237);
result = prime * result + ((places == null) ? 0 : places.hashCode());
result = prime * result + ((regattaName == null) ? 0 : regattaName.hashCode());
- result = prime * result + ((startOfRace == null) ? 0 : startOfRace.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
- result = prime * result + ((trackedRace == null) ? 0 : trackedRace.hashCode());
return result;
}
@@ -146,11 +90,6 @@ public class RaceDTO extends NamedDTO {
return false;
} else if (!boatClass.equals(other.boatClass))
return false;
- if (endOfRace == null) {
- if (other.endOfRace != null)
- return false;
- } else if (!endOfRace.equals(other.endOfRace))
- return false;
if (isTracked != other.isTracked)
return false;
if (places == null) {
@@ -163,21 +102,11 @@ public class RaceDTO extends NamedDTO {
return false;
} else if (!regattaName.equals(other.regattaName))
return false;
- if (startOfRace == null) {
- if (other.startOfRace != null)
- return false;
- } else if (!startOfRace.equals(other.startOfRace))
- return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
- if (trackedRace == null) {
- if (other.trackedRace != null)
- return false;
- } else if (!trackedRace.equals(other.trackedRace))
- return false;
return true;
}
}
diff --git a/java/com.sap.sailing.domain.shared.android/pom.xml b/java/com.sap.sailing.domain.shared.android/pom.xml
index b6b4b65e730..af7117f708a 100644
--- a/java/com.sap.sailing.domain.shared.android/pom.xml
+++ b/java/com.sap.sailing.domain.shared.android/pom.xml
@@ -1,39 +1,39 @@
-
-
- 4.0.0
-
- root
- com.sap.sailing
- 1.0.0-SNAPSHOT
-
- com.sap.sailing.domain.shared.android
- eclipse-plugin
-
-
-
- org.eclipse.tycho
- tycho-compiler-plugin
- ${tycho-version}
-
- 1.7
- 1.7
-
-
-
- org.eclipse.tycho
- tycho-source-plugin
- ${tycho-version}
-
-
- plugin-source
- generate-sources
-
- plugin-source
-
-
-
-
-
-
-
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sailing.domain.shared.android
+ eclipse-plugin
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-plugin
+ ${tycho-version}
+
+ 1.7
+ 1.7
+
+
+
+ org.eclipse.tycho
+ tycho-source-plugin
+ ${tycho-version}
+
+
+ plugin-source
+ generate-sources
+
+ plugin-source
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser b/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser
index 1fd2fb452b3..8b0225928aa 100755
Binary files a/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser and b/java/com.sap.sailing.domain.test/resources/IncrementalLeaderboardDTO.ser differ
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
index 7e7a7e29630..4b91a74d1aa 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java
@@ -60,7 +60,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=cce678c8-97e6-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
- /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
+ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -71,7 +71,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=11290bd6-97e7-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
- /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
+ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -82,7 +82,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=39635b24-97e7-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
- /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
+ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -93,7 +93,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=04498426-7dfd-11e0-8236-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
- /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
+ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java
index eefd9495d9c..3b907f3cf30 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java
@@ -67,7 +67,7 @@ public class UnicodeCharactersInCompetitorNamesTest {
+ TracTracConnectionConstants.PORT_STORED), new URI(
"http://tracms.traclive.dk/update_course"),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
- /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
+ /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, "tracTest", "tracTest", "", "",
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
index 2ee226e8b63..eca3d7cebdc 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl;
import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver;
import com.sap.sailing.domain.tractracadapter.impl.Simulator;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.tractrac.model.lib.api.data.IPosition;
@@ -95,7 +96,6 @@ public interface DomainFactory {
* Fetch a race definition previously created by a call to {@link #getOrCreateRaceDefinitionAndTrackedRace}. If no such
* race definition was created so far, the call blocks until such a definition is provided by a call to
* {@link #getOrCreateRaceDefinitionAndTrackedRace}.
- * @param raceId TODO
*/
RaceDefinition getAndWaitForRaceDefinition(UUID raceId);
@@ -105,7 +105,6 @@ public interface DomainFactory {
* A new {@link com.sap.sailing.domain.base.Regatta} is created if no event by
* an equal name with a boat class with an equal name as the event's
* boat class exists yet.
- * @param trackedRegattaRegistry TODO
*/
com.sap.sailing.domain.base.Regatta getOrCreateDefaultRegatta(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
IRace race, TrackedRegattaRegistry trackedRegattaRegistry);
@@ -134,27 +133,30 @@ public interface DomainFactory {
* if null, all stored data until the "end of time" will be loaded that the event has
* to provide, particularly for the mark positions which are stored per event, not per race; otherwise,
* particularly the mark position loading will be constrained to this end time.
+ * @param offsetToStartTimeOfSimulatedRace
+ * if non-null, the {@link Simulator} will be used with this duration as start offset
* @param windStore
* Provides the capability to obtain the {@link WindTrack}s for the different wind sources. A trivial
* implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always
* available but loses track of the wind, e.g., during server restarts.
- * @param raceLogResolver TODO
*/
TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException;
/**
- * Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)}, only that
- * a predefined {@link Regatta} is used to hold the resulting races.
- * @param raceLogResolver TODO
+ * Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)},
+ * only that a predefined {@link Regatta} is used to hold the resulting races.
+ *
+ * @param offsetToStartTimeOfSimulatedRace
+ * if non-null, the {@link Simulator} will be used with this duration as start offset
*/
RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
@@ -262,9 +264,13 @@ public interface DomainFactory {
Util.Pair, BoatClass> getCompetitorsAndDominantBoatClass(IRace race);
+ /**
+ * @param offsetToStartTimeOfSimulatedRace
+ * if non-null, the {@link Simulator} will be used with this duration as start offset
+ */
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility);
/**
* Removes all knowledge about tractracRace which includes removing it from the race cache, from the
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java
index 72fcb93662e..ca159e2ca8e 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java
@@ -17,6 +17,8 @@ import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
+import com.sap.sailing.domain.tractracadapter.impl.Simulator;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -65,12 +67,13 @@ public interface TracTracAdapter {
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
- * @param raceStatus
+ * @param offsetToStartTimeOfSimulatedRace
+ * if non-null, the {@link Simulator} will be used with this duration as start offset
*/
RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL,
URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint trackingStartTime,
TimePoint trackingEndTime, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
- long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm,
+ long timeoutForReceivingRaceDefinitionInMilliseconds, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
index ec6c5959d06..7df01e515ff 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
@@ -74,6 +74,7 @@ import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
import com.sap.sailing.domain.tractracadapter.TracTracControlPoint;
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -652,27 +653,27 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException,
URISyntaxException, CreateModelException, SubscriberInitializationException {
return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
- endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
+ endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException {
return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, courseDesignUpdateURI,
- startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
+ startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -690,11 +691,11 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
- long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
+ long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) {
return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI,
- startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
+ startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility);
}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
index a438f5b0450..17c00105762 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
@@ -31,7 +32,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
private final RegattaLogStore regattaLogStore;
private final DomainFactory domainFactory;
private final long delayToLiveInMillis;
- private final boolean simulateWithStartTimeNow;
+ private final Duration offsetToStartTimeOfSimulatedRace;
private final String tracTracUsername;
private final String tracTracPassword;
private final String raceStatus;
@@ -40,7 +41,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
DomainFactory domainFactory, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) {
super();
@@ -52,7 +53,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
this.endOfTracking = endOfTracking;
this.delayToLiveInMillis = delayToLiveInMillis;
this.domainFactory = domainFactory;
- this.simulateWithStartTimeNow = simulateWithStartTimeNow;
+ this.offsetToStartTimeOfSimulatedRace = offsetToStartTimeOfSimulatedRace;
this.raceLogStore = raceLogStore;
this.regattaLogStore = regattaLogStore;
this.tracTracUsername = tracTracUsername;
@@ -67,7 +68,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, URISyntaxException,
CreateModelException, SubscriberInitializationException {
RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI,
- startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
+ startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker;
@@ -77,7 +78,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws Exception {
RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI,
- courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm,
+ courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm,
raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker;
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RawPositionReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RawPositionReceiver.java
index 2e5cfea7755..7e5049f9cf4 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RawPositionReceiver.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RawPositionReceiver.java
@@ -4,7 +4,6 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
-import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -57,13 +56,13 @@ public class RawPositionReceiver extends AbstractReceiverWithQueuetimed's time point according to this simulator's delay and waits roughly until this time
@@ -157,12 +171,12 @@ public class Simulator {
* Like {@link #delay}, only that it doesn't wait until timePoint is reached in wall time.
*/
public TimePoint advance(TimePoint timePoint) {
- return new MillisecondsTimePoint(timePoint.asMillis()+getAdvanceInMillis());
+ return timePoint.plus(getAdvance());
}
/**
* If {@link #advanceInMillis} is already set to a non-negative value, it is left alone, and
- * {@link #delay(TimePoint)} is called. Otherwise, time is taken to be the original start time of the
+ * {@link #advance(TimePoint)} is called. Otherwise, time is taken to be the original start time of the
* race which is then used to compute {@link #advanceInMillis} such that
* time.asMillis() + advanceInMillis == System.currentTimeMillis().
*/
@@ -183,13 +197,13 @@ public class Simulator {
if (isAdvanceInMillisSet()) {
return advance(time);
} else {
- setAdvanceInMillis(System.currentTimeMillis() - time.asMillis());
+ setAdvanceInMillis(new MillisecondsDurationImpl(MillisecondsTimePoint.now().minus(time.asMillis()).plus(getOffsetToStart()).asMillis()));
return advance(time);
}
}
private boolean isAdvanceInMillisSet() {
- return advanceInMillis != -1;
+ return !advanceInMillis.equals(Duration.NULL.minus(1));
}
/**
@@ -238,21 +252,35 @@ public class Simulator {
// deliver an empty list now
trackedRace.updateMarkPassings(competitor, markPassings);
}
-
+ }
+
+ public void scheduleCompetitorPosition(final Competitor competitor, GPSFixMoving competitorFix) {
+ final RecordGPSFix recorder = (c, f)->trackedRace.recordFix(c, f);
+ scheduleFixRecording(competitor, competitorFix, recorder);
+ }
+
+ @FunctionalInterface
+ private static interface RecordGPSFix {
+ void recordFix(T objectForFix, GPSFixMoving fix);
}
public void scheduleMarkPosition(final Mark mark, GPSFixMoving markFix) {
- final TimePoint transformedTimepoint = advance(markFix.getTimePoint());
- final GPSFixMoving transformedMarkFix = new GPSFixMovingImpl(markFix.getPosition(), transformedTimepoint, markFix.getSpeed());
+ final RecordGPSFix recorder = (m, f)->trackedRace.recordFix(mark, f);
+ scheduleFixRecording(mark, markFix, recorder);
+ }
+
+ private void scheduleFixRecording(final T object, GPSFixMoving fix, final RecordGPSFix recorder) {
+ final TimePoint transformedTimepoint = advance(fix.getTimePoint());
+ final GPSFixMoving transformedMarkFix = new GPSFixMovingImpl(fix.getPosition(), transformedTimepoint, fix.getSpeed());
long waitTime = getWaitTimeInMillisUntil(transformedMarkFix.getTimePoint());
if (waitTime <= 0) {
- trackedRace.recordFix(mark, transformedMarkFix);
+ recorder.recordFix(object, transformedMarkFix);
} else {
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
- trackedRace.recordFix(mark, transformedMarkFix);
+ recorder.recordFix(object, transformedMarkFix);
} catch (Exception e) {
logger.throwing(Simulator.class.getName(), "scheduleMarkPosition", e);
}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java
index 8264853e299..0d7fd26a6ec 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.tractracadapter.JSONService;
import com.sap.sailing.domain.tractracadapter.RaceRecord;
import com.sap.sailing.domain.tractracadapter.TracTracAdapter;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -56,7 +57,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI,
/* startOfTracking */null,
- /* endOfTracking */null, delayToLiveInMillis, /* simulateWithStartTimeNow */false, /* ignoreTracTracMarkPassings */ false,
+ /* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false,
raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility),
timeoutInMilliseconds);
}
@@ -65,13 +66,13 @@ public class TracTracAdapterImpl implements TracTracAdapter {
public RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL,
URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword, String raceStatus,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) throws Exception {
return trackerManager.addRace(
regattaToAddTo,
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
- simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
+ offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
raceStatus, raceVisibility), timeoutInMilliseconds);
}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
index 4dac78b6701..50c376d419c 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
+import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.tractrac.model.lib.api.ModelLocator;
@@ -253,26 +254,26 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
- boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException,
SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), domainFactory, paramURL, liveURI,
storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
- simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
+ offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
private TracTracRaceTrackerImpl(IRace tractracRace, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
- long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
+ long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
FileNotFoundException, SubscriberInitializationException {
this(tractracRace, null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
- endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
+ endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -284,14 +285,14 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
- long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore,
+ long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException,
MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), regatta, domainFactory, paramURL,
liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
- simulateWithStartTimeNow, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
+ offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -300,15 +301,15 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
* @param regatta
* if null, then domainFactory.getOrCreateRegatta(tractracEvent) will be used
* to obtain a default regatta
- * @param simulateWithStartTimeNow
- * if true, the connector will adjust the time stamps of all events received such that the
+ * @param offsetToStartTimeOfSimulatedRace
+ * if not null, the connector will adjust the time stamps of all events received such that the
* first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all
- * events received such that they seem to be sent in "real-time." So, more or less the time points
+ * events received such that they seem to be sent in "real-time" + offsetToStartTimeOfSimulatedRace So, more or less the time points
* attached to the events sent to the receivers will again approximate the wall time.
*/
private TracTracRaceTrackerImpl(IRace tractracRace, final Regatta regatta, DomainFactory domainFactory,
URL paramURL, URI liveURI, URI storedURI, URI tracTracUpdateURI, TimePoint startOfTracking,
- TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm,
+ TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
@@ -323,8 +324,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.gpsFixStore = gpsFixStore;
this.domainFactory = domainFactory;
this.lastProgressPerID = new HashMap<>();
- if (simulateWithStartTimeNow) {
- simulator = new Simulator(windStore);
+ if (offsetToStartTimeOfSimulatedRace != null) {
+ simulator = new Simulator(windStore, offsetToStartTimeOfSimulatedRace);
// don't write the transformed wind fixes into the DB again... see also bug 1974
this.windStore = EmptyWindStore.INSTANCE;
} else {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Course.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Course.java
index fb1dadb2f61..b86693c71cf 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Course.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Course.java
@@ -43,4 +43,6 @@ public interface Course extends CourseBase {
void update(Iterable> newControlPoints, DomainFactory baseDomainFactory) throws PatchFailedException;
int getNumberOfWaypoints();
+
+ Leg getLeg(int zeroBasedIndexOfWaypoint);
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java
index b68ea6ac7ff..e3e39e010c1 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/CourseImpl.java
@@ -246,6 +246,11 @@ public class CourseImpl extends NamedImpl implements Course {
return waypoints.size();
}
+ @Override
+ public Leg getLeg(int zeroBasedIndexOfWaypoint) {
+ return legs.get(zeroBasedIndexOfWaypoint);
+ }
+
@Override
public String toString() {
lockForRead();
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
index de92f254836..454843ba3e2 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
@@ -1,1907 +1,1937 @@
-package com.sap.sailing.domain.leaderboard.impl;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.RunnableFuture;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.sap.sailing.domain.abstractlog.race.InvalidatesLeaderboardCache;
-import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
-import com.sap.sailing.domain.base.Competitor;
-import com.sap.sailing.domain.base.Course;
-import com.sap.sailing.domain.base.DomainFactory;
-import com.sap.sailing.domain.base.Fleet;
-import com.sap.sailing.domain.base.Leg;
-import com.sap.sailing.domain.base.RaceColumn;
-import com.sap.sailing.domain.base.RaceColumnInSeries;
-import com.sap.sailing.domain.base.RaceColumnListener;
-import com.sap.sailing.domain.base.Waypoint;
-import com.sap.sailing.domain.common.Distance;
-import com.sap.sailing.domain.common.LegType;
-import com.sap.sailing.domain.common.ManeuverType;
-import com.sap.sailing.domain.common.MaxPointsReason;
-import com.sap.sailing.domain.common.NoWindError;
-import com.sap.sailing.domain.common.NoWindException;
-import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
-import com.sap.sailing.domain.common.RegattaNameAndRaceName;
-import com.sap.sailing.domain.common.Speed;
-import com.sap.sailing.domain.common.SpeedWithBearing;
-import com.sap.sailing.domain.common.dto.CompetitorDTO;
-import com.sap.sailing.domain.common.dto.FleetDTO;
-import com.sap.sailing.domain.common.dto.LeaderboardDTO;
-import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO;
-import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
-import com.sap.sailing.domain.common.dto.LegEntryDTO;
-import com.sap.sailing.domain.common.dto.RaceDTO;
-import com.sap.sailing.domain.common.tracking.GPSFixMoving;
-import com.sap.sailing.domain.leaderboard.Leaderboard;
-import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
-import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
-import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result;
-import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
-import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
-import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCache;
-import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
-import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater;
-import com.sap.sailing.domain.racelog.RaceLogIdentifier;
-import com.sap.sailing.domain.ranking.RankingMetric.CompetitorRankingInfo;
-import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
-import com.sap.sailing.domain.tracking.GPSFixTrack;
-import com.sap.sailing.domain.tracking.Maneuver;
-import com.sap.sailing.domain.tracking.MarkPassing;
-import com.sap.sailing.domain.tracking.MarkPassingManeuver;
-import com.sap.sailing.domain.tracking.RaceChangeListener;
-import com.sap.sailing.domain.tracking.TrackedLeg;
-import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
-import com.sap.sailing.domain.tracking.TrackedRace;
-import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
-import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
-import com.sap.sailing.domain.tracking.WindPositionMode;
-import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
-import com.sap.sailing.util.impl.RaceColumnListeners;
-import com.sap.sse.common.Duration;
-import com.sap.sse.common.TimePoint;
-import com.sap.sse.common.Util;
-import com.sap.sse.common.impl.MillisecondsTimePoint;
-import com.sap.sse.concurrent.LockUtil;
-import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
-import com.sap.sse.util.impl.ThreadFactoryWithPriority;
-
-/**
- * Base implementation for various types of leaderboards. The {@link RaceColumnListener} implementation forwards events
- * received to all {@link RaceColumnListener} subscribed with this leaderboard. To which objects this leaderboard subscribes
- * as {@link RaceColumnListener} is left to the concrete subclasses to implement, but the race columns seem like useful
- * candidates.
- *
- * @author Axel Uhl (d043530)
- *
- */
-public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, RaceColumnListener {
- private static final long serialVersionUID = 330156778603279333L;
-
- private static final Logger logger = Logger.getLogger(AbstractSimpleLeaderboardImpl.class.getName());
-
- static final Double DOUBLE_0 = new Double(0);
-
- private final SettableScoreCorrection scoreCorrection;
-
- private ThresholdBasedResultDiscardingRule crossLeaderboardResultDiscardingRule;
-
- /**
- * The optional display name mappings for competitors. This allows a user to override the tracking-provided
- * competitor names for display in a leaderboard.
- */
- private final Map displayNames;
-
- /** the display name of the leaderboard */
- private String displayName;
-
- /**
- * Backs the {@link #getCarriedPoints(Competitor)} API with data. Can be used to prime this leaderboard
- * with aggregated results of races not tracked / displayed by this leaderboard in detail. The points
- * provided by this map are considered by {@link #getTotalPoints(Competitor, TimePoint)}.
- */
- private final Map carriedPoints;
-
- private final RaceColumnListeners raceColumnListeners;
-
- /**
- * A set that manages the difference between {@link #getCompetitors()} and {@link #getAllCompetitors()}. Access
- * is controlled by the {@link #suppressedCompetitorsLock} lock.
- */
- private final Set suppressedCompetitors;
- private final NamedReentrantReadWriteLock suppressedCompetitorsLock;
-
- private transient Map, RunnableFuture> raceDetailsAtEndOfTrackingCache;
-
- /**
- * This executor needs to be a different one than {@link #executor} because the tasks run by {@link #executor}
- * can depend on the results of the tasks run by {@link #raceDetailsExecutor}, and an {@link Executor} doesn't
- * move a task that is blocked by waiting for another {@link FutureTask} to the side but blocks permanently,
- * ending in a deadlock (one that cannot easily be detected by the Eclipse debugger either).
- */
- private transient Executor raceDetailsExecutor;
-
- /**
- * Used to remove all these listeners from their tracked races when this servlet is {@link #destroy() destroyed}.
- */
- private transient Set cacheInvalidationListeners;
-
- private transient ThreadPoolExecutor executor;
-
- private transient LiveLeaderboardUpdater liveLeaderboardUpdater;
-
- private transient LeaderboardDTOCache leaderboardDTOCache;
-
- /**
- * A leaderboard entry representing a snapshot of a cell at a given time point for a single race/competitor.
- *
- * @author Axel Uhl (d043530)
- *
- */
- class EntryImpl implements Entry {
- private final Callable trackedRankProvider;
- private final Double netPoints;
- private final Callable netPointsUncorrectedProvider;
- private final boolean isNetPointsCorrected;
- private final Double totalPoints;
- private final MaxPointsReason maxPointsReason;
- private final boolean discarded;
- private final Fleet fleet;
-
- private EntryImpl(Callable trackedRankProvider, Double netPoints,
- Callable netPointsUncorrectedProvider, boolean isNetPointsCorrected,
- Double totalPoints, MaxPointsReason maxPointsReason, boolean discarded, Fleet fleet) {
- super();
- this.trackedRankProvider = trackedRankProvider;
- this.netPoints = netPoints;
- this.netPointsUncorrectedProvider = netPointsUncorrectedProvider;
- this.isNetPointsCorrected = isNetPointsCorrected;
- this.totalPoints = totalPoints;
- this.maxPointsReason = maxPointsReason;
- this.discarded = discarded;
- this.fleet = fleet;
- }
- @Override
- public int getTrackedRank() {
- try {
- return trackedRankProvider.call();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- @Override
- public Double getNetPoints() {
- return netPoints;
- }
- @Override
- public boolean isNetPointsCorrected() {
- return isNetPointsCorrected;
- }
- @Override
- public Double getTotalPoints() {
- return totalPoints;
- }
- @Override
- public MaxPointsReason getMaxPointsReason() {
- return maxPointsReason;
- }
- @Override
- public boolean isDiscarded() {
- return discarded;
- }
- @Override
- public Fleet getFleet() {
- return fleet;
- }
- @Override
- public Double getNetPointsUncorrected() {
- try {
- return netPointsUncorrectedProvider.call();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /**
- * Computing the competitors can be a bit expensive, particularly if the fleet is large and there may be suppressed
- * competitors, and the leaderboard may be a meta-leaderboard that refers to other leaderboards which each have
- * several tracked races attached from where the competitors need to be retrieved. Ideally, the competitors list
- * would be cached, but that is again difficult because we would have to monitor all changes in all dependent
- * leaderboards and columns and tracked races properly.
- *
- *
- * As it turns out, one of the most frequent uses of the {@link AbstractSimpleLeaderboardImpl#getCompetitors}
- * competitors list is to determine their number which in turn is only required for high-point scoring systems and
- * for computing the default score for penalties. Again, the most frequently used low-point family of scoring schemes
- * does not require this number. Yet, the scoring scheme requires an argument for polymorphic use by those that
- * need it. Instead of computing it for each call, this interface lets us defer the actual calculation until the
- * point when it's really needed. Once asked, this object will cache the result. Therefore, a new one should be
- * constructed each time the number shall be computed.
- *
- * @author Axel Uhl (D043530)
- *
- */
- public class NumberOfCompetitorsFetcherImpl implements NumberOfCompetitorsInLeaderboardFetcher {
- private int numberOfCompetitors = -1;
-
- @Override
- public int getNumberOfCompetitorsInLeaderboard() {
- if (numberOfCompetitors == -1) {
- numberOfCompetitors = Util.size(getCompetitors());
- }
- return numberOfCompetitors;
- }
- }
-
- /**
- * Handles the invalidation of the {@link SailingServiceImpl#raceDetailsAtEndOfTrackingCache} entries if the tracked
- * race changes in any way. In particular, for {@link #statusChanged}, when the status changes away from LOADING,
- * calculations may start or resume, making it necessary to clear the cache.
- *
- * @author Axel Uhl (D043530)
- *
- */
- private class CacheInvalidationListener extends AbstractRaceChangeListener {
- private final TrackedRace trackedRace;
- private final Competitor competitor;
-
- public CacheInvalidationListener(TrackedRace trackedRace, Competitor competitor) {
- this.trackedRace = trackedRace;
- this.competitor = competitor;
- }
-
- public TrackedRace getTrackedRace() {
- return trackedRace;
- }
-
- public void removeFromTrackedRace() {
- trackedRace.removeListener(this);
- }
-
- private void invalidateCacheAndRemoveThisListenerFromTrackedRace() {
- synchronized (raceDetailsAtEndOfTrackingCache) {
- raceDetailsAtEndOfTrackingCache.remove(new com.sap.sse.common.Util.Pair(trackedRace, competitor));
- removeFromTrackedRace();
- }
- }
-
- @Override
- protected void defaultAction() {
- invalidateCacheAndRemoveThisListenerFromTrackedRace();
- }
- }
-
- private static class UUIDGenerator implements LeaderboardDTO.UUIDGenerator {
- @Override
- public String generateRandomUUID() {
- return UUID.randomUUID().toString();
- }
- }
-
- public AbstractSimpleLeaderboardImpl(ThresholdBasedResultDiscardingRule resultDiscardingRule) {
- this.carriedPoints = new HashMap();
- this.scoreCorrection = createScoreCorrection();
- this.displayNames = new HashMap();
- this.crossLeaderboardResultDiscardingRule = resultDiscardingRule;
- this.suppressedCompetitors = new HashSet();
- this.suppressedCompetitorsLock = new NamedReentrantReadWriteLock("suppressedCompetitorsLock", /* fair */ false);
- this.raceColumnListeners = new RaceColumnListeners();
- this.raceDetailsAtEndOfTrackingCache = new HashMap, RunnableFuture>();
- 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 {
- ois.defaultReadObject();
- initTransientFields();
- }
-
- private void initTransientFields() {
- this.raceDetailsAtEndOfTrackingCache = new HashMap, RunnableFuture>();
- this.raceDetailsExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
- this.cacheInvalidationListeners = new HashSet();
- // When many updates are triggered in a short period of time by a single thread, ensure that the single thread
- // providing the updates is not outperformed by all the re-calculations happening here. Leave at least one
- // core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur.
- final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
- executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
- /* maximumPoolSize */ THREAD_POOL_SIZE,
- /* keepAliveTime */ 60, TimeUnit.SECONDS,
- /* workQueue */ new LinkedBlockingQueue(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
- }
-
- @Override
- public void destroy() {
- for (CacheInvalidationListener cacheInvalidationListener : cacheInvalidationListeners) {
- cacheInvalidationListener.removeFromTrackedRace();
- }
- }
-
- @Override
- public SettableScoreCorrection getScoreCorrection() {
- return scoreCorrection;
- }
-
- @Override
- public String getDisplayName(Competitor competitor) {
- return displayNames.get(competitor);
- }
-
- @Override
- public String getDisplayName() {
- return displayName;
- }
-
- @Override
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
- @Override
- public ResultDiscardingRule getResultDiscardingRule() {
- return crossLeaderboardResultDiscardingRule;
- }
-
- @Override
- public void setCarriedPoints(Competitor competitor, double carriedPoints) {
- Double oldCarriedPoints = this.carriedPoints.put(competitor, carriedPoints);
- if (!Util.equalsWithNull(oldCarriedPoints, carriedPoints)) {
- getScoreCorrection().notifyListenersAboutCarriedPointsChange(competitor, oldCarriedPoints, carriedPoints);
- }
- }
-
- @Override
- public double getCarriedPoints(Competitor competitor) {
- Double result = carriedPoints.get(competitor);
- return result == null ? 0 : result;
- }
-
- @Override
- public Map getCompetitorsForWhichThereAreCarriedPoints() {
- return Collections.unmodifiableMap(carriedPoints);
- }
-
- @Override
- public void unsetCarriedPoints(Competitor competitor) {
- Double oldCarriedPoints = carriedPoints.remove(competitor);
- if (oldCarriedPoints != null) {
- getScoreCorrection().notifyListenersAboutCarriedPointsChange(competitor, oldCarriedPoints, null);
- }
- }
-
- @Override
- public boolean hasCarriedPoints() {
- return !carriedPoints.isEmpty();
- }
-
- @Override
- public boolean hasCarriedPoints(Competitor competitor) {
- return carriedPoints.containsKey(competitor);
- }
-
- @Override
- public void setDisplayName(Competitor competitor, String displayName) {
- String oldDisplayName = displayNames.get(competitor);
- displayNames.put(competitor, displayName);
- if (!Util.equalsWithNull(oldDisplayName, displayName)) {
- getRaceColumnListeners().notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, displayName);
- }
- }
-
- @Override
- public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
- ResultDiscardingRule oldDiscardingRule = getResultDiscardingRule();
- this.crossLeaderboardResultDiscardingRule = discardingRule;
- getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, discardingRule);
- }
-
- @Override
- public Double getNetPoints(final Competitor competitor, final RaceColumn raceColumn, final TimePoint timePoint) {
- return getScoreCorrection().getCorrectedScore(
- new Callable() {
- public Integer call() throws NoWindException {
- return getTrackedRank(competitor, raceColumn, timePoint);
- }
- }, competitor,
- raceColumn, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()).getCorrectedScore();
- }
-
- @Override
- public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
- return getScoreCorrection().getMaxPointsReason(competitor, raceColumn, timePoint);
- }
-
- @Override
- public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
- return isDiscarded(competitor, raceColumn, getRaceColumns(), timePoint);
- }
-
- private boolean isDiscarded(Competitor competitor, RaceColumn raceColumn,
- Iterable raceColumnsToConsider, TimePoint timePoint) {
- final Set discardedRaceColumns = getResultDiscardingRule()
- .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint);
- return isDiscarded(competitor, raceColumn, timePoint, discardedRaceColumns);
- }
-
- /**
- * Same as {@link #isDiscarded(Competitor, RaceColumn, TimePoint)}, only that the set of discarded race columns can
- * be specified which is useful when total points are to be computed for more than one column for the same
- * competitor because then the calculation of discards (which requires looking at all columns) only needs to be done
- * once and not again for each column (which would lead to quadratic effort).
- *
- * @param discardedRaceColumns
- * expected to be the result of what we would get if we called {@link #getResultDiscardingRule()}.
- * {@link ResultDiscardingRule#getDiscardedRaceColumns(Competitor, Leaderboard, Iterable, TimePoint)
- * getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint)}.
- */
- private boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
- final Set discardedRaceColumns) {
- return !raceColumn.isMedalRace()
- && getMaxPointsReason(competitor, raceColumn, timePoint).isDiscardable()
- && discardedRaceColumns.contains(raceColumn);
- }
-
- @Override
- public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
- return getTotalPoints(competitor, raceColumn, getRaceColumns(), timePoint);
- }
-
- @Override
- public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn,
- Iterable raceColumnsToConsider, TimePoint timePoint) throws NoWindException {
- final Set discardedRaceColumns = getResultDiscardingRule()
- .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint);
- return getTotalPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
- }
-
- /**
- * Same as {@link #getTotalPoints(Competitor, RaceColumn, Iterable, TimePoint)}, only that the set of discarded race columns can
- * be specified which is useful when total points are to be computed for more than one column for the same
- * competitor because then the calculation of discards (which requires looking at all columns) only needs to be done
- * once and not again for each column (which would lead to quadratic effort).
- *
- * @param discardedRaceColumns
- * expected to be the result of what we would get if we called {@link #getResultDiscardingRule()}.
- * {@link ResultDiscardingRule#getDiscardedRaceColumns(Competitor, Leaderboard, Iterable, TimePoint)
- * getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint)}.
- */
- @Override
- public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns) {
- Double result;
- if (isDiscarded(competitor, raceColumn, timePoint, discardedRaceColumns)) {
- result = 0.0;
- } else {
- final Double netPoints = getNetPoints(competitor, raceColumn, timePoint);
- if (netPoints == null) {
- result = null;
- } else {
- result = raceColumn.getFactor() * netPoints;
- }
- }
- return result;
- }
-
- @Override
- public Double getTotalPoints(Competitor competitor, TimePoint timePoint) {
- return getTotalPoints(competitor, getRaceColumns(), timePoint);
- }
-
- @Override
- public Double getTotalPoints(Competitor competitor, final Iterable raceColumnsToConsider,
- TimePoint timePoint) {
- // when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race from there on
- boolean needToResetScoreUponNextNonEmptyEntry = false;
- double result = getCarriedPoints(competitor);
- final Set discardedRaceColumns = getResultDiscardingRule()
- .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint);
- for (RaceColumn raceColumn : raceColumnsToConsider) {
- if (raceColumn.isStartsWithZeroScore()) {
- needToResetScoreUponNextNonEmptyEntry = true;
- }
- if (getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint)) {
- final Double totalPoints = getTotalPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
- if (totalPoints != null) {
- if (needToResetScoreUponNextNonEmptyEntry) {
- result = 0;
- needToResetScoreUponNextNonEmptyEntry = false;
- }
- result += totalPoints;
- }
- }
- }
- return result;
- }
-
- /**
- * All competitors with non-null net points are added to the result which is then sorted by net points in ascending
- * order. The fleet, if ordered, is the primary ordering criterion, followed by the net points.
- */
- @Override
- public List getCompetitorsFromBestToWorst(final RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
- final Map> netPointsAndFleet = new HashMap>();
- for (Competitor competitor : getCompetitors()) {
- Double netPoints = getNetPoints(competitor, raceColumn, timePoint);
- if (netPoints != null) {
- netPointsAndFleet.put(competitor, new com.sap.sse.common.Util.Pair(netPoints, raceColumn.getFleetOfCompetitor(competitor)));
- }
- }
- List result = new ArrayList(netPointsAndFleet.keySet());
- Collections.sort(result, new Comparator() {
- @Override
- public int compare(Competitor o1, Competitor o2) {
- int comparisonResult;
- if (o1 == o2) {
- comparisonResult = 0;
- } else {
- if (raceColumn.hasSplitFleets() && !raceColumn.hasSplitFleetContiguousScoring()) {
- // only check fleets if there are more than one and the column is not to be contiguously scored even in case
- // of split fleets
- final Fleet o1Fleet = netPointsAndFleet.get(o1).getB();
- final Fleet o2Fleet = netPointsAndFleet.get(o2).getB();
- if (o1Fleet == null) {
- if (o2Fleet == null) {
- comparisonResult = 0;
- } else {
- comparisonResult = 1; // o1 ranks "worse" because it doesn't have a fleet set while o2
- // has
- }
- } else {
- if (o2Fleet == null) {
- comparisonResult = -1; // o1 ranks "better" because it has a fleet set while o2 hasn't
- } else {
- comparisonResult = o1Fleet.compareTo(o2Fleet);
- }
- }
- } else {
- // either there are no split fleets or the split isn't relevant for scoring as for ordered fleets
- // the scoring runs contiguously from top to bottom
- comparisonResult = 0;
- }
- if (comparisonResult == 0) {
- comparisonResult = getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
- netPointsAndFleet.get(o1).getA(), netPointsAndFleet.get(o2).getA());
- }
- }
- return comparisonResult;
- }
- });
- return result;
- }
-
- @Override
- public List getCompetitorsFromBestToWorst(TimePoint timePoint) {
- return getCompetitorsFromBestToWorst(getRaceColumns(), timePoint);
- }
-
- private List getCompetitorsFromBestToWorst(Iterable raceColumnsToConsider, TimePoint timePoint) {
- List result = new ArrayList();
- for (Competitor competitor : getCompetitors()) {
- result.add(competitor);
- }
- Collections.sort(result, getTotalRankComparator(raceColumnsToConsider, timePoint));
- return result;
- }
-
- @Override
- public int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException {
- List competitorsFromBestToWorst = getCompetitorsFromBestToWorst(timePoint);
- return competitorsFromBestToWorst.indexOf(competitor) + 1;
- }
-
- protected Comparator super Competitor> getTotalRankComparator(Iterable raceColumnsToConsider, TimePoint timePoint) {
- return new LeaderboardTotalRankComparator(this, timePoint, getScoringScheme(), /* nullScoresAreBetter */ false, raceColumnsToConsider);
- }
-
- @Override
- public RaceColumn getRaceColumnByName(String columnName) {
- RaceColumn result = null;
- for (RaceColumn r : getRaceColumns()) {
- if (r.getName().equals(columnName)) {
- result = r;
- break;
- }
- }
- return result;
- }
-
- @Override
- public Competitor getCompetitorByName(String competitorName) {
- for (Competitor competitor : getAllCompetitors()) {
- if (competitor.getName().equals(competitorName)) {
- return competitor;
- }
- }
- return null;
- }
-
- @Override
- public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
- TrackedRace trackedRaceForCompetitorInColumn;
- return getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint) &&
- (getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) ||
- ((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
- trackedRaceForCompetitorInColumn.hasStarted(timePoint) &&
- trackedRaceForCompetitorInColumn.getRank(competitor, timePoint) != 0));
- }
-
- @Override
- public void addRaceColumnListener(RaceColumnListener listener) {
- getRaceColumnListeners().addRaceColumnListener(listener);
- }
-
- @Override
- public void removeRaceColumnListener(RaceColumnListener listener) {
- getRaceColumnListeners().removeRaceColumnListener(listener);
- }
-
- @Override
- public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint) throws NoWindException {
- final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint);
- return getEntry(competitor, race, timePoint, discardedRaceColumns);
- }
-
- @Override
- public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint,
- Set discardedRaceColumns) throws NoWindException {
- Callable trackedRankProvider = new Callable() {
- public Integer call() throws NoWindException {
- return getTrackedRank(competitor, race, timePoint);
- }
- };
- final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, race,
- timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme());
- boolean discarded = isDiscarded(competitor, race, timePoint, discardedRaceColumns);
- final Double correctedScore = correctedResults.getCorrectedScore();
- return new EntryImpl(trackedRankProvider, correctedScore, new Callable() {
- @Override
- public Double call() {
- return correctedResults.getUncorrectedScore();
- }
- }, correctedResults.isCorrected(), discarded ? DOUBLE_0 : correctedScore == null ? null
- : Double.valueOf(correctedScore * race.getFactor()), correctedResults.getMaxPointsReason(), discarded,
- race.getFleetOfCompetitor(competitor));
- }
-
- @Override
- public Map> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) throws NoWindException {
- Map> result = new LinkedHashMap<>();
- List raceColumnsToConsider = new ArrayList<>();
- for (RaceColumn raceColumn : getRaceColumns()) {
- raceColumnsToConsider.add(raceColumn);
- result.put(raceColumn, getCompetitorsFromBestToWorst(raceColumnsToConsider, timePoint));
- }
- return result;
- }
-
- @Override
- public Map> getTotalPointsSumAfterRaceColumn(final TimePoint timePoint)
- throws NoWindException {
- final Map> result = new LinkedHashMap<>();
- List raceColumnsToConsider = new ArrayList<>();
- Map>> futures = new HashMap<>();
- for (final RaceColumn raceColumn : getRaceColumns()) {
- raceColumnsToConsider.add(raceColumn);
- final Iterable finalRaceColumnsToConsider = new ArrayList<>(raceColumnsToConsider);
- futures.put(raceColumn, executor.submit(new Callable