Merge branch 'master' into bug2970

This commit is contained in:
Axel Uhl committed 2015-06-18 10:47:35 +02:00
commit 2cc61f15dd
5 files changed
+133 -125

No files matched your search

@@ -87,62 +87,66 @@ public class MarkPassingCalculator {
private class Listen implements Runnable {
@Override
public void run() {
logger.fine("MarkPassingCalculator is listening");
boolean finished = false;
Map<Competitor, List<GPSFix>> competitorFixes = new HashMap<>();
Map<Mark, List<GPSFix>> markFixes = new HashMap<>();
List<Waypoint> addedWaypoints = new ArrayList<>();
List<Waypoint> removedWaypoints = new ArrayList<>();
Integer smallestChangedWaypointIndex = null;
List<Triple<Competitor, Integer, TimePoint>> fixedMarkPassings = new ArrayList<>();
List<Pair<Competitor, Integer>> removedFixedMarkPassings = new ArrayList<>();
List<Pair<Competitor, Integer>> suppressedMarkPassings = new ArrayList<>();
List<Competitor> unsuppressedMarkPassings = new ArrayList<>();
while (!finished) {
logger.finer("MPC is checking the queue");
List<StorePositionUpdateStrategy> allNewFixInsertions = new ArrayList<>();
try {
allNewFixInsertions.add(listener.getQueue().take());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "MarkPassingCalculator threw exception " + e.getMessage()
+ " while waiting for new GPSFixes");
}
listener.getQueue().drainTo(allNewFixInsertions);
logger.finer("MPC recieved "+ allNewFixInsertions.size()+" new updates.");
for (StorePositionUpdateStrategy fixInsertion : allNewFixInsertions) {
if (listener.isEndMarker(fixInsertion)) {
logger.info("Stopping "+MarkPassingCalculator.this+"'s listener");
finished = true;
} else {
fixInsertion.storePositionUpdate(competitorFixes, markFixes, addedWaypoints, removedWaypoints,
smallestChangedWaypointIndex, fixedMarkPassings, removedFixedMarkPassings,
suppressedMarkPassings, unsuppressedMarkPassings);
try {
logger.fine("MarkPassingCalculator is listening");
boolean finished = false;
Map<Competitor, List<GPSFix>> competitorFixes = new HashMap<>();
Map<Mark, List<GPSFix>> markFixes = new HashMap<>();
List<Waypoint> addedWaypoints = new ArrayList<>();
List<Waypoint> removedWaypoints = new ArrayList<>();
Integer smallestChangedWaypointIndex = null;
List<Triple<Competitor, Integer, TimePoint>> fixedMarkPassings = new ArrayList<>();
List<Pair<Competitor, Integer>> removedFixedMarkPassings = new ArrayList<>();
List<Pair<Competitor, Integer>> suppressedMarkPassings = new ArrayList<>();
List<Competitor> unsuppressedMarkPassings = new ArrayList<>();
while (!finished) {
logger.finer("MPC is checking the queue");
List<StorePositionUpdateStrategy> allNewFixInsertions = new ArrayList<>();
try {
allNewFixInsertions.add(listener.getQueue().take());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "MarkPassingCalculator threw exception " + e.getMessage()
+ " while waiting for new GPSFixes");
}
}
if (!suspended) {
if (smallestChangedWaypointIndex != null) {
Map<Competitor, Util.Pair<List<Candidate>, List<Candidate>>> candidateDeltas = finder
.updateWaypoints(addedWaypoints, removedWaypoints, smallestChangedWaypointIndex);
chooser.removeWaypoints(removedWaypoints);
chooser.addWaypoints(addedWaypoints);
for (Entry<Competitor, Util.Pair<List<Candidate>, List<Candidate>>> entry : candidateDeltas
.entrySet()) {
Util.Pair<List<Candidate>, List<Candidate>> pair = entry.getValue();
chooser.calculateMarkPassDeltas(entry.getKey(), pair.getA(), pair.getB());
listener.getQueue().drainTo(allNewFixInsertions);
logger.finer("MPC recieved "+ allNewFixInsertions.size()+" new updates.");
for (StorePositionUpdateStrategy fixInsertion : allNewFixInsertions) {
if (listener.isEndMarker(fixInsertion)) {
logger.info("Stopping "+MarkPassingCalculator.this+"'s listener");
finished = true;
} else {
fixInsertion.storePositionUpdate(competitorFixes, markFixes, addedWaypoints, removedWaypoints,
smallestChangedWaypointIndex, fixedMarkPassings, removedFixedMarkPassings,
suppressedMarkPassings, unsuppressedMarkPassings);
}
}
updateManuallySetMarkPassings(fixedMarkPassings, removedFixedMarkPassings, suppressedMarkPassings,
unsuppressedMarkPassings);
computeMarkPasses(competitorFixes, markFixes);
competitorFixes.clear();
markFixes.clear();
addedWaypoints.clear();
removedWaypoints.clear();
fixedMarkPassings.clear();
removedFixedMarkPassings.clear();
suppressedMarkPassings.clear();
unsuppressedMarkPassings.clear();
if (!suspended) {
if (smallestChangedWaypointIndex != null) {
Map<Competitor, Util.Pair<List<Candidate>, List<Candidate>>> candidateDeltas = finder
.updateWaypoints(addedWaypoints, removedWaypoints, smallestChangedWaypointIndex);
chooser.removeWaypoints(removedWaypoints);
chooser.addWaypoints(addedWaypoints);
for (Entry<Competitor, Util.Pair<List<Candidate>, List<Candidate>>> entry : candidateDeltas
.entrySet()) {
Util.Pair<List<Candidate>, List<Candidate>> pair = entry.getValue();
chooser.calculateMarkPassDeltas(entry.getKey(), pair.getA(), pair.getB());
}
}
updateManuallySetMarkPassings(fixedMarkPassings, removedFixedMarkPassings, suppressedMarkPassings,
unsuppressedMarkPassings);
computeMarkPasses(competitorFixes, markFixes);
competitorFixes.clear();
markFixes.clear();
addedWaypoints.clear();
removedWaypoints.clear();
fixedMarkPassings.clear();
removedFixedMarkPassings.clear();
suppressedMarkPassings.clear();
unsuppressedMarkPassings.clear();
}
}
} finally {
logger.info("MarkPassingCalculator Listen thread terminating");
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.markpassingcalculation.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -76,7 +77,7 @@ public class CandidateChooserImpl implements CandidateChooser {
candidates = new HashMap<>();
List<Candidate> startAndEnd = Arrays.asList(start, end);
for (Competitor c : race.getRace().getCompetitors()) {
candidates.put(c, new TreeSet<Candidate>());
candidates.put(c, Collections.synchronizedSet(new TreeSet<Candidate>()));
currentMarkPasses.put(c, new HashMap<Waypoint, MarkPassing>());
TreeSet<Candidate> fixedPasses = new TreeSet<Candidate>(new Comparator<Candidate>() {
@Override
@@ -172,69 +173,72 @@ public class CandidateChooserImpl implements CandidateChooser {
final Boolean isGateStart = race.isGateStart();
Map<Candidate, Set<Edge>> edges = allEdges.get(c);
for (Candidate newCan : newCandidates) {
for (Candidate oldCan : candidates.get(c)) {
final Candidate early;
final Candidate late;
if (oldCan.getOneBasedIndexOfWaypoint() < newCan.getOneBasedIndexOfWaypoint()) {
early = oldCan;
late = newCan;
} else if (oldCan.getOneBasedIndexOfWaypoint() > newCan.getOneBasedIndexOfWaypoint()) {
late = oldCan;
early = newCan;
} else {
continue; // don't create edge from/to same waypoint
}
final double estimatedDistanceProbability;
final double startTimingProbability;
if (early == start) {
// An edge starting at the start proxy node. If the late candidate is for a start mark passing,
// determine a probability not based on distance traveled but based on the
// time difference between scheduled start time and candidate's time point. If the "late" candidate
// is not for the start mark/line, meaning that mark passings including the actual start are
// skipped, as usual use getDistanceEstimationBasedProbability assuming a start mark passing at
// the race's start time.
if (isGateStart == Boolean.TRUE || start.getTimePoint() == null) { // TODO for gate start read gate timing and scale probability accordingly
startTimingProbability = 1; // no start time point known; all candidate time points equally likely
estimatedDistanceProbability = 1; // can't tell distance sailed either because we don't know the start time
final Set<Candidate> competitorCandidates = candidates.get(c);
synchronized (competitorCandidates) {
for (Candidate oldCan : competitorCandidates) {
final Candidate early;
final Candidate late;
if (oldCan.getOneBasedIndexOfWaypoint() < newCan.getOneBasedIndexOfWaypoint()) {
early = oldCan;
late = newCan;
} else if (oldCan.getOneBasedIndexOfWaypoint() > newCan.getOneBasedIndexOfWaypoint()) {
late = oldCan;
early = newCan;
} else {
// no gate start and we know the race start time
if (late.getWaypoint() == race.getRace().getCourse().getFirstWaypoint()) {
// no skips; going from the start proxy node to a candidate for the start mark passing;
// calculate the probability for the start being the start given its timing and multiply
// with the estimation for the distance-based probability:
final Duration timeGapBetweenStartOfRaceAndCandidateTimePoint = early.getTimePoint()
.plus(MILLISECONDS_BEFORE_STARTTIME).until(late.getTimePoint());
// Being MILLISECONDS_BEFORE_STARTTIME off means a probability of 1/2; being twice this time
// off means 1/3, and so on
startTimingProbability = (double) MILLISECONDS_BEFORE_STARTTIME
/ (double) (MILLISECONDS_BEFORE_STARTTIME + Math
.abs(timeGapBetweenStartOfRaceAndCandidateTimePoint.asMillis()));
continue; // don't create edge from/to same waypoint
}
final double estimatedDistanceProbability;
final double startTimingProbability;
if (early == start) {
// An edge starting at the start proxy node. If the late candidate is for a start mark passing,
// determine a probability not based on distance traveled but based on the
// time difference between scheduled start time and candidate's time point. If the "late" candidate
// is not for the start mark/line, meaning that mark passings including the actual start are
// skipped, as usual use getDistanceEstimationBasedProbability assuming a start mark passing at
// the race's start time.
if (isGateStart == Boolean.TRUE || start.getTimePoint() == null) { // TODO for gate start read gate timing and scale probability accordingly
startTimingProbability = 1; // no start time point known; all candidate time points equally likely
estimatedDistanceProbability = 1; // can't tell distance sailed either because we don't know the start time
} else {
// no gate start and we know the race start time
if (late.getWaypoint() == race.getRace().getCourse().getFirstWaypoint()) {
// no skips; going from the start proxy node to a candidate for the start mark passing;
// calculate the probability for the start being the start given its timing and multiply
// with the estimation for the distance-based probability:
final Duration timeGapBetweenStartOfRaceAndCandidateTimePoint = early.getTimePoint()
.plus(MILLISECONDS_BEFORE_STARTTIME).until(late.getTimePoint());
// Being MILLISECONDS_BEFORE_STARTTIME off means a probability of 1/2; being twice this time
// off means 1/3, and so on
startTimingProbability = (double) MILLISECONDS_BEFORE_STARTTIME
/ (double) (MILLISECONDS_BEFORE_STARTTIME + Math
.abs(timeGapBetweenStartOfRaceAndCandidateTimePoint.asMillis()));
estimatedDistanceProbability = 1;
} else {
startTimingProbability = 0.1; // can't really tell how well the start time was matched when
// we don't have a start candidate
estimatedDistanceProbability = late == end ? 1 : getDistanceEstimationBasedProbability(c, early, late);
}
}
} else {
startTimingProbability = 1; // no penalty for any start time difference because this edge doesn't cover a start
if (late == end) {
// final edge; we don't know anything about distances for the end proxy node
estimatedDistanceProbability = 1;
} else {
startTimingProbability = 0.1; // can't really tell how well the start time was matched when
// we don't have a start candidate
estimatedDistanceProbability = late == end ? 1 : getDistanceEstimationBasedProbability(c, early, late);
estimatedDistanceProbability = getDistanceEstimationBasedProbability(c, early, late);
}
}
} else {
startTimingProbability = 1; // no penalty for any start time difference because this edge doesn't cover a start
if (late == end) {
// final edge; we don't know anything about distances for the end proxy node
estimatedDistanceProbability = 1;
} else {
estimatedDistanceProbability = getDistanceEstimationBasedProbability(c, early, late);
// If one of the candidates is fixed, the edge is always created unless they travel backwards in time.
// Otherwise the edge is only created if the distance estimation, which can be calculated as long as the
// candidates are not the proxy and or start is close enough to the actual distance sailed.
final NavigableSet<Candidate> fixed = fixedPassings.get(c);
// TODO this comparison does not exactly implement the condition "if distance is more likely than skipping"
if (travelingForwardInTimeOrUnknown(early, late) &&
(fixed.contains(early) || fixed.contains(late) || estimatedDistanceProbability > MINIMUM_PROBABILITY)) {
addEdge(edges, new Edge(early, late, startTimingProbability * estimatedDistanceProbability, race.getRace().getCourse().getNumberOfWaypoints()));
}
}
// If one of the candidates is fixed, the edge is always created unless they travel backwards in time.
// Otherwise the edge is only created if the distance estimation, which can be calculated as long as the
// candidates are not the proxy and or start is close enough to the actual distance sailed.
final NavigableSet<Candidate> fixed = fixedPassings.get(c);
// TODO this comparison does not exactly implement the condition "if distance is more likely than skipping"
if (travelingForwardInTimeOrUnknown(early, late) &&
(fixed.contains(early) || fixed.contains(late) || estimatedDistanceProbability > MINIMUM_PROBABILITY)) {
addEdge(edges, new Edge(early, late, startTimingProbability * estimatedDistanceProbability, race.getRace().getCourse().getNumberOfWaypoints()));
}
}
}
}
@@ -306,13 +310,15 @@ public class CandidateChooserImpl implements CandidateChooser {
// currentCheapestEdge.getEnd(), summing up their cost with the cost required
// to reach currentCheapestEdge.getEnd()
Set<Edge> edgesForNewCandidate = allCompetitorEdges.get(currentCheapestEdge.getEnd());
for (Edge e : edgesForNewCandidate) {
int oneBasedIndexOfEndOfEdge = e.getEnd().getOneBasedIndexOfWaypoint();
// only add edge if it stays within the current segment, not exceeding
// the next fixed mark passing
if (oneBasedIndexOfEndOfEdge <= indexOfEndOfFixedInterval
&& (oneBasedIndexOfEndOfEdge < oneBasedIndexOfSuppressedWaypoint || e.getEnd() == end)) {
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(e, currentCheapestCost + e.getCost()));
if (edgesForNewCandidate != null) {
for (Edge e : edgesForNewCandidate) {
int oneBasedIndexOfEndOfEdge = e.getEnd().getOneBasedIndexOfWaypoint();
// only add edge if it stays within the current segment, not exceeding
// the next fixed mark passing
if (oneBasedIndexOfEndOfEdge <= indexOfEndOfFixedInterval
&& (oneBasedIndexOfEndOfEdge < oneBasedIndexOfSuppressedWaypoint || e.getEnd() == end)) {
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(e, currentCheapestCost + e.getCost()));
}
}
}
}
@@ -2935,12 +2935,6 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
} else if (oldStatus == TrackedRaceStatusEnum.LOADING && newStatus.getStatus() != TrackedRaceStatusEnum.LOADING) {
resumeAllCachesNotUpdatingWhileLoading();
}
if (newStatus.getStatus() == TrackedRaceStatusEnum.FINISHED) {
// no more new data can be expected; stop mark passing calculator if one is being used
if (isUsingMarkPassingCalculator()) {
markPassingCalculator.stop();
}
}
}
private void suspendAllCachesNotUpdatingWhileLoading() {
@@ -73,6 +73,10 @@ public class BoatClassImageResolver {
}
public static ImageResource getBoatClassIconResource(String displayName) {
return boatClassIconsMap.get(displayName);
ImageResource imageResource = boatClassIconsMap.get(displayName);
if(imageResource == null) {
imageResource = BoatClassImageResources.INSTANCE.genericBoatClass();
}
return imageResource;
}
}
@@ -106,8 +106,8 @@ public class OldLeaderboard extends Composite {
lastScoringUpdateTimeDiv.setInnerText(lastUpdate);
lastScoringUpdateTextDiv.setInnerText(TextMessages.INSTANCE.eventRegattaLeaderboardLastScoreUpdate());
} else {
lastScoringUpdateTimeDiv.setInnerText("&nbsp;");
lastScoringUpdateTextDiv.setInnerText("&nbsp;");
lastScoringUpdateTimeDiv.setInnerHTML("&nbsp;");
lastScoringUpdateTextDiv.setInnerHTML("&nbsp;");
}
setVisible(hasLiveRaceDiv, hasLiveRace);