mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-02 01:00:46 +00:00
The existing isAllEmpty() self-heal in ManeuverCacheDelegate.resume() only caught the case where every competitor's persisted maneuver list was null or literally empty. A separate poisoned state slipped through: lists are non-empty but every stored maneuver is typed as UNKNOWN (or PENALTY_CIRCLE), which happens when the detector ran while getWind(pos, at) still returned null everywhere and createManeuverFromManeuverCurveAndWind hit its 'wind == null' fall-through in ManeuverDetectorImpl. Feeding such records to the estimator via alreadyClassifiedManeuversAvailable produces zero wind fixes because IncrementalMstHmmWindEstimationForTrackedRace.mapManeuverType maps UNKNOWN and PENALTY_CIRCLE to null and the whole batch gets filtered out. Observable as hasWindData=false on the affected races even after the wind estimation callback has fired. Confirmed on archive-candidate for W10 of "The Final Push 49erFX" (982 stored maneuvers, all UNKNOWN) and for Abeam Training 2016 Race 1 (453 stored, all UNKNOWN); production shows the same event with hasWindData=true because it ran with older code paths that populated the MBE track through a different route. The fix adds a companion check, hasNoClassifiableManeuver, invoked after isAllEmpty in resume(). It returns true iff no stored maneuver anywhere in the loaded map is typed as TACK/JIBE/HEAD_UP/BEAR_AWAY (the four types mapManeuverType turns into a non-null ManeuverTypeForClassification). When it fires, resume() logs the reason and falls through to the compute path, which now correctly sequences its storage after the wind estimator has settled (see computeAndStore / retypeAndStoreAfterWindEstimationSettled) so the recompute produces properly-typed maneuvers and overwrites the poison. isClassifiableType() is a tiny helper listing the four classifiable types; a comment reminds future maintainers to keep it in sync with mapManeuverType.