From 76c41ae0672c0042908b4b32853378c76c01a6e1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 29 Jan 2025 13:48:09 +0100 Subject: [PATCH] bug6091: implemented percent target boat speed for chart/tails --- .../com/sap/sailing/domain/common/DetailType.java | 4 +++- .../sailing/gwt/ui/client/DetailTypeFormatter.java | 5 +++++ .../sap/sailing/gwt/ui/client/StringMessages.java | 2 ++ .../gwt/ui/client/StringMessages.properties | 4 +++- .../gwt/ui/client/StringMessages_de.properties | 4 +++- .../sailing/gwt/ui/server/SailingServiceImpl.java | 4 ++-- ...SFixDTOWithSpeedWindTackAndLegTypeIterable.java | 3 ++- .../server/gateway/jaxrs/api/RegattasResource.java | 2 +- .../server/interfaces/RacingEventService.java | 3 ++- .../server/impl/RacingEventServiceImpl.java | 14 +++++++++++++- 10 files changed, 36 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java index 71106a38516..e2640851b9a 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java @@ -215,7 +215,8 @@ public enum DetailType implements Serializable, SecuredEnum { EXPEDITION_LEG_JIB_CAR_STBD(2, ASCENDING, null, ValueSpreader.CUBIC_ROOT), EXPEDITION_LEG_MAST_BUTT(2, ASCENDING, null, ValueSpreader.CUBIC_ROOT), BRAVO_RACE_HEEL_IN_DEGREES(2, DESCENDING, null, ValueSpreader.CUBIC_ROOT), - BRAVO_RACE_PITCH_IN_DEGREES(2, DESCENDING, null, ValueSpreader.CUBIC_ROOT); + BRAVO_RACE_PITCH_IN_DEGREES(2, DESCENDING, null, ValueSpreader.CUBIC_ROOT), + PERCENT_TARGET_BOAT_SPEED(2, DESCENDING, PREMIUM_LEADERBOARD_INFORMATION, ValueSpreader.CUBIC_ROOT); private int precision; @@ -323,6 +324,7 @@ public enum DetailType implements Serializable, SecuredEnum { availableDetailsTypes.add(DetailType.BRAVO_RACE_CURRENT_RIDE_HEIGHT_IN_METERS); availableDetailsTypes.add(DetailType.BRAVO_RACE_HEEL_IN_DEGREES); availableDetailsTypes.add(DetailType.BRAVO_RACE_PITCH_IN_DEGREES); + availableDetailsTypes.add(DetailType.PERCENT_TARGET_BOAT_SPEED); return availableDetailsTypes; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java index 7e8f58481f7..b37c78f3264 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java @@ -386,6 +386,8 @@ public class DetailTypeFormatter { return stringMessages.currentHeelInDegree(); case BRAVO_RACE_PITCH_IN_DEGREES: return stringMessages.currentPitchInDegree(); + case PERCENT_TARGET_BOAT_SPEED: + return stringMessages.percentTargetBoatSpeed(); } return detailType.name(); } @@ -414,6 +416,7 @@ public class DetailTypeFormatter { return stringMessages.knotsUnit(); case BRAVOEXTENDED_RACE_CURRENT_DEFLECTOR_PERCENTAGE: case BRAVOEXTENDED_RACE_CURRENT_TARGET_BOATSPEED_PERCENTAGE: + case PERCENT_TARGET_BOAT_SPEED: return stringMessages.percent(); case CHART_WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_CHANGE: return stringMessages.metersPerSecondUnit(); @@ -914,6 +917,8 @@ public class DetailTypeFormatter { return ""; case LEG_TACKTYPE_LONGTACK_SHORTTACK: return stringMessages.tackTypeTooltip(); + case PERCENT_TARGET_BOAT_SPEED: + return stringMessages.percentTargetBoatSpeedTooltip(); } return ""; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 56b22214508..4c46938bda3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2506,4 +2506,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String gapToLeaderInSecondsChangeTooltip(); String windwardDistanceToCompetitorFarthestAheadInMetersChange(); String windwardDistanceToCompetitorFarthestAheadInMetersChangeTooltip(); + String percentTargetBoatSpeed(); + String percentTargetBoatSpeedTooltip(); } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 5717b9ae493..d801ccd25e7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -2542,4 +2542,6 @@ errorRestartingIgtimiDevice=Error restarting Igtimi device {0}: {1} successfullyRestartedIgtimiDevice=Successfully restarted Igtimi device {0} restart=Restart device desktop=Desktop -mobile=Mobile \ No newline at end of file +mobile=Mobile +percentTargetBoatSpeed=Percent Target Boat Speed +percentTargetBoatSpeedTooltip=Percentage of the speed over ground the boat should be sailing at with the given true wind speed and angle based on the polar data for the boat class \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index b0187594c1e..ea43d169cd0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -2536,4 +2536,6 @@ errorRestartingIgtimiDevice=Fehler beim Neustart des Igtimi-Geräts {0}: {1} successfullyRestartedIgtimiDevice=Igtimi-Gerät {0} erfolgreich neu gestartet restart=Gerät neu starten desktop=Desktop -mobile=Mobilgerät \ No newline at end of file +mobile=Mobilgerät +percentTargetBoatSpeed=Prozent Zielgeschwindigkeit +percentTargetBoatSpeedTooltip=Prozentualer Anteil, den die aktuelle Geschwindigkeit über Grund an der aus den Polardaten für die Bootsklasse vorhergesagten Zielgeschwindigkeit für die aktuelle Windstärke und den gesegelten Winkel zum wahren Wind \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 100c9af2094..3796f129ed1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2605,7 +2605,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet for (final CompetitorDTO competitorDTO : competitors) { FutureTask future = new FutureTask(new Callable() { @Override - public CompetitorRaceDataDTO call() throws NoWindException { + public CompetitorRaceDataDTO call() throws NoWindException, NotEnoughDataHasBeenAddedException { Competitor competitor = getCompetitorByIdAsString(trackedRace.getRace().getCompetitors(), competitorDTO.getIdAsString()); ArrayList> markPassingsData = new ArrayList>(); @@ -2678,7 +2678,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet public Double getCompetitorRaceDataEntry(DetailType detailType, TrackedRace trackedRace, Competitor competitor, TimePoint timePoint, String leaderboardGroupName, UUID leaderboardGroupId, String leaderboardName, - WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException, NotEnoughDataHasBeenAddedException { return getService().getCompetitorRaceDataEntry(detailType, trackedRace, competitor, timePoint, getLeaderboardGroupByIdOrName(leaderboardGroupId, leaderboardGroupName), leaderboardName, cache); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GPSFixDTOWithSpeedWindTackAndLegTypeIterable.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GPSFixDTOWithSpeedWindTackAndLegTypeIterable.java index ad6cc45c075..77b8a4affbf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GPSFixDTOWithSpeedWindTackAndLegTypeIterable.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GPSFixDTOWithSpeedWindTackAndLegTypeIterable.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; @@ -278,7 +279,7 @@ public class GPSFixDTOWithSpeedWindTackAndLegTypeIterable implements IsSerializa try { detailValue = sailingService.getCompetitorRaceDataEntry(detailType, trackedRace, competitor, fix.getTimePoint(), leaderboardGroupName, leaderboardGroupId, leaderboardName, cache); - } catch (NoWindException nwe) { + } catch (NoWindException | NotEnoughDataHasBeenAddedException nwe) { detailValue = null; } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index e8c9a44e2ce..a4c2964dc7c 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -2214,7 +2214,7 @@ public class RegattasResource extends AbstractSailingServerResource { final FutureTask>>> future =new FutureTask>>>( new Callable>>>() { @Override - public Iterable>> call() throws NoWindException { + public Iterable>> call() throws NoWindException, NotEnoughDataHasBeenAddedException { final List>> raceData = new ArrayList<>(); if (startTime != null && endTime != null) { for (long i = startTime.asMillis(); i <= endTime.asMillis(); i += adjustedStepSizeInMillis) { diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java index 3de9974b25e..0246b44ac60 100644 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java @@ -68,6 +68,7 @@ import com.sap.sailing.domain.common.WindFinderReviewedSpotsCollectionIdProvider import com.sap.sailing.domain.common.dto.AnniversaryType; import com.sap.sailing.domain.common.impl.MasterDataImportObjectCreationCountImpl; import com.sap.sailing.domain.common.media.MediaTrack; +import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException; import com.sap.sailing.domain.common.racelog.tracking.MarkAlreadyUsedInRaceException; @@ -1101,5 +1102,5 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch */ Double getCompetitorRaceDataEntry(DetailType dataType, TrackedRace trackedRace, Competitor competitor, TimePoint timePoint, LeaderboardGroup leaderboardGroup, String leaderboardName, - WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException; + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException, NotEnoughDataHasBeenAddedException; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 1ae5b047ef0..484aa4e1d4d 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -123,6 +123,7 @@ import com.sap.sailing.domain.base.RemoteSailingServerReference; import com.sap.sailing.domain.base.SailingServerConfiguration; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Sideline; +import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.configuration.DeviceConfiguration; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; @@ -167,6 +168,7 @@ import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO; import com.sap.sailing.domain.common.impl.DataImportProgressImpl; import com.sap.sailing.domain.common.impl.MasterDataImportObjectCreationCountImpl; import com.sap.sailing.domain.common.media.MediaTrack; +import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException; @@ -5464,7 +5466,7 @@ Replicator { @Override public Double getCompetitorRaceDataEntry(DetailType dataType, TrackedRace trackedRace, Competitor competitor, TimePoint timePoint, LeaderboardGroup leaderboardGroup, String leaderboardName, - WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException, NotEnoughDataHasBeenAddedException { Double result = null; final Course course = trackedRace.getRace().getCourse(); course.lockForRead(); // make sure the tracked leg survives this call even if a course update is pending @@ -5883,6 +5885,16 @@ Replicator { result = getBravoDoubleValue(BravoFixTrack::getExpeditionVMGTargVMGDeltaIfAvailable, trackedRace, competitor, timePoint); break; } + case PERCENT_TARGET_BOAT_SPEED: { + final PolarDataService polarDataService = getPolarDataService(); + final GPSFixTrack competitorTrack = trackedRace.getTrack(competitor); + final Wind wind = trackedRace.getWind(competitorTrack.getEstimatedPosition(timePoint, /* extrapolate */ true), timePoint); + final SpeedWithConfidence targetSpeed = polarDataService.getSpeed(trackedRace.getBoatOfCompetitor(competitor).getBoatClass(), + wind, trackedRace.getTWA(competitor, timePoint, cache)); + final Speed sog = competitorTrack.getEstimatedSpeed(timePoint); + result = targetSpeed != null && targetSpeed.getObject() != null && sog != null ? 100.0 * sog.getKnots() / targetSpeed.getObject().getKnots() : null; + break; + } default: throw new UnsupportedOperationException("There is currently no support for the enum value '" + dataType + "' in this method.");