Closes the FIXME at TrackedRaceImpl.setWindEstimation. When a non-null
IncrementalWindEstimation is installed on a race (either because
setWindEstimation was called for the first time after the wind factory
service arrived, or because setPolarDataService recomputed the estimator's
context), the already-known maneuvers for each competitor are read from
maneuverCache -- which is polymorphic across the two SerializableManeuverCache
implementations, so it works both for DB-loaded races
(ManeuversFromDatabase) and for computed-maneuvers races
(ManeuversFromSmartFutureCache) -- and handed off to the estimator via
IncrementalWindEstimation.alreadyClassifiedManeuversAvailable. This
skips detection entirely; only the final wind-fix conversion runs on the
estimator side.
The hand-off is scheduled on the shared background executor per
competitor so that setWindEstimation returns quickly without blocking
its caller (typically the OSGi service-tracker thread or a
RaceAdditionListener callback thread) on maneuverCache.get(_, true)
which may wait for a pending detection to complete.
Each per-competitor task checks that the race's current windEstimation
is still the one it was scheduled with before doing the hand-off, so a
subsequent setWindEstimation that replaces the estimator does not
retroactively feed a stale estimator.
Removes the stale 'no need to trigger maneuver recalculation' comment
that was preventing exactly this feed, and removes the TODO about
'reusing the state of incremental maneuver detectors' which is now
addressed by this hand-off (we do reuse the computed / loaded maneuvers,
not the incremental detector state, but the effect is the same).
Removed the FIXME bug6142 (typo for bug6241) that this commit closes.
Adds a hand-off for already-classified maneuvers (e.g., loaded from the
persistent maneuver cache after a fingerprint match, or held in memory
after a prior detection run) to be fed to the wind estimator without
re-running detection or the MST/HMM classification graph. The estimator
adapts each Maneuver into SimpleManeuverForEstimation +
SimpleManeuverWithEstimatedType, then runs only the final wind-fix
conversion step (WindTrackCalculator.getWindTrackFromManeuverClassifications)
and reconciles the resulting fixes into its estimated wind track.
The default interface method is empty so existing implementors remain
source- and binary-compatible; only
IncrementalMstHmmWindEstimationForTrackedRace overrides it.
Refactoring:
* GraphRecalculationTask now polls a queue of PendingUpdate instances
with two implementations: NewSpotsUpdate (the existing detection path)
and PreClassifiedUpdate (the new DB-load / re-adaptation path).
* The reconciliation body (windTrackCalculator + estimatedWindTrack
merge) is factored into applyManeuverClassificationsToWindTrack and
called from both paths, preserving the FIXME bug6026 note.
* The domain->ManeuverTypeForClassification mapping is centralized as a
static helper, matching ManeuverWithEstimatedTypeFromManeuverTest.
* newManeuverSpotsDetected and alreadyClassifiedManeuversAvailable
both go through a shared submit(PendingUpdate) method that enqueues
and, if needed, starts a GraphRecalculationTask.
This is the wind-estimation side of the fix; the caller side (feeding
a race's cached maneuvers into a newly-installed estimator) comes in
subsequent commits.
Adds a way to register a drain-callback on the polar-data loading
pipeline for a specific race without also triggering fresh ingestion of
that race's fixes (which is what raceFinishedLoading does). The new
method may be called any number of times, before or after
raceFinishedLoading, and coordinates with it via a per-miner gate so a
caller never accidentally registers a drain callback while the pipeline
is momentarily idle before this race's fixes have entered it.
PolarDataMiner now maintains:
- callbacksWaitingForFixIngestion: parked callbacks for races whose
fixes haven't been queued yet;
- racesWithIngestedFixes: races whose fixes have been fully queued.
raceFinishedLoading, after queueing all of a race's competitor fixes,
promotes the race into racesWithIngestedFixes and registers all parked
callbacks (plus the caller-supplied one, if any) on the drain.
runWhenPolarLoadingFinishedFor either registers immediately (race
already ingested) or parks the callback for later promotion.
Removed the stale 'TODO bug6241:' comment atop raceFinishedLoading; the
mechanism it sketched is now in place (loading pipeline + drain
callback) so the TODO no longer applies.
This method is the building block that the per-race installation
primitive for the maneuver-based wind estimator (task 14) will
compose with TrackedRace.runWhenPastLoading.
Introduces a listener-based primitive that fires a callback exactly once
when a tracked race reaches a status strictly beyond LOADING (TRACKING,
FINISHED, ERROR, or REMOVED via status transition), provided the race is
still a member of its regatta at that moment. If the race is removed
from its regatta before the status condition is met, the callback is
never fired and both the status listener and the regatta-removal
listener are torn down.
The 'strictly beyond LOADING' condition (rather than 'at or beyond
LOADING') is what makes this primitive work for RaceLogRaceTracker-tracked
races that transition directly from PREPARED to TRACKING without ever
entering the LOADING state.
This will be used to wait for the moment when a tracked race is ready
for maneuver-based wind estimation to be attached (i.e., loading is
done, whatever there was of it) without leaking listeners on races that
never make it there because the user removed them.
Implementations added on TrackedRaceImpl (real behavior), DummyTrackedRace
(no-op stub), and MockedTrackedRaceWithStartTimeAndRanks (no-op stub).
New test TrackedRaceRunWhenPastLoadingTest covers the fast path,
PREPARED->TRACKING skipping LOADING, exactly-once semantics across
multiple transitions, and the removal-before-firing cancellation path.
Delegating leaderboards were adding to the count, although they refer to
the same RaceColumn to which the same TrackedRace is attached.
Therefore, waiting for the number of attached RaceLogs would wait
endlessly because only one race log got attached for the single
RaceColumn.
The key was to extend the queue length in FixWindow to the duration used
for validity/outlier detection. This uses the averaging interval in
*both* directions starting from a fix, other than COG/SOG estimation
which uses fixes only in half that interval in each direction.
There are still sporadic test failures, with maneuver candidates being
at slightly different time points between early and late initialization.
Another puzzle to solve?