mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-02 01:00:46 +00:00
bug6241: two-round retype-and-store to actually see estimator wind fixes
The previous single-round retypeAndStoreAfterWindEstimationSettled was
incorrect: it called waitUntilDone on the estimator before any spots
had been re-emitted (the compute-path's initial detection ran with a
null WindEstimationInteraction because the estimator hadn't been
installed yet, so no spots were pushed). waitUntilDone then returned
immediately on an empty queue, and the subsequent recalculate emitted
spots but returned before the estimator could process them and produce
wind fixes. Net effect: stored maneuvers were still typed with null
wind (mostly UNKNOWN), reproducing the poisoned-DB pattern the fix was
supposed to prevent.
Split into two rounds:
1. First recalculate + get per competitor. Since setWindEstimation
cleared maneuverDetectorPerCompetitorCache, this builds a fresh
detector with a non-null WindEstimationInteraction, runs a full
scan, and *does* emit spots to the estimator during this pass.
2. waitUntilDone -- now the estimator's queue has the spots from
round 1 and this actually waits for the graph inference to
produce wind fixes and flow them into the tracked race's
MANEUVER_BASED_ESTIMATION track.
3. Second recalculate + get per competitor. The detector's
lastManeuverDetectionResult is now populated and no new raw fixes
have arrived, so it takes the re-type branch of detectManeuverSpots
-- which routes through determineManeuversFromManeuverCurve and
types the spots using trackedRace.getWind(...), which now sees the
estimator's fixes (bug6274 removed the MANEUVER_BASED_ESTIMATION
exclusion). The isManeuverSpotWindNearlySame check correctly
detects that the previous typing had null wind and the current
has non-null, so it doesn't short-circuit to the UNKNOWN spot.
4. Snapshot the classified maneuvers and store.
For W1 in 49erFX (5 competitors), pre-fix Mongo had 1 competitor with
362 typed maneuvers (whose detection happened to run after
setWindEstimation) and 1 with 628 all-UNKNOWN (whose detection ran
before setWindEstimation). Post-fix, both should be typed.
This commit is contained in:
1 parent
8ce464986b
commit
610df7c5a4
1 file changed
+34
-5
+34
-5
@@ -176,23 +176,52 @@ public class ManeuverCacheDelegate implements SerializableManeuverCache {
|
||||
}
|
||||
|
||||
/**
|
||||
* Second phase of {@link #computeAndStore()}: waits for the wind estimator to drain, triggers
|
||||
* per-competitor recalculation so maneuvers get re-typed with the estimator's wind fixes,
|
||||
* then snapshots and persists. Invoked from
|
||||
* {@link com.sap.sailing.domain.tracking.TrackedRace#runWhenWindEstimationInstalled(Runnable)}.
|
||||
* Second phase of {@link #computeAndStore()}: sequences maneuver-cache re-typing so that the
|
||||
* stored maneuvers reflect the wind fixes the estimator produces from the spots emitted by
|
||||
* the detector.
|
||||
* <p>
|
||||
*
|
||||
* The choreography is two-round: the first round of {@link ManeuverCache#recalculate}
|
||||
* triggers a fresh full-scan pass of the detector for each competitor (their per-competitor
|
||||
* detectors were {@link TrackedRaceImpl#setWindEstimation cleared} when the wind estimator
|
||||
* was installed, so the fresh detector emits spots into the now-non-null
|
||||
* {@code WindEstimationInteraction}). The estimator asynchronously processes those spots and
|
||||
* publishes wind fixes on its {@code MANEUVER_BASED_ESTIMATION} track. Once the estimator
|
||||
* has drained (via {@code waitUntilDone}), a second round of recalculation re-types the
|
||||
* spots using the newly-available wind and captures the classified maneuvers, which are then
|
||||
* persisted. See bug6241; without this sequencing the initial recalc runs while the
|
||||
* estimator has queued but not yet processed the spots, so typing still sees no wind and
|
||||
* produces UNKNOWN maneuvers which then get persisted permanently.
|
||||
*/
|
||||
private void retypeAndStoreAfterWindEstimationSettled() {
|
||||
// Round 1: trigger fresh detection per competitor. Their detector cache was cleared when
|
||||
// setWindEstimation ran, so new detectors are built with a non-null wind-estimation
|
||||
// interaction and emit spots to the estimator during this pass.
|
||||
for (final Competitor competitor : race.getRace().getCompetitors()) {
|
||||
cacheToUse.recalculate(competitor);
|
||||
cacheToUse.get(competitor, /* waitForLatest */ true);
|
||||
}
|
||||
// Wait for the estimator to drain the spots emitted in Round 1 and produce its wind
|
||||
// fixes. Those fixes flow into the tracked race's MANEUVER_BASED_ESTIMATION track and
|
||||
// become visible to getWind(pos, at) lookups.
|
||||
final IncrementalWindEstimation windEstimation = race.getWindEstimation();
|
||||
boolean estimatorSettled = true;
|
||||
if (windEstimation != null) {
|
||||
try {
|
||||
windEstimation.waitUntilDone();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
estimatorSettled = false;
|
||||
logger.log(Level.WARNING, "Interrupted while waiting for wind estimation to finish inference for race "
|
||||
+race.getRaceIdentifier()+"; skipping maneuver re-type and store");
|
||||
}
|
||||
}
|
||||
if (!Thread.currentThread().isInterrupted()) {
|
||||
if (estimatorSettled) {
|
||||
// Round 2: recalculate per competitor. This time the detector's
|
||||
// lastManeuverDetectionResult is populated from Round 1 and no new raw fixes have
|
||||
// arrived, so it takes the re-type branch of detectManeuverSpots (line 150 of
|
||||
// IncrementalManeuverDetectorImpl) and re-types the existing spots using the current
|
||||
// wind -- which now includes the estimator's fixes.
|
||||
final Map<Competitor, List<Maneuver>> maneuvers = new HashMap<>();
|
||||
for (final Competitor competitor : race.getRace().getCompetitors()) {
|
||||
cacheToUse.recalculate(competitor);
|
||||
|
||||
Reference in new issue
Block a user