Merge branch 'design2015' into design2015-races

This commit is contained in:
Steffen Schaefer committed 2015-07-16 11:34:14 +02:00
commit d865123cea
5 files changed
+122 -28

No files matched your search

@@ -1,7 +1,10 @@
package com.sap.sailing.domain.base.impl;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Set;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
@@ -13,6 +16,7 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition {
private static final long serialVersionUID = -1900955198751393727L;
private final Course course;
private final LinkedHashMap<Serializable, Competitor> competitorsById;
private final Set<Competitor> competitors;
private final BoatClass boatClass;
private final Serializable id;
@@ -23,11 +27,12 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition {
public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable<? extends Competitor> competitors, Serializable id) {
super(name);
assert name != null;
this.course = course;
this.competitors = new HashSet<>();
this.competitorsById = new LinkedHashMap<>();
for (Competitor competitor : competitors) {
Competitor competitorWithEqualID = competitorsById.put(competitor.getId(), competitor);
this.competitors.add(competitor);
if (competitorWithEqualID != null && competitorWithEqualID != competitor) {
throw new IllegalArgumentException("Two distinct competitors with equal ID "+competitor.getId()+" are not allowed within the single race "+name);
}
@@ -48,7 +53,7 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition {
@Override
public Iterable<Competitor> getCompetitors() {
return competitorsById.values();
return Collections.unmodifiableSet(competitors);
}
@Override
@@ -99,6 +99,14 @@ public interface ScoreCorrection extends Serializable {
*/
boolean hasCorrectionFor(RaceColumn raceInLeaderboard);
/**
* Similar to {@link #hasCorrectionFor(RaceColumn)}, but returns <code>true</code> only if score corrections are found
* for any competitor who in <code>raceInLeaderboard</code> is not in a tracked race and hence the fleet assignment
* cannot be determined. This is helpful, e.g., for progress detection. If score corrections are present for such
* untracked competitors then all untracked fleets need to be assumed as finished.
*/
boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard);
/**
* @return all race columns for which this score corrections object has at least one correction; note that this
* object may hold corrections also for competitors that are not currently returned by
@@ -472,19 +472,30 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
@Override
public boolean hasCorrectionFor(RaceColumn raceInLeaderboard) {
return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ false);
}
private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard, boolean considerOnlyUntrackedRaces) {
for (com.sap.sse.common.Util.Pair<Competitor, RaceColumn> correctedScoresKey : correctedScores.keySet()) {
if (correctedScoresKey.getB() == raceInLeaderboard) {
if (correctedScoresKey.getB() == raceInLeaderboard &&
(!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(correctedScoresKey.getA()) == null)) {
return true;
}
}
for (com.sap.sse.common.Util.Pair<Competitor, RaceColumn> maxPointsReasonsKey : maxPointsReasons.keySet()) {
if (maxPointsReasonsKey.getB() == raceInLeaderboard) {
if (maxPointsReasonsKey.getB() == raceInLeaderboard &&
(!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(maxPointsReasonsKey.getA()) == null)) {
return true;
}
}
return false;
}
@Override
public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard) {
return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ true);
}
@Override
public TimePoint getTimePointOfLastCorrectionsValidity() {
return timePointOfLastCorrectionsValidity;
@@ -41,7 +41,7 @@ import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ScoreCorrection;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
@@ -517,8 +517,8 @@ public class RaceContext {
}
}
}
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if(!hasTrackingForRaceColumn() && scoreCorrection != null && scoreCorrection.hasCorrectionFor(raceColumn)) {
ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if(trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) {
return RaceViewState.FINISHED;
}
if(startTime != null) {
@@ -527,15 +527,6 @@ public class RaceContext {
return RaceViewState.PLANNED;
}
private boolean hasTrackingForRaceColumn() {
for(Fleet fleetOfRaceColumn : raceColumn.getFleets()) {
if(raceColumn.getTrackedRace(fleetOfRaceColumn) != null) {
return true;
}
}
return false;
}
private Wind checkForWindFixesFromRaceLog() {
WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog);
List<Wind> windList = windFixesFinder.analyze();
@@ -34,8 +34,11 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
@@ -46,6 +49,8 @@ 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.WindPositionMode;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -97,7 +102,7 @@ public class LeaderboardData extends ExportAction {
* </competitor>
* </pre>
*/
private Element createLeaderboardXML(Leaderboard leaderboard, List<Element> competitors, List<Element> races, Util.Pair<Double, Vector<String>> leaderboardConfidenceAndErrorMessages) {
private Element createLeaderboardXML(Leaderboard leaderboard, List<Element> windData, List<Element> competitors, List<Element> races, Util.Pair<Double, Vector<String>> leaderboardConfidenceAndErrorMessages) {
Element leaderboardElement = new Element("leaderboard");
addNamedElementWithValue(leaderboardElement, "name", leaderboard.getName());
addNamedElementWithValue(leaderboardElement, "display_name", leaderboard.getDisplayName());
@@ -106,6 +111,9 @@ public class LeaderboardData extends ExportAction {
addNamedElementWithValue(leaderboardElement, "boat_class", getBoatClassName(leaderboard));
leaderboardElement.addContent(createTimedXML("last_modification_", leaderboard.getTimePointOfLatestModification()));
leaderboardElement.addContent(createDataConfidenceXML(leaderboardConfidenceAndErrorMessages));
Element windContainer = new Element("wind");
windContainer.addContent(windData);
leaderboardElement.addContent(windContainer);
leaderboardElement.addContent(competitors);
leaderboardElement.addContent(races);
return leaderboardElement;
@@ -202,6 +210,35 @@ public class LeaderboardData extends ExportAction {
return windElements;
}
private Element createRaceXMLWithoutRaceData(final Fleet fleet, final RaceColumn column, final Leaderboard leaderboard, int sameDayGroupIndex, int raceCounter, Util.Pair<Double, Vector<String>> raceConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException {
Element raceElement = new Element("race");
addNamedElementWithValue(raceElement, "name", column.getName()+'-'+fleet.getName());
addNamedElementWithValue(raceElement, "race_index_in_leaderboard", raceCounter);
addNamedElementWithValue(raceElement, "fleet_name", fleet.getName());
addNamedElementWithValue(raceElement, "same_day_index", sameDayGroupIndex);
addNamedElementWithValue(raceElement, "is_live", "false");
TimePoint timepointToBeUsed = MillisecondsTimePoint.now();
List<Competitor> competitors = leaderboard.getCompetitorsFromBestToWorst(column, timepointToBeUsed);
for (Competitor competitorInLeaderboard : competitors) {
MaxPointsReason mpr = leaderboard.getScoreCorrection().getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed);
if (mpr != null && !mpr.equals(MaxPointsReason.NONE)) {
// add this competitor to the list to have him evaluated
Element competitorElement = createCompetitorXML(competitorInLeaderboard, leaderboard, /*shortVersion*/ true, null);
Element competitorRaceDataElement = new Element("competitor_race_data");
MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed);
addNamedElementWithValue(competitorRaceDataElement, "max_points_reason", maxPointsReason.toString());
boolean isDiscardedForCompetitor = leaderboard.isDiscarded(competitorInLeaderboard, column, timepointToBeUsed);
addNamedElementWithValue(competitorRaceDataElement, "is_discarded", isDiscardedForCompetitor == true ? "true" : "false");
Double finalRaceScore = leaderboard.getTotalPoints(competitorInLeaderboard, column, timepointToBeUsed);
addNamedElementWithValue(competitorRaceDataElement, "final_race_score", finalRaceScore);
competitorElement.addContent(competitorRaceDataElement);
raceElement.addContent(competitorElement);
raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitorInLeaderboard.getName() + " has no valid data for this race!", 0.2, raceConfidenceAndErrorMessages);
}
}
return raceElement;
}
/**
* Creates elements containing information about a race
*/
@@ -233,7 +270,7 @@ public class LeaderboardData extends ExportAction {
addNamedElementWithValue(raceElement, "start_of_tracking_time_as_millis", handleValue(race.getStartOfTracking()));
addNamedElementWithValue(raceElement, "end_of_tracking_time_as_millis", handleValue(race.getEndOfTracking()));
LineDetails start = race.getStartLine(race.getStartOfTracking());
LineDetails start = race.getStartLine(race.getStartOfRace());
if (start != null) {
if (start.getLength() != null) {
addNamedElementWithValue(raceElement, "start_line_length_in_meters", start.getLength().getMeters());
@@ -568,13 +605,16 @@ public class LeaderboardData extends ExportAction {
// will kick in
}
}
if (endOfRaceForLastTrackedRaceInLeaderboard != null) {
timePointOfLatestModification = endOfRaceForLastTrackedRaceInLeaderboard;
} else {
// first look for latest modification of leaderboard
// if there is no latest modification then take the last races time
if (leaderboard.getTimePointOfLatestModification() != null) {
timePointOfLatestModification = leaderboard.getTimePointOfLatestModification();
} else {
timePointOfLatestModification = MillisecondsTimePoint.now();
if (endOfRaceForLastTrackedRaceInLeaderboard != null) {
timePointOfLatestModification = endOfRaceForLastTrackedRaceInLeaderboard;
} else {
// make sure that the timepoint is far in the future - could be that the export is called too early
timePointOfLatestModification = MillisecondsTimePoint.now().plus(Duration.ONE_YEAR);
}
}
Duration totalTimeSailed = leaderboard.getTotalTimeSailed(competitor, timePointOfLatestModification);
@@ -708,6 +748,36 @@ public class LeaderboardData extends ExportAction {
return legElement;
}
private List<Element> createFullWindDataXML(TrackedRace race, Fleet fleet, int sameDayIndex) {
List<Element> windElements = new ArrayList<>();
List<WindSource> windSourcesToDeliver = new ArrayList<WindSource>();
WindSourceImpl windSource = new WindSourceImpl(WindSourceType.COMBINED);
windSourcesToDeliver.add(windSource);
TimePoint fromTimePoint = race.getStartOfRace();
TimePoint toTimePoint = race.getEndOfRace();
int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis()) / /*resolutionInMillis*/ 1000*60*5);
WindTrack windTrack = race.getOrCreateWindTrack(windSource);
TimePoint timePoint = fromTimePoint;
for (int i = 0; i < numberOfFixes && toTimePoint != null && timePoint.compareTo(toTimePoint) < 0; i++) {
WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> averagedWindWithConfidence = windTrack
.getAveragedWindWithConfidence(null, timePoint);
if (averagedWindWithConfidence != null) {
Element windElement = new Element("windFix");
addNamedElementWithValue(windElement, "same_day_index", sameDayIndex);
addNamedElementWithValue(windElement, "race_name", race.getRace().getName());
addNamedElementWithValue(windElement, "fleet_name", fleet.getName());
windElement.addContent(createTimedXML("", averagedWindWithConfidence.getObject().getTimePoint()));
addNamedElementWithValue(windElement, "bearingInDegrees", averagedWindWithConfidence.getObject().getBearing().getDegrees());
addNamedElementWithValue(windElement, "directionFromInDegrees", averagedWindWithConfidence.getObject().getBearing().reverse().getDegrees());
windElement.addContent(createWindXML("", new SpeedWithConfidenceImpl<TimePoint>(new KnotSpeedImpl(averagedWindWithConfidence.getObject().getKnots()),
averagedWindWithConfidence.getConfidence(), toTimePoint)));
windElements.add(windElement);
}
timePoint = new MillisecondsTimePoint(timePoint.asMillis() + 1000*60*5);
}
return windElements;
}
/**
* Creates xml elements that indicate confidence.
*/
@@ -784,6 +854,10 @@ public class LeaderboardData extends ExportAction {
private Util.Pair<Double, Vector<String>> checkData(TrackedRace race) throws Exception {
double simpleConfidence = 1.0; Vector<String> messages = new Vector<String>();
TimePoint dateAtYear2000 = new MillisecondsTimePoint(946681200000l);
if (race == null) {
messages.add("Tracked race is null - no race seems to be connected!");
simpleConfidence -= 1;
} else {
if (race.getStartOfRace() == null || race.getStartOfRace().before(dateAtYear2000)) {
messages.add("Start time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!");
simpleConfidence -= 1;
@@ -803,6 +877,7 @@ public class LeaderboardData extends ExportAction {
if (race.isLive(MillisecondsTimePoint.now())) {
messages.add("This race is live - data for this race will not be available until the race has been finished!");
}
}
return new Util.Pair<Double, Vector<String>>(simpleConfidence, messages);
}
@@ -886,24 +961,25 @@ public class LeaderboardData extends ExportAction {
TimePoint timeSpent = MillisecondsTimePoint.now();
log.info("Starting XML export of " + leaderboard.getName());
Util.Pair<Double, Vector<String>> leaderboardConfidenceAndErrorMessages = checkData(leaderboard);
final List<Element> racesElements = new ArrayList<Element>();
final List<Element> competitorElements = new ArrayList<Element>();
for (Competitor competitor : leaderboard.getAllCompetitors()) {
Util.Pair<Double, Vector<String>> competitorConfidenceAndErrorMessages = checkData(competitor);
competitorElements.add(createCompetitorXML(competitor, leaderboard, /*shortVersion*/ false, competitorConfidenceAndErrorMessages));
}
List<Element> windData = new ArrayList<Element>();
TrackedRace raceBefore = null; int sameDayGroupIndex = 0; int raceCounter = 0;
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null && trackedRace.hasGPSData()) {
if (trackedRace != null) {
sameDayGroupIndex += getSameDayGroupIndex(raceColumn.getTrackedRace(fleet), raceBefore);
}
TimePoint timeSpentForRace = MillisecondsTimePoint.now();
Util.Pair<Double, Vector<String>> raceConfidenceAndErrorMessages = checkData(trackedRace);
final List<Element> legs = new ArrayList<Element>();
if (trackedRace != null && trackedRace.hasGPSData()) {
windData.addAll(createFullWindDataXML(trackedRace, fleet, sameDayGroupIndex));
int legCounter = 0;
for (TrackedLeg leg : trackedRace.getTrackedLegs()) {
Util.Pair<Double, Vector<String>> legConfidenceAndErrorMessages = checkData(leg);
@@ -914,12 +990,15 @@ public class LeaderboardData extends ExportAction {
addNamedElementWithValue(raceElement, "generation_time_in_milliseconds", elapsedTimeForRace.asMillis());
racesElements.add(raceElement);
log.info("Exported complete race " + trackedRace.getRace().getName() + " in " + elapsedTimeForRace.asMillis() + " milliseconds!");
} else {
racesElements.add(createRaceXMLWithoutRaceData(fleet, raceColumn, leaderboard, sameDayGroupIndex, ++raceCounter, raceConfidenceAndErrorMessages));
log.info("Exported empty race in race column " + raceColumn.getName() + " with fleet " + fleet.getName());
}
raceBefore = trackedRace;
}
}
}
log.info("Finished XML export of leaderboard " + leaderboard.getName() + " in " + MillisecondsTimePoint.now().minus(timeSpent.asMillis()).asMillis() + " milliseconds");
Element dataToExport = createLeaderboardXML(leaderboard, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages);
Element dataToExport = createLeaderboardXML(leaderboard, windData, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages);
sendDocument(dataToExport, leaderboard.getName() + ".xml");
}
}