mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-09 22:01:02 +00:00
Merge branch 'master' into bug2970
This commit is contained in:
commit
2cc61f15dd
5 files changed
+133
-125
No files matched your search
+56
-52
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+70
-64
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -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() {
|
||||
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -106,8 +106,8 @@ public class OldLeaderboard extends Composite {
|
||||
lastScoringUpdateTimeDiv.setInnerText(lastUpdate);
|
||||
lastScoringUpdateTextDiv.setInnerText(TextMessages.INSTANCE.eventRegattaLeaderboardLastScoreUpdate());
|
||||
} else {
|
||||
lastScoringUpdateTimeDiv.setInnerText(" ");
|
||||
lastScoringUpdateTextDiv.setInnerText(" ");
|
||||
lastScoringUpdateTimeDiv.setInnerHTML(" ");
|
||||
lastScoringUpdateTextDiv.setInnerHTML(" ");
|
||||
}
|
||||
setVisible(hasLiveRaceDiv, hasLiveRace);
|
||||
|
||||
|
||||
Reference in new issue
Block a user