diff --git a/configuration/copyarchivestore.sh b/configuration/copyarchivestore.sh new file mode 100755 index 00000000000..bbef602cc10 --- /dev/null +++ b/configuration/copyarchivestore.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Start by exporting the production archive SecurityService-related data +./export_security.sh archive winddb 10201 +# Then import into a new security_service DB on the live replica set +./import_security.sh archive security_service 10203 diff --git a/configuration/export_security.sh b/configuration/export_security.sh new file mode 100755 index 00000000000..0b6a9a44b24 --- /dev/null +++ b/configuration/export_security.sh @@ -0,0 +1,13 @@ +#!/bin/bash +if [ "$1" = "" ]; then + echo "Usage: $0 {variant} {dbname} {port}" + echo "Example: $0 archive winddb 10201" +else + variant=$1 + dbname=$2 + port=$3 + COLLECTIONS="USERS USER_GROUPS OWNERSHIPS ACCESS_CONTROL_LISTS PREFERENCES ROLES SESSIONS" + for c in $COLLECTIONS; do + mongoexport --port $port -d $dbname -c $c >${c}_${variant}.json + done +fi diff --git a/configuration/import_security.sh b/configuration/import_security.sh new file mode 100755 index 00000000000..8a0cc619b68 --- /dev/null +++ b/configuration/import_security.sh @@ -0,0 +1,14 @@ +#!/bin/bash +if [ "$1" = "" ]; then + echo "Usage: $0 {variant} {dbname} {port}" + echo "Example: $0 archive winddb 10201" +else + variant=$1 + dbname=$2 + port=$3 + export PATH="${PATH}:/cygdrive/c/Program Files/MongoDB/Server/3.6/bin" + COLLECTIONS="USERS USER_GROUPS OWNERSHIPS ACCESS_CONTROL_LISTS PREFERENCES ROLES SESSIONS" + for c in $COLLECTIONS; do + mongoimport --drop --port $3 -d $dbname -c $c ${c}_${variant}.json + done +fi diff --git a/configuration/list-of-archive-dbs b/configuration/list-of-archive-dbs new file mode 100644 index 00000000000..9679bc61609 --- /dev/null +++ b/configuration/list-of-archive-dbs @@ -0,0 +1,318 @@ +20180125120615 +20180323121107 +20180323123903 +20180323130352master +20180323130352master-replica +29erWorlds2016 +29erWorlds2016-replica +49er +49er-n17-euros2017 +49er-n17-euros2017-replica +49erEuros2015 +49erEuros2016 +49erEuros2016-replica +49erJWC2015 +49erWorlds2019 +49erWorlds_2016 +49erWorlds_2016-replica +49erWorlds_2016-replica2 +49erWorlds_2016-replica3 +49erprincesssophiathrophy +49erworlds2015 +505-2017 +505-2017-replica +505-2017-replica-1 +505-2017-replica-2 +505Worlds2016 +505Worlds2016-replica +505_WORLDS_2015 +505worlds2018 +505worlds2018-replica +505worlds2019 +505worlds2019-replica +6mworlds2017 +6mworlds2017-replica +8MWORLDS2017 +8MWORLDS2017-replica +AARHUSSEJLKLUB +AST +ASVIA +AUDI_BUSINESS_CUP +AustrianLeague2015 +BAR +BYC +Bodenseewoche_2015 +Championsleague_2015 +Championsleague_2015-Replica +DANISHLEAGUE2017 +DANISHLEAGUE2017-replica +DJSL2019 +DNSTEST2 +DNSTEST2-replica +DUTCH_FEDERATION +DUTCH_FEDERATION-replica +DUTCH_LEAGUE_2017 +DUTCH_LEAGUE_2017-replica +DanishSailingLeague_2015 +DutchSailingLeague_2015 +FinnishSailingLeague_2015 +GC32WorldDB +GermanSailingLeague_2015 +GermanSailingLeague_2015-Replica +HANSESAIL2017 +HNV +HUMBA +IDMFINN2017 +ISR +JOJOPOLGAR +KBSC +KJSCS +KSSS +KW2015 +KW2015-replica +KW2015-replica2 +KW2017 +KW2017-replica +KW2018 +KW2018-replica +KW2018ST +LYC +MEDEMBLIK2018 +NCS2019-championships +NOR +Norwegianleague_2015 +PMYC +POLISHLEAGUE2017 +RDSAILING +RosAtomCup_2015 +SAILCANADA +SAILINGACADEMY +SAPOPTICUP2015 +SCL2017 +SCL2017-replica +SEGELZENTRUM +SGCN_Smartphone_2016 +SGCN_Smartphone_2016-replica +SINGAPORESAILING +SITGES +SPEEDSAILING2016 +SRN +SRV +SSC +SSV +STARLAB +SWC2016-Melbourne +SWC2016-Melbourne-replica +SWC2016-Qingdao +SWC2016-Qingdao-replica +SWC2017-Aarhus +SWC2017-HYERES +SWC2017-HYERES-replica +SWC2017-Miami +SWC2017-Miami-replica +SWC2017-SANTANDER +SWC2017-SANTANDER-replica +SWEDISHLEAGUE2017 +SWEDISHLEAGUE2017-replica +Sailtracks +TW2015 +TW2015-REPLICA +TW2015-test-replica +TW2017 +TW2017-replica +TW2018 +TW2018-replica +TracTracTest +USSAILING +VSAW +WCS-2018-APITEST +WCS2018-GAMAGORI +WCS2018-GAMAGORI-replica +WCS2018-Hyeres +WCS2018-Hyeres-replica +WCS2018-MIAMI +WCS2018-MIAMI-replica +WORLDCUPSYLT2015 +YES2015 +YES2015-REPLICA +YES2015-REPLICA2 +abeam-training +admin +aeroe-rund2018 +alarie505 +allsvenskan2015 +austrianleague2016 +austrianleague2017 +austrianleague2018 +austrianleague2019 +bartsbash2015 +bermuda2019 +bermuda2019-1 +bermuda2019-2 +bug1924test +bug2465 +bug2465-replica +bug4652-test +bundesliga +bundesliga1-2019 +bundesliga2-2017 +bundesliga2-2017-replica +bundesliga2-2018 +bundesliga2-2019 +bundesliga2016 +bundesliga2016-replica +bundesliga2016_2 +bundesliga2017 +bundesliga2017-replica +bundesliga2018 +bundesliga2018-replica +croatiacoastcup2019 +crw2016 +crw2016-replica +d-labs +danishleague2016 +danishleague2016-replica +danishleague2018 +danishleague2019 +dev +dsl-pokal2017 +dsl-pokal2018 +dsl-pokal2019 +dutchleague2016 +dutchleague2016-replica +dutchleague2018 +ess +ess-team-training +ess2015 +ess2016 +ess2017 +ess2017_foiling_test +ess2018 +ess2018-replica +ess2018_VE +ess2018testsd +ess40-2015-replica2 +finnishleague2016 +finnishleague2017 +finnishleague2018 +finnishleague2019 +finnishleague2019-final +finnishleague2019-qualifier2 +foiling-smartphone-test-replica +forumvostok2016 +frenchleague2017 +frenchleague2018 +frenchleague2019 +galvestaure +germanleague2016-replica +helgacup2019 +hsc-womensteam +hwcs2020-round1 +hwcs2020-round2 +hwcs2020-tablettraining +idmstar2016 +image-update +imagetest +irishdbscdw3 +isafwc2014 +janekbalster +jonaswitt +kielerwoche2016 +kielerwoche2016-replica +kiellauf2017 +kornaticup2019 +kw2019 +local +mdf +medemblik2019 +multitenant +nordstreamrace2018 +nordstreamrace2019 +northsearegatta2017 +northstreamrace2017 +norwegianleague2016 +norwegianleague2016-replica +norwegianleague2017 +norwegianleague2018 +norwegianleague2019 +performance-test +permission-vertical +peterwagner +phillip2018-1 +phoenix +polishleague2018 +polishleague2019 +rbfg2018 +rbfg2018-replica +replication-test-replica +rheinwoche2019 +rsc2016 +sailracer +sapexperience2018tw +sapexperience2018twsmartphone +sapexperience2019-nrv +sapexperience2019tw +sapjvm8 +sapnow2019 +schadewaldt +schanzenberg +scl2016 +scl2016-replica +scl2018-portocervo +scl2018-sanktpetersburg +scl2018saintmoritz +scl2019-final +scl2019-qualifier1 +scl2019-qualifier2 +scl2019-qualifier3 +scoringtest2018-v2 +seascape +sharding-replica +slmultiinstancekiel +swedishleague201-6replica +swedishleague2016 +swedishleague2016-replica +swedishleague2018 +swedishleague2019 +tamaratestserver +tempestworlds2019 +test +test-phillip +test-steffen +test-steffen123-1 +test-steffen2018-1 +test-steffentest-1 +testevent2019 +testshared +testshared2 +testshared3 +testshared4 +testshared6 +testshared7 +testshared9 +thetis2018 +thorbennowak +tw2016 +tw2016-replica +ubilabs-test +ubilabstest +usoda2016 +wcs2018-marseille +wcs2018-marseille-replica +wcs2019-enoshima +wcs2019-enoshima-freeze-thursday +wcs2019-enoshima-replica +wcs2019-genoa +wcs2019-marseille +wcs2019-miami +wcs2019-miami-tablettraining +wcs2019-tablettraining +wcs2019-test +winddb +windestimation +wmrt2016 +worlds2018 +worlds2018-replica +worlds2018shadow +wow2018 +bye diff --git a/configuration/list-of-dbs-to-merge b/configuration/list-of-dbs-to-merge new file mode 100644 index 00000000000..6ff441cee20 --- /dev/null +++ b/configuration/list-of-dbs-to-merge @@ -0,0 +1,48 @@ +ARHUSSEJLKLUB +abeam-training +alarie505 +AST +ASVIA +baldeneysee +BYC +DUTCH_FEDERATION +first-seascape-germany +formula18 +galvestaure +HNV +hsc-womensteam +irishdbscdw3 +ISR +janekbalster +JOJOPOLGAR +jonaswitt +KBSC +KJSCS +KSSS +kyc +LYC +mdf +my +NOR +oakcliff +peterwagner +phoenix +PMYC +RDSAILING +rheinwoche2019 +SAILCANADA +SAILINGACADEMY +sailracer +schadewaldt +schanzenberg +schwielochsee +SEGELZENTRUM +SINGAPORESAILING +SITGES +SRN +SRV +SSC +SSV +STARLAB +TracTracTest +USSAILING diff --git a/configuration/list-of-live-dbs b/configuration/list-of-live-dbs new file mode 100644 index 00000000000..7ee3be13204 --- /dev/null +++ b/configuration/list-of-live-dbs @@ -0,0 +1,37 @@ +BYC +DUTCH_FEDERATION +HNV +JOJOPOLGAR +KJSCS +LYC +NOR +RDSAILING +SEGELZENTRUM +SITGES +SRN +SRV +STARLAB +Sailtracks +TracTracTest +USSAILING +VSAW +abeam-training +admin +apachetest +baldeneysee +config +first-seascape-germany +formula18 +galvestaure +jonaswitt +kyc +local +my +oakcliff +peterwagner +rheinwoche2019 +sailracer +schwielochsee +seascape +security_service +swisstimingtest diff --git a/configuration/mergedbs.sh b/configuration/mergedbs.sh new file mode 100755 index 00000000000..c09c3899f9a --- /dev/null +++ b/configuration/mergedbs.sh @@ -0,0 +1,13 @@ +#!/bin/sh +cat list-of-dbs-to-merge | while read i; do + if grep $i list-of-live-dbs ; then + echo "Found $i in live DBs" + java -Dmongo.uri="mongodb://localhost:10203/security_service?replicaSet=live&retryWrites=true" -Ddefault.tenant.name=ARCHIVE-server -jar SecurityStoreMerger.jar "mongodb://localhost:10203/${i}?replicaSet=live&retryWrites=true" ${i}-server 2>&1 | tee storemerge_${i}.log + elif grep $i list-of-archive-dbs ; then + echo "Found $i in archive DBs" + java -Dmongo.uri="mongodb://localhost:10203/security_service?replicaSet=live&retryWrites=true" -Ddefault.tenant.name=ARCHIVE-server -jar SecurityStoreMerger.jar "mongodb://localhost:10202/${i}" ${i}-server 2>&1 | tee storemerge_${i}.log + else + echo "DB $i not found in either archive or live DBs. Aborting" + exit 1 + fi +done diff --git a/configuration/problems_during_security_service_merge.log b/configuration/problems_during_security_service_merge.log new file mode 100644 index 00000000000..a12c5f80f14 --- /dev/null +++ b/configuration/problems_during_security_service_merge.log @@ -0,0 +1,21 @@ +storemerge_abeam-training.log:SEVERE: User Masanobu from group abeam-training-server dropped. Removing from group. + nobu@jp.northsails.com and Kousakabeer@docomo.ne.jo +storemerge_AST.log:SEVERE: Dropping unqualified permission DATA_MINING for user elliss +storemerge_ASVIA.log:SEVERE: User axel.schmidt.91 from group ASVIA-server dropped. Removing from group. + axel.sebastian.schmidt@rwth-aachen.de and Axel.sebastian.schmidt@rwth-aachen.de +storemerge_NOR.log:SEVERE: User thomas from group NOR-server dropped. Removing from group. + thomas@no.northsails.com and mail@t-klug.de +storemerge_NOR.log:SEVERE: User frank from group NOR-server dropped. Removing from group. + "" and bode.f@gmx.de +storemerge_peterwagner.log:SEVERE: User peter.wagner from group peterwagner-server dropped. Removing from group. + ueberwagner@gmail.com and peter.wagner@segelverband.at +storemerge_peterwagner.log:SEVERE: User tamara from group peterwagner-server dropped. Removing from group. + tamara96fischer@gmai.com and tamara96fischer@gmail.com +storemerge_schwielochsee.log:SEVERE: User rahxam from group schwielochsee-server dropped. Removing from group. + mh@sc-schwielochsee.de and maximilian.hartig@sap.com +storemerge_SEGELZENTRUM.log:SEVERE: User max from group SEGELZENTRUM-server dropped. Removing from group. + maximilian.gross@sap.com and maxgross@t-online.de +storemerge_TracTracTest.log:SEVERE: User jorge from group TracTracTest-server dropped. Removing from group. + maximilian.gross@sap.com and maxgross@t-online.de +storemerge_USSAILING.log:SEVERE: User Peter from group USSAILING-server dropped. Removing from group. + maximilian.gross@sap.com and maxgross@t-online.de diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java index d3e24db6872..c8eba0b2e79 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java @@ -113,6 +113,7 @@ public enum DetailType implements Serializable { RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING), RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING), RACE_CALCULATED_TIME_TRAVELED(0, ASCENDING), + RACE_IMPLIED_WIND(5, DESCENDING), RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING), RACE_TIME_TRAVELED(0, ASCENDING), RACE_TIME_TRAVELED_UPWIND(0, ASCENDING), @@ -262,6 +263,7 @@ public enum DetailType implements Serializable { availableDetailsTypes.add(DetailType.LEG_GAP_TO_LEADER_IN_SECONDS); availableDetailsTypes.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS); availableDetailsTypes.add(DetailType.RACE_RANK); + availableDetailsTypes.add(DetailType.RACE_IMPLIED_WIND); availableDetailsTypes.add(DetailType.REGATTA_RANK); availableDetailsTypes.add(DetailType.CHART_DISTANCE_TO_START_LINE); availableDetailsTypes.add(DetailType.CHART_BEAT_ANGLE); @@ -368,6 +370,7 @@ public enum DetailType implements Serializable { allowed.add(RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START); allowed.add(RACE_TIME_TRAVELED); allowed.add(RACE_CALCULATED_TIME_TRAVELED); + allowed.add(RACE_IMPLIED_WIND); allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD); allowed.add(RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS); allowed.add(RACE_CURRENT_DISTANCE_FOILED_IN_METERS); @@ -409,6 +412,7 @@ public enum DetailType implements Serializable { allowed.add(OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND); allowed.add(OVERALL_TOTAL_TIME_SAILED_IN_SECONDS); allowed.add(OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS); + // TODO bug5209 make ToT / ToD details depend on ranking metric allowed.add(OVERALL_TIME_ON_TIME_FACTOR); allowed.add(OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); allowed.add(OVERALL_TOTAL_SCORED_RACE_COUNT); @@ -425,6 +429,21 @@ public enum DetailType implements Serializable { return allowed; } + public static Collection getAllToTToDHandicapDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD); + allowed.add(RACE_CALCULATED_TIME_TRAVELED); + return allowed; + } + + public static Collection getAllOrcPerformanceCurveDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD); + allowed.add(RACE_CALCULATED_TIME_TRAVELED); + allowed.add(RACE_IMPLIED_WIND); + return allowed; + } + public static Collection getLegDetailColumnTypes() { final Collection allowed = new LinkedHashSet<>(); allowed.add(LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS); @@ -574,5 +593,4 @@ public enum DetailType implements Serializable { } throw new IllegalArgumentException("Could not restore " + value + " to an DetailType enum"); } - } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java index c3b55b0444b..7b25efa40a9 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java @@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; +import com.sap.sse.common.Speed; import com.sap.sse.common.impl.MillisecondsDurationImpl; /** @@ -121,6 +122,19 @@ public class LeaderboardEntryDTO implements Serializable { */ public Duration calculatedTime; + /** + * If this is a leaderboard entry for an ORC Performance Curve Scoring (PCS) race, + * the field holds the so-called "implied wind" as a speed. It is the wind speed with which, + * according to its polar, the competitor would have sailed the part of the course sailed + * so far in the time elapsed so far. For ORC PCS before 2015 this was the primary ranking + * criterion. Since 2015, however, implied wind has lost in significance because it is + * used only to compute a default wind speed by maximizing all implied wind values of all + * competitors in the race, and then using this wind speed to determine the time allowance + * for each competitor so that a different between time elapsed and the allowance can then + * be used for ranking. + */ + public Speed impliedWind; + /** * The corrections applied to the time and distance sailed when the competitor would have reached the * competitor farthest ahead, based on average VMG on the current leg and equal performance to the boat diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPublicCertificateDatabase.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPublicCertificateDatabase.java index 03180de4dfa..78f2d0bdec0 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPublicCertificateDatabase.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPublicCertificateDatabase.java @@ -75,14 +75,14 @@ public class TestORCPublicCertificateDatabase { assertSoulmate(referenceNumber, result); } - // TODO this test will probably break 2020 when 2019 certificates will no longer be returned as valid... + // TODO this test will probably break 2021 when 2020 certificates will no longer be returned as valid... @Test public void testGetCertificate() throws Exception { - final String referenceNumber = "FRA00013881"; + final String referenceNumber = "03360000CE8"; final CertificateHandle handle = db.getCertificateHandle(referenceNumber); final ORCCertificate result = db.getCertificate(referenceNumber); assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001); - assertEquals(handle.getIssueDate(), result.getIssueDate()); + assertTrue(handle.getIssueDate().until(result.getIssueDate()).abs().asSeconds() < 10); assertEquals(handle.getSailNumber(), result.getSailNumber()); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index 15625d5e916..707f0dd4da1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -25,6 +25,9 @@ import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; + import com.sap.sailing.domain.abstractlog.AbstractLogEvent; import com.sap.sailing.domain.abstractlog.race.InvalidatesLeaderboardCache; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; @@ -73,6 +76,7 @@ import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCache; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater; import com.sap.sailing.domain.leaderboard.meta.MetaLeaderboardColumn; +import com.sap.sailing.domain.orc.impl.ORCPerformanceCurveByImpliedWindRankingMetric; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.ranking.RankingMetric.CompetitorRankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; @@ -701,9 +705,11 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { : raceDetails.getAverageSignedCrossTrackError().getMeters(); entryDTO.timeSailedSinceRaceStart = raceDetails.getTimeSailedSinceRaceStart(); entryDTO.calculatedTime = raceDetails.getCorrectedTime(); + if (trackedRace != null && trackedRace.getRankingMetric() instanceof ORCPerformanceCurveByImpliedWindRankingMetric) { + entryDTO.impliedWind = ((ORCPerformanceCurveByImpliedWindRankingMetric) trackedRace.getRankingMetric()).getImpliedWind(competitor, timePoint, cache); + } entryDTO.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead = raceDetails.getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead(); entryDTO.gapToLeaderInOwnTime = raceDetails.getGapToLeaderInOwnTime(); - try { BravoFixTrack sensorTrack = trackedRace.getSensorTrack(competitor, BravoFixTrack.TRACK_NAME); if (sensorTrack != null) { @@ -787,7 +793,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { } } catch (InterruptedException e) { throw new RuntimeException(e); - } catch (ExecutionException e) { + } catch (ExecutionException | MaxIterationsExceededException | FunctionEvaluationException e) { throw new RuntimeException(e); // the future used to calculate the leg details was interrupted; escalate as runtime exception } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurveRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurveRankingMetric.java new file mode 100644 index 00000000000..798dfca8bd2 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPerformanceCurveRankingMetric.java @@ -0,0 +1,23 @@ +package com.sap.sailing.domain.orc; + +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; +import com.sap.sse.common.Speed; +import com.sap.sse.common.TimePoint; + +/** + * Marker interface for all ORC Performance Curve Scoring (PCS) ranking + * metrics ({@link RankingMetric}). + * + * @author Axel Uhl (D043530) + * + */ +public interface ORCPerformanceCurveRankingMetric { + Speed getImpliedWind(Competitor competitor, TimePoint timePoint, + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) + throws FunctionEvaluationException, MaxIterationsExceededException; +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java index c3206d824f0..17ec95f091a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java @@ -76,7 +76,8 @@ import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsDurationImpl; -public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRankingMetric { +public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRankingMetric +implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric { private static final long serialVersionUID = -7814822523533929816L; private static final Logger logger = Logger.getLogger(ORCPerformanceCurveByImpliedWindRankingMetric.class.getName()); @@ -492,7 +493,8 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki return performanceCurveForPartialCourse; } - protected Speed getImpliedWind(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws FunctionEvaluationException, MaxIterationsExceededException { + @Override + public Speed getImpliedWind(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws FunctionEvaluationException, MaxIterationsExceededException { final Speed result; final ORCPerformanceCurve performanceCurveForPartialCourse = cache.getPerformanceCurveForPartialCourse(timePoint, getTrackedRace(), competitor, getPerformanceCurveSupplier(cache)); if (performanceCurveForPartialCourse != null) { @@ -565,7 +567,7 @@ public class ORCPerformanceCurveByImpliedWindRankingMetric extends AbstractRanki return result; }; } - + @Override public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { Duration result = null; diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 76a2b51c84c..dcf12e25167 100644 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -61,7 +61,8 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sse.datamining.ui, org.mongodb.mongo-java-driver;bundle-version="3.6.4", com.sap.sailing.server.gateway.serialization.shared.android, - com.sap.sailing.shared.server + com.sap.sailing.shared.server, + org.apache.commons.math;bundle-version="2.1.0" Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy Import-Package: javax.servlet;version="3.1.0", diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java index 66161b71d22..9e94b2a8277 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java @@ -166,7 +166,6 @@ public abstract class LeaderboardSettingsDialogComponent extends A setValuesWithReferenceOrder(Util.retainCopy(newSettings.getOverallDetailsToShow(), availableDetailTypes), DetailType.getAvailableOverallDetailColumnTypes(), selectedOverallDetailColumns); } - setShowCompetitorNationality(newSettings.isShowCompetitorNationality()); setShowAddedScores(newSettings.isShowAddedScores()); setShowCompetitorShortName(newSettings.isShowCompetitorShortNameColumn()); setShowCompetitorFullName(newSettings.isShowCompetitorFullNameColumn()); setShowCompetitorBoatInfo(newSettings.isShowCompetitorBoatInfoColumn()); - if (newSettings.getManeuverDetailsToShow() != null) { setValuesWithReferenceOrder(Util.retainCopy(newSettings.getManeuverDetailsToShow(), availableDetailTypes), ManeuverCountRaceColumn.getAvailableManeuverDetailColumnTypes(), selectedManeuverDetails); @@ -1273,6 +1271,10 @@ public abstract class LeaderboardPanel extends A new TotalTimeColumn(DetailType.RACE_CALCULATED_TIME_TRAVELED, new RaceCalculatedTimeTraveledInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_IMPLIED_WIND, + new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.RACE_IMPLIED_WIND, + new RaceImpliedWindInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); result.put(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, new TotalTimeColumn(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, new RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(), @@ -1798,6 +1800,18 @@ public abstract class LeaderboardPanel extends A } } + private class RaceImpliedWindInKnots implements DataExtractor { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.impliedWind == null ? null : fieldsForRace.impliedWind.getKnots(); + } + return result; + } + } + private class RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds implements DataExtractor { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 8d83387124a..fbf334253d8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -112,7 +112,6 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint implements Pr raceboardData.getAvailableDetailTypesForLeaderboard(), raceboardData.getRace()); RaceBoardComponentContext componentContext = new RaceBoardComponentContext(lifeCycle, getUserService(), storageDefinition); - componentContext.getInitialSettings( new DefaultOnSettingsLoadedCallback>() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 1df7c9caf5b..76f35fc29cc 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -65,6 +66,8 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.MaxIterationsExceededException; import org.apache.http.client.ClientProtocolException; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.AuthorizationException; @@ -364,6 +367,7 @@ import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuse import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.meta.MetaLeaderboardColumn; +import com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric; import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase; import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CertificateHandle; import com.sap.sailing.domain.persistence.DomainObjectFactory; @@ -382,6 +386,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; +import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike; @@ -3524,7 +3529,8 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet } private Double getCompetitorRaceDataEntry(DetailType dataType, TrackedRace trackedRace, Competitor competitor, - TimePoint timePoint, String leaderboardGroupName, String leaderboardName, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { + TimePoint timePoint, String leaderboardGroupName, String leaderboardName, + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException { Double result = null; Course course = trackedRace.getRace().getCourse(); course.lockForRead(); // make sure the tracked leg survives this call even if a course update is pending @@ -3596,6 +3602,20 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet result = (distanceToLeader == null) ? null : distanceToLeader.getMeters(); } break; + case RACE_IMPLIED_WIND: + final RankingMetric rankingMetric = trackedRace.getRankingMetric(); + if (rankingMetric instanceof ORCPerformanceCurveRankingMetric) { + final ORCPerformanceCurveRankingMetric orcPcsRankingMetric = + (ORCPerformanceCurveRankingMetric) rankingMetric; + try { + final Speed impliedWind = orcPcsRankingMetric.getImpliedWind(competitor, timePoint, cache); + result = impliedWind == null ? null : impliedWind.getKnots(); + } catch (MaxIterationsExceededException | FunctionEvaluationException e) { + logger.log(Level.WARNING, "Problem computing implied wind", e); + result = null; + } + } + break; case RACE_RANK: if (trackedLeg != null) { result = (double) trackedLeg.getRank(timePoint, cache); @@ -8308,7 +8328,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet @Override public Iterable determineDetailTypesForCompetitorChart(String leaderboardGroupName, RegattaAndRaceIdentifier identifier) { - final List availableDetailsTypes = new ArrayList<>(); + final LinkedHashSet availableDetailsTypes = new LinkedHashSet<>(); availableDetailsTypes.addAll(DetailType.getAutoplayDetailTypesForChart()); availableDetailsTypes.removeAll(DetailType.getRaceBravoDetailTypes()); final DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier); @@ -8329,6 +8349,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet availableDetailsTypes.addAll(DetailType.getRaceExtendedBravoDetailTypes()); availableDetailsTypes.addAll(DetailType.getRaceExpeditionDetailTypes()); } + final RankingMetrics rankingMetricType = trackedRace.getRankingMetric().getType(); + switch (rankingMetricType) { + case ONE_DESIGN: + case TIME_ON_TIME_AND_DISTANCE: + break; + case ORC_PERFORMANCE_CURVE: + case ORC_PERFORMANCE_CURVE_BY_IMPLIED_WIND: + case ORC_PERFORMANCE_CURVE_LEADER_FOR_BASELINE: + availableDetailsTypes.addAll(DetailType.getAllOrcPerformanceCurveDetailTypes()); + break; + } } if (leaderboardGroupName != null) { final LeaderboardGroupDTO group = getLeaderboardGroupByName(leaderboardGroupName, false); @@ -8572,8 +8603,24 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet final Set allowed = new HashSet<>(); allowed.addAll(DetailType.getAllNonRestrictedDetailTypes()); final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); - getSecurityService().checkCurrentUserReadPermission(leaderboard); if (leaderboard != null) { + getSecurityService().checkCurrentUserReadPermission(leaderboard); + if (leaderboard instanceof RegattaLeaderboard) { + final RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard; + final Regatta regatta = regattaLeaderboard.getRegatta(); + switch (regatta.getRankingMetricType()) { + case TIME_ON_TIME_AND_DISTANCE: + allowed.addAll(DetailType.getAllToTToDHandicapDetailTypes()); + break; + case ORC_PERFORMANCE_CURVE: + case ORC_PERFORMANCE_CURVE_BY_IMPLIED_WIND: + case ORC_PERFORMANCE_CURVE_LEADER_FOR_BASELINE: + allowed.addAll(DetailType.getAllOrcPerformanceCurveDetailTypes()); + break; + case ONE_DESIGN: + break; // no additional columns for one-design + } + } boolean hasBravoTrack = false; boolean hasExtendedBravoFixes = false; abort: for (RaceColumn race : leaderboard.getRaceColumns()) { @@ -8581,7 +8628,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet if (raceIdentifierOrNull != null && !raceIdentifierOrNull.equals(race.getRaceIdentifier(fleet))) { continue; } - final TrackedRace trace = race.getTrackedRace(fleet); if (trace != null) { final DynamicTrackedRace trackedRace = getService().getTrackedRace(trace.getRaceIdentifier()); @@ -9268,7 +9314,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet @Override public MailInvitationType getMailType() { MailInvitationType type = MailInvitationType - .valueOf(System.getProperty(MAILTYPE_PROPERTY, MailInvitationType.SailInsight2.name())); + .valueOf(System.getProperty(MAILTYPE_PROPERTY, MailInvitationType.SailInsight3.name())); return type; } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/leaderboard/DetailCheckboxInfo.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/leaderboard/DetailCheckboxInfo.java index b33139f8f85..e9f8a2d5a7a 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/leaderboard/DetailCheckboxInfo.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/leaderboard/DetailCheckboxInfo.java @@ -4,19 +4,19 @@ import com.sap.sailing.domain.common.DetailType; public enum DetailCheckboxInfo { - //Overall details + // Overall details REGATTA_RANK(DetailType.REGATTA_RANK, "Regatta Rank"), TOTAL_DISTANCE(DetailType.OVERALL_TOTAL_DISTANCE_TRAVELED , "Total distance"), TOTAL_AVERAGE_SPEED_OVER_GROUND(DetailType.OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND, "∅ SOG"), TOTAL_TIME(DetailType.OVERALL_TOTAL_TIME_SAILED_IN_SECONDS, "Total time"), MAXIMUM_SPEED_OVER_GROUND(DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, "Max SOG"), TIME_ON_TIME_FACTOR(DetailType.OVERALL_TIME_ON_TIME_FACTOR, "ToT Factor"), TIME_ON_DISTANCE_ALLOWANCE(DetailType.OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, "ToD Allowance"), - //Race details - RACE_GAP_TO_LEADER(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, "Gap to leader"), RACE_AVERAGE_SPEED_OVER_GROUND(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, "∅ Speed"), RACE_DISTANCE(DetailType.RACE_DISTANCE_TRAVELED, "Distance"), RACE_DISTANCE_INCLUDING_GATE_START(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, "Distance (w/ Gate Start)"), RACE_TIME(DetailType.RACE_TIME_TRAVELED, "Time"), RACE_CALCULATED_TIME(DetailType.RACE_CALCULATED_TIME_TRAVELED, "Calculated Time"), RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, "Calc.Time @ Fastest"), RACE_CURRENT_SPEED_OVER_GROUND(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, "SOG"), RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, "Gap"), NUMBER_OF_MANEUVERS(DetailType.NUMBER_OF_MANEUVERS, "Maneuvers"), DISPLAY_LEGS(DetailType.RACE_DISPLAY_LEGS, "Legs"), CURRENT_LEG(DetailType.RACE_CURRENT_LEG, "On Leg"), RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, "∅ XTE"), RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, "∅ XTE +/-"), RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, "GPS Lag"), + // Race details + RACE_GAP_TO_LEADER(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, "Gap to leader"), RACE_AVERAGE_SPEED_OVER_GROUND(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, "∅ Speed"), RACE_DISTANCE(DetailType.RACE_DISTANCE_TRAVELED, "Distance"), RACE_DISTANCE_INCLUDING_GATE_START(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, "Distance (w/ Gate Start)"), RACE_TIME(DetailType.RACE_TIME_TRAVELED, "Time"), RACE_CALCULATED_TIME(DetailType.RACE_CALCULATED_TIME_TRAVELED, "Calculated Time"), RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, "Calc.Time @ Fastest"), RACE_IMPLIED_WIND(DetailType.RACE_IMPLIED_WIND, "Implied Wind"), RACE_CURRENT_SPEED_OVER_GROUND(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, "SOG"), RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, "Gap"), NUMBER_OF_MANEUVERS(DetailType.NUMBER_OF_MANEUVERS, "Maneuvers"), DISPLAY_LEGS(DetailType.RACE_DISPLAY_LEGS, "Legs"), CURRENT_LEG(DetailType.RACE_CURRENT_LEG, "On Leg"), RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, "∅ XTE"), RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, "∅ XTE +/-"), RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, "GPS Lag"), - //Race Start Analysis + // Race Start Analysis RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_START(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, "Distance to line 5s before start"), RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, "SOG 5s before start"), DISTANCE_TO_START_AT_RACE_START(DetailType.DISTANCE_TO_START_AT_RACE_START, "Distance to line at Start"), TIME_BETWEEN_RACE_START_AND_COMPETITOR_START(DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, "Start delay"), SPEED_OVER_GROUND_AT_RACE_START(DetailType.SPEED_OVER_GROUND_AT_RACE_START, "SOG at Start"), SPEED_OVER_GROUND_WHEN_STARTING(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, "SOG when Starting"), DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_STARTING(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, "Dist. Stb-Side of Line when Starting"), START_TACK(DetailType.START_TACK, "Start Tack"), - //Leg Details + // Leg Details AVERAGE_SPEED_OVER_GROUND(DetailType.LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, "∅ Speed"), DISTANCE(DetailType.LEG_DISTANCE_TRAVELED, "Distance"), DISTANCE_INCLUDING_START(DetailType.LEG_DISTANCE_TRAVELED_INCLUDING_GATE_START, "Distance (w/ Gate Start)"), GAP_TO_LEADER(DetailType.LEG_GAP_TO_LEADER_IN_SECONDS, "Gap to leader"), GAP_CHANGE_SINCE_LEG_START(DetailType.LEG_GAP_CHANGE_SINCE_LEG_START_IN_SECONDS, "Gap Change"), SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(DetailType.LEG_SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED, "Mark rounded to"), CURRENT_SPEED_OVER_GROUND(DetailType.LEG_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, "SOG (∅ at end)"), WINDWARD_DISTANCE_TO_GO(DetailType.LEG_WINDWARD_DISTANCE_TO_GO_IN_METERS, "Windward distance to go"), NUMBER_OF_MANEVEURS(DetailType.NUMBER_OF_MANEUVERS, "Maneuvers"), ESTIMATED_TIME_TO_NEXT_WAYPOINT(DetailType.LEG_ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, "ETA"), VELOCITY_MADE_GOOD(DetailType.LEG_VELOCITY_MADE_GOOD_IN_KNOTS, "VMG"), TIME(DetailType.LEG_TIME_TRAVELED, "Time"), CORRECTED_TIME(DetailType.LEG_CORRECTED_TIME_TRAVELED, "Calculated Time"), AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR(DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, "∅ XTE"), AVERAGE_SIGNED_CROSS_TRACK_ERROR(DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS , "∅ XTE +/-"), RANK_GAIN(DetailType.LEG_RANK_GAIN, "Rank Gain"), - //Maneuvers + // Maneuvers TACK(DetailType.TACK, "Tack"), AVERAGE_TACK_LOSS(DetailType.AVERAGE_TACK_LOSS_IN_METERS, "∅ Tack Loss"), JIBE(DetailType.JIBE, "Jibe"), AVERAGE_JIBE_LOSS(DetailType.AVERAGE_JIBE_LOSS_IN_METERS, "∅ Jibe Loss"), PENALTY_CIRCLE(DetailType.PENALTY_CIRCLE, "Penalty circle"), AVERAGE_MANEUVER_LOSS(DetailType.AVERAGE_MANEUVER_LOSS_IN_METERS, "∅ Maneuver Loss"); public static final String CHECK_BOX_DEBUGID_CONSTANT = "CheckBox"; diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index 3ca2d629be4..fd52179d2f6 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -305,6 +305,7 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TRAVELED, DetailType.RACE_TIME_TRAVELED, + DetailType.RACE_IMPLIED_WIND, DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_CURRENT_COURSE_OVER_GROUND_IN_TRUE_DEGREES, DetailType.RACE_CURRENT_POSITION_LAT_DEG, @@ -362,6 +363,12 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { value = timeTraveled.asSeconds(); } break; + case RACE_IMPLIED_WIND: + name = "impliedWind-kts"; + if (entry.impliedWind != null) { + value = entry.impliedWind.getKnots(); + } + break; case RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS: name = "currentSpeedOverGround-kts"; if (currentLegEntry != null && currentLegEntry.currentSpeedOverGroundInKnots != null) { diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateServerConfiguration.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateServerConfiguration.java index 683ea8547d0..a87e7919eca 100644 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateServerConfiguration.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateServerConfiguration.java @@ -13,14 +13,6 @@ public class UpdateServerConfiguration extends AbstractRacingEventServiceOperati this.serverConfiguration = serverConfiguration; } - /** - * {@link #internalApplyTo(RacingEventService)} already replicates the effects - */ - @Override - public boolean isRequiresExplicitTransitiveReplication() { - return false; - } - @Override public Void internalApplyTo(RacingEventService toState) throws Exception { toState.updateServerConfiguration(serverConfiguration); diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch index 45e2a84a909..a44e29981ee 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch index 8649245abb2..e61aed2536c 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch @@ -19,7 +19,7 @@ - + diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java index e054d325c83..03557bf2aee 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java @@ -340,9 +340,12 @@ public class ReplicationServlet extends AbstractHttpServlet { } private ReplicaDescriptor getReplicaDescriptor(HttpServletRequest req) throws UnknownHostException { - InetAddress ipAddress = InetAddress.getByName(req.getRemoteAddr()); - UUID uuid = UUID.fromString(req.getParameter(SERVER_UUID)); - String additional = req.getParameter(ADDITIONAL_INFORMATION); + final String forwardedFor = req.getHeader("X-Forwarded-For"); // could have come through a load balancer / reverse proxy + final InetAddress ipAddress = forwardedFor != null && !forwardedFor.trim().isEmpty() + ? InetAddress.getByName(forwardedFor) + : InetAddress.getByName(req.getRemoteAddr()); + final UUID uuid = UUID.fromString(req.getParameter(SERVER_UUID)); + final String additional = req.getParameter(ADDITIONAL_INFORMATION); final String[] replicableIdsAsStrings = req.getParameter(REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED).split(","); return new ReplicaDescriptorImpl(ipAddress, uuid, additional, replicableIdsAsStrings); } diff --git a/java/target/env.sh b/java/target/env.sh index d18faeeb8c7..0190a75fcab 100755 --- a/java/target/env.sh +++ b/java/target/env.sh @@ -88,6 +88,8 @@ fi # Credentials can be provided either as a combination of username and password, # or alternatively as a single bearer token that was obtained, e.g., through # curl -d "username=myuser&password=mysecretpassword" "https://master-server.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token +# or by logging in to the master server using your web browser and then navigating to +# https://master-server.sapsailing.com/security/api/restsecurity/access_token # # REPLICATE_MASTER_USERNAME= # REPLICATE_MASTER_PASSWORD= @@ -161,6 +163,17 @@ else JAVA_VERSION_SPECIFIC_ARGS=$JAVA_8_ARGS fi ADDITIONAL_JAVA_ARGS="$JAVA_VERSION_SPECIFIC_ARGS $ADDITIONAL_JAVA_ARGS -Dpersistentcompetitors.clear=false -Drestore.tracked.races=true -Dpolardata.source.url=https://www.sapsailing.com -Dwindestimation.source.url=https://www.sapsailing.com -XX:MaxGCPauseMillis=500" + +# To enable the use of the shared SecurityService and SharedSailingData from security-service.sapsailing.com, uncomment and fill in the following: +#ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dsecurity.sharedAcrossSubdomainsOf=sapsailing.com -Dsecurity.baseUrlForCrossDomainStorage=https://security-service.sapsailing.com -Dgwt.acceptableCrossDomainStorageRequestOriginRegexp=https?://(.*\.)?sapsailing\.com(:[0-9]*)?$" +#REPLICATE_ON_START=com.sap.sse.security.impl.SecurityServiceImpl,com.sap.sailing.shared.server.impl.SharedSailingDataImpl +#REPLICATE_MASTER_SERVLET_HOST=security-service.sapsailing.com +#REPLICATE_MASTER_SERVLET_PORT=443 +#REPLICATE_MASTER_EXCHANGE_NAME=security_service +# Obtain the bearer token for user security-service-replicator by logging on to https://security-service.sapsailing.com and then +# getting https://security-service.sapsailing.com/security/api/restsecurity/access_token +#REPLICATE_MASTER_BEARER_TOKEN="..." + echo ADDITIONAL_JAVA_ARGS=${ADDITIONAL_JAVA_ARGS} ON_AMAZON=`command -v ec2-metadata` diff --git a/wiki/info/landscape/amazon-ec2.md b/wiki/info/landscape/amazon-ec2.md index c9ca47a1206..0bb66640cc7 100644 --- a/wiki/info/landscape/amazon-ec2.md +++ b/wiki/info/landscape/amazon-ec2.md @@ -73,7 +73,6 @@ SERVER_NAME=MYSPECIFICEVENT REPLICATION_CHANNEL=myspecificevent MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true" SERVER_STARTUP_NOTIFY=you@email.com -ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.domain.tracking.MailInvitationType=SailInsight2" - After your master server is ready, note the internal IP and configure your replica instances. Set up a user account there that has the following permission: ``SERVER:REPLICATE:{SERVERNAME}``. You will need this user's credentials to authenticate your replicas for replication. @@ -97,7 +96,6 @@ SERVER_NAME=MYSPECIFICEVENT MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent-replica?replicaSet=live&retryWrites=true" EVENT_ID=<some-uuid-of-an-event-you-want-to-feature> SERVER_STARTUP_NOTIFY=you@email.com -ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.domain.tracking.MailInvitationType=SailInsight2" #### Setting up a Multi Instance @@ -199,8 +197,6 @@ To set up a multi instance for a server with name "SSV", subdomain "ssv.sapsaili 13. Your multi instance is now configured and started. It can be reached over ec2-34-250-136-229.eu-west-1.compute.amazonaws.com:8888. - - ##### Reachability To reach your multi instance via "ssv.sapsailing.com", perform the following steps within the AWS Web Console inside region Ireland. diff --git a/wiki/projects/consolidating-user-stores.md b/wiki/projects/consolidating-user-stores.md index 72157d74fa4..a3a4ed71044 100644 --- a/wiki/projects/consolidating-user-stores.md +++ b/wiki/projects/consolidating-user-stores.md @@ -332,9 +332,9 @@ Should we implement a migration of Local Storage properties? If a server is conf ## The Actual Merging Process -There is a new server instance ``security-service.sapsailing.com`` with MongoDB ``mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/security_service?replicaSet=live&retryWrites=true``. The initial contents of the DB were the copies of the security-related collections as taken from the ``ARCHIVE`` server, with DB connectivity string ``mongodb://dbserver.internal.sapsailing.com:10201/winddb?replicaSet=archive&retryWrites=true``. This process is encoded in the script ``copyarchivestore.sh``. +There is a new server instance ``security-service.sapsailing.com`` with MongoDB ``mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/security_service?replicaSet=live&retryWrites=true``. The initial contents of the DB were the copies of the security-related collections as taken from the ``ARCHIVE`` server, with DB connectivity string ``mongodb://dbserver.internal.sapsailing.com:10201/winddb?replicaSet=archive&retryWrites=true``. This process is encoded in the script ``dbserver.internal.sapsailing.com:/var/lib/mongodb/security_service/copyarchivestore.sh``. A copy of this script can also be found in git under ``configuration/``. It uses the scripts ``export_security.sh`` and ``import_security.sh`` which are found in the same directory, respectively. -An exported JAR file based on a launch configuration for the ``SecurityStoreMerger`` class has been placed under ``dbserver.internal.sapsailing.com:/var/lib/mongodb/security_service``. To merge, e.g., the ``VSAW`` database, use the following command: +An exported JAR file based on a launch configuration (``MergeSecurityAndAccessControlStores.launch``) for the ``SecurityStoreMerger`` class has been placed under ``dbserver.internal.sapsailing.com:/var/lib/mongodb/security_service``. To merge, e.g., the ``VSAW`` database, use the following command: ``` java -Dmongo.uri="mongodb://localhost:10203/security_service?replicaSet=live&retryWrites=true" -Ddefault.tenant.name=ARCHIVE-server -jar SecurityStoreMerger.jar "mongodb://localhost:10203/VSAW?replicaSet=live&retryWrites=true" VSAW-server 2>&1 | tee storemerge_VSAW.log @@ -372,4 +372,28 @@ So the total sequence of commands for running the entire import/merge process is ./copyarchivestore.sh ./mergedbs.sh ``` -This produces all ``storemerge_....log`` files and a merge result in the ``mongodb://localhost:10203/security_service?replicaSet=live&retryWrites=true`` database. Again, search for WARNING and Exception in the logs, understand dropped objects and do some spot checks. Also validate by launching the ``security-service.sapsailing.com`` server and inspect security-related objects in the AdminConsole. \ No newline at end of file +This produces all ``storemerge_....log`` files and a merge result in the ``mongodb://localhost:10203/security_service?replicaSet=live&retryWrites=true`` database. Again, search for WARNING and Exception in the logs, understand dropped objects and do some spot checks. Also validate by launching the ``security-service.sapsailing.com`` server and inspect security-related objects in the AdminConsole. + +### Updating the server instances + +The first step is to restart the security-service.sapsailing.com instance based on the new DB. It uses the following configuration options in its env.sh to expose the shared services: + +``` +ADDITIONAL_JAVA_ARGS=... -Dsecurity.sharedAcrossSubdomainsOf=sapsailing.com -Dsecurity.baseUrlForCrossDomainStorage=https://security-service.sapsailing.com -Dgwt.acceptableCrossDomainStorageRequestOriginRegexp=https?://(.*\.)?sapsailing\.com(:[0-9]*)?$ +REPLICATION_CHANNEL=security_service +``` + +A new user ``security-service-replicator`` has been added that has replication permissions for the ``security-service`` server. Other servers need to obtain an access token for that user and add it in their startup options as follows: + +``` +ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dsecurity.sharedAcrossSubdomainsOf=sapsailing.com -Dsecurity.baseUrlForCrossDomainStorage=https://security-service.sapsailing.com -Dgwt.acceptableCrossDomainStorageRequestOriginRegexp=https?://(.*\.)?sapsailing\.com(:[0-9]*)?$" +REPLICATE_ON_START=com.sap.sse.security.impl.SecurityServiceImpl,com.sap.sailing.shared.server.impl.SharedSailingDataImpl +REPLICATE_MASTER_SERVLET_HOST=security-service.sapsailing.com +REPLICATE_MASTER_SERVLET_PORT=443 +REPLICATE_MASTER_EXCHANGE_NAME=security_service +REPLICATE_MASTER_BEARER_TOKEN="..." +``` + +The bearer token can be obtained by logging on as user ``security-service-replicator`` to ``https://security-service.sapsailing.com`` and then getting the access token from ``https://security-service.sapsailing.com/security/api/restsecurity/access_token``. Ask one of j.hamann@sailtracks.tv, alessandro.stoltenberg@sap.com and axel.uhl@sap.com for the user credentials. Of course, any other user that has permissions ``SERVER:REPLICATE:security-service`` and ``SERVER:READ_REPLICATOR:security-service`` can do the same, so a user may also ask someone who has the premissions required to transfer the permissions to another user. + +These properties have now been appended to the env.sh files of the existing servers on the multi-instance set-up (including swisstimingtest, sailtracks, and tractractest). \ No newline at end of file