diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_es.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_es.properties index 95ff91055b5..4800033761f 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_es.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_es.properties @@ -81,6 +81,7 @@ Tack=Amura SpeedBefore=Velocidad antes de maniobra SpeedAfter=Velocidad después de maniobra DirectionChange=Cambio de dirección en grados +DirectionChangeWithinMainCurve=Cambio de dirección con curva principal en grados AbsoluteDirectionChange=Cambio absoluto de dirección en grados (positivo) ToSide=Al lado (babor / estribor) ManeuverLoss=Pérdida en maniobra @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=Promedio del ángulo absoluto de viento real AbsoluteTrueWindAngle=Ángulo absoluto del viento real (TWA) Wind=Viento VMG=Velocity Made Good +MaxAngularVelocityInDegreesPerSecond=Velocidad máxima angular en grados +ManeuverEnteringAbsTWA=TWA absoluto antes de maniobra +ManeuverExitingAbsTWA=TWA absoluto tras maniobra +ManeuverSpeedDetailsRetrieverChain=Detalles de velocidad de maniobra +ManeuverSpeedDetails=Detalles de velocidad +SpeedSlope=Inclinación velocidad +ManeuverDirection=Dirección de maniobra +RatioToEnteringSpeed=Ratio por velocidad de entrada en maniobra +RatioToPreviousTWASpeed=Ratio por velocidad de TWA previo en maniobra +LowestRatioToEnteringSpeed=Ratio más baja por velocidad de entrada en maniobra +HighestRatioToEnteringSpeed=Ratio más alta por velocidad de entrada en maniobra +HighestRatioToEnteringSpeedMinusLowest=Ratio más alta por velocidad de entrada menos más baja en maniobra +EnteringManeuverSpeedMinusExitingSpeed=Velocidad de maniobra de entrada menos velocidad de salida +RatioBetweenManeuverEnteringAndExitingSpeed=Ratio entre velocidad de entrada y salida de maniobra +AvgTrendForTWAs=Promedio por TWA +MedianTrendForTWAs=Media por TWA +ManeuverDurationInSeconds=Duración de maniobra en segundos +AbsTWAAtManeuverClimax=TWA absoluto en clímax de maniobra +ManeuverEnteringSpeedInKnots=Velocidad de entrada de maniobra (kts) +ManeuverExitingSpeedInKnots=Velocidad de salida de maniobra (kts) +TackBeforeManeuver=Bordo antes de maniobra +ManeuverBoundariesRetrieverChain=Límites de maniobra +ManeuverBoundaries=Límites de maniobra +ManeuverStartSpeedDeviationRatioFromAvg=Ratio de desviación de velocidad de inicio de maniobra con velocidad media antes de maniobra +ManeuverStartCogDeviationFromAvgInDegrees=Desviación COG de inicio de maniobra con COG medio antes de maniobra en grados +ManeuverEndSpeedDeviationRatioFromAvg=Ratio de desviación de velocidad de fin de maniobra con velocidad media tras maniobra +ManeuverEndCogDeviationFromAvgInDegrees=Desviación COG de fin de maniobra con COG medio tras maniobra en grados +DurationToNextManeuverInSeconds=Duración de fin de maniobra actual hasta siguiente inicio de maniobra en segundos +DurationFromPreviousManeuverInSeconds=Duración de fin de maniobra anterior hasta siguiente inicio de maniobra actual en segundos +NextManeuverAtLeastOneSecondInFront=Siguiente inicio de maniobra es al menos un segundo después de fin de maniobra actual +PreviousManeuverAtLeastOneSecondBehind=Anterior fin de maniobra es al menos un segundo antes de inicio de maniobra actual +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=Duración entre velocidad estable con rumbo e inicio de curva principal en segundos +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=Ratio de velocidad entre velocidad estable con rumbo e inicio de curva principal +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=Diferencia de rumbo absoluto entre velocidad estable con rumbo e inicio de curva principal en grados +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=Duración entre velocidad estable con rumbo y fin de curva principal en segundos +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=Ratio de velocidad entre velocidad estable con rumbo y fin de curva principal +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=Diferencia de rumbo absoluto entre velocidad estable con rumbo y fin de curva principal en grados +AbsTwaAtLowestRatioToEnteringSpeed=TWA absoluto en ratio más baja para velocidad de entrada +AbsTwaAtHighestRatioToEnteringSpeed=TWA absoluto en ratio más alta para velocidad de entrada diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_fr.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_fr.properties index 3ce43dc3a30..42a5e6f2c5a 100755 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_fr.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_fr.properties @@ -81,6 +81,7 @@ Tack=Virement SpeedBefore=Vitesse avant la manœuvre SpeedAfter=Vitesse après la manœuvre DirectionChange=Changement de cap en degrés +DirectionChangeWithinMainCurve=Changement de cap sur la courbe principale en degrés AbsoluteDirectionChange=Changement de cap absolu en degrés (positif) ToSide=À bâbord/À tribord ManeuverLoss=Perte suite à manœuvre @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=Angle du vent réel moyen AbsoluteTrueWindAngle=Angle du vent réel (TWA) Wind=Vent VMG=Compromis Cap/Vitesse (VMG) +MaxAngularVelocityInDegreesPerSecond=Vitesse angulaire maximale en degrés/seconde +ManeuverEnteringAbsTWA=TWA absolu avant manœuvre +ManeuverExitingAbsTWA=TWA absolu après manœuvre +ManeuverSpeedDetailsRetrieverChain=Détails sur la vitesse lors de la manœuvre +ManeuverSpeedDetails=Détails sur la vitesse +SpeedSlope=Inclinaison en fonction de la vitesse +ManeuverDirection=Cap pendant la manœuvre +RatioToEnteringSpeed=Ratio de vitesse d''entrée lors de la manœuvre +RatioToPreviousTWASpeed=Ratio par rapport à la vitesse du précédent TWA lors de la manœuvre +LowestRatioToEnteringSpeed=Ratio le plus bas par rapport à la vitesse d''entrée lors de la manœuvre +HighestRatioToEnteringSpeed=Ratio le plus élevé par rapport à la vitesse d''entrée lors de la manœuvre +HighestRatioToEnteringSpeedMinusLowest=Ratio le plus élevé par rapport à la vitesse d''entrée moins ratio le plus bas +EnteringManeuverSpeedMinusExitingSpeed=Vitesse d''entrée de manœuvre moins vitesse de sortie +RatioBetweenManeuverEnteringAndExitingSpeed=Ratio de variation entre la vitesse d''entrée et de sortie de manœuvre +AvgTrendForTWAs=Moyenne par TWA +MedianTrendForTWAs=Médiane par TWA +ManeuverDurationInSeconds=Durée de la manœuvre en secondes +AbsTWAAtManeuverClimax=TWA absolu lors du point culminant de la manœuvre +ManeuverEnteringSpeedInKnots=Vitesse d''entrée de manœuvre (nds) +ManeuverExitingSpeedInKnots=Vitesse de sortie de manœuvre (nds) +TackBeforeManeuver=Virement avant manœuvre +ManeuverBoundariesRetrieverChain=Limites de manœuvre +ManeuverBoundaries=Limites de manœuvre +ManeuverStartSpeedDeviationRatioFromAvg=Ratio de variation de vitesse entre le début de la manœuvre et la vitesse moyenne avant manœuvre +ManeuverStartCogDeviationFromAvgInDegrees=Écart de route fond au début de la manœuvre avec route fond moyenne avant manœuvre en degrés +ManeuverEndSpeedDeviationRatioFromAvg=Ratio d''écart de vitesse à la fin de la manœuvre avec vitesse moyenne après manœuvre +ManeuverEndCogDeviationFromAvgInDegrees=Écart de route fond à la fin de la manœuvre avec route fond moyenne après manœuvre en degrés +DurationToNextManeuverInSeconds=Durée entre la fin de la manœuvre actuelle et le début de la prochaine manœuvre (en secondes) +DurationFromPreviousManeuverInSeconds=Durée entre la fin de la manœuvre précédente et le début de la manœuvre actuelle (en secondes) +NextManeuverAtLeastOneSecondInFront=La prochaine manœuvre débute au moins une seconde après la fin de la manœuvre actuelle +PreviousManeuverAtLeastOneSecondBehind=La manœuvre précédente se termine au moins une seconde avant le début de la manœuvre actuelle +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=Durée entre vitesse stable de la course et début de la courbe principale en secondes +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=Ratio de variation de vitesse entre vitesse stable de la course et début de la courbe principale +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=Écart de course absolu entre vitesse stable de la course et début de la courbe principale en degrés +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=Durée entre vitesse stable de la course et fin de la courbe principale en secondes +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=Ratio de variation de vitesse entre vitesse stable de la course et fin de la courbe principale +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=Écart de course absolu entre vitesse stable de la course et fin de la courbe principale en degrés +AbsTwaAtLowestRatioToEnteringSpeed=TWA absolu au ratio le plus bas par rapport à la vitesse d''entrée +AbsTwaAtHighestRatioToEnteringSpeed=TWA absolu au ratio le plus élevé par rapport à la vitesse d''entrée diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ja.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ja.properties index f717f279487..d845c9fa791 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ja.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ja.properties @@ -81,6 +81,7 @@ Tack=タック SpeedBefore=マニューバー前の艇速 SpeedAfter=マニューバー後の艇速 DirectionChange=方向転換 (度) +DirectionChangeWithinMainCurve=主曲線内の方向転換 (度) AbsoluteDirectionChange=方向転換 (度) の絶対値 ToSide=方向転換する側 (ポート/スターボード) ManeuverLoss=マニューバーロス @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=平均絶対真風角度 AbsoluteTrueWindAngle=絶対真風角度 (TWA) Wind=風 VMG=VMG (有効速度) +MaxAngularVelocityInDegreesPerSecond=最大角速度 (deg/s) +ManeuverEnteringAbsTWA=マニューバー前の絶対 TWA +ManeuverExitingAbsTWA=マニューバー後の絶対 TWA +ManeuverSpeedDetailsRetrieverChain=マニューバー速度詳細 +ManeuverSpeedDetails=速度詳細 +SpeedSlope=速度勾配 +ManeuverDirection=マニューバー方向 +RatioToEnteringSpeed=マニューバー内の進入速度との比率 +RatioToPreviousTWASpeed=マニューバー内の前 TWA の速度との比率 +LowestRatioToEnteringSpeed=マニューバー内の進入速度との最低比率 +HighestRatioToEnteringSpeed=マニューバー内の進入速度との最高比率 +HighestRatioToEnteringSpeedMinusLowest=マニューバー内の進入速度から最低速度を引いたものとの最高比率 +EnteringManeuverSpeedMinusExitingSpeed=マニューバーの進入速度から脱出速度を引いたもの +RatioBetweenManeuverEnteringAndExitingSpeed=マニューバーの進入速度と脱出速度の比率 +AvgTrendForTWAs=TWA 別の平均値 +MedianTrendForTWAs=TWA 別の中央値 +ManeuverDurationInSeconds=マニューバー持続時間 (秒) +AbsTWAAtManeuverClimax=マニューバー頂点の絶対 TWA +ManeuverEnteringSpeedInKnots=マニューバー進入速度 (kts) +ManeuverExitingSpeedInKnots=マニューバー脱出速度 (kts) +TackBeforeManeuver=マニューバー前のタック +ManeuverBoundariesRetrieverChain=マニューバー境界 +ManeuverBoundaries=マニューバー境界 +ManeuverStartSpeedDeviationRatioFromAvg=マニューバー前平均速度と比較したマニューバー開始時の速度差異比 +ManeuverStartCogDeviationFromAvgInDegrees=マニューバー前平均 COG と比較したマニューバー開始時の COG 差異 (度) +ManeuverEndSpeedDeviationRatioFromAvg=マニューバー後平均速度と比較したマニューバー終了時の速度差異比 +ManeuverEndCogDeviationFromAvgInDegrees=マニューバー後平均 COG と比較したマニューバー終了時の COG 差異 (度) +DurationToNextManeuverInSeconds=現マニューバー終了から次マニューバー開始までの時間 (秒) +DurationFromPreviousManeuverInSeconds=前マニューバー終了から現マニューバー開始までの時間 (秒) +NextManeuverAtLeastOneSecondInFront=次マニューバー開始は現マニューバー終了の少なくとも 1 秒後 +PreviousManeuverAtLeastOneSecondBehind=前マニューバー終了は現マニューバー開始の少なくとも 1 秒前 +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=コース安定速度と主曲線始点の間の時間 (秒) +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=コース安定速度と主曲線始点の速度比 +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=コース安定速度と主曲線始点の絶対コース差異 (度) +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=コース安定速度と主曲線終点の間の時間 (秒) +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=コース安定速度と主曲線終点の速度比 +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=コース安定速度と主曲線終点の絶対コース差異 (度) +AbsTwaAtLowestRatioToEnteringSpeed=進入速度との最低比率での絶対 TWA +AbsTwaAtHighestRatioToEnteringSpeed=進入速度との最高比率での絶対 TWA diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_pt.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_pt.properties index 85f42265561..ae5146cf7cb 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_pt.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_pt.properties @@ -81,6 +81,7 @@ Tack=Cambar SpeedBefore=Velocidade antes de manobra SpeedAfter=Velocidade depois de manobra DirectionChange=Mudança de direção em graus +DirectionChangeWithinMainCurve=Mudança de direção na curva principal em graus AbsoluteDirectionChange=Mudança absoluta de direção em graus (positiva) ToSide=Até lado (bombordo/boreste) ManeuverLoss=Perda de manobra @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=Ângulo de vento verdadeiro absoluto médio AbsoluteTrueWindAngle=Ângulo de vento verdadeiro absoluto (TWA) Wind=Vento VMG=Velocidade diretamente na linha do vento real (VMG) +MaxAngularVelocityInDegreesPerSecond=Velocidade angular máxima em graus/s +ManeuverEnteringAbsTWA=TWA absoluto antes da manobra +ManeuverExitingAbsTWA=TWA absoluto após a manobra +ManeuverSpeedDetailsRetrieverChain=Detalhes da velocidade da manobra +ManeuverSpeedDetails=Detalhes da velocidade +SpeedSlope=Inclinação de velocidade +ManeuverDirection=Direção da manobra +RatioToEnteringSpeed=Proporção em relação à velocidade de entrada na manobra +RatioToPreviousTWASpeed=Proporção em relação à velocidade do TWA anterior na manobra +LowestRatioToEnteringSpeed=Proporção mínima em relação à velocidade de entrada na manobra +HighestRatioToEnteringSpeed=Proporção máxima em relação à velocidade de entrada na manobra +HighestRatioToEnteringSpeedMinusLowest=Proporção máxima em relação à velocidade de entrada menos a mínima na manobra +EnteringManeuverSpeedMinusExitingSpeed=Velocidade de entrada da manobra menos velocidade de saída +RatioBetweenManeuverEnteringAndExitingSpeed=Proporção entre velocidade de entrada e saída da manobra +AvgTrendForTWAs=Média por TWA +MedianTrendForTWAs=Mediana por TWA +ManeuverDurationInSeconds=Duração da manobra em segundos +AbsTWAAtManeuverClimax=TWA absoluto no ponto culminante da manobra +ManeuverEnteringSpeedInKnots=Velocidade de entrada da manobra (nós) +ManeuverExitingSpeedInKnots=Velocidade de saída da manobra (nós) +TackBeforeManeuver=Cambada antes da manobra +ManeuverBoundariesRetrieverChain=Limites da manobra +ManeuverBoundaries=Limites da manobra +ManeuverStartSpeedDeviationRatioFromAvg=Proporção de desvio de velocidade de início da manobra com velocidade média antes da manobra +ManeuverStartCogDeviationFromAvgInDegrees=Desvio de percurso no fundo de início da manobra com percurso médio no fundo antes da manobra em graus +ManeuverEndSpeedDeviationRatioFromAvg=Proporção de desvio de velocidade de fim da manobra com velocidade média após a manobra +ManeuverEndCogDeviationFromAvgInDegrees=Desvio de percurso no fundo de fim da manobra com percurso médio no fundo após a manobra em graus +DurationToNextManeuverInSeconds=Duração desde o fim da manobra atual até o início da próxima manobra em segundos +DurationFromPreviousManeuverInSeconds=Duração desde o fim da manobra anterior até o início da manobra atual em segundos +NextManeuverAtLeastOneSecondInFront=O início da manobra seguinte é pelo menos um segundo após o fim da manobra atual +PreviousManeuverAtLeastOneSecondBehind=O fim da manobra anterior é pelo menos um segundo antes do início da manobra atual +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=Duração entre velocidade estável com percurso e início da curva principal em segundos +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=Proporção de velocidade entre velocidade estável com percurso e início da curva principal +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=Diferença de percurso absoluto entre velocidade estável com percurso e início da curva principal em graus +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=Duração entre velocidade estável com percurso e fim da curva principal em segundos +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=Proporção de velocidade entre velocidade estável com percurso e fim da curva principal +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=Diferença de percurso absoluto entre velocidade estável com percurso e fim da curva principal em graus +AbsTwaAtLowestRatioToEnteringSpeed=TWA absoluto na proporção mínima em relação a velocidade de entrada +AbsTwaAtHighestRatioToEnteringSpeed=TWA absoluto na proporção máxima em relação a velocidade de entrada diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ru.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ru.properties index 85f6623a1fa..bf787d80fbd 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ru.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ru.properties @@ -81,6 +81,7 @@ Tack=Оверштаг SpeedBefore=Скорость до маневра SpeedAfter=Скорость после маневра DirectionChange=Изменение направления в градусах +DirectionChangeWithinMainCurve=Изменение направления в пределах основной кривой в градусах AbsoluteDirectionChange=Абсолютное изменение направления в градусах (положительное) ToSide=Борт (левый/правый) ManeuverLoss=Потеря маневра @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=Средний абсолютный угол ист AbsoluteTrueWindAngle=Абсолютный угол истинного ветра (TWA) Wind=Ветер VMG=Эффективная скорость лавирования (VMG) +MaxAngularVelocityInDegreesPerSecond=Максимальная угловая скорость (град./с) +ManeuverEnteringAbsTWA=Абсолютный TWA до маневра +ManeuverExitingAbsTWA=Абсолютный TWA после маневра +ManeuverSpeedDetailsRetrieverChain=Скорость маневра подробно +ManeuverSpeedDetails=Скорость подробно +SpeedSlope=Градиент скорости +ManeuverDirection=Направление маневра +RatioToEnteringSpeed=Отношение к входной скорости маневра +RatioToPreviousTWASpeed=Отношение к скорости предыдущего TWA в маневре +LowestRatioToEnteringSpeed=Наименьшее отношение к входной скорости маневра +HighestRatioToEnteringSpeed=Наибольшее отношение к входной скорости маневра +HighestRatioToEnteringSpeedMinusLowest=Разность наибольшего и наименьшего отношений к входной скорости маневра в маневре +EnteringManeuverSpeedMinusExitingSpeed=Разность входной и выходной скоростей маневра +RatioBetweenManeuverEnteringAndExitingSpeed=Отношение входной и выходной скоростей маневра +AvgTrendForTWAs=Среднее по TWA +MedianTrendForTWAs=Медиана по TWA +ManeuverDurationInSeconds=Длительность маневра в секундах +AbsTWAAtManeuverClimax=Абсолютный TWA в крайней точке маневра +ManeuverEnteringSpeedInKnots=Входная скорость маневра (уз.) +ManeuverExitingSpeedInKnots=Выходная скорость маневра (уз.) +TackBeforeManeuver=Галс до маневра +ManeuverBoundariesRetrieverChain=Границы маневра +ManeuverBoundaries=Границы маневра +ManeuverStartSpeedDeviationRatioFromAvg=Коэффициент отклонения скорости начала маневра со ср. скоростью до маневра +ManeuverStartCogDeviationFromAvgInDegrees=Отклонение COG начала маневра со ср. COG до маневра в градусах +ManeuverEndSpeedDeviationRatioFromAvg=Коэффициент отклонения скорости завершения маневра со ср. скоростью после маневра +ManeuverEndCogDeviationFromAvgInDegrees=Отклонение COG завершения маневра со ср. COG после маневра в градусах +DurationToNextManeuverInSeconds=Длительность от завершения текущего маневра до начала следующего маневра в секундах +DurationFromPreviousManeuverInSeconds=Длительность от завершения предыдущего маневра до начала текущего маневра в секундах +NextManeuverAtLeastOneSecondInFront=Начало следующего маневра не ранее одной секунды после завершения текущего маневра +PreviousManeuverAtLeastOneSecondBehind=Завершение предыдущего маневра не ранее одной секунды после начала текущего маневра +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=Длительность между стабильной скоростью с курсом и началом основной кривой в секундах +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=Соотношение скоростей между стабильной скоростью с курсом и основной кривой +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=Абс. разница курса между стабильной скоростью с курсом и началом основной кривой в градусах +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=Длительность между стабильной скоростью с курсом и завершением основной кривой в секундах +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=Соотношение скоростей между стабильной скоростью с курсом и завершением основной кривой +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=Абс. разница курса между стабильной скоростью с курсом и завершением основной кривой в градусах +AbsTwaAtLowestRatioToEnteringSpeed=Абс. TWA при наименьшем отношении к входной скорости +AbsTwaAtHighestRatioToEnteringSpeed=Абс. TWA при наибольшем отношении к входной скорости diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_zh.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_zh.properties index 3ef6c3ac327..76c24aabd74 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_zh.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_zh.properties @@ -81,6 +81,7 @@ Tack=迎风转向 SpeedBefore=操作前的速度 SpeedAfter=操作后的速度 DirectionChange=风向变化角度 +DirectionChangeWithinMainCurve=主曲线内的风向变化角度 AbsoluteDirectionChange=绝对风向变化角度(正向) ToSide=至侧(左舷/右舷) ManeuverLoss=操作损失 @@ -126,3 +127,42 @@ AverageAbsoluteTrueWindAngle=平均绝对实际风向角 AbsoluteTrueWindAngle=绝对实际风向角 (TWA) Wind=风力 VMG=沿计划航线上的航速 +MaxAngularVelocityInDegreesPerSecond=最大角速度 (deg/s) +ManeuverEnteringAbsTWA=操作前的绝对实际风向角 +ManeuverExitingAbsTWA=操作后的绝对实际风向角 +ManeuverSpeedDetailsRetrieverChain=操作速度详情 +ManeuverSpeedDetails=速度详情 +SpeedSlope=速度坡度 +ManeuverDirection=操作方向 +RatioToEnteringSpeed=操作内驶入速度的比率 +RatioToPreviousTWASpeed=操作内先前实际风向角速度的比率 +LowestRatioToEnteringSpeed=操作内驶入速度的最低比率 +HighestRatioToEnteringSpeed=操作内驶入速度的最高比率 +HighestRatioToEnteringSpeedMinusLowest=操作内驶入速度减去最低速度的最高比率 +EnteringManeuverSpeedMinusExitingSpeed=驶入操作速度减去驶出速度 +RatioBetweenManeuverEnteringAndExitingSpeed=操作驶入速度与驶出速度之间的比率 +AvgTrendForTWAs=各实际风向角的平均值 +MedianTrendForTWAs=各实际风向角的中值 +ManeuverDurationInSeconds=操作持续时间(秒) +AbsTWAAtManeuverClimax=操作顶点的绝对实际风向角 +ManeuverEnteringSpeedInKnots=操作驶入速度 (kts) +ManeuverExitingSpeedInKnots=操作驶出速度 (kts) +TackBeforeManeuver=操作前迎风转向 +ManeuverBoundariesRetrieverChain=操作边界 +ManeuverBoundaries=操作边界 +ManeuverStartSpeedDeviationRatioFromAvg=操作前以平均速度开始操作的速度偏差率 +ManeuverStartCogDeviationFromAvgInDegrees=操作前以平均对地航向开始操作的对地航向偏差角度 +ManeuverEndSpeedDeviationRatioFromAvg=操作后以平均速度结束操作的速度偏差率 +ManeuverEndCogDeviationFromAvgInDegrees=操作后以平均对地航向结束操作的对地航向偏差角度 +DurationToNextManeuverInSeconds=自当前操作结束到下次操作开始的持续时间(秒) +DurationFromPreviousManeuverInSeconds=自先前操作结束到当前操作开始的持续时间(秒) +NextManeuverAtLeastOneSecondInFront=下次操作开始至少在当前操作结束后一秒 +PreviousManeuverAtLeastOneSecondBehind=先前操作结束至少在当前操作开始前一秒 +DurationBetweenStableSpeedWithCourseAndMainCurveBeginningInSeconds=场地内稳定速度与主曲线开始之间的持续时间(秒) +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveBeginning=场地内稳定速度与主曲线开始之间的速度比率 +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveBeginningInDegrees=场地内稳定速度与主曲线开始之间的绝对场地差异角度 +DurationBetweenStableSpeedWithCourseAndMainCurveEndInSeconds=场地内稳定速度与主曲线结束之间的持续时间(秒) +SpeedRatioBetweenStableSpeedWithCourseAndMainCurveEnd=场地内稳定速度与主曲线结束之间的速度比率 +AbsCourseDifferenceBetweenStableSpeedWithCourseAndMainCurveEndInDegrees=场地内稳定速度与主曲线结束之间的绝对场地差异角度 +AbsTwaAtLowestRatioToEnteringSpeed=最低比率下驶入速度的绝对实际风向角 +AbsTwaAtHighestRatioToEnteringSpeed=最高比率下驶入速度的绝对实际风向角 diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java index 1af5c610cea..46e2684f8bf 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java @@ -60,6 +60,8 @@ import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerM import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.RaceHandle; +import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.RacingEventService; @@ -126,7 +128,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { Exception { RaceColumn column = leaderboard.getRaceColumnByName(columnName); exception.expect(NotDenotedForRaceLogTrackingException.class); - adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + trackRace(column); } private void testSize(Track track, int expected) { @@ -198,7 +200,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1, boat1)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); // start tracking - adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + trackRace(column); // now there is a tracked race @@ -220,6 +222,23 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { raceLogFixTrackerManager.stop(/* preemptive */ false, /* willBeRemoved */ false); addFixes3(race, comp1, dev1); } + + private void trackRace(RaceColumn column) throws NotDenotedForRaceLogTrackingException, Exception { + final RaceHandle raceHandle = adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + + // The following lines ensure that we received all necessary events from the TrackedRegatta + // When removing a listener we get a Future whose get method will block until all previously triggered events are processed. + final RaceListener raceListener = new RaceListener() { + @Override + public void raceRemoved(TrackedRace trackedRace) { + } + @Override + public void raceAdded(TrackedRace trackedRace) { + } + }; + raceHandle.getTrackedRegatta().addRaceListener(raceListener); + raceHandle.getTrackedRegatta().removeRaceListener(raceListener).get(); + } /** * See bug 4114: when the regatta is set to control the tracking times from start / finish times, @@ -235,6 +254,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { adapter.denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, "race"); // start tracking adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + // now there is a trackedrace TrackedRace race = column.getTrackedRace(fleet); assertNotNull(race); @@ -254,7 +274,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { // now remove the tracked race and track again; again, the explicit tracking times in the race log must prevail service.removeRace(regatta, race.getRace()); - adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + trackRace(column); TrackedRace race2 = column.getTrackedRace(fleet); assertEquals(explicitStartOfTracking, race2.getStartOfTracking()); assertEquals(explicitEndOfTracking, race2.getEndOfTracking()); @@ -278,8 +298,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper { regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(t(), t(), author, UUID.randomUUID(), comp1)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); - // start tracking - adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); + trackRace(column); // now there is a trackedrace TrackedRace race = column.getTrackedRace(fleet); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index 2524548d27d..9c80e079044 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.Future; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; @@ -622,10 +623,6 @@ public class MockedTrackedRace implements DynamicTrackedRace { return null; } - @Override - public void removeTrackedRace(RaceDefinition raceDefinition) { - } - @Override public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable sidelines, WindStore windStore, long delayToLiveInMillis, @@ -652,7 +649,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void removeRaceListener(RaceListener listener) { + public Future removeRaceListener(RaceListener listener) { + return null; } }; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java new file mode 100644 index 00000000000..0c18176671a --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaTest.java @@ -0,0 +1,122 @@ +package com.sap.sailing.domain.tracking.impl; + +import static org.mockito.Mockito.mock; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Phaser; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Course; +import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.Mark; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Sideline; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; +import com.sap.sailing.domain.base.impl.RegattaImpl; +import com.sap.sailing.domain.base.impl.WaypointImpl; +import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; +import com.sap.sailing.domain.ranking.OneDesignRankingMetric; +import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; +import com.sap.sailing.domain.tracking.RaceListener; +import com.sap.sailing.domain.tracking.TrackedRace; + +public class TrackedRegattaTest { + + private final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark"); + private final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2"); + private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er"); + + private Course course = new CourseImpl("course", + Arrays.asList(new Waypoint[] { new WaypointImpl(mark), new WaypointImpl(mark2) })); + private DynamicTrackedRegatta regatta; + + @Before + public void setUp() { + regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, + EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, + /* startDate */ null, /* endDate */null, null, null, "a", null)); + } + + @Test(expected = TimeoutException.class) + public void testBug4429() throws Exception { + final Phaser addPhaser = new Phaser(2); + final CyclicBarrier removeBarrier = new CyclicBarrier(2); + + regatta.addRaceListener(new RaceListener() { + @Override + public void raceRemoved(TrackedRace trackedRace) { + try { + removeBarrier.await(1, TimeUnit.SECONDS); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void raceAdded(TrackedRace trackedRace) { + try { + addPhaser.arriveAndAwaitAdvance(); + addPhaser.arriveAndAwaitAdvance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + + DynamicTrackedRace race1 = createRace("R1"); + Thread thread1 = new Thread(() -> { + regatta.addTrackedRace(race1); + }); + thread1.start(); + // This ensures, that the add event is being processed but is not finished because + // this unblocks the first arriveAndAwaitAdvance() in raceAdded, but not the second. + // This way, the raceRemoved(...) call is expected to not be started because it has + // to wait for the raceAdded(...) call to have finished. + addPhaser.arriveAndAwaitAdvance(); + + Thread thread2 = new Thread(() -> { + regatta.removeTrackedRace(race1); + }); + thread2.start(); + // If the implementation ensures that the events are fired in order, + // the removeBarrier will run into a TimeoutException because the addBarrier + // is not solved and while the first event is processed the second one + // should not be started to be processed. + try { + removeBarrier.await(10, TimeUnit.MILLISECONDS); + // if this line is reached, the order of events is not correctly ensured + Assert.fail(); + } finally { + addPhaser.forceTermination(); + try { + // solved the barrier if one hangs at this point + // a TimeoutException can occur but we do not care about it + removeBarrier.await(10, TimeUnit.MILLISECONDS); + } catch (Exception e) { + } + thread1.join(1000); + thread2.join(1000); + } + + } + + private DynamicTrackedRace createRace(String name) { + RaceDefinition race1 = new RaceDefinitionImpl(name, course, boatClass, Collections.emptyList()); + return new DynamicTrackedRaceImpl(regatta, race1, Collections. emptyList(), + EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new, + mock(RaceLogResolver.class)); + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java index 3915af62602..bdfacf6644e 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractTrackedRegattaAndRaceObserver.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.tracking; import java.io.Serializable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; @@ -134,10 +135,24 @@ public abstract class AbstractTrackedRegattaAndRaceObserver implements TrackedRe * Called by {@link AbstractTrackedRegattaAndRaceObserver} when the {@link TrackedRegatta} was removed or the * {@link TrackedRegatta} shouldn't be tracked anymore (this is e.g. the case in replication state). */ - public synchronized void stop() { - trackedRegatta.removeRaceListener(raceListener); - knownTrackedRaces.keySet().forEach(this::remove); - knownTrackedRaces.clear(); + public void stop() { + try { + // It's necessary that we wait for the returned future to be completed + // to ensure that all regular removals are correctly processed before + // manually removing the left over ones (see below). + // This is now necessary due to the fact that TrackedRegattaImpl fires events concurrently, + // which makes it possible to receive the removal of the TrackedRegatta before the removals + // for the contained TrackedRaces are received. + trackedRegatta.removeRaceListener(raceListener).get(); + } catch (InterruptedException | ExecutionException e) { + log.log(Level.SEVERE, "Error while waiting for the raceListener to be removed", e); + } + // Depending on the code path (e.g. replication) it's possible that we still track some TrackedRace at this point. + // We need to ensure that all listeners are correctly stopped to not leak any instances. + synchronized (this) { + knownTrackedRaces.keySet().forEach(this::remove); + knownTrackedRaces.clear(); + } } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java index aa4d5e17418..e752254ad08 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.tracking; import java.io.Serializable; +import java.util.concurrent.Future; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.Competitor; @@ -89,10 +90,12 @@ public interface TrackedRegatta extends Serializable { */ void addRaceListener(RaceListener listener); - void removeRaceListener(RaceListener listener); + /** + * Removes the given listener and returns a {@link Future} that will be completed + * when it is guaranteed that no more events will be fired to the listener. + */ + Future removeRaceListener(RaceListener listener); int getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException; - void removeTrackedRace(RaceDefinition raceDefinition); - } \ No newline at end of file diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java index c2a16855e18..e1185b9adfc 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java @@ -6,10 +6,15 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,6 +54,24 @@ public class TrackedRegattaImpl implements TrackedRegatta { private final Map trackedRaces; private transient ConcurrentMap raceListeners; + + /** + * This {@link WorkQueue} is used to do all event related work. This ensures that e.g. events are fired in order. + * The following cases need to be handles through this queue: + *
    + *
  • Firing events when adding/removing {@link TrackedRace} instances (see {@link #enqueEvent}). The list of + * listeners to fire the event to need to be the list of listeners existing when enqueuing the event. This ensures + * that newly added listeners only receive events after the initial {@link TrackedRace} instances are delivered to + * this listener.
  • + *
  • Firing events for the already existing {@link TrackedRace} instances when adding a new listener (see + * {@link #addRaceListener(RaceListener)}). This ensures that all events are correctly fired to this listener that + * are triggered after the listener was added while suppressing inconsistent events before/while the initial + * {@link TrackedRace} instances are delivered to this listener.
  • + *
  • Completing the future returned by {@link #removeRaceListener(RaceListener)} to ensure that the receiver gets + * to know when it is guaranteed that no more event will be fired to the listener. + *
+ */ + private transient WorkQueue eventQueue; public TrackedRegattaImpl(Regatta regatta) { super(); @@ -56,11 +79,13 @@ public class TrackedRegattaImpl implements TrackedRegatta { this.regatta = regatta; this.trackedRaces = new HashMap(); raceListeners = new ConcurrentHashMap(); + eventQueue = new WorkQueue(); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); this.raceListeners = new ConcurrentHashMap(); + eventQueue = new WorkQueue(); } @Override @@ -110,28 +135,25 @@ public class TrackedRegattaImpl implements TrackedRegatta { logger.info("adding tracked race for "+trackedRace.getRace()+" to tracked regatta "+getRegatta().getName()+ " with regatta hash code "+getRegatta().hashCode()); oldTrackedRace = trackedRaces.put(trackedRace.getRace(), trackedRace); + if (oldTrackedRace != trackedRace) { + notifyListenersAboutTrackedRaceAdded(trackedRace); + } } finally { unlockTrackedRacesAfterWrite(); } - if (oldTrackedRace != trackedRace) { - notifyListenersAboutTrackedRaceAdded(trackedRace); - } } protected void notifyListenersAboutTrackedRaceAdded(TrackedRace trackedRace) { - for (RaceListener listener : raceListeners.keySet()) { - listener.raceAdded(trackedRace); - } + enqueEvent(listener -> listener.raceAdded(trackedRace)); } - @Override - public void removeTrackedRace(RaceDefinition raceDefinition) { - lockTrackedRacesForWrite(); - try { - trackedRaces.remove(raceDefinition); - } finally { - unlockTrackedRacesAfterWrite(); - } + protected void enqueEvent(Consumer fireEventCallback) { + final Set listenersToInform = new HashSet<>(raceListeners.keySet()); + eventQueue.addWork(() -> { + for (RaceListener listener : listenersToInform) { + fireEventCallback.accept(listener); + } + }); } @Override @@ -139,19 +161,14 @@ public class TrackedRegattaImpl implements TrackedRegatta { lockTrackedRacesForWrite(); try { trackedRaces.remove(trackedRace.getRace()); + notifyListenersAboutTrackedRaceRemoved(trackedRace); } finally { unlockTrackedRacesAfterWrite(); } - // Fix for bug4414: put this into a separate thread to avoid deadlock caused by - // a synchronized RegattaListener.raceRemoved while holding the TrackedRegattaImpl.trackedRacesLock's write lock - // acquired in RacingEventServiceImpl.removeRace - new Thread(()->notifyListenersAboutTrackedRaceRemoved(trackedRace)).start(); } protected void notifyListenersAboutTrackedRaceRemoved(TrackedRace trackedRace) { - for (RaceListener listener : raceListeners.keySet()) { - listener.raceRemoved(trackedRace); - } + enqueEvent(listener -> listener.raceRemoved(trackedRace)); } @Override @@ -216,27 +233,37 @@ public class TrackedRegattaImpl implements TrackedRegatta { @Override public void addRaceListener(RaceListener listener) { - final List trackedRacesCopy = new ArrayList<>(); lockTrackedRacesForRead(); try { raceListeners.put(listener, listener); + final List trackedRacesCopy = new ArrayList<>(); Util.addAll(getTrackedRaces(), trackedRacesCopy); + eventQueue.addWork(() -> { + for (TrackedRace trackedRace : trackedRacesCopy) { + listener.raceAdded(trackedRace); + } + }); } finally { unlockTrackedRacesAfterRead(); } - for (TrackedRace trackedRace : trackedRacesCopy) { - listener.raceAdded(trackedRace); - } } @Override - public void removeRaceListener(RaceListener listener) { + public Future removeRaceListener(RaceListener listener) { + final CompletableFuture result = new CompletableFuture(); lockTrackedRacesForRead(); try { - raceListeners.remove(listener); + if (raceListeners.remove(listener) != null) { + eventQueue.addWork(() -> { + result.complete(Boolean.TRUE); + }); + } else { + result.complete(Boolean.TRUE); + } } finally { unlockTrackedRacesAfterRead(); } + return result; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/WorkQueue.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/WorkQueue.java new file mode 100644 index 00000000000..3620c2ad5c2 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/WorkQueue.java @@ -0,0 +1,54 @@ +package com.sap.sailing.domain.tracking.impl; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Queue that executes {@link Runnable} instances in order on a distinct {@link Thread}. The {@link WorkQueue} ensures + * that a Thread is started when the first {@link Runnable} is added via {@link #addWork(Runnable)} and that the + * {@link Thread} is terminated after executing the last available {@link Runnable}. + */ +public class WorkQueue { + private static final Logger LOG = Logger.getLogger(WorkQueue.class.getName()); + + private final LinkedBlockingQueue queue; + private Thread workThread; + + public WorkQueue() { + queue = new LinkedBlockingQueue<>(); + } + + /** + * Adds the given {@link Runnable} to the {@link Queue} of work to be executed. + * Ensures that a Thread is started if there isn't one running yet. + */ + public synchronized void addWork(Runnable workToAdd) { + queue.add(workToAdd); + if (workThread == null) { + workThread = new Thread(this::doWork); + workThread.start(); + } + } + + private void doWork() { + while (true) { + final Runnable workToDo; + synchronized (this) { + workToDo = queue.poll(); + if (workToDo == null) { + workThread = null; + break; + } + } + if (workToDo != null) { + try { + workToDo.run(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Error while executing work in queue", e); + } + } + } + } +} diff --git a/java/com.sap.sailing.grib.test/src/com/sap/sailing/grib/test/SimpleGridFileReadingTest.java b/java/com.sap.sailing.grib.test/src/com/sap/sailing/grib/test/SimpleGridFileReadingTest.java index 490e4cb8019..269f8f8dbce 100644 --- a/java/com.sap.sailing.grib.test/src/com/sap/sailing/grib/test/SimpleGridFileReadingTest.java +++ b/java/com.sap.sailing.grib.test/src/com/sap/sailing/grib/test/SimpleGridFileReadingTest.java @@ -139,9 +139,9 @@ public class SimpleGridFileReadingTest { // alternatively for the 4-6 day forecast: http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.oceanic/VP.004-007/ // validate graphically at http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.oceanic/VP.004-007/ final Formatter errorLog = new Formatter(System.err); - Files.copy(new URL("http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.oceanic/VP.001-003/ds.wdir.bin").openStream(), + Files.copy(new URL("http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.conus/VP.001-003/ds.wdir.bin").openStream(), new File("resources/ds.wdir.bin").toPath(), StandardCopyOption.REPLACE_EXISTING); - Files.copy(new URL("http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.oceanic/VP.001-003/ds.wspd.bin").openStream(), + Files.copy(new URL("http://tgftp.nws.noaa.gov/SL.us008001/ST.expr/DF.gr2/DC.ndfd/AR.conus/VP.001-003/ds.wspd.bin").openStream(), new File("resources/ds.wspd.bin").toPath(), StandardCopyOption.REPLACE_EXISTING); FeatureDataset dataSetDirection = FeatureDatasetFactoryManager.open(FeatureType.ANY, "resources/ds.wdir.bin", /* task */ null, errorLog); diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index fb946ac243d..1460f643077 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.List; import java.util.NavigableSet; import java.util.Set; +import java.util.concurrent.Future; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; @@ -321,10 +322,6 @@ public class MockedTrackedRace implements DynamicTrackedRace { return null; } - @Override - public void removeTrackedRace(RaceDefinition raceDefinition) { - } - @Override public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable sidelines, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, @@ -350,7 +347,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void removeRaceListener(RaceListener listener) { + public Future removeRaceListener(RaceListener listener) { + return null; } }; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 53ce0c014f3..2bf2bdf7e05 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -29,7 +29,7 @@ playSpeedHelp=Utilice "1" para velocidad de reproducción normal, "2" para doble playModeLive=En directo playModeReplay=Modo de reproducción timeDelay=Retraso -timeDelayHelp=Deje el campo vacío para reproducir arbitrariamente; indique una cantidad de segundos de retraso (p.ej., «60» para un minuto, o bien «0») previa a la transmisión de datos en directo. +timeDelayHelp=Deje el campo vacío para reproducir arbitrariamente; indique una cantidad de segundos de retraso (p.ej., "60" para un minuto, o bien "0") previa a la transmisión de datos en directo. speedInKnots=Velocidad (nudos) fromDeg=Desde (grados) noRacesYet=Todavía no hay ninguna carrera. @@ -141,7 +141,7 @@ delayMustBeNonNegative=El retraso debe ser mayor que cero. chooseUpdateIntervalOfAtLeastOneSecond=Seleccione un intervalo de actualización de al menos un segundo errorSettingWindForRace=Error al configurar viento para la carrera errorFetchingWindInformationForRace=Error al obtener información de viento para la carrera -errorWhileTryingToSetWindSourceForRace=Error al intentar fijar el indicador «viento empieza a barlovento» +errorWhileTryingToSetWindSourceForRace=Error al intentar fijar el indicador "viento empieza a barlovento" inEvent=en evento to=a toSide=a @@ -233,6 +233,32 @@ hoverOverAPoint=Pase el cursor por encima de un punto para obtener detalles noSelection=Ninguna selección raceIsKnownToStartUpwind=La carrera empieza con ceñida events=Eventos +pairingList=Lista de emparejamientos +pairingLists=Listas de emparejamientos +pairingListCreationInfo=Inicialmente se calculará un plantilla que contenga solo los números de competidores y que pueda aplicarse a otras clasificaciones. Si el recuento de competidores no es divisible por recuento de embarcaciones sin remanente, se introducirán marcadores de posición en blanco en la lista de emparejamientos. \ Además, si el recuento de competidores difiere del recuento de competidores registrado en la clasificación real, la lista de emparejamientos no podrá aplicarse a los registros de carrera. \ El recuento de flights, que se utiliza para generar una lista de emparejamientos, se fija con todos los flights de la clasificación que no son carreras de medalla. +setCompetitors=Fije el recuento de competidores: +seriesHint=Seleccione una o más series que deberían estar en una lista de emparejamientos: +amountOfFlightRepeats=Cantidad de repeticiones de flight +insertIntoRegatta=Insertar en regata +recalculate=Recalcular +csvExport=Exportar como CSV +printView=Imprimir vista preliminar +print=Imprimir +invalidCompetitorCount=Recuento de competidores no válido +invalidFlightMultiplier=Repeticiones de flight no válidas +invalidSeriesSelection=Selección de series no válida +flightsMustBeAMultipleOfMultiplier=Los flights deben ser múltiplos de multiplicador de flight +parameters=Parámetros +numberOfFlights=Cantidad de flights +numberOfFleets=Cantidad de carreras por flight +numberOfCompetitors=Cantidad de competidores +numberOfRaces=Número de carreras +quality=Calidad +pairingListTemplate=Plantilla de lista de emparejamientos +printHint=Imprime la versión aplicada +blockedApplyButton=Los competidores registrados difieren de los competidores de la lista de emparejamientos +multiplierInfo=Multiplica los flights y los crea uno junto a otro de modo que sea posible una competición con menos modificaciones de embarcación. +noPairingListAvailable=La función de impresión solo está disponible si ya se han aplicado las listas de emparejamientos a los registros de carrera de las clasificaciones seleccionadas. settingsForComponent=Opciones para {0} noEventsFound=No se han encontrado eventos noEventSelected=Ningún evento seleccionado @@ -351,6 +377,7 @@ replicatingFromMaster=Replicando desde el maestro {0}:{2}, puerto de mensajería showWindSpeedSeries=Mostrar datos de velocidad del viento showWindDirectionSeries=Mostrar datos de dirección del viento fleet=Flota +boat=Bote boatClass=Clase de bote setDelayToLive=Fijar retraso en en directo. pleaseEnterNonEmptyVenue=Indique un lugar de celebración. @@ -524,7 +551,6 @@ additionalUrlSettings=Opciones de URL adicionales restrictToSingleSelectedRace=Restringir a la carrera individual seleccionada expandSelectedRace=Expandir carrera seleccionada showRaceDetails=Permitir detalles de carrera -numberOfRaces=Número de carreras numberOfRacesMustBeNonNegativeNumber=Indique el número de últimas carreras a visualizar chooseTheWayYouSelectRaces=Elija la manera de seleccionar carreras selectFromAllRaces=Seleccionar desde todas las carreras @@ -1393,7 +1419,7 @@ racesQuickfinder=Carreras Quickfinder profileQuickfinder=Perfil Quickfinder statistics=Estadísticas statisticsFor=Estadísticas para {0} -impressions=Impresiones +impressions=Instantáneas notAvailableOnMobileMessage=Este contenido no está disponible para la versión móvil. ¿Desea pasar a la versión de escritorio? gotoDesktop=Ir a la versión de escritorio goBack=Volver @@ -1454,6 +1480,7 @@ plainResultsPresenter=Presentador de resultados simples resultsChart=Gráfico de resultados tabbedResultsPresenter=Presentador de resultados por pestañas polarResultsPresenter=Presentador de resultados de coordenadas polares +maneuverSpeedDetailsResultsPresenter=Presentador de resultados detallados de velocidad de maniobra dataMiningRetrieval=Recuperación de datos actionWatch=Observar actionAnalyze=Analizar @@ -1463,8 +1490,12 @@ doYouWantToCreateADefaultRegattaLeaderboard=¿Desea crear una tabla de clasifica copyCourse=Copiar regata copyCompetitors=Copiar competidores smartphoneTracking=Rastreo de smartphone -flightsCount={0,number} vuelos -flightsCount[one]={0,number} vuelo +titelOfChooseNameDialog=Seleccione un nombre +ownPrefix=Prefijo propio +defaultName=Por defecto +exampleTextForName=Su nombre aparece como: +flightsCount={0,number} flights +flightsCount[one]={0,number} flight viewQueryDefinition=Ver definición de consulta queryDefinitionViewer=Visor de definición de consulta groupAverageAscending=Promedio de grupo (ascendente) @@ -1702,7 +1733,7 @@ headerLogo=Sailing Analytics headerSearchPlaceholder=Buscar mainEventShowAll=Mostrar todos los eventos mainEventTitle=Últimos eventos de Sailing World -mainMediaSubtitle=Recomendamos perderse en las mejores y más recientes impresiones de su evento de navegación favorito. +mainMediaSubtitle=Recomendamos perderse en las mejores y más recientes instantáneas de su evento de navegación favorito. mainMediaTitle=Fotógrafos amantes de la navegación mainMediaVideosSubtitle=Sumérjase en el mundo de la navegación y revise los eventos más recientes para más vídeos. mainMediaVideosTitle=Entradas de vídeo recientes @@ -1873,6 +1904,26 @@ showEstimatedDuration=Mostrar duración estimada estimatedDuration=Duración estimada: countDown=Evento comienza en {0} {1} countDownEnd=El evento comenzará en pocos segundos +maneuverSpeedDetails=Detalles de velocidad de maniobra +maneuverDirectionNormalization=Normalización de dirección de maniobra +maneuver=Maniobra +minManeuverDuration=Duración mínima de maniobra (s) +maxManeuverDuration=Duración máxima de maniobra (s) +minManeuverEnteringSpeedInKnots=Velocidad mínima de entrada de maniobra (kts) +maxManeuverEnteringSpeedInKnots=Velocidad máxima de entrada de maniobra (kts) +minManeuverExitingSpeedInKnots=Velocidad mínima de salida de maniobra (kts) +maxManeuverExitingSpeedInKnots=Velocidad máxima de salida de maniobra (kts) +minManeuverEnteringAbsTWA=TWA absoluto de velocidad mínima de entrada de maniobra +maxManeuverEnteringAbsTWA=TWA absoluto de velocidad máxima de entrada de maniobra +minManeuverExitingAbsTWA=TWA absoluto de velocidad mínima de salida de maniobra +maxManeuverExitingAbsTWA=TWA absoluto de velocidad máxima de salida de maniobra +maneuverDirectionEqualWeightingEnabled=Activar ponderación 50%-50% para la dirección de maniobra en agregaciones de "valor por TWA" +minValue=Valor mínimo +maxValue=Valor máximo +zeroTo360AxisLabeling=Etiqueta de eje 0-360° +exportStatisticsCurveToCsv=Curva estadística de exportación a CSV +csvCopiedToClipboard=Se ha copiado el contenido CSV al portapapeles. Péguelo donde quiera. +minDataCount=Registro de datos mínimo moreLoginInformationHeadline=Ventajas de registrarse en SAP Sailing Analytics moreLoginInformationIntroduction=Como usuario registrado de SAP Sailing Analytics dispone de acceso a la funcionalidad que no está disponible para usuarios anónimos. Simplemente regístrese para acceder a las funciones que se muestras más abajo. moreLoginInformationSectionUserSettingsHeading=Opciones de usuario @@ -1918,6 +1969,7 @@ currentStbdRudderRake=Inclinación del timón a estribor currentStbdRudderRakeTooltip=La inclinación actual del timón a estribor currentMastRotationInDegree=Rotación del mástil currentMastRotationInDegreeTooltip=La rotación actual del mástil en grados +mainCurveAnalysis=Analizar solo curva principal finishingTime=Hora de llegada finishTimeString=Hora de fin setFinishingAndFinishTime=Fijar hora de llegada y de fin @@ -1969,3 +2021,15 @@ totalDurationFoiledInSeconds=Tiempo total con foils totalDistanceFoiledInMeters=Distancia total con foils totalDurationFoiledInSecondsTooltip=Tiempo total con foils, a lo largo de todas la clasificación totalDistanceFoiledInMetersTooltip=Distancia total con foils, a lo largo de todas la clasificación +threeSixtyVideoHint=Se trata de un vídeo de 360º, la cámara se puede girar +processingMP4=MP4 se analiza (esto durará unos segundos) +resetStartTimeToDefault=Fijar en Por defecto +importFullExpeditionData=Importar datos de expedición completos +importCanceledNoCompetitorAdded=No se ha añadido ningún competidor. Se cancelará la importación. +importCanceledByUser=Importación cancelada +importFinished=Datos de importación finalizados +importFinishedMessage=Datos de expedición importados correctamente. +importFinishedGotoRaceboard=Abrir Raceboard +importFinishedGotoEvent=Abrir evento +chooseAName=Seleccione un nombre +unexpectedErrorDuringFileImport=Se ha producido un error inesperado durante la carga del fichero. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index f3db1e03d4f..4ea3990fe20 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -233,6 +233,32 @@ hoverOverAPoint=Placez le curseur sur un point pour obtenir des détails. noSelection=Aucune sélection raceIsKnownToStartUpwind=La course a commencé avec une portion de parcours au vent. events=Manifestations +pairingList=Liste d''appariement +pairingLists=Listes d''appariement +pairingListCreationInfo=Tout d''abord, un modèle contenant uniquement le nombre de concurrents sera calculé. Il pourra être appliqué à d''autres palmarès. Si la division du nombre de concurrents par le nombre de bateaux ne permet pas d''obtenir un nombre entier, des caractères génériques vides seront ajoutés à la liste d''appariement. \ En outre, si le nombre de concurrents ne correspond pas au nombre de concurrents inscrits au palmarès actuel, la liste d''appariement ne pourra pas être appliquée aux journaux de course. \ Le nombre de flights, utilisé pour générer une liste d''appariement, est défini par tous les flights du palmarès, hors courses médaillées. +setCompetitors=Veuillez définir le nombre de concurrents : +seriesHint=Veuillez sélectionner une ou plusieurs séries à ajouter à une liste de paires : +amountOfFlightRepeats=Nombre de répétitions de flights +insertIntoRegatta=Ajouter à la régate +recalculate=Recalculer +csvExport=Exporter au format CSV +printView=Aperçu avant impression +print=Imprimer +invalidCompetitorCount=Nombre de concurrents non valide +invalidFlightMultiplier=Répétitions de flights non valides +invalidSeriesSelection=Sélection de séries non valide +flightsMustBeAMultipleOfMultiplier=Les flights doivent être un multiple du multiplicateur de flight. +parameters=Paramètres +numberOfFlights=Nombre de flights +numberOfFleets=Nombre de courses par flight +numberOfCompetitors=Nombre de concurrents +numberOfRaces=Nombre de courses +quality=Qualité +pairingListTemplate=Modèle de liste d''appariement +printHint=Imprime la version appliquée +blockedApplyButton=Le nombre de concurrents inscrits ne correspond pas au nombre de concurrents de la liste d''appariement. +multiplierInfo=Multipliez les flights et créez-les les uns après les autres pour obtenir une compétition avec le moins de bateaux possible. +noPairingListAvailable=La fonction d''impression est uniquement disponible lorsqu''une liste d''appariement a déjà été appliquée aux journaux de course des palmarès sélectionnés. settingsForComponent=Options pour {0} noEventsFound=Aucune manifestation trouvée noEventSelected=Aucune manifestation sélectionnée @@ -351,6 +377,7 @@ replicatingFromMaster=Réplication du serveur maître {0}:{2}, port de messageri showWindSpeedSeries=Afficher données de vitesse du vent showWindDirectionSeries=Afficher données de direction du vent fleet=Flotte +boat=Bateau boatClass=Catégorie de bateau setDelayToLive=Définir décalage par rapport au direct pleaseEnterNonEmptyVenue=Saisissez un lieu de manifestation. @@ -524,7 +551,6 @@ additionalUrlSettings=Options d''URL supplémentaires restrictToSingleSelectedRace=Limiter à la seule course sélectionnée expandSelectedRace=Développer course sélectionnée showRaceDetails=Autoriser détails de la course -numberOfRaces=Nombre de courses numberOfRacesMustBeNonNegativeNumber=Saisissez le nombre de dernières courses à afficher. chooseTheWayYouSelectRaces=Indiquez de quelle façon vous souhaitez sélectionner les courses selectFromAllRaces=Sélectionner parmi toutes les courses @@ -1454,6 +1480,7 @@ plainResultsPresenter=Visualiseur de résultats bruts resultsChart=Graphique des résultats tabbedResultsPresenter=Visualiseur de résultats avec onglets polarResultsPresenter=Visualiseur de résultats polaires +maneuverSpeedDetailsResultsPresenter=Visualiseur des résultats : détails sur la vitesse lors de la manœuvre dataMiningRetrieval=Récupération de données actionWatch=Regarder actionAnalyze=Analyser @@ -1463,6 +1490,10 @@ doYouWantToCreateADefaultRegattaLeaderboard=Voulez-vous créer un palmarès de r copyCourse=Copier course copyCompetitors=Copier concurrents smartphoneTracking=Suivi smartphone +titelOfChooseNameDialog=Indiquer un nom +ownPrefix=Préfixe personnalisé +defaultName=Par défaut +exampleTextForName=Votre nom apparaît de la manière suivante : flightsCount={0,number} départs de deux équipages flightsCount[one]={0,number} départ de deux équipages viewQueryDefinition=Afficher la définition de la requête @@ -1873,6 +1904,26 @@ showEstimatedDuration=Afficher la durée estimée estimatedDuration=Durée estimée : countDown=La manifestation commence dans {0} {1} countDownEnd=La manifestation commencera dans quelques secondes. +maneuverSpeedDetails=Détails sur la vitesse lors de la manœuvre +maneuverDirectionNormalization=Normalisation du cap pendant la manœuvre +maneuver=Manœuvre +minManeuverDuration=Durée min. de la manœuvre (s) +maxManeuverDuration=Durée max. de la manœuvre (s) +minManeuverEnteringSpeedInKnots=Vitesse min. d''entrée de manœuvre (nds) +maxManeuverEnteringSpeedInKnots=Vitesse max. d''entrée de manœuvre (nds) +minManeuverExitingSpeedInKnots=Vitesse min. de sortie de manœuvre (nds) +maxManeuverExitingSpeedInKnots=Vitesse max. de sortie de manœuvre (nds) +minManeuverEnteringAbsTWA=TWA absolu min. d''entrée de manœuvre +maxManeuverEnteringAbsTWA=TWA absolu max. d''entrée de manœuvre +minManeuverExitingAbsTWA=TWA absolu min. de sortie de manœuvre +maxManeuverExitingAbsTWA=TWA absolu max. de sortie de manœuvre +maneuverDirectionEqualWeightingEnabled=Activer pondération 50 %-50 % pour le cap pendant la manœuvre dans les agrégations "valeur par TWA" +minValue=Valeur min. +maxValue=Valeur max. +zeroTo360AxisLabeling=Étiquetage axe 0-360° +exportStatisticsCurveToCsv=Exporter la courbe des statistiques au format CSV +csvCopiedToClipboard=Le contenu du CSV a été copié dans le presse-papiers. Copiez-le où vous le souhaitez. +minDataCount=Nombre de données min. moreLoginInformationHeadline=Avantages de SAP Sailing Analytics moreLoginInformationIntroduction=Si vous êtes un utilisateur inscrit sur SAP Sailing Analytics, vous avez accès à des fonctionnalités qui ne sont pas disponibles pour les utilisateurs anonymes. Inscrivez-vous pour accéder à toutes les fonctionnalités indiquées ci-dessous. moreLoginInformationSectionUserSettingsHeading=Options utilisateur @@ -1918,6 +1969,7 @@ currentStbdRudderRake=Inclinaison du gouvernail tribord currentStbdRudderRakeTooltip=Inclinaison actuelle du gouvernail tribord currentMastRotationInDegree=Rotation du mât currentMastRotationInDegreeTooltip=Rotation actuelle du mât en degrés +mainCurveAnalysis=Analyser uniquement la courbe principale finishingTime=Heure d''arrivée du premier finishTimeString=Heure de fermeture de la ligne d''arrivée setFinishingAndFinishTime=Définir heure d''arrivée du premier et de fermeture de la ligne d''arrivée @@ -1969,3 +2021,15 @@ totalDurationFoiledInSeconds=Temps total passé en planing totalDistanceFoiledInMeters=Distance totale parcourue en planing totalDurationFoiledInSecondsTooltip=Temps total passé en planing, sur la totalité du palmarès totalDistanceFoiledInMetersTooltip=Distance totale parcourue en planing, sur la totalité du palmarès +threeSixtyVideoHint=Il s''agit d''une vidéo à 360°. Vous pouvez tourner la caméra. +processingMP4=Analyse du Mp4 en cours (patientez quelques secondes). +resetStartTimeToDefault=Act. valeur par défaut +importFullExpeditionData=Importer toutes les données d''Expedition +importCanceledNoCompetitorAdded=Aucun concurrent n''a été ajouté ! L''importation sera annulée. +importCanceledByUser=Importation annulée ! +importFinished=Importation des données terminée +importFinishedMessage=Données d''Expedition correctement importées +importFinishedGotoRaceboard=Ouvrir tableau des courses +importFinishedGotoEvent=Ouvrir manifestation +chooseAName=Indiquer un nom +unexpectedErrorDuringFileImport=Une erreur inattendue s''est produite lors du chargement du fichier. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 3137b3eb3bb..6ecb77223cd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -236,6 +236,32 @@ hoverOverAPoint=詳細についてはポイントをホバー noSelection=未選択 raceIsKnownToStartUpwind=アップウィンドレグでレーススタート events=イベント +pairingList=対戦表 +pairingLists=対戦表 +pairingListCreationInfo=まず最初に、競技者番号のみが含まれていて、他のリーダーボードに適用できるテンプレートが計算されます。競技者カウントが艇カウントで割り切れない場合、空のプレースホルダーが対戦表に入力されます。 \ また、競技者カウントが実際のリーダーボードにある登録済の競技者カウントと等しくない場合、対戦表をレースログに適用することはできません。 \ 対戦表を生成するために使用されるフライトのカウントは、リーダーボードの全フライト (非メダルレース) によって設定されます。 +setCompetitors=競技者カウントを設定してください: +seriesHint=対戦表にあることが必要なシリーズを 1 つ以上選択してください: +amountOfFlightRepeats=フライト繰返回数 +insertIntoRegatta=レガッタに挿入 +recalculate=再計算 +csvExport=エクスポート (CSV) +printView=印刷プレビュー +print=印刷 +invalidCompetitorCount=競技者カウントが無効 +invalidFlightMultiplier=フライト繰返回数が無効 +invalidSeriesSelection=シリーズ選択が無効 +flightsMustBeAMultipleOfMultiplier=フライト回数はフライト乗数の倍数であることが必要 +parameters=パラメータ +numberOfFlights=フライト回数 +numberOfFleets=フライト毎のレース回数 +numberOfCompetitors=競技者数 +numberOfRaces=レース数 +quality=質 +pairingListTemplate=対戦表テンプレート +printHint=適用バージョンを印刷 +blockedApplyButton=登録されている競技者数が対戦表にある競技者数と同じでありません +multiplierInfo=複数のフライトを艇変更がより少なく競技が可能となるよう互いに隣り合わせに作成 +noPairingListAvailable=印刷機能は選択したリーダーボードレースログに対戦表がすでに適用されている場合にのみ利用が可能です。 settingsForComponent={0} の設定 noEventsFound=イベントが見つかりませんでした noEventSelected=イベントが選択されていません @@ -354,6 +380,7 @@ replicatingFromMaster=マスタ {0}:{2}、メッセージングポート {3}:{1} showWindSpeedSeries=風速データ表示 showWindDirectionSeries=風向データ表示 fleet=フリート +boat=艇 boatClass=艇種 setDelayToLive=ライブに遅延を設定 pleaseEnterNonEmptyVenue=開催地を入力してください。 @@ -527,7 +554,6 @@ additionalUrlSettings=追加 URL 設定 restrictToSingleSelectedRace=単一の選択レースに制限 expandSelectedRace=選択レースを拡大 showRaceDetails=レース詳細を許容 -numberOfRaces=レース数 numberOfRacesMustBeNonNegativeNumber=表示する最終レース数を入力してください chooseTheWayYouSelectRaces=レース選択方法の選択 selectFromAllRaces=全レースから選択 @@ -1457,6 +1483,7 @@ plainResultsPresenter=プレーン結果表示ツール resultsChart=結果チャート tabbedResultsPresenter=タブ結果表示ツール polarResultsPresenter=ポーラー結果表示ツール +maneuverSpeedDetailsResultsPresenter=マニューバー速度詳細結果表示ツール dataMiningRetrieval=データ取得 actionWatch=視聴 actionAnalyze=分析 @@ -1466,6 +1493,10 @@ doYouWantToCreateADefaultRegattaLeaderboard=レガッタリーダーボードを copyCourse=コースのコピー copyCompetitors=競技者のコピー smartphoneTracking=スマートフォン追跡 +titelOfChooseNameDialog=名前を選択 +ownPrefix=自身のプレフィクス +defaultName=デフォルト +exampleTextForName=名前は次のようになります: flightsCount={0,number} フライト flightsCount[one]={0,number} フライト viewQueryDefinition=クエリ定義の表示 @@ -1876,6 +1907,26 @@ showEstimatedDuration=推定持続時間の表示 estimatedDuration=推定持続時間: countDown=イベントは {0} {1} に開始 countDownEnd=このイベントはあと数秒で開始します +maneuverSpeedDetails=マニューバー速度詳細 +maneuverDirectionNormalization=マニューバー方向正規化 +maneuver=マニューバー +minManeuverDuration=マニューバー最小持続時間 (s) +maxManeuverDuration=マニューバー最大持続時間 (s) +minManeuverEnteringSpeedInKnots=マニューバー最小進入速度 (kts) +maxManeuverEnteringSpeedInKnots=マニューバー最大進入速度 (kts) +minManeuverExitingSpeedInKnots=マニューバー最小脱出速度 (kts) +maxManeuverExitingSpeedInKnots=マニューバー最大脱出速度 (kts) +minManeuverEnteringAbsTWA=マニューバー最小進入絶対 TWA +maxManeuverEnteringAbsTWA=マニューバー最大進入絶対 TWA +minManeuverExitingAbsTWA=マニューバー最小脱出絶対 TWA +maxManeuverExitingAbsTWA=マニューバー最大脱出絶対 TWA +maneuverDirectionEqualWeightingEnabled=マニューバー方向の 50%-50% 重み付けを有効化 ("TWA 別の値" の集約) +minValue=最小値 +maxValue=最大値 +zeroTo360AxisLabeling=0-360° 軸のラベリング +exportStatisticsCurveToCsv=統計曲線を CSV にエクスポート +csvCopiedToClipboard=CSV の内容がクリップボードにコピーされました。好きな場所にペーストしてください。 +minDataCount=最小データカウント moreLoginInformationHeadline=SAP Sailing Analytics にサインインする利点 moreLoginInformationIntroduction=SAP Sailing Analytics の登録ユーザーには、匿名ユーザーに許されていない機能へのアクセス権限があります。登録を行って、下記の機能にアクセスしてください。 moreLoginInformationSectionUserSettingsHeading=ユーザー設定 @@ -1888,7 +1939,7 @@ anniversaryUnitText=レース anniversaryUnitText[one]=レース anniversaryMajorCountdownTeaser=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 anniversaryMajorCountdownTeaser[one]=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 -anniversaryMajorCountdownDescription=SAP では、www.sapsailing.com における {0,number,#,###} 番目のレースを祝うことにしています。どのレースがゴールテープを切るでしょうか。その記念レースの主催者には、義援目的で合計で 10,000 ユーロが贈呈されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 +anniversaryMajorCountdownDescription=SAP では、www.sapsailing.com における {0,number,#,###} 番目のレースを祝うことにしています。どのレースがゴールテープを切るでしょうか。その記念レースの主催者には、義援目的で合計で 10,000 ユーロが贈呈されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 anniversaryRepdigitCountdownTeaser=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 anniversaryRepdigitCountdownTeaser[one]=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 anniversaryRepdigitCountdownDescription=SAP では、縁起のいい番号のレースを祝うことにしています。www.sapsailing.com における {0,number, #,###} 番目のレースは誰が行うことになるでしょうか。そのレースの参加者には、SAP から最上級のサマーフェスティバルが無料で提供されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 @@ -1921,6 +1972,7 @@ currentStbdRudderRake=右舷ラダー傾斜角 currentStbdRudderRakeTooltip=現在の右舷ラダー傾斜角 currentMastRotationInDegree=マスト回転 currentMastRotationInDegreeTooltip=現在のマスト回転 (度) +mainCurveAnalysis=主曲線のみ分析 finishingTime=フィニッシュ予定時刻 finishTimeString=フィニッシュ時刻 setFinishingAndFinishTime=フィニッシュ予定時刻とフィニッシュ時刻の設定 @@ -1972,3 +2024,15 @@ totalDurationFoiledInSeconds=合計フォイリング時間 totalDistanceFoiledInMeters=合計フォイリング距離 totalDurationFoiledInSecondsTooltip=合計フォイリング時間 (リーダーボード全体) totalDistanceFoiledInMetersTooltip=合計フォイリング距離 (リーダーボード全体) +threeSixtyVideoHint=これは 360 度の動画であり、カメラを回転できます +processingMP4=Mp4 が解析されます (これには数秒かかります) +resetStartTimeToDefault=デフォルトに設定 +importFullExpeditionData=Expedition データ全体をインポート +importCanceledNoCompetitorAdded=競技者が追加されませんでした。インポートは取り消されます。 +importCanceledByUser=インポートが取り消されました +importFinished=データのインポートが終了 +importFinishedMessage=Expedition データがインポートされました。 +importFinishedGotoRaceboard=レースボードを開く +importFinishedGotoEvent=イベントを開く +chooseAName=名称を選択 +unexpectedErrorDuringFileImport=ファイルのアップロード時に予期しないエラーが発生しました。 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index d07b26923ae..0f76cd0e86b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -233,6 +233,32 @@ hoverOverAPoint=Passar com o mouse sobre um ponto para detalhes noSelection=Nenhuma seleção raceIsKnownToStartUpwind=A corrida começa com perna de contravento events=Eventos +pairingList=Lista de pares +pairingLists=Listas de pares +pairingListCreationInfo=Primeiro será calculado um modelo que contém somente os números dos competidores e pode ser aplicado a outros painéis de classificação. Se a contagem de competidores não for divisível pela contagem de barcos sem restante, os caracteres de preenchimento em branco serão inseridos na lista de pares. \ Além disso, se a contagem de competidores for diferente da contagem de competidores registrados no painel de classificação real, a lista de pares não pode ser aplicada aos registros das corridas. \ A contagem dos voos, que é utilizada para gerar uma lista de pares, é definida por todos os voos do painel de classificação que não sejam corridas para medalha. +setCompetitors=Defina a contagem dos competidores: +seriesHint=Selecione uma ou mais séries que devam estar em uma lista de pares: +amountOfFlightRepeats=Número de repetições de voos +insertIntoRegatta=Inserir na regata +recalculate=Recalcular +csvExport=Exportar como CSV +printView=Visualizar impressão +print=Imprimir +invalidCompetitorCount=Contagem de competidores inválida +invalidFlightMultiplier=Repetições de voos inválidas +invalidSeriesSelection=Seleção de séries inválida +flightsMustBeAMultipleOfMultiplier=Os voos devem ser um múltiplo do multiplicador de voos +parameters=Parâmetros +numberOfFlights=Número de voos +numberOfFleets=Número de corridas por voo +numberOfCompetitors=Número de competidores +numberOfRaces=Número de corridas +quality=Qualidade +pairingListTemplate=Modelo da lista de pares +printHint=Imprime a versão aplicada +blockedApplyButton=Os competidores registrados são diferentes dos competidores da lista de pares! +multiplierInfo=Multiplica os voos e cria-os uns junto aos outros de modo que seja possível uma competição com menos mudanças de barco. +noPairingListAvailable=A função de impressão só está disponível caso já tenha sido aplicada uma lista de pares aos registros de corridas dos painéis de classificação selecionados. settingsForComponent=Configurações para {0} noEventsFound=Nenhum evento encontrado noEventSelected=Nenhum evento selecionado @@ -351,6 +377,7 @@ replicatingFromMaster=Replicando a partir do mestre {0}:{2}, porta de mensagens showWindSpeedSeries=Visualizar dados de velocidade do vento showWindDirectionSeries=Visualizar dados de direções do vento fleet=Flotilha +boat=Barco boatClass=Classe de barcos setDelayToLive=Definir atraso ao vivo pleaseEnterNonEmptyVenue=Insira um local. @@ -524,7 +551,6 @@ additionalUrlSettings=Configurações de URL adicionais restrictToSingleSelectedRace=Restringir a uma única corrida selecionada expandSelectedRace=Expandir corrida selecionada showRaceDetails=Permitir detalhes da corrida -numberOfRaces=Número de corridas numberOfRacesMustBeNonNegativeNumber=Insira o número das últimas corridas a serem exibidas chooseTheWayYouSelectRaces=Selecionar a forma de seleção das corridas selectFromAllRaces=Selecionar a partir de todas as corridas @@ -1454,6 +1480,7 @@ plainResultsPresenter=Apresentador de resultados simples resultsChart=Diagrama de resultados tabbedResultsPresenter=Apresentador de resultados por fichas polarResultsPresenter=Apresentador de resultados da carta polar +maneuverSpeedDetailsResultsPresenter=Apresentador de resultados detalhados da velocidade da manobra dataMiningRetrieval=Obtenção de dados actionWatch=Ver actionAnalyze=Analisar @@ -1463,6 +1490,10 @@ doYouWantToCreateADefaultRegattaLeaderboard=Você quer criar um painel de classi copyCourse=Copiar percurso copyCompetitors=Copiar competidores smartphoneTracking=Rastreamento do Smartphone +titelOfChooseNameDialog=Selecionar um nome +ownPrefix=Prefixo próprio +defaultName=Padrão +exampleTextForName=Seu nome é parecido com: flightsCount={0,number} voos flightsCount[one]={0,number} voo viewQueryDefinition=Ver definição da consulta @@ -1873,6 +1904,26 @@ showEstimatedDuration=Exibir duração estimada estimatedDuration=duração estimada: countDown=O evento começa em {0} {1} countDownEnd=O evento irá começar dentro de alguns segundos +maneuverSpeedDetails=Detalhes da velocidade da manobra +maneuverDirectionNormalization=Normalização da direção da manobra +maneuver=Manobra +minManeuverDuration=Duração mínima da manobra (s) +maxManeuverDuration=Duração máxima da manobra (s) +minManeuverEnteringSpeedInKnots=Velocidade mínima de entrada da manobra (nós) +maxManeuverEnteringSpeedInKnots=Velocidade máxima de entrada da manobra (nós) +minManeuverExitingSpeedInKnots=Velocidade mínima de saída da manobra (nós) +maxManeuverExitingSpeedInKnots=Velocidade máxima de saída da manobra (nós) +minManeuverEnteringAbsTWA=TWA absoluto mínimo de entrada da manobra +maxManeuverEnteringAbsTWA=TWA absoluto máximo de entrada da manobra +minManeuverExitingAbsTWA=TWA absoluto mínimo de saída da manobra +maxManeuverExitingAbsTWA=TWA absoluto máximo de saída da manobra +maneuverDirectionEqualWeightingEnabled=Permitir ponderação 50%-50% para mudança de direção em agregações "valor por TWA" +minValue=Valor mínimo +maxValue=Valor máximo +zeroTo360AxisLabeling=Etiquetagem de eixo 0-360° +exportStatisticsCurveToCsv=Exportar curva de estatística para CSV +csvCopiedToClipboard=O conteúdo CSV foi copiado para o clipboard. Cole-o onde quiser. +minDataCount=Contagem de dados mínimos moreLoginInformationHeadline=Benefícios da conexão ao SAP Sailing Analytics moreLoginInformationIntroduction=Como usuário registrado do SAP Sailing Analytics, você tem acesso a funções que não estão abertas para usuários anônimos. Registre-se simplesmente para ter acesso às funcionalidades exibidas embaixo. moreLoginInformationSectionUserSettingsHeading=Configurações do usuário @@ -1918,6 +1969,7 @@ currentStbdRudderRake=Inclinação leme boreste currentStbdRudderRakeTooltip=A inclinação atual do leme para boreste currentMastRotationInDegree=Rotação do mastro currentMastRotationInDegreeTooltip=A rotação atual do mastro em graus +mainCurveAnalysis=Analisar somente curva principal finishingTime=Hora de chegada finishTimeString=Hora de conclusão setFinishingAndFinishTime=Definir hora de chegada e conclusão @@ -1969,3 +2021,15 @@ totalDurationFoiledInSeconds=Total de tempo com hidrofólio totalDistanceFoiledInMeters=Total de distância com hidrofólio totalDurationFoiledInSecondsTooltip=Total de tempo com hidrofólio, em todo o painel de classificação totalDistanceFoiledInMetersTooltip=Total de distância com hidrofólio, em todo o painel de classificação +threeSixtyVideoHint=Este é um vídeo de 360°, a câmera pode ser virada +processingMP4=O Mp4 é analisado (isso demorará alguns segundos) +resetStartTimeToDefault=Definir como padrão +importFullExpeditionData=Importar todos os dados do Expedition +importCanceledNoCompetitorAdded=Nenhum competidor adicionado! A importação será cancelada. +importCanceledByUser=Importação cancelada! +importFinished=Importação de dados concluída +importFinishedMessage=Dados do Expedition importados com êxito. +importFinishedGotoRaceboard=Abrir painel de corridas +importFinishedGotoEvent=Abrir evento +chooseAName=Selecionar um nome +unexpectedErrorDuringFileImport=Ocorreu um erro inesperado durante o carregamento do arquivo. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index a8ec9a3cdb8..73ae848cb91 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -236,6 +236,32 @@ hoverOverAPoint=Наведите указатель для просмотра п noSelection=Ничего не выбрано raceIsKnownToStartUpwind=Гонка начинается против ветра events=События +pairingList=Список пар +pairingLists=Списки пар +pairingListCreationInfo=Первым будет рассчитан шаблон, который содержит только номера участников и может быть применен к другим таблицам лидеров. Если число участников не делится на число лодок без остатка, в списке пар будут пустые места. \ Если число участников не равно числу зарегистрированных участников в фактической таблице лидеров, список пар не может быть применен к журналам гонки. \ Для генерирования списка пар используется число всех флайтов таблицы лидеров, не являющихся гонками на медали. +setCompetitors=Укажите число участников: +seriesHint=Выберите не менее одной серии для списка пар: +amountOfFlightRepeats=Число повторов флайта +insertIntoRegatta=Вставить в регату +recalculate=Пересчитать +csvExport=Экспорт в CSV +printView=Просмотр перед печатью +print=Печать +invalidCompetitorCount=Недопустимое число участников +invalidFlightMultiplier=Недопустимое число повторов флайта +invalidSeriesSelection=Недопустимый выбор серий +flightsMustBeAMultipleOfMultiplier=Число флайтов должно быть кратно множителю флайтов +parameters=Параметры +numberOfFlights=Число флайтов +numberOfFleets=Число гонок на флайт +numberOfCompetitors=Число участников +numberOfRaces=Число гонок +quality=Качество +pairingListTemplate=Шаблон списка пар +printHint=Печать примененной версии +blockedApplyButton=Число зарегистрированных участников не равно числу участников из списка пар! +multiplierInfo=Планируйте флайты таким образом, чтобы обеспечить соревнования с минимально возможной переменой лодок +noPairingListAvailable=Функция печати доступна только после применения списка пар к журналам гонок выбранных таблиц лидеров. settingsForComponent={0} — параметры noEventsFound=События не найдены noEventSelected=Не выбрано событие @@ -354,6 +380,7 @@ replicatingFromMaster=Репликация с главного экземпля showWindSpeedSeries=Показать данные о скорости ветра showWindDirectionSeries=Показать данные о направлениях ветра fleet=Флот +boat=Лодка boatClass=Класс лодок setDelayToLive=Установить задержку оперативных данных pleaseEnterNonEmptyVenue=Введите место. @@ -527,7 +554,6 @@ additionalUrlSettings=Дополнительные параметры URL-адр restrictToSingleSelectedRace=Ограничить одной выбранной гонкой expandSelectedRace=Развернуть выбранную гонку showRaceDetails=Разрешить подробности гонки -numberOfRaces=Число гонок numberOfRacesMustBeNonNegativeNumber=Введите число последних гонок для просмотра chooseTheWayYouSelectRaces=Выберите способ выбора гонок selectFromAllRaces=Выбрать из всех гонок @@ -1457,6 +1483,7 @@ plainResultsPresenter=Презентатор простых результато resultsChart=Диаграмма результатов tabbedResultsPresenter=Презентатор результатов со вкладками polarResultsPresenter=Полярный презентатор результатов +maneuverSpeedDetailsResultsPresenter=Демонстратор результатов детализации скорости маневра dataMiningRetrieval=Извлечение данных actionWatch=Смотреть actionAnalyze=Анализировать @@ -1466,6 +1493,10 @@ doYouWantToCreateADefaultRegattaLeaderboard=Создать таблицу лид copyCourse=Копировать курс copyCompetitors=Копировать участников smartphoneTracking=Отслеживание смартфона +titelOfChooseNameDialog=Выберите имя +ownPrefix=Префикс +defaultName=По умолчанию +exampleTextForName=Выше имя выглядит так: flightsCount={0,number} полетов flightsCount[one]={0,number} полет viewQueryDefinition=Просмотреть определение запроса @@ -1876,6 +1907,26 @@ showEstimatedDuration=Показать оценку длительности estimatedDuration=оценка длительности: countDown=Начало события в {0} {1} countDownEnd=Событие начнется через несколько секунд +maneuverSpeedDetails=Скорость маневра подробно +maneuverDirectionNormalization=Нормализация направления маневра +maneuver=Маневр +minManeuverDuration=Мин. длительность маневра (с) +maxManeuverDuration=Макс. длительность маневра (с) +minManeuverEnteringSpeedInKnots=Мин. входная скорость маневра (уз.) +maxManeuverEnteringSpeedInKnots=Макс. входная скорость маневра (уз.) +minManeuverExitingSpeedInKnots=Мин. выходная скорость маневра (уз.) +maxManeuverExitingSpeedInKnots=Макс. выходная скорость маневра (уз.) +minManeuverEnteringAbsTWA=Мин. входной абс. TWA маневра +maxManeuverEnteringAbsTWA=Макс. входной абс. TWA маневра +minManeuverExitingAbsTWA=Мин. выходной абс. TWA маневра +maxManeuverExitingAbsTWA=Макс. выходной абс. TWA маневра +maneuverDirectionEqualWeightingEnabled=Включить взвешивание 50%-50% для направления маневра в агрегациях значений по TWA +minValue=Мин. значение +maxValue=Макс. значение +zeroTo360AxisLabeling=Маркировка оси 0–360° +exportStatisticsCurveToCsv=Экспортировать кривую статистики в CSV +csvCopiedToClipboard=Содержимое CSV скопировано в буфер обмена. Вставьте его в нужном месте. +minDataCount=Мин. число данных moreLoginInformationHeadline=Преимущества регистрации в SAP Sailing Analytics moreLoginInformationIntroduction=Зарегистрированные пользователи SAP Sailing Analytics имеют доступ к функциям, которые закрыты для анонимных пользователей. Зарегистрируйтесь, чтобы получить доступ к перечисленным ниже функциям. moreLoginInformationSectionUserSettingsHeading=Настройки пользователя @@ -1921,6 +1972,7 @@ currentStbdRudderRake=Скос задней кромки руля на прав currentStbdRudderRakeTooltip=Текущий скос задней кромки руля на правый борт currentMastRotationInDegree=Вращение мачты currentMastRotationInDegreeTooltip=Текущее значение вращения мачты в градусах +mainCurveAnalysis=Анализировать только основную кривую finishingTime=Время финиширования finishTimeString=Время финиша setFinishingAndFinishTime=Указать время финиширования и финиша @@ -1972,3 +2024,15 @@ totalDurationFoiledInSeconds=Общее время в крыльевом реж totalDistanceFoiledInMeters=Общая дистанция в крыльевом режиме totalDurationFoiledInSecondsTooltip=Общее время в крыльевом режиме, по всей таблице лидеров totalDistanceFoiledInMetersTooltip=Общая дистанция в крыльевом режиме, по всей таблице лидеров +threeSixtyVideoHint=Это панорамное видео, камеру можно повернуть +processingMP4=Анализ Mp4 (это займет несколько секунд) +resetStartTimeToDefault=Установить по умолчанию +importFullExpeditionData=Импортировать полные данные Expedition +importCanceledNoCompetitorAdded=Не добавлено ни одного участника! Импорт будет отменен. +importCanceledByUser=Импорт отменен! +importFinished=Импорт данных завершен +importFinishedMessage=Данные Expedition успешно импортированы. +importFinishedGotoRaceboard=Открыть таблицу гонки +importFinishedGotoEvent=Открыть событие +chooseAName=Выберите имя +unexpectedErrorDuringFileImport=При отправке файла возникла непредвиденная ошибка. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 18e0a01e083..ae21295659b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -236,6 +236,32 @@ hoverOverAPoint=将鼠标移到得分,查看详情 noSelection=无选择 raceIsKnownToStartUpwind=比赛轮次从迎风航段开始 events=活动 +pairingList=配对清单 +pairingLists=配对清单 +pairingListCreationInfo=首先,将计算仅包含参赛队数并且可以应用到其他积分榜的模板。如果参赛队数量无法被船只数量除尽,则在配对清单中输入空占位符。\ 此外,如果参赛队数量不等于实际积分榜中注册的参赛队数量,配对清单无法应用到比赛轮次日志。\ 用于生成配对清单的航程数由积分榜中不是奖牌轮的所有航程确定。 +setCompetitors=请设置参赛队数量: +seriesHint=请选择一场或多场应位于配对清单的系列赛: +amountOfFlightRepeats=航程重复次数 +insertIntoRegatta=插入比赛 +recalculate=重新计算 +csvExport=导出为 CSV +printView=打印预览 +print=打印 +invalidCompetitorCount=参赛队数量无效 +invalidFlightMultiplier=航程重复无效 +invalidSeriesSelection=系列赛选择无效 +flightsMustBeAMultipleOfMultiplier=航程需要是航程乘数的倍数 +parameters=参数 +numberOfFlights=航程数 +numberOfFleets=各航程的比赛轮次数 +numberOfCompetitors=参赛队数 +numberOfRaces=比赛轮次数 +quality=质量 +pairingListTemplate=配对清单模板 +printHint=打印应用的版本 +blockedApplyButton=注册的参赛队不等于配对清单中的参赛队! +multiplierInfo=多个航程,彼此相邻,这样就可以减少船只更换情况进行比赛 +noPairingListAvailable=打印功能仅在配对清单可应用于选择的积分榜比赛轮次日志时可用。 settingsForComponent={0}设置 noEventsFound=未找到活动 noEventSelected=未选择活动 @@ -354,6 +380,7 @@ replicatingFromMaster=正在从主服务器{0}:{2}、消息传送端口{3}:{ showWindSpeedSeries=显示风速数据 showWindDirectionSeries=显示风向数据 fleet=船队 +boat=船只 boatClass=船只级别 setDelayToLive=设置实况延迟 pleaseEnterNonEmptyVenue=请输入场地。 @@ -527,7 +554,6 @@ additionalUrlSettings=附加 URL 设置 restrictToSingleSelectedRace=限制为单个选择的比赛轮次 expandSelectedRace=展开选择的比赛轮次 showRaceDetails=允许比赛轮次详情 -numberOfRaces=比赛轮次数 numberOfRacesMustBeNonNegativeNumber=请输入要显示的最后比赛轮次的数量 chooseTheWayYouSelectRaces=选择选取比赛轮次的方式 selectFromAllRaces=从所有比赛轮次中选择 @@ -1457,6 +1483,7 @@ plainResultsPresenter=纯结果展示区 resultsChart=结果记录 tabbedResultsPresenter=选项卡式结果展示区 polarResultsPresenter=极坐标结果展示区 +maneuverSpeedDetailsResultsPresenter=操作速度详细结果展示区 dataMiningRetrieval=数据检索 actionWatch=观看 actionAnalyze=分析 @@ -1466,6 +1493,10 @@ doYouWantToCreateADefaultRegattaLeaderboard=是否确认默认的比赛积分榜 copyCourse=复制场地 copyCompetitors=复制参赛队 smartphoneTracking=智能手机跟踪 +titelOfChooseNameDialog=选择名称 +ownPrefix=自己的前缀 +defaultName=默认值 +exampleTextForName=名称如下所示: flightsCount={0,number} 个航程 flightsCount[one]={0,number} 个航程 viewQueryDefinition=查看查询定义 @@ -1876,6 +1907,26 @@ showEstimatedDuration=显示预计持续时间 estimatedDuration=预计持续时间: countDown=活动于{0} {1}开始 countDownEnd=活动将在数秒后开始 +maneuverSpeedDetails=操作速度详情 +maneuverDirectionNormalization=操作方向规范化 +maneuver=操作 +minManeuverDuration=最短操作持续时间 (s) +maxManeuverDuration=最长操作持续时间 (s) +minManeuverEnteringSpeedInKnots=最小操作驶入速度 (kts) +maxManeuverEnteringSpeedInKnots=最大操作驶入速度 (kts) +minManeuverExitingSpeedInKnots=最小操作驶出速度 (kts) +maxManeuverExitingSpeedInKnots=最大操作驶出速度 (kts) +minManeuverEnteringAbsTWA=最小操作驶入绝对实际风向角 +maxManeuverEnteringAbsTWA=最大操作驶入绝对实际风向角 +minManeuverExitingAbsTWA=最小操作驶出绝对实际风向角 +maxManeuverExitingAbsTWA=最大操作驶出绝对实际风向角 +maneuverDirectionEqualWeightingEnabled=以“各实际风向角的值”汇总为操作方向启用 50%-50% 加权 +minValue=最小值 +maxValue=最大值 +zeroTo360AxisLabeling=0-360° 轴标签 +exportStatisticsCurveToCsv=将统计曲线导出到 CSV +csvCopiedToClipboard=CSV 内容已复制到剪贴板。将其粘贴在需要的任何地方。 +minDataCount=最小数据数 moreLoginInformationHeadline=注册 SAP Sailing Analytics 的优势 moreLoginInformationIntroduction=作为 SAP Sailing Analytics 的注册用户,您可以访问尚未对匿名用户开放的功能。注册即可访问以下显示的功能。 moreLoginInformationSectionUserSettingsHeading=用户设置 @@ -1921,6 +1972,7 @@ currentStbdRudderRake=右舵倾斜度 currentStbdRudderRakeTooltip=当前右舵倾斜度 currentMastRotationInDegree=桅杆旋转度 currentMastRotationInDegreeTooltip=当前桅杆旋转度数 +mainCurveAnalysis=仅分析主曲线 finishingTime=结束时间 finishTimeString=终点线时间 setFinishingAndFinishTime=设置结束和终点线时间 @@ -1972,3 +2024,15 @@ totalDurationFoiledInSeconds=水翼腾空总时间 totalDistanceFoiledInMeters=水翼腾空总距离 totalDurationFoiledInSecondsTooltip=水翼腾空总时间(在整个积分榜中) totalDistanceFoiledInMetersTooltip=水翼腾空总距离(在整个积分榜中) +threeSixtyVideoHint=这是 360° 视频,相机可以转动 +processingMP4=分析 Mp4(将需要几秒钟) +resetStartTimeToDefault=设为默认值 +importFullExpeditionData=导入全部导航图数据 +importCanceledNoCompetitorAdded=未添加参赛队!导入将被取消。 +importCanceledByUser=导入已取消! +importFinished=已完成数据导入 +importFinishedMessage=已成功导入导航图数据。 +importFinishedGotoRaceboard=打开比赛板 +importFinishedGotoEvent=打开活动 +chooseAName=选择名称 +unexpectedErrorDuringFileImport=文件上传期间发生意外错误。 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 2e996b2bb3c..35002e5fa6e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -19,6 +19,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.shiro.SecurityUtils; import org.mp4parser.IsoFile; import org.mp4parser.boxes.UserBox; import org.mp4parser.boxes.iso14496.part12.MovieBox; @@ -36,13 +37,11 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.VideoMetadataDTO; import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.security.Permission; -import com.sap.sailing.domain.common.security.SailingPermissionsForRoleProvider; +import com.sap.sailing.domain.common.security.Permission.Mode; import com.sap.sailing.gwt.ui.client.MediaService; import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.Duration; import com.sap.sse.common.impl.MillisecondsDurationImpl; -import com.sap.sse.security.SecurityService; -import com.sap.sse.security.User; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { @@ -51,19 +50,14 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi private static final int METADATA_CONNECTION_TIMEOUT = 10000; DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); - private ServiceTracker racingEventServiceTracker; - private ServiceTracker securityServiceTracker; - private static final int REQUIRED_SIZE = 1000000; private static final long serialVersionUID = -8917349579281305977L; public MediaServiceImpl() { super(); BundleContext context = Activator.getDefault(); - securityServiceTracker = new ServiceTracker<>(context, SecurityService.class, /* customizer */ null); - securityServiceTracker.open(); racingEventServiceTracker = new ServiceTracker(context, RacingEventService.class.getName(), null); racingEventServiceTracker.open(); @@ -89,19 +83,7 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi } private void ensureUserCanManageMedia() { - SecurityService service = securityServiceTracker.getService(); - if (service == null) { - throw new IllegalStateException("SecurityService not initialized"); - } - User user = service.getCurrentUser(); - if (user == null) { - throw new IllegalStateException("User is missing permission to MANAGER_MEDIA"); - } - boolean hasPermission = user.hasPermission(Permission.MANAGE_MEDIA.getStringPermission(), - SailingPermissionsForRoleProvider.INSTANCE); - if (!hasPermission) { - throw new IllegalStateException("User is missing permission to MANAGER_MEDIA"); - } + SecurityUtils.getSubject().checkPermission(Permission.MANAGE_MEDIA.getStringPermissionForObjects(Mode.UPDATE)); } @Override diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 49ebfd1d575..14d68f21298 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -6,6 +6,7 @@ import java.io.StringWriter; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -16,6 +17,8 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; @@ -51,6 +54,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMa import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.impl.OpenEndedDeviceMappingFinder; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; @@ -62,7 +66,9 @@ import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.DeviceIdentifier; @@ -113,6 +119,7 @@ import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseJsonSe import com.sap.sailing.server.gateway.serialization.coursedata.impl.GateJsonSerializer; import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer; import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithBoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FlatGPSFixJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.MarkJsonSerializerWithPosition; import com.sap.sse.InvalidDateException; @@ -1086,6 +1093,117 @@ public class LeaderboardsResource extends AbstractSailingServerResource { return Response.ok(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); } + /** + * @return the actual or anticipated start order for the race identified by {@code raceColumnName}, {@code fleetName}. + * Those competitors for which a start mark passing is already known are sorted by those start mark passings. All other + * boats are ordered by their geometric distance from the start line or the windward distance from the start mark + * if the start for some reason is defined by a single mark. See {@link #compareDistanceFromStartLine(Competitor, Competitor)}. + */ + @GET + @Produces("application/json;charset=UTF-8") + @Path("{leaderboardName}/startorder") + public Response getStartOrder(@PathParam("leaderboardName") String leaderboardName, + @QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName, + @QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName) { + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + return Response.status(Status.NOT_FOUND) + .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } + + RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); + if (raceColumn == null) { + return Response + .status(Status.NOT_FOUND) + .entity("Could not find a race column '" + StringEscapeUtils.escapeHtml(raceColumnName) + "' in leaderboard '" + + StringEscapeUtils.escapeHtml(leaderboardName) + "'.").type(MediaType.TEXT_PLAIN).build(); + } + Fleet fleet = raceColumn.getFleetByName(fleetName); + if (fleet == null) { + return Response + .status(Status.NOT_FOUND) + .entity("Could not find a fleet '" + StringEscapeUtils.escapeHtml(fleetName) + "' in raceColumn '" + + StringEscapeUtils.escapeHtml(raceColumnName) + "'.").type(MediaType.TEXT_PLAIN).build(); + } + final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + if (trackedRace == null) { + return Response.status(Status.NOT_FOUND) + .entity("Could not find a tracked race for raceColumn '" + StringEscapeUtils.escapeHtml(raceColumnName) + + " and fleet '" + StringEscapeUtils.escapeHtml(fleetName) + "'.").type(MediaType.TEXT_PLAIN) + .build(); + }; + + Course course = trackedRace.getRace().getCourse(); + final List competitors = new ArrayList<>(); + final Waypoint firstWaypoint = course.getFirstWaypoint(); + final TimePoint timeToUseForStart; + if (trackedRace.getStartOfRace() != null) { + timeToUseForStart = trackedRace.getStartOfRace(); + } else { + timeToUseForStart = MillisecondsTimePoint.now(); + } + if (firstWaypoint != null) { + final List startWaypointMarkPositions = + StreamSupport.stream(firstWaypoint.getMarks().spliterator(), /* parallel */ false). + map(mark->trackedRace.getTrack(mark)).filter(markTrack->markTrack!=null). + map(markTrack->markTrack.getEstimatedPosition(timeToUseForStart, /* extrapolate */ false)). + collect(Collectors.toList()); + Util.addAll(trackedRace.getRace().getCompetitors(), competitors); + competitors.sort((a, b)->{ + final MarkPassing aStartMarkPassing = trackedRace.getMarkPassing(a, firstWaypoint); + final MarkPassing bStartMarkPassing = trackedRace.getMarkPassing(b, firstWaypoint); + final int result; + if (aStartMarkPassing == null) { + if (bStartMarkPassing == null) { + result = compareDistanceFromStartLine(trackedRace, startWaypointMarkPositions, timeToUseForStart, a, b); + } else { + result = 1; // b consider less than a because it has a start mark passing and therefore is assumed to have started before a + } + } else if (bStartMarkPassing == null) { + result = -1; // a consider less than b because it has a start mark passing and therefore is assumed to have started before b + } else { + // both have a start mark passing; compare time points + result = aStartMarkPassing.getTimePoint().compareTo(bStartMarkPassing.getTimePoint()); + } + return result; + }); + } + CompetitorWithBoatJsonSerializer serializer = new CompetitorWithBoatJsonSerializer(); + JSONArray result = new JSONArray(); + for (final Competitor c : competitors) { + Boat boat = trackedRace.getBoatOfCompetitor(c); + JSONObject jsonCompetitor = serializer.serialize(new CompetitorWithBoatImpl(c, (DynamicBoat) boat)); + result.add(jsonCompetitor); + } + String json = result.toJSONString(); + return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } + + private int compareDistanceFromStartLine(TrackedRace trackedRace, Iterable startWaypointMarkPositions, + TimePoint timeToUseForStart, Competitor a, Competitor b) { + final Position aPos = trackedRace.getTrack(a).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true); + final Position bPos = trackedRace.getTrack(b).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true); + final Distance aDist = aPos==null?null:getDistanceFromStartWaypoint(aPos, startWaypointMarkPositions); + final Distance bDist = bPos==null?null:getDistanceFromStartWaypoint(bPos, startWaypointMarkPositions); + return Comparator.nullsLast(Comparator.naturalOrder()).compare(aDist, bDist); + } + + private Distance getDistanceFromStartWaypoint(Position pos, Iterable startWaypointMarkPositions) { + final Distance result; + if (Util.isEmpty(startWaypointMarkPositions)) { + result = null; + } else if (Util.size(startWaypointMarkPositions) == 1) { + // single mark start; strange, but possible: + result = startWaypointMarkPositions.iterator().next().getDistance(pos); + } else { + final Position first = Util.get(startWaypointMarkPositions, 0); + final Position second = Util.get(startWaypointMarkPositions, 1); + result = pos.getDistanceToLine(first, second).abs(); + } + return result; + } + @GET @Produces(MediaType.APPLICATION_JSON) @Path("{leaderboardName}/marks/{markId}") 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 7c60b1cb126..34b1a63c080 100644 --- 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 @@ -3,7 +3,6 @@ package com.sap.sailing.server.gateway.jaxrs.api; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -14,8 +13,6 @@ import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; @@ -46,13 +43,11 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.BoatImpl; -import com.sap.sailing.domain.base.impl.CompetitorWithBoatImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.NoWindException; -import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -68,7 +63,6 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.GPSFixTrack; -import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; @@ -1074,103 +1068,6 @@ public class RegattasResource extends AbstractSailingServerResource { return response; } - /** - * @return the actual or anticipated start order for the race identified by {@coode regattaName} and {@code raceName}. - * Those competitors for which a start mark passing is already known are sorted by those start mark passings. All other - * boats are ordered by their geometric distance from the start line or the windward distance from the start mark - * if the start for some reason is defined by a single mark. See {@link #compareDistanceFromStartLine(Competitor, Competitor)}. - */ - @GET - @Produces("application/json;charset=UTF-8") - @Path("{regattaname}/races/{racename}/startorder") - public Response getStartOrder(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) { - Response response; - Regatta regatta = findRegattaByName(regattaName); - if (regatta == null) { - response = Response.status(Status.NOT_FOUND) - .entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.").type(MediaType.TEXT_PLAIN) - .build(); - } else { - RaceDefinition race = findRaceByName(regatta, raceName); - if (race == null) { - response = Response.status(Status.NOT_FOUND) - .entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.").type(MediaType.TEXT_PLAIN) - .build(); - } else { - final TrackedRace trackedRace = findTrackedRace(regattaName, raceName); - Course course = trackedRace.getRace().getCourse(); - final List competitors = new ArrayList<>(); - final Waypoint firstWaypoint = course.getFirstWaypoint(); - final TimePoint timeToUseForStart; - if (trackedRace.getStartOfRace() != null) { - timeToUseForStart = trackedRace.getStartOfRace(); - } else { - timeToUseForStart = MillisecondsTimePoint.now(); - } - if (firstWaypoint != null) { - final List startWaypointMarkPositions = - StreamSupport.stream(firstWaypoint.getMarks().spliterator(), /* parallel */ false). - map(mark->trackedRace.getTrack(mark)).filter(markTrack->markTrack!=null). - map(markTrack->markTrack.getEstimatedPosition(timeToUseForStart, /* extrapolate */ false)). - collect(Collectors.toList()); - Util.addAll(trackedRace.getRace().getCompetitors(), competitors); - competitors.sort((a, b)->{ - final MarkPassing aStartMarkPassing = trackedRace.getMarkPassing(a, firstWaypoint); - final MarkPassing bStartMarkPassing = trackedRace.getMarkPassing(b, firstWaypoint); - final int result; - if (aStartMarkPassing == null) { - if (bStartMarkPassing == null) { - result = compareDistanceFromStartLine(trackedRace, startWaypointMarkPositions, timeToUseForStart, a, b); - } else { - result = 1; // b consider less than a because it has a start mark passing and therefore is assumed to have started before a - } - } else if (bStartMarkPassing == null) { - result = -1; // a consider less than b because it has a start mark passing and therefore is assumed to have started before b - } else { - // both have a start mark passing; compare time points - result = aStartMarkPassing.getTimePoint().compareTo(bStartMarkPassing.getTimePoint()); - } - return result; - }); - } - CompetitorWithBoatJsonSerializer serializer = new CompetitorWithBoatJsonSerializer(); - JSONArray result = new JSONArray(); - for (final Competitor c : competitors) { - Boat boat = trackedRace.getBoatOfCompetitor(c); - JSONObject jsonCompetitor = serializer.serialize(new CompetitorWithBoatImpl(c, (DynamicBoat) boat)); - result.add(jsonCompetitor); - } - String json = result.toJSONString(); - return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); - } - } - return response; - } - - private int compareDistanceFromStartLine(TrackedRace trackedRace, Iterable startWaypointMarkPositions, - TimePoint timeToUseForStart, Competitor a, Competitor b) { - final Position aPos = trackedRace.getTrack(a).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true); - final Position bPos = trackedRace.getTrack(b).getEstimatedPosition(timeToUseForStart, /* extrapolate */ true); - final Distance aDist = aPos==null?null:getDistanceFromStartWaypoint(aPos, startWaypointMarkPositions); - final Distance bDist = bPos==null?null:getDistanceFromStartWaypoint(bPos, startWaypointMarkPositions); - return Comparator.nullsLast(Comparator.naturalOrder()).compare(aDist, bDist); - } - - private Distance getDistanceFromStartWaypoint(Position pos, Iterable startWaypointMarkPositions) { - final Distance result; - if (Util.isEmpty(startWaypointMarkPositions)) { - result = null; - } else if (Util.size(startWaypointMarkPositions) == 1) { - // single mark start; strange, but possible: - result = startWaypointMarkPositions.iterator().next().getDistance(pos); - } else { - final Position first = Util.get(startWaypointMarkPositions, 0); - final Position second = Util.get(startWaypointMarkPositions, 1); - result = pos.getDistanceToLine(first, second).abs(); - } - return result; - } - @GET @Produces("application/json;charset=UTF-8") @Path("{regattaname}/races") diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html index b0a9904648c..9501aa42504 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html @@ -185,6 +185,10 @@ name to make sure you get to the event you're interested in. /api/v1/leaderboards/{name}/marks Gets the marks of a race (from the RaceLog and if possible the TrackedRace) + + /api/v1/leaderboards/{name}/startorder + Gets the order in which competitors have started or are expected to start for a race + /api/v1/leaderboards/{name}/settrackingtimes Modifies the race's tracking interval @@ -217,10 +221,6 @@ name to make sure you get to the event you're interested in. /api/v1/competitors/{id}/team/image Sets the team image - - /api/v1/regattas/{regattaname}/races/{racename}/startorder - Gets the order in which competitors have started or are expected to start - /api/v1/regattas/{regattaname}/races/{racename}/maneuvers Gets all maneuvers performed by all competitors of a race diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/startorderGetDoc.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceStartorderGetDoc.html similarity index 78% rename from java/com.sap.sailing.server.gateway/webservices/api/v1/startorderGetDoc.html rename to java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceStartorderGetDoc.html index d95afdecbac..4581612874c 100755 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/startorderGetDoc.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceStartorderGetDoc.html @@ -9,10 +9,10 @@

SAP Sailing Analytics Webservices API Version 1.0

-

URL: /api/v1/regattas/{regattaname}/races/{racename}/startorder

+

URL: /api/v1/leaderboards/{name}/startorder?race_column={name}&fleet={name}

Description: -

Gets the order in which competitors have started or are expected to start. The output +

Gets the order in which competitors have started or are expected to start for a race. The output is a JSON array that contains the race competitors in the order in which they have passed the start waypoint or are expected to do so. Those competitors for which a start mark passing is already known are sorted by those start mark passings. All other @@ -31,7 +31,8 @@ Mandatory parameters: - None + race_column +
fleet Optional parameters: @@ -43,7 +44,7 @@ Example: - https://dev.sapsailing.com/sailingserver/api/v1/regattas/ESS%202016%20Cardiff/races/Race%2016/startorder + https://dev.sapsailing.com/sailingserver/api/v1/leaderboards/ESS%202016%20Cardiff/startorder?race_column=R1&fleet=Default

diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java index 77400742011..8f86575fa5a 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/statistics/StatisticsTest.java @@ -41,6 +41,8 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.RaceListener; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; @@ -88,14 +90,28 @@ public class StatisticsTest { regatta.addTrackedRace(trackedRace); } - private TrackedRaceStatisticsCacheImpl getStatisticsCacheWithRegattaAdded() { + private TrackedRaceStatisticsCacheImpl getStatisticsCacheWithRegattaAdded() throws Exception { TrackedRaceStatisticsCacheImpl trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl(); trackedRaceStatisticsCache.regattaAdded(regatta); + + // The following lines ensure that we received all necessary events from the TrackedRegatta + // When removing a listener we get a Future whose get method will block until all previously triggered events are processed. + RaceListener raceListener = new RaceListener() { + @Override + public void raceRemoved(TrackedRace trackedRace) { + } + @Override + public void raceAdded(TrackedRace trackedRace) { + } + }; + regatta.addRaceListener(raceListener); + regatta.removeRaceListener(raceListener).get(); + return trackedRaceStatisticsCache; } @Test - public void testTrackedRaceStatisticsCacheWithoutFixes() { + public void testTrackedRaceStatisticsCacheWithoutFixes() throws Exception { TrackedRaceStatisticsCacheImpl trackedRaceStatisticsCache = getStatisticsCacheWithRegattaAdded(); TrackedRaceStatistics statisticsForRace = trackedRaceStatisticsCache.getStatisticsWaitingForLatest(trackedRace); @@ -105,7 +121,7 @@ public class StatisticsTest { } @Test - public void testTrackedRaceStatisticsCacheWithFixes() { + public void testTrackedRaceStatisticsCacheWithFixes() throws Exception { trackedRace.recordFix(comp, new GPSFixMovingImpl(new DegreePosition(49.295970, 8.638958), new MillisecondsTimePoint(START_OF_RACE), new KilometersPerHourSpeedWithBearingImpl(1, new DegreeBearingImpl(100)))); @@ -165,5 +181,4 @@ public class StatisticsTest { double distanceInMeters = statisticsForRace.getDistanceTraveled().getMeters(); assertTrue(distanceInMeters > 53 && distanceInMeters < 57); } - } diff --git a/java/com.sap.sailing.targetplatform.base/site.xml b/java/com.sap.sailing.targetplatform.base/site.xml index 176c5f65dda..d8787556f2f 100644 --- a/java/com.sap.sailing.targetplatform.base/site.xml +++ b/java/com.sap.sailing.targetplatform.base/site.xml @@ -9,9 +9,6 @@ - - - diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-remote.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-remote.target index fe0bcdef426..0d20a8eba31 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-remote.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-remote.target @@ -7,7 +7,7 @@ - + diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/User.java b/java/com.sap.sse.security/src/com/sap/sse/security/User.java index dd6ea3bc5f5..d14b7d5aef5 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/User.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/User.java @@ -17,8 +17,6 @@ import com.sap.sse.common.NamedWithID; import com.sap.sse.common.WithID; import com.sap.sse.security.shared.Account; import com.sap.sse.security.shared.Account.AccountType; -import com.sap.sse.security.shared.PermissionsForRoleProvider; -import com.sap.sse.security.shared.WildcardPermission; public class User implements NamedWithID { private static final long serialVersionUID = 1788215575606546042L; @@ -271,21 +269,4 @@ public class User implements NamedWithID { return validationSecret; } - public boolean hasPermission(String stringPermission, PermissionsForRoleProvider provider) { - WildcardPermission requested = new WildcardPermission(stringPermission); - for (String permission : getPermissions()) { - if (new WildcardPermission(permission).implies(requested)) { - return true; - } - } - for (String role : getRoles()) { - for (String permission : provider.getPermissions(role)) { - if (new WildcardPermission(permission).implies(requested)) { - return true; - } - } - } - return false; - } - } diff --git a/java/com.sap.sse/src/com/sap/sse/util/apachelog/UniqueIPsPerReferrer.java b/java/com.sap.sse/src/com/sap/sse/util/apachelog/UniqueIPsPerReferrer.java index 7c7f17258db..0b15d0fc8e0 100755 --- a/java/com.sap.sse/src/com/sap/sse/util/apachelog/UniqueIPsPerReferrer.java +++ b/java/com.sap.sse/src/com/sap/sse/util/apachelog/UniqueIPsPerReferrer.java @@ -52,6 +52,7 @@ public class UniqueIPsPerReferrer { private static final Logger logger = Logger.getLogger(UniqueIPsPerReferrer.class.getName()); private static final String HOSTNAME_FILE_EXTENSION = ".ips"; private static final String UNIQUE_SUFFIX = ".unique"; + private static final String MONTH_AND_YEAR_TOTALS_SUFFIX = "_totals"; private static final char YEAR_MONTH_SEPARATOR = '_'; private final File CACHE; private final File VISITED_FILES; @@ -160,6 +161,8 @@ public class UniqueIPsPerReferrer { private void computePerMonthPerYearPerEventAndTotals() throws IOException, ParseException { final Map monthFileWritersPerFileBaseName = new HashMap<>(); final Map yearFileWritersPerFileBaseName = new HashMap<>(); + final Map monthTotalsFileWritersPerFileBaseName = new HashMap<>(); + final Map yearTotalsFileWritersPerFileBaseName = new HashMap<>(); final Writer eventTotalsWriter = new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(EVENTTOTALS))); final Writer totalsWriter = new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(TOTALS))); for (final File perHostnameFile : getAllHostnameFiles()) { @@ -170,16 +173,22 @@ public class UniqueIPsPerReferrer { String line; while ((line=r.readLine()) != null) { final PerHostnameEntry entry = new PerHostnameEntry(line); + final String combinedLine = hostname+" "+line+"\n"; if (entry.getYear() != 0) { final String monthFileBasename = String.format("%04d"+YEAR_MONTH_SEPARATOR+"%02d", entry.getYear(), entry.getZeroBasedMonth()+1); final Writer monthFileWriter = getFileWriter(monthFileBasename, monthFileWritersPerFileBaseName, this::getMonthFileCompressed, /* gzipCompressed */ true, /* append */ false); - final String combinedLine = hostname+" "+line+"\n"; monthFileWriter.write(combinedLine); + final String monthTotalsFileBasename = monthFileBasename+MONTH_AND_YEAR_TOTALS_SUFFIX; + final Writer monthTotalsFileWriter = getFileWriter(monthTotalsFileBasename, monthTotalsFileWritersPerFileBaseName, this::getMonthFileCompressed, /* gzipCompressed */ true, /* append */ false); + monthTotalsFileWriter.write(line+"\n"); final String yearFileBasename = String.format("%04d", entry.getYear()); final Writer yearFileWriter = getFileWriter(yearFileBasename, yearFileWritersPerFileBaseName, this::getYearFileCompressed, /* gzipCompressed */ true, /* append */ false); yearFileWriter.write(combinedLine); + final String yearTotalsFileBasename = yearFileBasename+MONTH_AND_YEAR_TOTALS_SUFFIX; + final Writer yearTotalsFileWriter = getFileWriter(yearTotalsFileBasename, yearTotalsFileWritersPerFileBaseName, this::getYearFileCompressed, /* gzipCompressed */ true, /* append */ false); + yearTotalsFileWriter.write(line+"\n"); } - eventTotalsWriter.write(hostname+" "+line+"\n"); + eventTotalsWriter.write(combinedLine); totalsWriter.write(line+"\n"); } } finally { @@ -192,6 +201,12 @@ public class UniqueIPsPerReferrer { for (final Writer yearFileWriter : yearFileWritersPerFileBaseName.values()) { yearFileWriter.close(); } + for (final Writer monthTotalsFileWriter : monthTotalsFileWritersPerFileBaseName.values()) { + monthTotalsFileWriter.close(); + } + for (final Writer yearTotalsFileWriter : yearTotalsFileWritersPerFileBaseName.values()) { + yearTotalsFileWriter.close(); + } eventTotalsWriter.close(); totalsWriter.close(); } @@ -249,9 +264,21 @@ public class UniqueIPsPerReferrer { } private void countTotalHits() throws IOException { - logger.info("Counting total number of unique visitors"); - final FileWriter totalsCountWriter = new FileWriter(TOTALS_UNIQUE); - final Set uniqueTotalLines = readDistinctLinesFromGzippedFile(TOTALS); + logger.info("Counting total numbers of unique visitors for all, months, and years"); + countUniqueTotals(TOTALS, TOTALS_UNIQUE); + for (final File monthGzippedTotals : MONTHS.listFiles((dir, filename)->filename.endsWith(MONTH_AND_YEAR_TOTALS_SUFFIX+GZIP_EXTENSION))) { + countUniqueTotals(monthGzippedTotals, new File(monthGzippedTotals.getParentFile(), + monthGzippedTotals.getName().substring(0, monthGzippedTotals.getName().length()-GZIP_EXTENSION.length())+UNIQUE_SUFFIX)); + } + for (final File yearGzippedTotals : YEARS.listFiles((dir, filename)->filename.endsWith(MONTH_AND_YEAR_TOTALS_SUFFIX+GZIP_EXTENSION))) { + countUniqueTotals(yearGzippedTotals, new File(yearGzippedTotals.getParentFile(), + yearGzippedTotals.getName().substring(0, yearGzippedTotals.getName().length()-GZIP_EXTENSION.length())+UNIQUE_SUFFIX)); + } + } + + private void countUniqueTotals(File gzippedEntriesWithoutHostname, File uniqueCount) throws IOException { + final FileWriter totalsCountWriter = new FileWriter(uniqueCount); + final Set uniqueTotalLines = readDistinctLinesFromGzippedFile(gzippedEntriesWithoutHostname); totalsCountWriter.write(uniqueTotalLines.size()+"\n"); totalsCountWriter.close(); } diff --git a/wiki/bug4232-review.md b/wiki/bug4232-review.md index 4986b038e8d..f89ff10d1ce 100644 --- a/wiki/bug4232-review.md +++ b/wiki/bug4232-review.md @@ -12,13 +12,13 @@ The edit dialog (open with the pencil) should always be closed if a warning (yellow or red) will be displayed. In case of a red warning, the publish button is always disabled. -One device will be called **A** and the other **B**, so they are have unique names while testing. +One device will be called **A** and the other **B**, so they have unique names while testing. ## While starting ### Test 1 * devices open the penalty fragment -* device A give Omar Air OSC, device B set DNS for Omar Air +* device A give Omar Air OCS, device B set DNS for Omar Air * device A closes the penalty view (without publishing) * device B should see a red warning sign (without active publish button) after the automatic refresh @@ -32,7 +32,7 @@ One device will be called **A** and the other **B**, so they are have unique nam ### Test 4 * both devices open the penalty fragment -* device A give Omar Air OSC, device B set for Alinghi the value DNS +* device A gives Omar Air OCS, device B sets DNS for Alinghi * device A closes the penalty view (without publishing) * device B should see both entries in the list after refresh @@ -42,20 +42,20 @@ One device will be called **A** and the other **B**, so they are have unique nam ### Test 6 * same as [test 4](#test-scenarios_while-starting_test-4), but after refresh device B should publish the merged result -* after the refresh device A also see both entries +* after the refresh device A also sees both entries ## While finishing ### Test 7 * both devices open result list -* device A add Omar Air, Alinghi and LR BAR into the list +* device A adds Omar Air, Alinghi and LR BAR into the list * device A closes the result list * after refresh device B see the three competitors in the result list ### Test 8 * both devices open result list * both devices add Omar Air to the result list -* device A set the penalty to OSC and closes the result list +* device A sets the penalty to OCS and closes the result list * device B should see after the refresh a yellow warning sign ### Test 9 @@ -68,7 +68,7 @@ One device will be called **A** and the other **B**, so they are have unique nam ### Test 10 * both devices open result list * both devices add Omar Air to the result list -* device A changes to penalty view and set the value to DNF and changes back to result list +* device A changes to penalty view and sets the value to DNF and changes back to result list * device B should see a yellow warning sign after refresh ### Test 11 diff --git a/wiki/howto/eventmanagers/Manage-media-content.md b/wiki/howto/eventmanagers/Manage-media-content.md index 08207a32f40..1a0f33ef08b 100644 --- a/wiki/howto/eventmanagers/Manage-media-content.md +++ b/wiki/howto/eventmanagers/Manage-media-content.md @@ -1,38 +1,48 @@ -This page descrips how to manage the media content like images for the events at www.sapsailing.com, providing a guidance based on user's best experience. For information about where to get media content itself, please refer to the [[media|wiki/events/Sailing-events-media-content]] page. +This page descrips how to manage the media content like images and videos for the events on www.sapsailing.com, providing a guidance based on user's best experiences. For information about where to get media content itself, please refer to the [[media|wiki/events/Sailing-events-media-content]] page. [[_TOC_]] # Images ## Image types and their impact -There are four image types which can be added to an event carrying different roles: +There are four image types which can be added to an event where they carry different roles: -* **logo:** the sailing event or league logo which gets displayed e.g. in the upper left corner next to the name of the sailing event. -* **stage:** used as a banner for the sailing event and gets displayed e.g. on the landing page. -* **teaser:** used to give a preview about the sailing event and gets displayed e.g. in the event overview where all events are grouped by years. -* **galery:** representing the images which are displayed under the _media_ tab of each event. +* **Logo:** the sailing event or league logo which gets displayed e.g. in the upper left corner next to the name of the sailing event. +* **Stage:** used as a banner for the sailing event and gets displayed e.g. on the landing page. +* **Teaser:** used to give a preview about the sailing event and gets displayed e.g. in the event overview where all events are grouped by years. +* **Gallery:** representing the images which are displayed under the _media_ tab of each event. ## Scale guidelines for several purposes For performance reasons, images should be scaled before uploading them. The following image resolutions showed up as good manner: -* **logo:** about 150 x 150 pixel (file size < 100 kB); take a .png file with transparent background! -* **teaser:** about 640 x 480 pixel (file size < 100 kB) -* **stage:** about 1800 x 500 pixel (file size < 500 kB) -* **gallery:** about 1400 x 1000 pixel (file size < 350 kB) +* **logo.png:** about 150 x 150 pixel (file size < 100 kB) +* **teaser.jpeg:** about 640 x 480 pixel (file size < 100 kB) +* **stage.jpeg:** about 1800 x 500 pixel (file size < 500 kB) +* **gallery.jpeg:** about 1400 x 1000 pixel (file size < 350 kB) + +Note: the _logo_ requires the .png file format with transparent background. _Teaser_, _stage_ and _gallery_ images require the .jpg (.jpeg) file format. ## S3 folder structure -Before uploading the images an appropirate folder needs to be created at the S3 storage. For a good contribution please stick to the following naming conventions: +Before uploading the images an appropirate folder needs to be created at the S3 storage. For a good contribution please stick to the following conventions: -Build a event root folder for the event. Please add preceding number for providing a well grouped events order. -**pattern:** `event year`/`league name`/`event name with preceding number`/ -e.g. `2017`/`1. Segel-Bundesliga`/`01 Prien am Chiemsee`/ +Access the S3 folder of the corresponding event year. Create an event root folder and set its name to the name of the league or series of events (see **figure 1**). +![An exemplary event overview for 2017](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/year-and-event.jpeg) +**Figure 1: An exemplary event overview for 2017** -Add a folder for the _logo_, _stage_ and _teaser_ images -**pattern:** `{event root folder}`/`Images_Homepage`/ -e.g. `2017`/`1. Segel-Bundesliga`/`01 Prien am Chiemsee`/`Images_Homepage`/ +Within this folder add a new subfolder, set its name to the event's name and add a preceding number which indicates the order of it (see **figure 2**). +![folder structure](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/folder-structure.jpeg) +**Figure 2: folder structure with preceding event numbers within an event series** -Add a folder for the _galery_ images -**pattern:** `{event root folder}`/`Images_Photos`/ +According to this description result in following folder structure: +**pattern:** `~`/`event year`/`series name`/`event name with preceding number`/ +e.g. `~`/`2017`/`1. Segel-Bundesliga`/`01 Prien am Chiemsee`/ + +Access the corresponding event folder and create two subfolders: _Images\_Homepage_ and _Images\_Photos_. The first one is for the _stage_, _teaser_ and _logo_ images. The second one will carry _gallery_ images. + +**pattern:** `~`/`event year`/`series name`/`event name with preceding number`/`Images_Homepage`/ +e.g. `2017`/`1. Segel-Bundesliga`/`01 Prien am Chiemsee`/`Images_Homepage`/ + +**pattern:** `~`/`event year`/`series name`/`event name with preceding number`/`Images_Photos`/ e.g. `2017`/`1. Segel-Bundesliga`/`01 Prien am Chiemsee`/`Images_Photos`/ ## Image manipulation with GIMP @@ -43,7 +53,63 @@ The following guide is based on the tool [GIMP](https://www.gimp.org/) and its [ 3. Add -> Add a watermark -> leave defaults -> insert copyright information -> Ok 4. Add images -> ... -> Apply +## Exemplary instructions +1. Access the administration console _eventname(-master?)_.sapsailing.com/gwt/AdminConsle.html and login with your user credentials. +2. Select the corresponding event and click at the _edit_ button (see **figure 3**) +![Events overview on admin console](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/events-overview-admin-console.JPG) +**Figure 3: the edit button on the admin console** +3. By selecting the _Images_ tab the possibilities to add the four image types like described [above](https://wiki.sapsailing.com/preview#images_image-types-and-their-impact) show up (see **figure 4**). +![The four image types](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/edit-media-content.JPG) +**Figure 4: the four image types** +4. Click on the desired _add `image type` button_ and insert the _Image URL_ according to the memory location of the image. Usually, no further settings are required. +# Videos +A guidance about how to synchronize races with livestream videos can be found [here](https://wiki.sapsailing.com/wiki/howto/eventmanagers/linking-race-videos). +## Video types and their impact +We distinguish between _highlight_, _livestream_ and _gallery_ videos and a special _featured_ option. + +* **Highlight:** most event organizers provide highlight videos of their sailing events which summarize the best moments of the event. Such videos belong to the _highlight_ category. +* **Livestream:** some event organizers record livestreams of the races during the event. This videos belong to the _livestream_ category and can be linked to the _Race Analytics_ solution. +* **Gallery:** Some times interviews with sailors or other insights of the sailing event are provided by the organizers. These videos belong to the _gallery_ category. +* **Featured:** is a special option for an event video. This tag can be additionally added to one of the three categories _highlight_, _livestream_ and _gallery_. A video with the _featured_ tag is presented at the event's page instead of the _stage image_. +Note: Always define the most populare video to be _featured_ e.g. the current livestream during the event. Never define more than _one_ featured video. + +## Exemplary instructions +1. Access the administration console _eventname(-master?)_.sapsailing.com/gwt/AdminConsle.html and login with your user credentials. + +2. Select the corresponding event and click at the _edit_ button (see **figure 5**). +![Events overview on admin console](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/events-overview-admin-console.JPG) +**Figure 5: the edit button on the admin console** + +3. By selecting the _Videos_ tab the possibilities to add the three video types like described [above](https://wiki.sapsailing.com/wiki/howto/eventmanagers/Manage-media-content#videos_video-types-and-their-impact) show up (see **figure 6**). +![Figure 6: the three video types](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/edit-media-content-video.JPG) +**Figure 6: the three video types** + +4. Click on the desired _add_ `video type` _button_ and set the _Video URL_, the _Mime Type_, the _Title_ and the _Laguage_ of the video (see **figure 7**). +![insert a video](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/add-video.JPG) +**Figure 7: link a video with _Video URL_, _Mime Type_, _Title_ and _Language_** + +5. In the case you want to set a _featured_ video, select the _Enter tags for the video_ input field and enter _Featured_ (a small popup supports your input). When you're done, you'll need to click the _Add button_ next to the text box that attaches this tag to the video (see **figure 8**). +![Add a featured video](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/add-featured-video.JPG) +**Figure 8: add a featured video** + +# Competitor logos +The competitors of the sailing events can be equipped with their club flags. The following steps provide an exemplary guidance about how to add a club flag to its corresponding competitor. All available clubflags are located at the S3 storage under the folder `~`/`clubflags`/. + +## Required file format +* **File format:** .png with transparent background +* **File resolution:** about 100 x 50 pixel (file size < 25 kB) +* **Storage location:** `~`/`clubflags`/`country`/ +e.g. `~`/`clubflags`/`germany`/`clubflag1.png` + +## Exemplary instructions +1. Access the administration console _eventname(-master?)_.sapsailing.com/gwt/AdminConsle.html and login with your user credentials. + +2. Select _Tracked races_ and click on the _Competitors_ tab (see **figure 9**). + +3. Select the competitor, click on the _Edit_ button (see **figure 9**) and insert the Flag image URL of the corresponding club flag. +![Select the competitors tab within the admin console](https://s3-eu-west-1.amazonaws.com/media.sapsailing.com/wiki/how%20to/media%20content/clubflag-selection.JPG) +**Figure 9: competitors tab of the admin console** diff --git a/wiki/info/landscape/amazon-ec2.md b/wiki/info/landscape/amazon-ec2.md index 8ff007360ff..033b8f29024 100644 --- a/wiki/info/landscape/amazon-ec2.md +++ b/wiki/info/landscape/amazon-ec2.md @@ -200,6 +200,8 @@ To reach your multi instance via "ssv.sapsailing.com", perform the following ste Notice the overwritten health check port that is now pointing directly to the instance with its `SERVER_PORT` 8888. +BE CAREFUL please use for a live-server and live-master-server the traffic port for Health Checks. + 2. Add the "SL Multi-Instance Sailing Server" instance to the target group.