Merge branch 'master' into translation

Change-Id: I37c105cecd6ce319dbf4c93795dfc6afafea2faf
This commit is contained in:
Axel Uhl
2017-04-05 14:38:00 +02:00
349 changed files with 5608 additions and 1937 deletions
+3 -1
View File
@@ -85,7 +85,9 @@ git checkout $ANDROID_RELEASE_BRANCH
git merge -m "Merging $GIT_REMOTE/$ANDROID_RELEASE_BRANCH" $GIT_REMOTE/$ANDROID_RELEASE_BRANCH git merge -m "Merging $GIT_REMOTE/$ANDROID_RELEASE_BRANCH" $GIT_REMOTE/$ANDROID_RELEASE_BRANCH
git fetch $GIT_REMOTE $RELEASE_BRANCH:$RELEASE_BRANCH git fetch $GIT_REMOTE $RELEASE_BRANCH:$RELEASE_BRANCH
git merge -m "Merging $RELEASE_BRANCH into $ANDROID_RELEASE_BRANCH, probably incorporating version setting to -SNAPSHOT" $GIT_REMOTE/$RELEASE_BRANCH git merge -m "Merging $RELEASE_BRANCH into $ANDROID_RELEASE_BRANCH, probably incorporating version setting to -SNAPSHOT" $GIT_REMOTE/$RELEASE_BRANCH
git push $GIT_REMOTE $ANDROID_RELEASE_BRANCH:$ANDROID_RELEASE_BRANCH if [ "$PERFORM_GIT_OPERATIONS" = "1" ]; then
git push $GIT_REMOTE $ANDROID_RELEASE_BRANCH:$ANDROID_RELEASE_BRANCH
fi
# Patch the AndroidManifest.xml files to upgrade the android:versionCode sequential counter relevant for the PlayStore # Patch the AndroidManifest.xml files to upgrade the android:versionCode sequential counter relevant for the PlayStore
# and the android:versionName which is what the user sees and which we expect to follow a major.minor version scheme, # and the android:versionName which is what the user sees and which we expect to follow a major.minor version scheme,
Binary file not shown.
+149 -148
View File
@@ -1,148 +1,149 @@
# Welcome to the SAP Sailing Wiki # Welcome to the SAP Sailing Wiki
This is the <img src="https://www.sapsailing.com/images/sap-logo_grey.png"/> Wiki where useful information regarding this project can be found. This is the <img src="https://www.sapsailing.com/images/sap-logo_grey.png"/> Wiki where useful information regarding this project can be found.
### The Pitch ### The Pitch
Like businesses, sailors need the latest information to make strategic decisions - but they need it even faster. One wrong tack, a false estimation of the current, or the slightest wind shift can cost the skipper the entire race. As premium sponsor of the Kieler Woche 2011, and co-sponsor of Sailing Team Germany (STG), SAP is showing how innovative IT solutions providing real time data analysis can give teams the competitive edge. Like businesses, sailors need the latest information to make strategic decisions - but they need it even faster. One wrong tack, a false estimation of the current, or the slightest wind shift can cost the skipper the entire race. As premium sponsor of the Kieler Woche 2011, and co-sponsor of Sailing Team Germany (STG), SAP is showing how innovative IT solutions providing real time data analysis can give teams the competitive edge.
SAP is at the center of todays technology revolution, developing innovations that not only help businesses run like never before, but also improve the lives of people everywhere. As market leader in enterprise application software, SAP SE (NYSE: SAP) helps companies of all sizes and industries run better. From back office to boardroom, warehouse to storefront, desktop to mobile device SAP empowers people and organizations to work together more efficiently and use business insight more effectively to stay ahead of the competition. SAP applications and services enable more than 258,000 customers to operate profitably, adapt continuously, and grow sustainably. SAP is at the center of todays technology revolution, developing innovations that not only help businesses run like never before, but also improve the lives of people everywhere. As market leader in enterprise application software, SAP SE (NYSE: SAP) helps companies of all sizes and industries run better. From back office to boardroom, warehouse to storefront, desktop to mobile device SAP empowers people and organizations to work together more efficiently and use business insight more effectively to stay ahead of the competition. SAP applications and services enable more than 258,000 customers to operate profitably, adapt continuously, and grow sustainably.
# Table of Contents # Table of Contents
## Information ## Information
### General ### General
* [[Information about this Wiki and HowTo|wiki/info/general/wiki]] * [[Information about this Wiki and HowTo|wiki/info/general/wiki]]
* [[General Project Information|wiki/info/general/general-information]] * [[General Project Information|wiki/info/general/general-information]]
* [[Inventory|wiki/info/general/inventar-liste]] * [[Inventory|wiki/info/general/inventar-liste]]
### Landscape and Development ### Landscape and Development
* [[Architecture and Infrastructure|wiki/info/landscape/architecture-and-infrastructure]] * [[Architecture and Infrastructure|wiki/info/landscape/architecture-and-infrastructure]]
* [[Runtime Environment|wiki/info/landscape/runtime-environment]] * [[Runtime Environment|wiki/info/landscape/runtime-environment]]
* [[Basic architectual principles|wiki/info/landscape/basic-architectural-principles]] * [[Basic architectual principles|wiki/info/landscape/basic-architectural-principles]]
* [[User Management|wiki/info/landscape/usermanagement]] * [[User Management|wiki/info/landscape/usermanagement]]
* [[Development Environment|wiki/info/landscape/development-environment]] * [[Development Environment|wiki/info/landscape/development-environment]]
* [[Production Environment|wiki/info/landscape/production-environment]] * [[Production Environment|wiki/info/landscape/production-environment]]
* [[Typical Development Scenarios|wiki/info/landscape/typical-development-scenarios]] * [[Typical Development Scenarios|wiki/info/landscape/typical-development-scenarios]]
* [[RaceLog Tracking Server Architecture|wiki/info/landscape/server]] * [[RaceLog Tracking Server Architecture|wiki/info/landscape/server]]
* Environment Overview [[PDF|wiki/info/mobile/event-tracking/architecture.pdf]] | [[SVG|wiki/info/mobile/event-tracking/architecture.svg]] * Environment Overview [[PDF|wiki/info/mobile/event-tracking/architecture.pdf]] | [[SVG|wiki/info/mobile/event-tracking/architecture.svg]]
* Amazon * Amazon
* [[Amazon EC2|wiki/info/landscape/amazon-ec2]] * [[Amazon EC2|wiki/info/landscape/amazon-ec2]]
* [[EC2 Backup Strategy|wiki/info/landscape/amazon-ec2-backup-strategy]] * [[EC2 Backup Strategy|wiki/info/landscape/amazon-ec2-backup-strategy]]
* [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]] * [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]]
* [[EC2 mail relaying|wiki/info/landscape/mail-relaying]] * [[EC2 mail relaying|wiki/info/landscape/mail-relaying]]
* [[Building and Deploying|wiki/info/landscape/building-and-deploying]] * [[Building and Deploying|wiki/info/landscape/building-and-deploying]]
* [[Data Mining Architecture|wiki/info/landscape/data-mining-architecture]] * [[Data Mining Architecture|wiki/info/landscape/data-mining-architecture]]
* [[Typical Data Mining Scenarios|wiki/info/landscape/typical-data-mining-scenarios]] * [[Typical Data Mining Scenarios|wiki/info/landscape/typical-data-mining-scenarios]]
* [[Create clickable UI prototypes with Axure|wiki/info/landscape/ui-clickable-prototypes]] * [[Create clickable UI prototypes with Axure|wiki/info/landscape/ui-clickable-prototypes]]
* [[Webdesign|wiki/info/landscape/webdesign]] * [[Webdesign|wiki/info/landscape/webdesign]]
### Mobile ### Mobile
* [[Mobile Development|wiki/info/mobile/mobile-development]] * [[Mobile Development|wiki/info/mobile/mobile-development]]
* Tracking App * Tracking App
* [[Tracking App Specification|wiki/info/mobile/app-spec/app-spec]] * [[Tracking App Specification|wiki/info/mobile/app-spec/app-spec]]
* [[Event Tracking|wiki/info/mobile/event-tracking/event-tracking]] * [[Event Tracking|wiki/info/mobile/event-tracking/event-tracking]]
* [[Steps for setting up Smartphone Tracking|wiki/info/mobile/smartphone-tracking-steps]] * [[Steps for setting up Smartphone Tracking|wiki/info/mobile/smartphone-tracking-steps]]
* [[Tracking App Prototype Architecture|wiki/info/mobile/app]] * [[Tracking App Prototype Architecture|wiki/info/mobile/app]]
* Racecommittee App * Racecommittee App
* [[Racecommittee App|wiki/info/mobile/racecommittee-app]] * [[Racecommittee App|wiki/info/mobile/racecommittee-app]]
* [[Environment|wiki/info/mobile/racecommittee-app-environment]] * [[Environment|wiki/info/mobile/racecommittee-app-environment]]
* [[Administrator|wiki/info/mobile/racecommittee-app-administrator]] * [[Administrator|wiki/info/mobile/racecommittee-app-administrator]]
* [[User|wiki/info/mobile/racecommittee-app-user]] * [[User|wiki/info/mobile/racecommittee-app-user]]
* [[Android and Release Build|wiki/info/mobile/android-and-release-build]] * [[Android and Release Build|wiki/info/mobile/android-and-release-build]]
* [[Energy consumption of mobile apps|wiki/info/mobile/energy-consumption]] * [[Energy consumption of mobile apps|wiki/info/mobile/energy-consumption]]
* [[Data consumption of mobile apps|wiki/info/mobile/data-consumption]] * [[Data consumption of mobile apps|wiki/info/mobile/data-consumption]]
* [[Mobile Sailing Analytics|wiki/info/mobile/mobilesailinganalytics]] * [[Mobile Sailing Analytics|wiki/info/mobile/mobilesailinganalytics]]
* [[Push Notifications|wiki/info/mobile/push-notifications]] * [[Push Notifications|wiki/info/mobile/push-notifications]]
* [[NMEA|wiki/info/mobile/nmea]]
### API
### API
* [[Web Services API|wiki/info/api/sailing-webservices]]
* [[API v1|wiki/info/api/api-v1]] * [[Web Services API|wiki/info/api/sailing-webservices]]
* [[Tracking App API|wiki/info/api/tracking-app-api]] * [[API v1|wiki/info/api/api-v1]]
* [[Training API v1 draft|wiki/info/api/training-api-v1-draft]] * [[Tracking App API|wiki/info/api/tracking-app-api]]
* [[Training API v1 draft|wiki/info/api/training-api-v1-draft]]
### Security
* [[Fortify Tests|wiki/info/security/fortify]] ### Security
* [[SSL / HTTPS Support|wiki/info/security/ssl-support]] * [[Fortify Tests|wiki/info/security/fortify]]
* [[SSL / HTTPS Support|wiki/info/security/ssl-support]]
### Miscellaneous
### Miscellaneous
* [[Big Data - Numbers|wiki/info/misc/big-data-numbers]]
* [[Data Quality|wiki/info/misc/data-quality]] * [[Big Data - Numbers|wiki/info/misc/big-data-numbers]]
* [[Sailing Domain Algorithms|wiki/info/misc/sailing-domain-algorithms]] * [[Data Quality|wiki/info/misc/data-quality]]
* [[Google Analytics (Web Page Tracking)|wiki/info/misc/ganalytics]] * [[Sailing Domain Algorithms|wiki/info/misc/sailing-domain-algorithms]]
* [[S3 Development Sample|wiki/info/misc/s3-sample]] * [[Google Analytics (Web Page Tracking)|wiki/info/misc/ganalytics]]
* [[S3 Development Sample|wiki/info/misc/s3-sample]]
* [[FAQ for League Operators|wiki/info/misc/league-operators-faq]]
* [[Prionet related information|wiki/info/misc/prionet-related-information]] * [[FAQ for League Operators|wiki/info/misc/league-operators-faq]]
* [[TracTrac Workshop 2013|wiki/info/misc/tractracworkshop2013]] * [[Prionet related information|wiki/info/misc/prionet-related-information]]
* [[TracTrac Workshop 2013|wiki/info/misc/tractracworkshop2013]]
## HowTo
## HowTo
* [[Onboarding|wiki/howto/onboarding]]
* [[Onboarding|wiki/howto/onboarding]]
### Development
### Development
* [[Create boat graphics for the 2D race viewer|wiki/howto/development/boatgraphicssvg]]
* [[Continuous Integration with Hudson/Jenkins|wiki/howto/development/ci]] * [[Create boat graphics for the 2D race viewer|wiki/howto/development/boatgraphicssvg]]
* [[Dispatch|wiki/howto/development/dispatch]] * [[Continuous Integration with Hudson/Jenkins|wiki/howto/development/ci]]
* [[Working with GWT UI Binder|wiki/howto/development/gwt-ui-binder]] * [[Dispatch|wiki/howto/development/dispatch]]
* [[Java De(Serialization) and Circular Dependencies|wiki/howto/development/java-de-serialization-and-circular-dependencies]] * [[Working with GWT UI Binder|wiki/howto/development/gwt-ui-binder]]
* [[JMX Support|wiki/howto/development/jmx]] * [[Java De(Serialization) and Circular Dependencies|wiki/howto/development/java-de-serialization-and-circular-dependencies]]
* [[Working with GWT Locally|wiki/howto/development/local-gwt]] * [[JMX Support|wiki/howto/development/jmx]]
* [[Log File Analysis|wiki/howto/development/log-file-analysis]] * [[Working with GWT Locally|wiki/howto/development/local-gwt]]
* [[UI Tests with Selenium|wiki/howto/development/selenium-ui-tests]] * [[Log File Analysis|wiki/howto/development/log-file-analysis]]
* [[Profiling|wiki/howto/development/profiling]] * [[UI Tests with Selenium|wiki/howto/development/selenium-ui-tests]]
* [[Working with GWT Super Dev Mode|wiki/howto/development/super-dev-mode]] * [[Profiling|wiki/howto/development/profiling]]
* [[Working with GWT Super Dev Mode|wiki/howto/development/super-dev-mode]]
### For Event Managers
### For Event Managers
* [[Set up local network with replication server|wiki/howto/eventmanagers/event-network-with-replica]]
* [[Operating Igtimi WindBots|wiki/howto/eventmanagers/windbot-operations]] * [[Set up local network with replication server|wiki/howto/eventmanagers/event-network-with-replica]]
* [[Linking Race Videos|wiki/howto/eventmanagers/linking-race-videos]] * [[Operating Igtimi WindBots|wiki/howto/eventmanagers/windbot-operations]]
* [[Import official results|wiki/howto/eventmanagers/results-import]] * [[Linking Race Videos|wiki/howto/eventmanagers/linking-race-videos]]
* [[Import official results|wiki/howto/eventmanagers/results-import]]
### Setup
### Setup
* [[Configure Races on Server|wiki/howto/setup/configure-races-on-server]]
* [[Setting up internal Jenkins on SAP Monsoon|wiki/howto/setup/setting-up-jenkins-on-sap-monsoon]] * [[Configure Races on Server|wiki/howto/setup/configure-races-on-server]]
* [[Setting up internal Jenkins on SAP Monsoon|wiki/howto/setup/setting-up-jenkins-on-sap-monsoon]]
### Miscellaneous
### Miscellaneous
* [[Cook Book|wiki/howto/misc/cook-book]]
* [[Polars|wiki/howto/misc/polars]] * [[Cook Book|wiki/howto/misc/cook-book]]
* [[Server Replication|wiki/howto/misc/server-replication]] * [[Polars|wiki/howto/misc/polars]]
* [[TracTrac|wiki/howto/misc/tractrac-lifecycle]] * [[Server Replication|wiki/howto/misc/server-replication]]
* [[UI Tests|wiki/howto/misc/ui-tests-tutorial]] * [[TracTrac|wiki/howto/misc/tractrac-lifecycle]]
* [[Uploading Media Content|wiki/howto/misc/uploading-media-content]] * [[UI Tests|wiki/howto/misc/ui-tests-tutorial]]
* [[Monitoring Apache and RabbitMQ|wiki/misc/monitoring-apache-and-rabbitmq]] * [[Uploading Media Content|wiki/howto/misc/uploading-media-content]]
* [[Monitoring Apache and RabbitMQ|wiki/misc/monitoring-apache-and-rabbitmq]]
## Projects
* [[Analytics on a stick|wiki/projects/analytics-on-a-stick]] ## Projects
* [[Analytics on a stick|wiki/projects/analytics-on-a-stick]]
## Events and Planning
* [[Project Planning (bigger development)|wiki/events/planning]] ## Events and Planning
* [[General Event Planning|wiki/events/general-event-planning]] * [[Project Planning (bigger development)|wiki/events/planning]]
* [[Information about Extreme Sailing Series|wiki/events/extreme-sailing-series]] * [[General Event Planning|wiki/events/general-event-planning]]
* [[Travem&uuml;nder Woche 2014 event page|wiki/events/tw2014]] * [[Information about Extreme Sailing Series|wiki/events/extreme-sailing-series]]
* [[505 worlds Kiel 2014 event page|wiki/events/505-worlds-kiel-2014]] * [[Travem&uuml;nder Woche 2014 event page|wiki/events/tw2014]]
* [[Kieler Woche event page|wiki/events/kieler-woche-2015]] * [[505 worlds Kiel 2014 event page|wiki/events/505-worlds-kiel-2014]]
* [[Charleston Race Week 2016|wiki/events/Charleston-Race-Week-2016]] * [[Kieler Woche event page|wiki/events/kieler-woche-2015]]
* [[Sailing Leagues 2016|wiki/events/sailing-Leagues-2016]] * [[Charleston Race Week 2016|wiki/events/Charleston-Race-Week-2016]]
* [[Sailing Leagues 2016|wiki/events/sailing-Leagues-2016]]
## Planning
* [[Overview|https://wiki.sapsailing.com/pages/wiki/planning/]] ## Planning
* [[Overview|https://wiki.sapsailing.com/pages/wiki/planning/]]
## Internal services (not related to wiki but useful)
## Internal services (not related to wiki but useful)
* [Bugzilla Issue Tracking System](http://bugzilla.sapsailing.com/bugzilla/)
* [GIT Repository (SAP)](ssh://git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git) * [Bugzilla Issue Tracking System](http://bugzilla.sapsailing.com/bugzilla/)
* [Maven Repository Browser](http://maven.sapsailing.com/maven/) (see [[how to setup repository for Android builds|wiki/info/mobile/racecommittee-app-environment]]) * [GIT Repository (SAP)](ssh://git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git)
* [Main Sailing Website](http://www.sapsailing.com) * [Maven Repository Browser](http://maven.sapsailing.com/maven/) (see [[how to setup repository for Android builds|wiki/info/mobile/racecommittee-app-environment]])
* [Visitor Statistics](http://analysis.sapsailing.com/) * [Main Sailing Website](http://www.sapsailing.com)
* [Visitor Statistics](http://analysis.sapsailing.com/)
@@ -3,7 +3,7 @@
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> <booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/> <stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/> <booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/> <stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9874"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/> <booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES"> <listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/> <listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
@@ -38,7 +38,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9874 com.sap.sailing.dashboards.gwt.RibDashboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
</launchConfiguration> </launchConfiguration>
@@ -97,7 +97,7 @@ WindSourceName=Wind Source Name
windFrom=True Wind Direction (TWD) windFrom=True Wind Direction (TWD)
windSpeedKnots=True Wind Speed (kts) windSpeedKnots=True Wind Speed (kts)
speedInKnots=Speed (kts) speedInKnots=Speed (kts)
bearing=Direction (°T) bearing=Direction (°T)
WindTrack=Wind Track WindTrack=Wind Track
timeSpentFoiling=Time spent foiling timeSpentFoiling=Time spent foiling
yaw=Yaw yaw=Yaw
@@ -1,11 +1,11 @@
Competitor=Teilnehmer Competitor=Teilnehmer
DistanceTraveled=zurückgelegte Distanz DistanceTraveled=zurückgelegte Distanz
Boat=Boot Boat=Boot
Team=Team Team=Team
Name=Name Name=Name
SearchTag=Such-Tag SearchTag=Such-Tag
Nationality=Nationalitäts Nationality=Nationalitäts
Acronym=Abkürzung Acronym=Abkürzung
SailID=Segelnummer SailID=Segelnummer
LegNumber=Nummer des Schenkels LegNumber=Nummer des Schenkels
LegType=Art des Schenkels LegType=Art des Schenkels
@@ -26,7 +26,7 @@ WindFixSailingDomainRetrieverChain=Windmessungen
MarkPassingSailingDomainRetrieverChain=Tonnenrundungen MarkPassingSailingDomainRetrieverChain=Tonnenrundungen
RaceResultSailingDomainRetrieverChain=Rennergebnisse RaceResultSailingDomainRetrieverChain=Rennergebnisse
RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers
ManeuverSailingDomainRetrieverChain=Manöver ManeuverSailingDomainRetrieverChain=Manöver
LeaderboardGroup=Ranglisten Gruppe LeaderboardGroup=Ranglisten Gruppe
Leaderboard=Rangliste Leaderboard=Rangliste
Leg=Schenkel Leg=Schenkel
@@ -36,19 +36,19 @@ GpsFix=GPS-Fix
BravoFix=Bravo-Fix BravoFix=Bravo-Fix
BravoFixTrack=Bravo-Messreihe BravoFixTrack=Bravo-Messreihe
WindFix=Windmessung WindFix=Windmessung
Maneuver=Manöver Maneuver=Manöver
Speed=Geschwindigkeit Speed=Geschwindigkeit
SpeedTenSecondsBefore=Geschwindigkeit 10s vorher SpeedTenSecondsBefore=Geschwindigkeit 10s vorher
WindStrengthInBeaufort=Windstärke in Beaufort WindStrengthInBeaufort=Windstärke in Beaufort
Bft0=Windstille Bft0=Windstille
Bft1=leiser Zug Bft1=leiser Zug
Bft2=leichte Brise Bft2=leichte Brise
Bft3=schwacher Wind Bft3=schwacher Wind
Bft4=mäßiger Wind Bft4=mäßiger Wind
Bft5=frischer Wind Bft5=frischer Wind
Bft6=starker Wind Bft6=starker Wind
Bft7=steifer Wind Bft7=steifer Wind
Bft8=stürmischer Wind Bft8=stürmischer Wind
Bft9=Sturm Bft9=Sturm
Bft10=schwerer Sturm Bft10=schwerer Sturm
Bft11=orkanartiger Sturm Bft11=orkanartiger Sturm
@@ -59,53 +59,53 @@ RelativeScoreInRaceInPercent=Relative Wertung im Rennen in %
AbsoluteRank=Absolute Platzierung AbsoluteRank=Absolute Platzierung
WindSpeedInBeaufort=Windgeschwindigkeit in Bft. WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
SpeedWhenStarting=Geschwindigkeit beim Überqueren der Startlinie SpeedWhenStarting=Geschwindigkeit beim Überqueren der Startlinie
SpeedTenSecondsBeforeStart=Geschwindigkeit 10s vor dem Startzeitpunkt SpeedTenSecondsBeforeStart=Geschwindigkeit 10s vor dem Startzeitpunkt
RankAtFirstMark=Position beim ersten Wegpunkt RankAtFirstMark=Position beim ersten Wegpunkt
NumberOfManeuvers=Anzahl der Manöver NumberOfManeuvers=Anzahl der Manöver
NumberOfTacks=Anzahl der Wenden NumberOfTacks=Anzahl der Wenden
NumberOfJibes=Anzahl der Halsen NumberOfJibes=Anzahl der Halsen
NumberOfPenaltyCircles=Anzahl der Straf-Kreise NumberOfPenaltyCircles=Anzahl der Straf-Kreise
RankGainsOrLosses=Positions-Verluste oder -Gewinne RankGainsOrLosses=Positions-Verluste oder -Gewinne
DistanceToStarboardSideAtStart=Distanz zur Steuerbordseite (% der Länge) DistanceToStarboardSideAtStart=Distanz zur Steuerbordseite (% der Länge)
NumberOfPodiumFinish=Azahl der Podium-Siege NumberOfPodiumFinish=Azahl der Podium-Siege
NumberOfWins=Anzahl der Siege NumberOfWins=Anzahl der Siege
RankGainsOrLossesBetweenFirstMarkAndFinish=Positions-Verluste oder -Gewinne (1ter Wegpunkt -> Ziel) RankGainsOrLossesBetweenFirstMarkAndFinish=Positions-Verluste oder -Gewinne (1ter Wegpunkt -> Ziel)
IsTracked=Ist getracked IsTracked=Ist getracked
NumberOfCompetitorFixes=Anzahl der Teilnehmer-Fixes NumberOfCompetitorFixes=Anzahl der Teilnehmer-Fixes
NumberOfMarkFixes=Anzahl der Tonnen-Fixes NumberOfMarkFixes=Anzahl der Tonnen-Fixes
ManeuverType=Art des Manövers ManeuverType=Art des Manövers
Tack=Bug Tack=Bug
SpeedBefore=Geschwindigkeit bevor dem Manöver SpeedBefore=Geschwindigkeit bevor dem Manöver
SpeedAfter=Geschwindigkeit nach dem Manöver SpeedAfter=Geschwindigkeit nach dem Manöver
DirectionChange=Richtungsänderung in Grad DirectionChange=Richtungsänderung in Grad
AbsoluteDirectionChange=Absolute Richtungsänderung in Grad (positiv) AbsoluteDirectionChange=Absolute Richtungsänderung in Grad (positiv)
ToSide=In Richtung (Backbord / Steuerbord) ToSide=In Richtung (Backbord / Steuerbord)
ManeuverLoss=Manöververlust ManeuverLoss=Manöververlust
Waypoint=Wegpunkt Waypoint=Wegpunkt
PassingSide=Passierende Seite PassingSide=Passierende Seite
TackAtStart=Startbug TackAtStart=Startbug
SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt
RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
WindwardDistanceToAdvantageousEndOfLine=Windwärtige Distanz zum Bevorteilten Ende der Startlinie WindwardDistanceToAdvantageousEndOfLine=Windwärtige Distanz zum Bevorteilten Ende der Startlinie
RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels
AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie
TimeSpentInSeconds=Benötigte Zeit in Sekunden TimeSpentInSeconds=Benötigte Zeit in Sekunden
MedalRace=Medaillenrennen MedalRace=Medaillenrennen
WindSourceType=Art der Windmessung WindSourceType=Art der Windmessung
WindSourceName=Name der Windmessreihe WindSourceName=Name der Windmessreihe
windFrom=Wind aus Richtung (TWD) windFrom=Wind aus Richtung (TWD)
windSpeedKnots=Wahre Windgeschwindigkeit (kn) windSpeedKnots=Wahre Windgeschwindigkeit (kn)
speedInKnots=Fahrt (kn) speedInKnots=Fahrt (kn)
bearing=Richtung (°) bearing=Richtung (°)
WindTrack=Windmessreihe WindTrack=Windmessreihe
timeSpentFoiling=Zeit auf Foils timeSpentFoiling=Zeit auf Foils
yaw=Gierung yaw=Gierung
pitch=Neigung pitch=Neigung
heel=Krängung heel=Krängung
rideHeight=Foiling-Höhe rideHeight=Foiling-Höhe
rideHeightPort=Foiling-Höhe Backbord-Rumpf rideHeightPort=Foiling-Höhe Backbord-Rumpf
rideHeightStarboard=Foiling-Höhe Steuerbord-Rumpf rideHeightStarboard=Foiling-Höhe Steuerbord-Rumpf
IsFoiling=Foilt IsFoiling=Foilt
FoilingDuration=Foilingdauer FoilingDuration=Foilingdauer
FoilingDistance=Gefoilte Distanz FoilingDistance=Gefoilte Distanz
@@ -113,8 +113,8 @@ TakeoffSpeedInKnotsInKnots=Abhebegeschwindigkeit in Knoten
LandingSpeedInKnots=Landegeschwindigkeit in Knoten LandingSpeedInKnots=Landegeschwindigkeit in Knoten
AbsoluteTrueWindAngleAtTakeoff=Absoluter Winkel zum wahren Wind (TWA) beim Abheben AbsoluteTrueWindAngleAtTakeoff=Absoluter Winkel zum wahren Wind (TWA) beim Abheben
AbsoluteTrueWindAngleAtLanding=Absoluter Winkel zum wahren Wind (TWA) beim Landen AbsoluteTrueWindAngleAtLanding=Absoluter Winkel zum wahren Wind (TWA) beim Landen
WindStrengthInBeaufortAtTakeoff=Windstärke beim Abheben in Beaufort WindStrengthInBeaufortAtTakeoff=Windstärke beim Abheben in Beaufort
WindStrengthInBeaufortAtLanding=Windstärke beim Landen in Beaufort WindStrengthInBeaufortAtLanding=Windstärke beim Landen in Beaufort
WindAtTakeoff=Wind beim Abheben WindAtTakeoff=Wind beim Abheben
WindAtLanding=Wind beim Landen WindAtLanding=Wind beim Landen
StartsOnLegType=Bahnschnenkeltyp beim Abheben StartsOnLegType=Bahnschnenkeltyp beim Abheben
@@ -97,7 +97,7 @@ WindSourceName=Nom de la source du vent
windFrom=Direction réelle du vent (TWD) windFrom=Direction réelle du vent (TWD)
windSpeedKnots=Vitesse réelle du vent (en nds) windSpeedKnots=Vitesse réelle du vent (en nds)
speedInKnots=Vitesse (nds) speedInKnots=Vitesse (nds)
bearing=Direction (T) bearing=Direction (°T)
WindTrack=Enregistrement vent WindTrack=Enregistrement vent
timeSpentFoiling=Temps passé en planing timeSpentFoiling=Temps passé en planing
yaw=Dévier yaw=Dévier
@@ -1,5 +1,6 @@
package com.sap.sailing.datamining; package com.sap.sailing.datamining;
import java.nio.charset.StandardCharsets;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@@ -61,7 +62,8 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
private boolean dataSourceProvidersHaveBeenInitialized; private boolean dataSourceProvidersHaveBeenInitialized;
public Activator() { public Activator() {
sailingServerStringMessages = new ResourceBundleStringMessagesImpl(STRING_MESSAGES_BASE_NAME, getClassLoader()); sailingServerStringMessages = new ResourceBundleStringMessagesImpl(STRING_MESSAGES_BASE_NAME, getClassLoader(),
StandardCharsets.UTF_8.name());
dataRetrieverChainDefinitions = new SailingDataRetrievalChainDefinitions(); dataRetrieverChainDefinitions = new SailingDataRetrievalChainDefinitions();
predefinedQueries = new SailingPredefinedQueries(); predefinedQueries = new SailingPredefinedQueries();
} }
@@ -2,4 +2,5 @@ eclipse.preferences.version=1
encoding//src/com/sap/sailing/domain/common/AbstractBearing.java=UTF-8 encoding//src/com/sap/sailing/domain/common/AbstractBearing.java=UTF-8
encoding//src/com/sap/sailing/domain/common/confidence/impl/ConfidenceBasedAveragerImpl.java=utf-8 encoding//src/com/sap/sailing/domain/common/confidence/impl/ConfidenceBasedAveragerImpl.java=utf-8
encoding//src/com/sap/sailing/domain/common/impl/AbstractSpeedWithAbstractBearingImpl.java=UTF-8 encoding//src/com/sap/sailing/domain/common/impl/AbstractSpeedWithAbstractBearingImpl.java=UTF-8
encoding//src/com/sap/sailing/domain/common/tracking/impl/CompactGPSFixMovingImpl.java=UTF-8 encoding//src/com/sap/sailing/domain/common/tracking/impl/AbstractCompactGPSFixMovingImpl.java=UTF-8
encoding//src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java=UTF-8
@@ -3,16 +3,18 @@ package com.sap.sailing.domain.common;
import java.io.Serializable; import java.io.Serializable;
import java.util.Set; import java.util.Set;
public interface MasterDataImportObjectCreationCount extends Serializable{ public interface MasterDataImportObjectCreationCount extends Serializable {
int getLeaderboardCount();
public abstract int getLeaderboardCount(); int getLeaderboardGroupCount();
public abstract int getLeaderboardGroupCount(); int getEventCount();
public abstract int getEventCount(); int getRegattaCount();
public abstract int getRegattaCount(); int getMediaTrackCount();
public abstract Set<String> getOverwrittenRegattaNames(); Set<String> getOverwrittenRegattaNames();
void addOneMediaTrack();
} }
@@ -4,6 +4,6 @@ public enum ScoringSchemeType {
// make sure to also add new ones to ScoringSchemeTypeFormatter // make sure to also add new ones to ScoringSchemeTypeFormatter
LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL,HIGH_POINT_ESS_OVERALL_12, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN, LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL,HIGH_POINT_ESS_OVERALL_12, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN,
HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT, HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT,
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07, HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07,
LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY
} }
@@ -17,12 +17,12 @@ import com.sap.sailing.domain.common.MasterDataImportObjectCreationCount;
* *
*/ */
public class MasterDataImportObjectCreationCountImpl implements MasterDataImportObjectCreationCount { public class MasterDataImportObjectCreationCountImpl implements MasterDataImportObjectCreationCount {
private static final long serialVersionUID = -3677005970701170818L; private static final long serialVersionUID = -3677005970701170818L;
private int leaderboardCount = 0; private int leaderboardCount = 0;
private int leaderboardGroupCount = 0; private int leaderboardGroupCount = 0;
private int eventCount = 0; private int eventCount = 0;
private int regattaCount = 0; private int regattaCount = 0;
private int mediaTrackCount = 0;
private Set<String> createdLeaderboards = new HashSet<String>(); private Set<String> createdLeaderboards = new HashSet<String>();
private Set<String> createdLeaderboardGroups = new HashSet<String>(); private Set<String> createdLeaderboardGroups = new HashSet<String>();
@@ -52,7 +52,11 @@ public class MasterDataImportObjectCreationCountImpl implements MasterDataImport
public void addOneRegatta(String id) { public void addOneRegatta(String id) {
createdRegattas.add(id); createdRegattas.add(id);
regattaCount++; regattaCount++;
}
@Override
public void addOneMediaTrack() {
mediaTrackCount++;
} }
public void add(MasterDataImportObjectCreationCountImpl toAdd) { public void add(MasterDataImportObjectCreationCountImpl toAdd) {
@@ -101,6 +105,11 @@ public class MasterDataImportObjectCreationCountImpl implements MasterDataImport
public int getRegattaCount() { public int getRegattaCount() {
return regattaCount; return regattaCount;
} }
@Override
public int getMediaTrackCount() {
return mediaTrackCount;
}
@Override @Override
public Set<String> getOverwrittenRegattaNames() { public Set<String> getOverwrittenRegattaNames() {
@@ -33,7 +33,7 @@ public class WindImpl extends KnotSpeedWithBearingImpl implements Wind {
} }
public static int hashCode(double latDeg, double lngDeg, long timePointAsMillis) { public static int hashCode(double latDeg, double lngDeg, long timePointAsMillis) {
return (31 * (int) (timePointAsMillis & Integer.MAX_VALUE)) ^ (int) (latDeg*lngDeg); return (31 * (int) (timePointAsMillis & Integer.MAX_VALUE)) ^ (int) (Math.round(latDeg)*Math.round(lngDeg));
} }
/** /**
* Wind hash is determined based on time point and position only to speed this up a little. * Wind hash is determined based on time point and position only to speed this up a little.
@@ -69,7 +69,6 @@ public class MediaTrack implements Serializable {
Set<RegattaAndRaceIdentifier> assignedRaces) { Set<RegattaAndRaceIdentifier> assignedRaces) {
this(title, url, startTime, duration, mimeType, assignedRaces); this(title, url, startTime, duration, mimeType, assignedRaces);
this.dbId = dbId; this.dbId = dbId;
} }
public String toString() { public String toString() {
@@ -14,10 +14,11 @@ public enum BravoSensorDataMetadata {
INSTANCE; INSTANCE;
private static final String RIDE_HEIGHT_PORT_HULL = "RideHeightPortHull"; public static final String RIDE_HEIGHT = "RideHeight";
private static final String RIDE_HEIGHT_STBD_HULL = "RideHeightStbdHull"; public static final String RIDE_HEIGHT_PORT_HULL = "RideHeightPortHull";
private static final String HEEL = "Heel"; public static final String RIDE_HEIGHT_STBD_HULL = "RideHeightStbdHull";
private static final String PITCH = "ImuSensor_Pitch"; public static final String HEEL = "Heel";
public static final String PITCH = "ImuSensor_Pitch";
private final int HEADER_COLUMN_OFFSET = 3; private final int HEADER_COLUMN_OFFSET = 3;
@@ -1,11 +1,6 @@
package com.sap.sailing.domain.common.tracking.impl; package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.AbstractTimePoint; import com.sap.sse.common.impl.AbstractTimePoint;
@@ -21,9 +16,9 @@ import com.sap.sse.common.impl.AbstractTimePoint;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public class CompactGPSFixImpl extends AbstractGPSFixImpl { public abstract class AbstractCompactGPSFixImpl extends AbstractGPSFixImpl {
private static final long serialVersionUID = 8167588584536992501L; private static final long serialVersionUID = 3943572107870245437L;
/** /**
* Bit mask for {@link #whatIsCached}, telling whether validity is currently cached * Bit mask for {@link #whatIsCached}, telling whether validity is currently cached
*/ */
@@ -39,9 +34,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
* <code>{@link #whatIsCached}&amp;{@link #IS_VALIDITY_CACHED} != 0</code> * <code>{@link #whatIsCached}&amp;{@link #IS_VALIDITY_CACHED} != 0</code>
*/ */
private static final byte VALIDITY = 1<<2; private static final byte VALIDITY = 1<<2;
private final double latDeg;
private final double lngDeg;
private final long timePointAsMillis; private final long timePointAsMillis;
/** /**
@@ -52,32 +45,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
*/ */
private byte whatIsCached = 0; private byte whatIsCached = 0;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class CompactTimePoint extends AbstractTimePoint implements TimePoint { private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L; private static final long serialVersionUID = -2470922642359937437L;
@@ -87,37 +54,12 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
} }
} }
private class CompactEstimatedSpeedBearing extends AbstractBearing { public AbstractCompactGPSFixImpl(TimePoint timePoint) {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class CompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new CompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public CompactGPSFixImpl(Position position, TimePoint timePoint) {
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
timePointAsMillis = timePoint==null?-1:timePoint.asMillis(); timePointAsMillis = timePoint==null?-1:timePoint.asMillis();
} }
public CompactGPSFixImpl(GPSFix gpsFix) { public AbstractCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint()); this(gpsFix.getTimePoint());
} }
@Override @Override
@@ -125,11 +67,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
return getTimePoint() + ": " + getPosition(); return getTimePoint() + ": " + getPosition();
} }
@Override
public Position getPosition() {
return new CompactPosition();
}
@Override @Override
public TimePoint getTimePoint() { public TimePoint getTimePoint() {
return new CompactTimePoint(); return new CompactTimePoint();
@@ -166,12 +103,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
return (whatIsCached & IS_ESTIMATED_SPEED_CACHED) != 0; return (whatIsCached & IS_ESTIMATED_SPEED_CACHED) != 0;
} }
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new CompactEstimatedSpeed();
}
@Override @Override
public void invalidateEstimatedSpeedCache() { public void invalidateEstimatedSpeedCache() {
whatIsCached &= ~IS_ESTIMATED_SPEED_CACHED; whatIsCached &= ~IS_ESTIMATED_SPEED_CACHED;
@@ -179,10 +110,6 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
@Override @Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) { public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
whatIsCached |= IS_ESTIMATED_SPEED_CACHED; whatIsCached |= IS_ESTIMATED_SPEED_CACHED;
} }
} }
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.common.tracking.impl; package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractSpeedImpl; import com.sap.sailing.domain.common.AbstractSpeedImpl;
import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.CourseChange; import com.sap.sailing.domain.common.CourseChange;
@@ -21,23 +20,14 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public class CompactGPSFixMovingImpl extends CompactGPSFixImpl implements GPSFixMoving { public abstract class AbstractCompactGPSFixMovingImpl extends AbstractCompactGPSFixImpl implements GPSFixMoving {
private static final long serialVersionUID = 761582024504236533L; private static final long serialVersionUID = 3977983319207618335L;
private final double knotSpeed;
private final double degBearing; protected abstract class AbstractCompactSpeedWithBearing extends AbstractSpeedImpl implements SpeedWithBearing {
private class CompactSpeedWithBearing extends AbstractSpeedImpl implements SpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L; private static final long serialVersionUID = 1802065090733146728L;
@Override @Override
public double getKnots() { public abstract double getKnots();
return knotSpeed;
}
@Override
public Bearing getBearing() {
return new CompactBearing();
}
@Override @Override
public Position travelTo(Position pos, TimePoint from, TimePoint to) { public Position travelTo(Position pos, TimePoint from, TimePoint to) {
@@ -81,33 +71,12 @@ public class CompactGPSFixMovingImpl extends CompactGPSFixImpl implements GPSFix
} }
} }
private class CompactBearing extends AbstractBearing { public AbstractCompactGPSFixMovingImpl(TimePoint timePoint) {
private static final long serialVersionUID = -6474909210513108635L; super(timePoint);
@Override
public double getDegrees() {
return degBearing;
}
@Override
public double getRadians() {
return degBearing / 180. * Math.PI;
}
} }
public CompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) { public AbstractCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) throws CompactionNotPossibleException {
super(position, timePoint); this(gpsFixMoving.getTimePoint());
knotSpeed = speed.getKnots();
degBearing = speed.getBearing().getDegrees();
}
public CompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new CompactSpeedWithBearing();
} }
@Override @Override
@@ -0,0 +1,129 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.AbstractTimePoint;
public abstract class AbstractCompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implements Wind {
private static final long serialVersionUID = -5059956032663387929L;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the position is {@code null}.
*/
private static final byte POSITION_IS_NULL = 1<<0;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the bearing is {@code null}.
*/
private static final byte BEARING_IS_NULL = 1<<1;
/**
* bit mask for {@link #flags}, telling the bit encoding whether the time point is {@code null}.
*/
private static final byte TIME_POINT_IS_NULL = 1<<2;
/**
* See the bit mask values such as {@link #POSITION_IS_NULL}, {@link #BEARING_IS_NULL} and {@link #TIME_POINT_IS_NULL}.
*/
private final byte flags;
private final long timePointAsMillis;
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
public AbstractCompactWindImpl(Wind wind) {
final boolean bearingIsNull;
final boolean positionIsNull;
final boolean timePointIsNull;
bearingIsNull = wind.getBearing() == null;
positionIsNull = wind.getPosition() == null;
if (wind.getTimePoint() == null) {
timePointIsNull = true;
this.timePointAsMillis = 0;
} else {
timePointIsNull = false;
this.timePointAsMillis = wind.getTimePoint().asMillis();
}
flags = (byte) ((byte) (bearingIsNull ? BEARING_IS_NULL : (byte) 0) | (positionIsNull ? POSITION_IS_NULL : (byte) 0) | (timePointIsNull ? TIME_POINT_IS_NULL : (byte) 0));
}
@Override
public Position getPosition() {
if ((flags & POSITION_IS_NULL) != 0) {
return null;
} else {
return getCompactPosition();
}
}
protected abstract Position getCompactPosition();
@Override
public TimePoint getTimePoint() {
if ((flags & TIME_POINT_IS_NULL) != 0) {
return null;
} else {
return new CompactTimePoint();
}
}
@Override
public Bearing getBearing() {
if ((flags & BEARING_IS_NULL) != 0) {
return null;
} else {
return getCompactBearing();
}
}
protected abstract Bearing getCompactBearing();
/**
* Make abstract to force implementing subclasses to implement this method.
*/
@Override
public abstract double getKnots();
@Override
public Bearing getFrom() {
if (getBearing() == null) {
return null;
} else {
return getBearing().reverse();
}
}
@Override
public int hashCode() {
return WindImpl.hashCode(getPosition().getLatDeg(), getPosition().getLngDeg(), (flags&TIME_POINT_IS_NULL) != 0?0:timePointAsMillis);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof Wind))
return false;
Wind other = (Wind) obj;
return Util.equalsWithNull(getPosition(), other.getPosition()) && Util.equalsWithNull(getTimePoint(), other.getTimePoint());
}
@Override
public String toString() {
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
}
}
@@ -0,0 +1,75 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
/**
* A utility class that "compact" fix implementations such as {@link VeryCompactGPSFixImpl} etc. can use
* to obtain representations of latitudes, longitudes, knot speeds and degree bearings marshalled in
* types that sacrifice very little accuracy from a real-world perspective, yet use a data type for
* encoding that requires fewer bytes than a {@code double} value.<p>
*
* Latitude and longitude values are represented using a signed {@code int} value, scaling their range (-90°..+90° and
* -180°..+180°, respectively over the full {@code int} range. This gives a resolution of 4.6mm for latitudes,
* and at the equator of 9.3mm for longitudes.<p>
*
* Bearings / courses over ground are represented as a signed {@code short} value, mapping the range of -360°..+360° to the
* value range of the {@code short} datatype. This results in a resolution of 0.01°.<p>
*
* Speed values are represented in knots in their compact form; lossy compaction uses a signed {@code short} value for
* the knot speeds, assuming a range of -500kts..+500kts, spreading this range over the value range of the {@code short}
* datatype. This results in a resolution of 0.015kts.<p>
*
* If an attempt is made to obtain a compact form of a knot speed or bearing where the value if out of the range described above,
* a {@link CompactionNotPossibleException} will be thrown. Callers should then transparently resort to the precise
* compaction counterparts for the respective type.
*
* @author Axel Uhl (d043530)
*
*/
public class CompactPositionHelper {
private static final double LAT_SCALE = 90. / (double) (1 << 31); // int
private static final double LNG_SCALE = 180. / (double) (1 << 31); // int
private static final double KNOT_SPEED_SCALE = 500. /* knots; what that we track would ever be faster? */ / (double) (1<<15); // short
private static final double DEGREE_BEARING_SCALE = 360. / (double) (1<<15); // short
// Positions
public static double getLatDeg(int latDegScaled) {
return LAT_SCALE * latDegScaled;
}
public static double getLngDeg(int lngDegScaled) {
return LNG_SCALE * lngDegScaled;
}
public static int getLngDegScaled(Position position) {
return (int) (position.getLngDeg() / LNG_SCALE);
}
public static int getLatDegScaled(Position position) {
return (int) (position.getLatDeg() / LAT_SCALE);
}
// Speeds / Bearings
public static double getKnotSpeed(short knotSpeedScaled) {
return KNOT_SPEED_SCALE * knotSpeedScaled;
}
public static double getDegreeBearing(short degreeBearingScaled) {
return DEGREE_BEARING_SCALE * degreeBearingScaled;
}
public static short getKnotSpeedScaled(Speed speed) throws CompactionNotPossibleException {
final double knotSpeedScaled = speed.getKnots() / KNOT_SPEED_SCALE;
if (knotSpeedScaled > Short.MAX_VALUE || knotSpeedScaled < Short.MIN_VALUE) {
throw new CompactionNotPossibleException("Speed "+speed+" cannot be compacted; "+speed.getKnots()+" does not fit into a signed short value");
}
return (short) knotSpeedScaled;
}
public static short getDegreeBearingScaled(Bearing bearing) {
return (short) (bearing.getDegrees() / DEGREE_BEARING_SCALE);
}
}
@@ -1,161 +0,0 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.AbstractTimePoint;
public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implements Wind {
private static final long serialVersionUID = -5059956032663387929L;
private final double latDeg;
private final double lngDeg;
private final boolean positionIsNull;
private final boolean bearingIsNull;
private final boolean timePointIsNull;
private final double speedInKnots;
private final double degBearing;
private final long timePointAsMillis;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
@Override
public boolean equals(Object o) {
return this==o || o instanceof Position && getLatDeg() == ((Position) o).getLatDeg() && getLngDeg() == ((Position) o).getLngDeg();
}
}
private class CompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
@Override
public double getRadians() {
return degBearing / 180. * Math.PI;
}
@Override
public boolean equals(Object object) {
return this==object || object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
}
}
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
public CompactWindImpl(Wind wind) {
if (wind.getBearing() == null) {
bearingIsNull = true;
degBearing = 0;
} else {
this.degBearing = wind.getBearing().getDegrees();
bearingIsNull = false;
}
if (wind.getPosition() == null) {
positionIsNull = true;
this.latDeg = 0;
this.lngDeg = 0;
} else {
this.latDeg = wind.getPosition().getLatDeg();
this.lngDeg = wind.getPosition().getLngDeg();
positionIsNull = false;
}
this.speedInKnots = wind.getKnots();
if (wind.getTimePoint() == null) {
timePointIsNull = true;
this.timePointAsMillis = 0;
} else {
timePointIsNull = false;
this.timePointAsMillis = wind.getTimePoint().asMillis();
}
}
@Override
public Position getPosition() {
if (positionIsNull) {
return null;
} else {
return new CompactPosition();
}
}
@Override
public TimePoint getTimePoint() {
if (timePointIsNull) {
return null;
} else {
return new CompactTimePoint();
}
}
@Override
public Bearing getBearing() {
if (bearingIsNull) {
return null;
} else {
return new CompactBearing();
}
}
@Override
public double getKnots() {
return speedInKnots;
}
@Override
public Bearing getFrom() {
if (getBearing() == null) {
return null;
} else {
return getBearing().reverse();
}
}
@Override
public int hashCode() {
return WindImpl.hashCode(latDeg, lngDeg, timePointIsNull?0:timePointAsMillis);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof Wind))
return false;
Wind other = (Wind) obj;
return Util.equalsWithNull(getPosition(), other.getPosition()) && Util.equalsWithNull(getTimePoint(), other.getTimePoint());
}
@Override
public String toString() {
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
}
}
@@ -0,0 +1,37 @@
package com.sap.sailing.domain.common.tracking.impl;
/**
* Throws when an attempt is made to turn a fix of some sort into a more compact form, probably
* slightly reducing accuracy and/or reducing value ranges, such as for the speeds possible, where
* the fix presented cannot be turned into its compact form without exceeding a range. This would
* lead to an overflow or wrap-around and cannot be tolerated. Clients need to handle this exception
* by skipping compaction and using the original fix instead.
*
* @author Axel Uhl (d043530)
*
*/
public class CompactionNotPossibleException extends Exception {
private static final long serialVersionUID = -723044264487667717L;
public CompactionNotPossibleException() {
super();
}
public CompactionNotPossibleException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public CompactionNotPossibleException(String message, Throwable cause) {
super(message, cause);
}
public CompactionNotPossibleException(String message) {
super(message);
}
public CompactionNotPossibleException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,105 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
/**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
* memory overhead otherwise created by using too many individual fine-grained objects.<p>
*
* Objects of this type are assumed to be contained in at most one {@link DynamicGPSFixTrackImpl}. It is
* therefore permissible to cache information about validity to speed up the otherwise expensive
* {@link DynamicGPSFixTrackImpl#isValid(PartialNavigableSetView, GPSFix)} computation.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactGPSFixImpl extends AbstractCompactGPSFixImpl {
private static final long serialVersionUID = 8167588584536992501L;
private final double latDeg;
private final double lngDeg;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
public class PreciseCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class PreciseCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class PreciseCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new PreciseCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public PreciseCompactGPSFixImpl(Position position, TimePoint timePoint) {
super(timePoint);
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
}
public PreciseCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint());
}
@Override
public Position getPosition() {
return new PreciseCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new PreciseCompactEstimatedSpeed();
}
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
super.cacheEstimatedSpeed(estimatedSpeed);
}
}
@@ -0,0 +1,135 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.TimePoint;
/**
* A memory-conserving representation of a {@link GPSFixMoving} object that produces the fine-grained
* objects for {@link Position}, {@link SpeedWithBearing}, {@link Bearing} and {@link TimePoint} dynamically
* as thin wrappers around this object which holds all elementary attributes required. This saves several
* object references and object headers.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl {
private static final long serialVersionUID = 761582024504236533L;
private final double latDeg;
private final double lngDeg;
private final double knotSpeed;
private final double degBearing;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees.
*/
private double cachedEstimatedSpeedBearingInDegrees;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots.
*/
private double cachedEstimatedSpeedInKnots;
private class PreciseCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing implements SpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L;
@Override
public double getKnots() {
return knotSpeed;
}
@Override
public Bearing getBearing() {
return new PreciseCompactBearing();
}
}
private class PreciseCompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
}
public class PreciseCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
}
private class PreciseCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return cachedEstimatedSpeedBearingInDegrees;
}
}
private class PreciseCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new PreciseCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return cachedEstimatedSpeedInKnots;
}
}
public PreciseCompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(timePoint);
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
knotSpeed = speed==null?0:speed.getKnots();
degBearing = speed==null?0:speed.getBearing().getDegrees();
}
public PreciseCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new PreciseCompactSpeedWithBearing();
}
@Override
public Position getPosition() {
return new PreciseCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new PreciseCompactEstimatedSpeed();
}
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
super.cacheEstimatedSpeed(estimatedSpeed);
}
}
@@ -0,0 +1,81 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
/**
* Keeps the precise values of lat/lng and speed/course.
*
* @author Axel Uhl (d043530)
*
*/
public class PreciseCompactWindImpl extends AbstractCompactWindImpl {
private static final long serialVersionUID = -5059956032663387929L;
private final double latDeg;
private final double lngDeg;
private final double speedInKnots;
private final double degBearing;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return latDeg;
}
@Override
public double getLngDeg() {
return lngDeg;
}
@Override
public boolean equals(Object o) {
return this==o || o instanceof Position && getLatDeg() == ((Position) o).getLatDeg() && getLngDeg() == ((Position) o).getLngDeg();
}
}
private class CompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return degBearing;
}
}
public PreciseCompactWindImpl(Wind wind) {
super(wind);
if (wind.getBearing() == null) {
degBearing = 0;
} else {
this.degBearing = wind.getBearing().getDegrees();
}
if (wind.getPosition() == null) {
this.latDeg = 0;
this.lngDeg = 0;
} else {
this.latDeg = wind.getPosition().getLatDeg();
this.lngDeg = wind.getPosition().getLngDeg();
}
this.speedInKnots = wind.getKnots();
}
@Override
public double getKnots() {
return speedInKnots;
}
@Override
protected Position getCompactPosition() {
return new CompactPosition();
}
@Override
protected Bearing getCompactBearing() {
return new CompactBearing();
}
}
@@ -0,0 +1,128 @@
package com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
/**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
* memory overhead otherwise created by using too many individual fine-grained objects.<p>
*
* Objects of this type are assumed to be contained in at most one {@link DynamicGPSFixTrackImpl}. It is
* therefore permissible to cache information about validity to speed up the otherwise expensive
* {@link DynamicGPSFixTrackImpl#isValid(PartialNavigableSetView, GPSFix)} computation.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactGPSFixImpl extends AbstractCompactGPSFixImpl {
private static final long serialVersionUID = 3943572107870245437L;
private static final Logger logger = Logger.getLogger(VeryCompactGPSFixImpl.class.getName());
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedBearingInDegreesScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedInKnotsScaled;
public class CompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(cachedEstimatedSpeedBearingInDegreesScaled);
}
}
private class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new VeryCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(cachedEstimatedSpeedInKnotsScaled);
}
}
public VeryCompactGPSFixImpl(Position position, TimePoint timePoint) {
super(timePoint);
latDegScaled = CompactPositionHelper.getLatDegScaled(position);
lngDegScaled = CompactPositionHelper.getLngDegScaled(position);
}
public VeryCompactGPSFixImpl(GPSFix gpsFix) {
this(gpsFix.getPosition(), gpsFix.getTimePoint());
}
@Override
public Position getPosition() {
return new CompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new VeryCompactEstimatedSpeed();
}
/**
* Under rare circumstances, caching the speed may fail for the compact representation of a GPS fix.
* This can happen if the speed estimated exceeds the range that can be represented in this compact
* form which is less than in the original form where the speed amount is represented as a {@code double}
* value. In this case, a warning message is logged with level {@link Level#FINER}, and the speed remains
* uncached.
*/
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
try {
cachedEstimatedSpeedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(estimatedSpeed);
cachedEstimatedSpeedBearingInDegreesScaled = CompactPositionHelper.getDegreeBearingScaled(estimatedSpeed.getBearing());
super.cacheEstimatedSpeed(estimatedSpeed);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINER, "Cannot cache estimated speed "+estimatedSpeed+" in compact fix:", e);
}
}
}
@@ -0,0 +1,171 @@
package com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.TimePoint;
/**
* A memory-conserving representation of a {@link GPSFixMoving} object that produces the fine-grained
* objects for {@link Position}, {@link SpeedWithBearing}, {@link Bearing} and {@link TimePoint} dynamically
* as thin wrappers around this object which holds all elementary attributes required. This saves several
* object references and object headers.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl {
private static final long serialVersionUID = 3977983319207618335L;
private static final Logger logger = Logger.getLogger(VeryCompactGPSFixMovingImpl.class.getName());
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short speedInKnotsScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short degreeBearingScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
* true "bearing" (true course over ground) in degrees, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedBearingInDegreesScaled;
/**
* When <code>{@link #whatIsCached}&amp;{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
* in knots, scaled into a short value using {@link CompactPositionHelper}.
*/
private short cachedEstimatedSpeedInKnotsScaled;
public class VeryCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing {
private static final long serialVersionUID = 1802065090733146728L;
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(speedInKnotsScaled);
}
@Override
public Bearing getBearing() {
return new VeryCompactBearing();
}
}
private class VeryCompactBearing extends AbstractBearing {
private static final long serialVersionUID = 8167886382067060570L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(degreeBearingScaled);
}
}
private class VeryCompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(cachedEstimatedSpeedBearingInDegreesScaled);
}
}
private class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
private static final long serialVersionUID = -5871855443391817248L;
@Override
public Bearing getBearing() {
return new VeryCompactEstimatedSpeedBearing();
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(cachedEstimatedSpeedInKnotsScaled);
}
}
public VeryCompactGPSFixMovingImpl(Position position, TimePoint timePoint, SpeedWithBearing speed) throws CompactionNotPossibleException {
super(timePoint);
latDegScaled = CompactPositionHelper.getLatDegScaled(position);
lngDegScaled = CompactPositionHelper.getLngDegScaled(position);
if (speed == null) {
speedInKnotsScaled = 0;
degreeBearingScaled = 0;
} else {
speedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(speed);
degreeBearingScaled = CompactPositionHelper.getDegreeBearingScaled(speed.getBearing());
}
}
public VeryCompactGPSFixMovingImpl(GPSFixMoving gpsFixMoving) throws CompactionNotPossibleException {
this(gpsFixMoving.getPosition(), gpsFixMoving.getTimePoint(), gpsFixMoving.getSpeed());
}
@Override
public SpeedWithBearing getSpeed() {
return new VeryCompactSpeedWithBearing();
}
@Override
public Position getPosition() {
return new VeryCompactPosition();
}
@Override
public SpeedWithBearing getCachedEstimatedSpeed() {
assert isEstimatedSpeedCached();
return new VeryCompactEstimatedSpeed();
}
/**
* Under rare circumstances, caching the speed may fail for the compact representation of a GPS fix.
* This can happen if the speed estimated exceeds the range that can be represented in this compact
* form which is less than in the original form where the speed amount is represented as a {@code double}
* value. In this case, a warning message is logged with level {@link Level#FINER}, and the speed remains
* uncached.
*/
@Override
public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
try {
cachedEstimatedSpeedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(estimatedSpeed);
cachedEstimatedSpeedBearingInDegreesScaled = CompactPositionHelper.getDegreeBearingScaled(estimatedSpeed.getBearing());
super.cacheEstimatedSpeed(estimatedSpeed);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINER, "Cannot cache estimated speed "+estimatedSpeed+" in compact fix:", e);
}
}
}
@@ -0,0 +1,92 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.AbstractBearing;
import com.sap.sailing.domain.common.AbstractPosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
/**
* Uses scaled, rounded internal representations of the values for lat/lng and speed/course. See
* {@link CompactPositionHelper} for the details of how value ranges and accuracies are affected.
*
* @author Axel Uhl (d043530)
*
*/
public class VeryCompactWindImpl extends AbstractCompactWindImpl {
private static final long serialVersionUID = -5059956032663387929L;
/**
* See {@link CompactPositionHelper}
*/
private final int latDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final int lngDegScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short speedInKnotsScaled;
/**
* See {@link CompactPositionHelper}
*/
private final short degreeBearingScaled;
public class VeryCompactPosition extends AbstractPosition {
private static final long serialVersionUID = 5621506820766614178L;
@Override
public double getLatDeg() {
return CompactPositionHelper.getLatDeg(latDegScaled);
}
@Override
public double getLngDeg() {
return CompactPositionHelper.getLngDeg(lngDegScaled);
}
}
private class VeryCompactBearing extends AbstractBearing {
private static final long serialVersionUID = -6474909210513108635L;
@Override
public double getDegrees() {
return CompactPositionHelper.getDegreeBearing(degreeBearingScaled);
}
}
public VeryCompactWindImpl(Wind wind) throws CompactionNotPossibleException {
super(wind);
if (wind.getBearing() == null) {
degreeBearingScaled = 0;
} else {
this.degreeBearingScaled = CompactPositionHelper.getDegreeBearingScaled(wind.getBearing());
}
if (wind.getPosition() == null) {
this.latDegScaled = 0;
this.lngDegScaled = 0;
} else {
this.latDegScaled = CompactPositionHelper.getLatDegScaled(wind.getPosition());
this.lngDegScaled = CompactPositionHelper.getLngDegScaled(wind.getPosition());
}
this.speedInKnotsScaled = CompactPositionHelper.getKnotSpeedScaled(wind);
}
@Override
public double getKnots() {
return CompactPositionHelper.getKnotSpeed(speedInKnotsScaled);
}
@Override
protected Position getCompactPosition() {
return new VeryCompactPosition();
}
@Override
protected Bearing getCompactBearing() {
return new VeryCompactBearing();
}
}
@@ -1962,7 +1962,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (controlPointClass.equals(Mark.class.getSimpleName())) { if (controlPointClass.equals(Mark.class.getSimpleName())) {
Mark mark = loadMark((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); Mark mark = loadMark((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name()));
controlPoint = mark; controlPoint = mark;
} else if(controlPointClass.equals("Gate")) { } else if (controlPointClass.equals("Gate")) {
ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name())); ControlPointWithTwoMarks cpwtm = loadControlPointWithTwoMarks((DBObject) dbObject.get(FieldNames.CONTROLPOINT_VALUE.name()));
dbObject.put(FieldNames.CONTROLPOINT_CLASS.name(), ControlPointWithTwoMarks.class.getSimpleName()); dbObject.put(FieldNames.CONTROLPOINT_CLASS.name(), ControlPointWithTwoMarks.class.getSimpleName());
controlPoint = cpwtm; controlPoint = cpwtm;
@@ -2318,8 +2318,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} }
} }
try { try {
callback.accept(connectivityParamsPersistenceService.mapTo(map)); final RaceTrackingConnectivityParameters params = connectivityParamsPersistenceService.mapTo(map);
logger.info("Done restoring race #"+ finalI +"/"+count+" of type "+type); if (params != null) {
callback.accept(params);
logger.info("Done restoring race #"+ finalI +"/"+count+" of type "+type);
} else {
logger.warning("Couldn't restore race #"+ finalI +"/"+count+" of type "+type+
" because the parameters loaded from the DB couldn't be mapped. Maybe the owning leaderboard was removed?");
}
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to load race #"+ finalI +"/"+count+" of type "+type+ logger.log(Level.SEVERE, "Exception trying to load race #"+ finalI +"/"+count+" of type "+type+
" from restore connectivity parameters " " from restore connectivity parameters "
@@ -221,7 +221,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBCollection getWindTrackCollection() { public DBCollection getWindTrackCollection() {
DBCollection result = database.getCollection(CollectionNames.WIND_TRACKS.name()); DBCollection result = database.getCollection(CollectionNames.WIND_TRACKS.name());
result.createIndex(new BasicDBObject(FieldNames.REGATTA_NAME.name(), null)); result.createIndex(new BasicDBObject(FieldNames.REGATTA_NAME.name(), 1));
return result; return result;
} }
@@ -237,7 +237,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBCollection getGPSFixMetadataCollection() { public DBCollection getGPSFixMetadataCollection() {
DBCollection collection = database.getCollection(CollectionNames.GPS_FIXES_METADATA.name()); DBCollection collection = database.getCollection(CollectionNames.GPS_FIXES_METADATA.name());
DBObject index = new BasicDBObject(); DBObject index = new BasicDBObject();
index.put(FieldNames.DEVICE_ID.name(), null); index.put(FieldNames.DEVICE_ID.name(), 1);
collection.createIndex(index); collection.createIndex(index);
return collection; return collection;
} }
@@ -741,7 +741,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBCollection getRaceLogCollection() { public DBCollection getRaceLogCollection() {
DBCollection result = database.getCollection(CollectionNames.RACE_LOGS.name()); DBCollection result = database.getCollection(CollectionNames.RACE_LOGS.name());
result.createIndex(new BasicDBObject(FieldNames.RACE_LOG_IDENTIFIER.name(), null)); result.createIndex(new BasicDBObject(FieldNames.RACE_LOG_IDENTIFIER.name(), 1));
return result; return result;
} }
@@ -1372,8 +1372,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
public DBCollection getRegattaLogCollection() { public DBCollection getRegattaLogCollection() {
DBCollection result = database.getCollection(CollectionNames.REGATTA_LOGS.name()); DBCollection result = database.getCollection(CollectionNames.REGATTA_LOGS.name());
DBObject index = new BasicDBObject(FieldNames.REGATTA_LOG_IDENTIFIER_TYPE.name(), null); DBObject index = new BasicDBObject(FieldNames.REGATTA_LOG_IDENTIFIER_TYPE.name(), 1);
index.put(FieldNames.REGATTA_LOG_IDENTIFIER_NAME.name(), null); index.put(FieldNames.REGATTA_LOG_IDENTIFIER_NAME.name(), 1);
result.createIndex(index); result.createIndex(index);
return result; return result;
} }
@@ -566,8 +566,7 @@ public class SensorFixStoreAndLoadTest {
public static final String TRACK_NAME = "TestFixTrack"; public static final String TRACK_NAME = "TestFixTrack";
public TestFixTrackImpl(ItemType trackedItem, String trackName) { public TestFixTrackImpl(ItemType trackedItem, String trackName) {
super(trackedItem, trackName, TestFixImpl.COLUMNS, super(trackedItem, trackName, TRACK_NAME + " for " + trackedItem);
TRACK_NAME + " for " + trackedItem);
} }
public Double getTextValue(TimePoint timePoint) { public Double getTextValue(TimePoint timePoint) {
@@ -4,8 +4,8 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
@@ -26,9 +26,9 @@ public class MockGPSFixMongoServiceFinder extends AbstractTypeBasedServiceFinder
@Override @Override
public FixMongoHandler<?> findService(String fixType) { public FixMongoHandler<?> findService(String fixType) {
if (fixType.equals(GPSFixMovingImpl.class.getName()) || if (fixType.equals(GPSFixMovingImpl.class.getName()) ||
fixType.equals(CompactGPSFixMovingImpl.class.getName())) return movingHandler; fixType.equals(VeryCompactGPSFixMovingImpl.class.getName())) return movingHandler;
if (fixType.equals(GPSFixImpl.class.getName()) || if (fixType.equals(GPSFixImpl.class.getName()) ||
fixType.equals(CompactGPSFixImpl.class.getName())) return handler; fixType.equals(VeryCompactGPSFixImpl.class.getName())) return handler;
if (fixType.equals(DoubleVectorFixImpl.class.getName())) return doubleVectorFixHandler; if (fixType.equals(DoubleVectorFixImpl.class.getName())) return doubleVectorFixHandler;
return null; return null;
} }
@@ -5,7 +5,7 @@ iOSUsers=Start iOS App
androidUsers=Start Android App androidUsers=Start Android App
alternativelyVisitThisLink=If you are reading this mail on the device with the App already installed, simply click below to start: alternativelyVisitThisLink=If you are reading this mail on the device with the App already installed, simply click below to start:
buoyTender=buoy tender buoyTender=buoy tender
appStoreInstallText=If you don't have the App yet, please install: appStoreInstallText=If you don''t have the App yet, please install:
appIos=Install iOS App appIos=Install iOS App
appAndroid=Install Android App appAndroid=Install Android App
buoyPingerAppName=SAP Sailing Buoy Pinger buoyPingerAppName=SAP Sailing Buoy Pinger
@@ -1,11 +1,11 @@
trackingInvitationFor=Einladung zum Tracken für trackingInvitationFor=Einladung zum Tracken für
scanQRCodeOrVisitUrlToRegisterAs=Installiere die {0} App. Dann scanne den QRCode oder klicke den Link mit deinem Smartphone an, um dich zu registrieren als scanQRCodeOrVisitUrlToRegisterAs=Installiere die {0} App. Dann scanne den QRCode oder klicke den Link mit deinem Smartphone an, um dich zu registrieren als
welcomeTo=Willkommen zu welcomeTo=Willkommen zu
iOSUsers=iOS App starten iOSUsers=iOS App starten
androidUsers=Android App starten androidUsers=Android App starten
alternativelyVisitThisLink=Wenn die App bereits installiert ist, und du diese Mail auch auf dem Geräte offen hast, dann einfach hier clicken: alternativelyVisitThisLink=Wenn die App bereits installiert ist, und du diese Mail auch auf dem Geräte offen hast, dann einfach hier clicken:
buoyTender=Bojenleger buoyTender=Bojenleger
appStoreInstallText=Falls die App noch nicht installiert ist, kannst du sie hier finden:Ÿ appStoreInstallText=Falls die App noch nicht installiert ist, kannst du sie hier finden:Ÿ
appIos=iOS App installieren appIos=iOS App installieren
appAndroid=Android App installieren appAndroid=Android App installieren
buoyPingerAppName=SAP Sailing Buoy Pinger buoyPingerAppName=SAP Sailing Buoy Pinger
@@ -49,12 +49,22 @@ public class RaceLogConnectivityParamsHandler extends AbstractRaceTrackingConnec
@Override @Override
public RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws MalformedURLException, URISyntaxException { public RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws MalformedURLException, URISyntaxException {
final RaceTrackingConnectivityParameters result;
final RegattaLeaderboard leaderboard = (RegattaLeaderboard) racingEventService.getLeaderboardByName((String) map.get(LEADERBOARD_NAME)); final RegattaLeaderboard leaderboard = (RegattaLeaderboard) racingEventService.getLeaderboardByName((String) map.get(LEADERBOARD_NAME));
final RaceColumn raceColumn = leaderboard.getRaceColumnByName((String) map.get(RACE_COLUMN_NAME)); if (leaderboard == null) {
return new RaceLogConnectivityParams(racingEventService, leaderboard.getRegatta(), raceColumn, result = null;
raceColumn.getFleetByName((String) map.get(FLEET_NAME)), leaderboard, } else {
((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), domainFactory, isTrackWind(map), final RaceColumn raceColumn = leaderboard.getRaceColumnByName((String) map.get(RACE_COLUMN_NAME));
isCorrectWindDirectionByMagneticDeclination(map)); if (raceColumn == null) {
result = null;
} else {
result = new RaceLogConnectivityParams(racingEventService, leaderboard.getRegatta(), raceColumn,
raceColumn.getFleetByName((String) map.get(FLEET_NAME)), leaderboard,
((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), domainFactory, isTrackWind(map),
isCorrectWindDirectionByMagneticDeclination(map));
}
}
return result;
} }
@Override @Override
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl; package com.sap.sailing.domain.racelogtracking.impl;
import java.nio.charset.StandardCharsets;
import com.sap.sse.i18n.ResourceBundleStringMessages; import com.sap.sse.i18n.ResourceBundleStringMessages;
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl; import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
@@ -7,5 +9,5 @@ public class RaceLogTrackingI18n {
private static final String RESOURCE_BASE_NAME = "stringmessages/TrackingStringMessages"; private static final String RESOURCE_BASE_NAME = "stringmessages/TrackingStringMessages";
public static final ResourceBundleStringMessages STRING_MESSAGES = new ResourceBundleStringMessagesImpl( public static final ResourceBundleStringMessages STRING_MESSAGES = new ResourceBundleStringMessagesImpl(
RESOURCE_BASE_NAME, RaceLogTrackingI18n.class.getClassLoader()); RESOURCE_BASE_NAME, RaceLogTrackingI18n.class.getClassLoader(), StandardCharsets.UTF_8.name());
} }
@@ -17,8 +17,8 @@ VisitorT> extends BaseLogAnalyzer<LogT, EventT, VisitorT, List<EventT>> {
private final Class<?> ofType; private final Class<?> ofType;
private final boolean onlyUnrevoked; private final boolean onlyUnrevoked;
public AllEventsOfTypeFinder(LogT raceLog, boolean onlyUnrevoked, Class<?> ofType) { public AllEventsOfTypeFinder(LogT log, boolean onlyUnrevoked, Class<?> ofType) {
super(raceLog); super(log);
this.ofType = ofType; this.ofType = ofType;
this.onlyUnrevoked = onlyUnrevoked; this.onlyUnrevoked = onlyUnrevoked;
} }
@@ -35,14 +35,10 @@ public interface SwissTimingReplayService {
* @param link * @param link
* the URL without the implicit "http://" prefix, as obtained, e.g., from * the URL without the implicit "http://" prefix, as obtained, e.g., from
* {@link SwissTimingReplayRace#getLink()}. * {@link SwissTimingReplayRace#getLink()}.
* @param raceName TODO
* @param raceID * @param raceID
* the SwissTiming ID for the race * the SwissTiming ID for the race
* @param trackerManager TODO
* @param useInternalMarkPassingAlgorithm * @param useInternalMarkPassingAlgorithm
* use our own instead of the SwissTiming-provided mark rounding / split times * use our own instead of the SwissTiming-provided mark rounding / split times
* @param raceLogStore TODO
* @param regattaLogStore TODO
* @param boatClassNameName * @param boatClassNameName
* only required if {@code regattaToAddTo} is {@code null}; used for the retrieval/creation of a default * only required if {@code regattaToAddTo} is {@code null}; used for the retrieval/creation of a default
* regatta * regatta
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sailing.domain.tractracadapter.TracTracControlPoint; import com.sap.sailing.domain.tractracadapter.TracTracControlPoint;
import com.sap.sailing.domain.tractracadapter.impl.ControlPointAdapter; import com.sap.sailing.domain.tractracadapter.impl.ControlPointAdapter;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Pair;
import com.tractrac.model.lib.api.ModelLocator; import com.tractrac.model.lib.api.ModelLocator;
import com.tractrac.model.lib.api.event.CreateModelException; import com.tractrac.model.lib.api.event.CreateModelException;
@@ -36,6 +37,7 @@ import com.tractrac.subscription.lib.api.IRaceSubscriber;
import com.tractrac.subscription.lib.api.ISubscriberFactory; import com.tractrac.subscription.lib.api.ISubscriberFactory;
import com.tractrac.subscription.lib.api.SubscriberInitializationException; import com.tractrac.subscription.lib.api.SubscriberInitializationException;
import com.tractrac.subscription.lib.api.SubscriptionLocator; import com.tractrac.subscription.lib.api.SubscriptionLocator;
import com.tractrac.util.lib.api.exceptions.TimeOutException;
/** /**
* Subclassing tests have to call {@link #addListenersForStoredDataAndStartController(Iterable)} to kick off * Subclassing tests have to call {@link #addListenersForStoredDataAndStartController(Iterable)} to kick off
@@ -65,6 +67,7 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
* Default set-up for an STG training session in Weymouth, 2011 * Default set-up for an STG training session in Weymouth, 2011
* @throws SubscriberInitializationException * @throws SubscriberInitializationException
* @throws CreateModelException * @throws CreateModelException
* @throws TimeOutException
*/ */
@Before @Before
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException, SubscriberInitializationException, ParseException, CreateModelException { public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException, SubscriberInitializationException, ParseException, CreateModelException {
@@ -95,26 +98,30 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
protected void setUp(URL paramUrl, URI liveUri, URI storedUri) throws FileNotFoundException, MalformedURLException, URISyntaxException, SubscriberInitializationException, CreateModelException { protected void setUp(URL paramUrl, URI liveUri, URI storedUri) throws FileNotFoundException, MalformedURLException, URISyntaxException, SubscriberInitializationException, CreateModelException {
// Read event data from configuration file // Read event data from configuration file
final IRace race = ModelLocator.getEventFactory().createRace(new URI(paramUrl.toString())); try {
this.race = race; final IRace race = ModelLocator.getEventFactory().createRace(new URI(paramUrl.toString()), (int) /* timeout in milliseconds */ Duration.ONE_MINUTE.asMillis());
ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory(); this.race = race;
if (storedUri == null) { ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory();
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent()); if (storedUri == null) {
raceSubscriber = subscriberFactory.createRaceSubscriber(race); eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent());
} else { raceSubscriber = subscriberFactory.createRaceSubscriber(race);
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri); } else {
raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri); eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri);
} raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri);
assertNotNull(race);
// Initialize data controller using live and stored data sources
if (storedUri != null && storedUri.toString().startsWith("file:")) {
try {
URI oldStoredUri = storedUri;
storedUri = new URI(storedUri.toString().replaceFirst("file:/([^/])", "file:////$1"));
logger.info("Replaced storedUri "+oldStoredUri+" by "+storedUri);
} catch (URISyntaxException e) {
e.printStackTrace();
} }
assertNotNull(race);
// Initialize data controller using live and stored data sources
if (storedUri != null && storedUri.toString().startsWith("file:")) {
try {
URI oldStoredUri = storedUri;
storedUri = new URI(storedUri.toString().replaceFirst("file:/([^/])", "file:////$1"));
logger.info("Replaced storedUri "+oldStoredUri+" by "+storedUri);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
} catch (TimeOutException te) {
throw new RuntimeException(te);
} }
// test cases need to start the thread calling addListenersForStoredDataAndStartController // test cases need to start the thread calling addListenersForStoredDataAndStartController
// after adding their listeners // after adding their listeners
@@ -57,6 +57,6 @@ public class DistanceCacheInvalidationAfterLoadingFinishedTest extends TrackBase
final Distance distance = speed.travel(Duration.ONE_SECOND); final Distance distance = speed.travel(Duration.ONE_SECOND);
trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos.translateGreatCircle(bearing, distance), now.plus(Duration.ONE_SECOND), speed)); trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos.translateGreatCircle(bearing, distance), now.plus(Duration.ONE_SECOND), speed));
trackedRace.onStatusChanged(tdl, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* progress */ 1.0)); trackedRace.onStatusChanged(tdl, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* progress */ 1.0));
assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.000001); // ask 1s after the second fix assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.01); // ask 1s after the second fix
} }
} }
@@ -61,6 +61,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall; import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
@@ -1454,6 +1455,44 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[14], later)); assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[14], later));
assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[15], later)); assertEquals(new Double(1), leaderboardHighPointESSOverall.getNetPoints(competitors[15], later));
} }
@Test
public void testESS2017WithCompetitorsBeingDSQScoringTwoLessThanLastCompetingCompetitor() throws NoWindException {
Competitor[] competitors = createCompetitors(8).toArray(new Competitor[0]);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboardHighPoint12LastBreaksTie2017 = new FlexibleLeaderboardImpl("Test ESS Highpoint",
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new HighPointFirstGets12Or8AndLastBreaksTie2017(), null);
RaceColumn raceColumn = leaderboardHighPoint12LastBreaksTie2017
.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitors)), "R1", /* medalRace */
false);
// test basic scores with best getting 12 and worst getting 5 in a 12 point scheme
assertTrue(leaderboardHighPoint12LastBreaksTie2017.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later),
leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[3], later)) < 0); // c0 better than c3
assertEquals(8, leaderboardHighPoint12LastBreaksTie2017.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
// now let's assume that the first competitor is DSQ so it gets 4 points
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.DSQ);
assertEquals(8, leaderboardHighPoint12LastBreaksTie2017.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(4), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.NONE);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[0], raceColumn, MaxPointsReason.DNS);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(5), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[7], later));
// two competitors not started leading to tie
leaderboardHighPoint12LastBreaksTie2017.getScoreCorrection().setMaxPointsReason(competitors[1], raceColumn, MaxPointsReason.DNS);
assertEquals(new Double(12), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[2], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[1], later));
assertEquals(new Double(6), leaderboardHighPoint12LastBreaksTie2017.getNetPoints(competitors[0], later));
}
@Test @Test
public void testOverallLeaderboardWithESSHighPointScoring() throws NoWindException { public void testOverallLeaderboardWithESSHighPointScoring() throws NoWindException {
@@ -1538,6 +1577,7 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
assertEquals(c[0], rankedCompetitors.get(0)); assertEquals(c[0], rankedCompetitors.get(0));
assertEquals(c[2], rankedCompetitors.get(1)); assertEquals(c[2], rankedCompetitors.get(1));
} }
@Test @Test
public void testHighPointScoringEightWithInterpolationWhenFleetNotComplete() throws NoWindException { public void testHighPointScoringEightWithInterpolationWhenFleetNotComplete() throws NoWindException {
int competitorsCount = 21; int competitorsCount = 21;
@@ -74,7 +74,7 @@ public class LineAnalysisTest extends TrackBasedTest {
final Distance distanceBehindLine = new MeterDistance(10); final Distance distanceBehindLine = new MeterDistance(10);
final Position behindMiddleOfLine = lineMiddle.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine); final Position behindMiddleOfLine = lineMiddle.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindMiddleOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45)))); trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindMiddleOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
assertEquals(distanceBehindLine.getMeters(), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.000001); assertEquals(distanceBehindLine.getMeters(), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.01);
} }
@Test @Test
@@ -90,7 +90,7 @@ public class LineAnalysisTest extends TrackBasedTest {
final Position behindOutsideOfLine = outsideOfLine.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine); final Position behindOutsideOfLine = outsideOfLine.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindOutsideOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45)))); trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindOutsideOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
// the distance is that of the hypotenuse formed by the two cathesuses of 10m and 30m length, respectively: // the distance is that of the hypotenuse formed by the two cathesuses of 10m and 30m length, respectively:
assertEquals(Math.sqrt(10.*10.+30.*30.), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.001); assertEquals(Math.sqrt(10.*10.+30.*30.), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.01);
} }
private Mark getMark(String name) { private Mark getMark(String name) {
@@ -96,7 +96,7 @@ public class MultipleClassesInRegattaTest {
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory,
tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false); /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false, /* timeoutInMillis */ -1);
} }
@After @After
@@ -0,0 +1,51 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
public class NonCompetingCompetitorTest extends AbstractManeuverDetectionTestCase {
public NonCompetingCompetitorTest() throws MalformedURLException, URISyntaxException {
super();
}
@Override
protected String getExpectedEventName() {
return "ESS ST. Petersburg 2016";
}
@Before
public void setUp() throws URISyntaxException, IOException, InterruptedException, ParseException, SubscriberInitializationException, CreateModelException {
super.setUp();
super.setUp(
new URL("http://dev.tractrac.com/events/event_20160222_ESSSTPeter_1473057208/clientparams.php?event=event_20160222_ESSSTPeter_1473057208&race=5a265e70-519a-0134-dd23-10b11c4ed8fd&random=296858907"),
/* liveUri */null, /* storedUri */null, new ReceiverType[] { ReceiverType.MARKPASSINGS,
ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
}
/**
* Asserts that the "Ump..." competitors are considered non-competing and therefore don't show up
*/
@Test
public void testNoNonCompetingCompetitors() throws ParseException, NoWindException {
for (final Competitor competitor : getTrackedRace().getRace().getCompetitors()) {
assertFalse(competitor.getName().startsWith("Ump"));
assertSame(getDomainFactory().getOrCreateBoatClass("GC32"), competitor.getBoat().getBoatClass());
}
}
}
@@ -0,0 +1,77 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
/**
* Contains equality assertions for "compact" fixes, allowing for deltas to be specified, assuming
* that the "compact" version of a fix may vary slightly from its original.
*
* @author Axel Uhl (d043530)
*
*/
public class PositionAssert {
public static void assertPositionEquals(Position p1, Position p2, double degreeDelta) {
assertEquals(p1.getLatDeg(), p2.getLatDeg(), degreeDelta);
assertEquals(p1.getLngDeg(), p2.getLngDeg(), degreeDelta);
}
public static void assertGPSFixEquals(GPSFixMoving f1, GPSFixMoving f2, double positionDegreeDelta, double bearingDegreeDelta, double knotDelta) {
assertGPSFixEquals((GPSFix) f1, (GPSFix) f2, positionDegreeDelta);
assertSpeedEquals(f1.getSpeed(), f2.getSpeed(), bearingDegreeDelta, knotDelta);
}
public static void assertBearingEquals(Bearing b1, Bearing b2, double bearingDegreeDelta) {
assertEquals(b1.getDegrees(), b2.getDegrees(), bearingDegreeDelta);
}
public static void assertGPSFixEquals(GPSFix f1, GPSFix f2, double positionDegreeDelta) {
assertPositionEquals(f1.getPosition(), f2.getPosition(), positionDegreeDelta);
assertEquals(f1.getTimePoint(), f2.getTimePoint());
}
public static void assertWindEquals(Wind w1, Wind w2, double posDegDelta, double bearingDegreeDelta, double knotSpeedDelta) {
assertEquals(w1.getTimePoint(), w2.getTimePoint());
assertPositionEquals(w1.getPosition(), w2.getPosition(), posDegDelta);
assertBearingEquals(w1.getBearing(), w2.getBearing(), bearingDegreeDelta);
assertSpeedEquals(w1, w2, bearingDegreeDelta, knotSpeedDelta);
}
public static void assertSpeedEquals(Speed s1, Speed s2, double knotSpeedDelta) {
assertEquals(s1.getKnots(), s2.getKnots(), knotSpeedDelta);
}
public static void assertSpeedEquals(SpeedWithBearing s1, SpeedWithBearing s2, double bearingDegreeDelta, double knotSpeedDelta) {
assertBearingEquals(s1.getBearing(), s2.getBearing(), bearingDegreeDelta);
assertSpeedEquals((Speed) s1, (Speed) s2, knotSpeedDelta);
}
public static void assertWindEquals(Set<Wind> expectedWinds, Set<Wind> actualWinds, double posDegDelta,
double bearingDegreeDelta, double knotSpeedDelta) {
for (final Wind expectedWind : expectedWinds) {
boolean found = false;
for (final Wind actualWind : actualWinds) {
try {
assertWindEquals(expectedWind, actualWind, posDegDelta, bearingDegreeDelta, knotSpeedDelta);
found = true;
break;
} catch (AssertionError e) {
// not equals
}
}
if (!found) {
fail("Expected to find "+expectedWind+" in "+actualWinds+" but didn't");
}
}
}
}
@@ -16,8 +16,11 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance; import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class PositionTest { public class PositionTest {
@@ -32,20 +35,40 @@ public class PositionTest {
@Test @Test
public void testEqualityBetweenCompactAndVerbosePosition() { public void testEqualityBetweenCompactAndVerbosePosition() {
Position p1 = new DegreePosition(49.2, 008.3); Position p1 = new DegreePosition(49.2, 008.3);
CompactGPSFixImpl compactFix = new CompactGPSFixImpl(p1, MillisecondsTimePoint.now()); VeryCompactGPSFixImpl compactFix = new VeryCompactGPSFixImpl(p1, MillisecondsTimePoint.now());
assertNotSame(p1, compactFix.getPosition()); assertNotSame(p1, compactFix.getPosition());
assertEquals(p1, compactFix.getPosition()); PositionAssert.assertPositionEquals(p1, compactFix.getPosition(), 0.000001);
} }
@Test @Test
public void testEqualityBetweenCompactAndVerboseSpeedWithBearing() { public void testCompactFixRange() {
final TimePoint now = MillisecondsTimePoint.now();
assertCompactFixHasEqualLatLng(now, 0., 0.);
assertCompactFixHasEqualLatLng(now, 90., 0.);
assertCompactFixHasEqualLatLng(now, -90., 0.);
assertCompactFixHasEqualLatLng(now, 0., 180.);
assertCompactFixHasEqualLatLng(now, 0., -180.);
assertCompactFixHasEqualLatLng(now, 45., 45.);
assertCompactFixHasEqualLatLng(now, 0., -45.);
assertCompactFixHasEqualLatLng(now, -45., 45.);
assertCompactFixHasEqualLatLng(now, 0., 45.);
}
private void assertCompactFixHasEqualLatLng(final TimePoint now, final double latDeg, final double lngDeg) {
GPSFix fix = new VeryCompactGPSFixImpl(new DegreePosition(latDeg, lngDeg), now);
assertEquals(latDeg, fix.getPosition().getLatDeg(), 0.0000001);
assertEquals(lngDeg, fix.getPosition().getLngDeg(), 0.0000001);
}
@Test
public void testEqualityBetweenCompactAndVerboseSpeedWithBearing() throws CompactionNotPossibleException {
Position p1 = new DegreePosition(49.2, 008.3); Position p1 = new DegreePosition(49.2, 008.3);
SpeedWithBearing swb = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123)); SpeedWithBearing swb = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123));
CompactGPSFixMovingImpl compactFix = new CompactGPSFixMovingImpl(p1, MillisecondsTimePoint.now(), swb); VeryCompactGPSFixMovingImpl compactFix = new VeryCompactGPSFixMovingImpl(p1, MillisecondsTimePoint.now(), swb);
assertNotSame(p1, compactFix.getPosition()); assertNotSame(p1, compactFix.getPosition());
assertEquals(p1, compactFix.getPosition()); PositionAssert.assertPositionEquals(p1, compactFix.getPosition(), 0.000001);
assertNotSame(swb, compactFix.getSpeed()); assertNotSame(swb, compactFix.getSpeed());
assertEquals(swb, compactFix.getSpeed()); PositionAssert.assertSpeedEquals(swb, compactFix.getSpeed(), /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
} }
@Test @Test
@@ -338,7 +338,7 @@ public class ReachingLegTest extends TrackBasedTest {
Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE);
assertEquals(0., schomaekersDistanceToLeader.getMeters(), 0.00001); assertEquals(0., schomaekersDistanceToLeader.getMeters(), 0.00001);
// distance to leading Schomaeker expected to be the entire upwind distance plus the offset distance plus half the downwind // distance to leading Schomaeker expected to be the entire upwind distance plus the offset distance plus half the downwind
assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters()*1.5, plattnersDistanceToLeader.getMeters(), 0.001); assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters()*1.5, plattnersDistanceToLeader.getMeters(), 0.01);
} }
@Test @Test
@@ -373,9 +373,9 @@ public class ReachingLegTest extends TrackBasedTest {
.getEstimatedPosition(timePointToConsider, /* extrapolate */false) .getEstimatedPosition(timePointToConsider, /* extrapolate */false)
.getDistance( .getDistance(
getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */ getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */
false)).getMeters(), 0.00001); false)).getMeters(), 0.01);
// however, projected onto the leg their distance should be 0 // however, projected onto the leg their distance should be 0
Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE);
assertEquals(0., plattnersDistanceToLeader.getMeters(), 0.00001); assertEquals(0., plattnersDistanceToLeader.getMeters(), 0.01);
} }
} }
@@ -141,7 +141,7 @@ public class StarbordSideOfStartLineRecognitionTest {
MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition); MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition);
Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()). Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()).
getEstimatedPosition(now, /* extrapolate */ false); getEstimatedPosition(now, /* extrapolate */ false);
assertEquals(startStarboardPosition, p); PositionAssert.assertPositionEquals(startStarboardPosition, p, 0.0000001);
} }
@Test @Test
@@ -43,7 +43,8 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedWithDegreeBearingImpl; import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedWithDegreeBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
@@ -63,6 +64,7 @@ import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class TrackTest { public class TrackTest {
private static final int MILLIS_BETWEEN_FIXES = 3000000;
private DynamicGPSFixTrack<Boat, GPSFixMoving> track; private DynamicGPSFixTrack<Boat, GPSFixMoving> track;
private GPSFixMovingImpl gpsFix1; private GPSFixMovingImpl gpsFix1;
private GPSFixMovingImpl gpsFix2; private GPSFixMovingImpl gpsFix2;
@@ -75,7 +77,7 @@ public class TrackTest {
track = new DynamicGPSFixMovingTrackImpl<Boat>(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */ track = new DynamicGPSFixMovingTrackImpl<Boat>(new BoatImpl("MyFirstBoat", new BoatClassImpl("505", /* typicallyStartsUpwind */
true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null); true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null);
TimePoint now1 = MillisecondsTimePoint.now(); TimePoint now1 = MillisecondsTimePoint.now();
TimePoint now2 = addMillisToTimepoint(now1, 3); TimePoint now2 = addMillisToTimepoint(now1, MILLIS_BETWEEN_FIXES);
DegreePosition position1 = new DegreePosition(1, 2); DegreePosition position1 = new DegreePosition(1, 2);
DegreePosition position2 = new DegreePosition(1, 3); DegreePosition position2 = new DegreePosition(1, 3);
gpsFix1 = new GPSFixMovingImpl( gpsFix1 = new GPSFixMovingImpl(
@@ -84,19 +86,19 @@ public class TrackTest {
new DegreeBearingImpl(90))); new DegreeBearingImpl(90)));
gpsFix2 = new GPSFixMovingImpl(position2, now2, new KnotSpeedWithBearingImpl(position1.getDistance(position2) gpsFix2 = new GPSFixMovingImpl(position2, now2, new KnotSpeedWithBearingImpl(position1.getDistance(position2)
.inTime(now2.asMillis() - gpsFix1.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(90))); .inTime(now2.asMillis() - gpsFix1.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(90)));
TimePoint now3 = addMillisToTimepoint(now2, 3); TimePoint now3 = addMillisToTimepoint(now2, MILLIS_BETWEEN_FIXES);
Position position3 = new DegreePosition(1, 4); Position position3 = new DegreePosition(1, 4);
gpsFix3 = new GPSFixMovingImpl( gpsFix3 = new GPSFixMovingImpl(
position3, now3, new KnotSpeedWithBearingImpl(position2.getDistance(position3) position3, now3, new KnotSpeedWithBearingImpl(position2.getDistance(position3)
.inTime(now3.asMillis() - gpsFix2.getTimePoint().asMillis()).getKnots(), .inTime(now3.asMillis() - gpsFix2.getTimePoint().asMillis()).getKnots(),
new DegreeBearingImpl(0))); new DegreeBearingImpl(0)));
TimePoint now4 = addMillisToTimepoint(now3, 3); TimePoint now4 = addMillisToTimepoint(now3, MILLIS_BETWEEN_FIXES);
Position position4 = new DegreePosition(3, 4); Position position4 = new DegreePosition(3, 4);
gpsFix4 = new GPSFixMovingImpl( gpsFix4 = new GPSFixMovingImpl(
position4, now4, new KnotSpeedWithBearingImpl(position3.getDistance(position4) position4, now4, new KnotSpeedWithBearingImpl(position3.getDistance(position4)
.inTime(now4.asMillis() - gpsFix3.getTimePoint().asMillis()).getKnots(), .inTime(now4.asMillis() - gpsFix3.getTimePoint().asMillis()).getKnots(),
new DegreeBearingImpl(0))); new DegreeBearingImpl(0)));
TimePoint now5 = addMillisToTimepoint(now4, 3); TimePoint now5 = addMillisToTimepoint(now4, MILLIS_BETWEEN_FIXES);
Position position5 = new DegreePosition(5, 4); Position position5 = new DegreePosition(5, 4);
gpsFix5 = new GPSFixMovingImpl(position5, now5, new KnotSpeedWithBearingImpl(position4.getDistance(position5) gpsFix5 = new GPSFixMovingImpl(position5, now5, new KnotSpeedWithBearingImpl(position4.getDistance(position5)
.inTime(now5.asMillis() - gpsFix4.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(0))); .inTime(now5.asMillis() - gpsFix4.getTimePoint().asMillis()).getKnots(), new DegreeBearingImpl(0)));
@@ -106,6 +108,7 @@ public class TrackTest {
track.addGPSFix(gpsFix4); track.addGPSFix(gpsFix4);
track.addGPSFix(gpsFix5); track.addGPSFix(gpsFix5);
} }
/** /**
* Tests for a incorrect method of estimating Positions * Tests for a incorrect method of estimating Positions
*/ */
@@ -128,7 +131,6 @@ public class TrackTest {
assertEquals(45, track.getEstimatedPosition(t1.plus(15), true).getLatDeg(), 0.00000001); assertEquals(45, track.getEstimatedPosition(t1.plus(15), true).getLatDeg(), 0.00000001);
assertEquals(60, track.getEstimatedPosition(t1.plus(20), true).getLatDeg(), 0.00000001); assertEquals(60, track.getEstimatedPosition(t1.plus(20), true).getLatDeg(), 0.00000001);
assertEquals(90, track.getEstimatedPosition(t1.plus(30), true).getLatDeg(), 0.00000001); assertEquals(90, track.getEstimatedPosition(t1.plus(30), true).getLatDeg(), 0.00000001);
} finally { } finally {
track.unlockAfterRead(); track.unlockAfterRead();
} }
@@ -221,7 +223,7 @@ public class TrackTest {
now = nextNow; now = nextNow;
} }
assertEquals(speed.travel(start, now.minus(NUMBER_OF_FIXES_AT_SAME_POSITION * 1000)).getMeters(), assertEquals(speed.travel(start, now.minus(NUMBER_OF_FIXES_AT_SAME_POSITION * 1000)).getMeters(),
track.getDistanceTraveled(start, now).getMeters(), 0.00000001); track.getDistanceTraveled(start, now).getMeters(), 0.01);
} }
private void addFixesWithSamePositionButProgressingTime(DynamicGPSFixTrack<Object, GPSFixMoving> track, private void addFixesWithSamePositionButProgressingTime(DynamicGPSFixTrack<Object, GPSFixMoving> track,
@@ -320,7 +322,7 @@ public class TrackTest {
cacheBarrier.await(20, TimeUnit.SECONDS); cacheBarrier.await(20, TimeUnit.SECONDS);
cacheDone.await(20, TimeUnit.SECONDS); cacheDone.await(20, TimeUnit.SECONDS);
testDone.await(); testDone.await();
assertEquals(2., maxSpeed[0], 0.001); assertEquals(2., maxSpeed[0], 0.1);
} }
@Test @Test
@@ -366,12 +368,12 @@ public class TrackTest {
1, new DegreeBearingImpl(123))); 1, new DegreeBearingImpl(123)));
track.addGPSFix(fix3); track.addGPSFix(fix3);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)). assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
getB().getKnots(), 0.001); // produces a cache entry that ends getB().getKnots(), 0.01); // produces a cache entry that ends
GPSFixMoving fix4 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(10800000), new KnotSpeedWithBearingImpl( GPSFixMoving fix4 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(10800000), new KnotSpeedWithBearingImpl(
1, new DegreeBearingImpl(123))); 1, new DegreeBearingImpl(123)));
track.addGPSFix(fix4); track.addGPSFix(fix4);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)). assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001); getB().getKnots(), 0.01);
} }
@Test @Test
@@ -383,7 +385,7 @@ public class TrackTest {
2, new DegreeBearingImpl(123))); 2, new DegreeBearingImpl(123)));
track.addGPSFix(fast); track.addGPSFix(fast);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)). assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001); getB().getKnots(), 0.01);
track.addGPSFix(slow); track.addGPSFix(slow);
track.lockForRead(); track.lockForRead();
try { try {
@@ -392,7 +394,7 @@ public class TrackTest {
track.unlockAfterRead(); track.unlockAfterRead();
} }
assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)). assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001); getB().getKnots(), 0.01);
} }
@Test @Test
@@ -405,7 +407,7 @@ public class TrackTest {
2, new DegreeBearingImpl(123))); 2, new DegreeBearingImpl(123)));
track.addGPSFix(fix2); track.addGPSFix(fix2);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)). assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
getB().getKnots(), 0.001); // produces a cache entry that ends getB().getKnots(), 0.01); // produces a cache entry that ends
GPSFixMoving fix3 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(7200000), new KnotSpeedWithBearingImpl( GPSFixMoving fix3 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(7200000), new KnotSpeedWithBearingImpl(
1, new DegreeBearingImpl(123))); 1, new DegreeBearingImpl(123)));
track.addGPSFix(fix3); track.addGPSFix(fix3);
@@ -413,7 +415,7 @@ public class TrackTest {
1, new DegreeBearingImpl(123))); 1, new DegreeBearingImpl(123)));
track.addGPSFix(fix4); track.addGPSFix(fix4);
assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)). assertEquals(2., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(10800000)).
getB().getKnots(), 0.001); getB().getKnots(), 0.01);
} }
/** /**
@@ -462,7 +464,7 @@ public class TrackTest {
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
SpeedWithBearing average = track.getEstimatedSpeed(t2); SpeedWithBearing average = track.getEstimatedSpeed(t2);
assertEquals(0, average.getBearing().getDegrees(), 0.00001); PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing(), 0.1);
} }
@Test @Test
@@ -477,7 +479,7 @@ public class TrackTest {
track.addGPSFix(f1); track.addGPSFix(f1);
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored PositionAssert.assertGPSFixEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
} }
@Test @Test
@@ -492,7 +494,7 @@ public class TrackTest {
track.addGPSFix(f1); track.addGPSFix(f1);
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
assertEquals(f3, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the f2 fix to be invalid PositionAssert.assertGPSFixEquals(f3, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
} }
@Test @Test
@@ -508,7 +510,7 @@ public class TrackTest {
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
// expect the f2 fix to be invalid because provided speed is too different // expect the f2 fix to be invalid because provided speed is too different
assertEquals(0, f3.getPosition().getDistance(track.getFirstFixAtOrAfter(f2.getTimePoint()).getPosition()).getMeters(), 0.001); assertEquals(0, f3.getPosition().getDistance(track.getFirstFixAtOrAfter(f2.getTimePoint()).getPosition()).getMeters(), 0.01);
} }
@Test @Test
@@ -523,9 +525,9 @@ public class TrackTest {
track.addGPSFix(f1); track.addGPSFix(f1);
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored PositionAssert.assertGPSFixEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint()), /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01); // expect the fix to still be valid, but only its provided speed shall be ignored
SpeedWithBearing average = track.getEstimatedSpeed(t2); SpeedWithBearing average = track.getEstimatedSpeed(t2);
assertEquals(1, average.getKnots(), 0.001); assertEquals(1, average.getKnots(), 0.01);
} }
@Test @Test
@@ -541,7 +543,7 @@ public class TrackTest {
track.addGPSFix(f2); track.addGPSFix(f2);
track.addGPSFix(f3); track.addGPSFix(f3);
SpeedWithBearing average = track.getRawEstimatedSpeed(t2); SpeedWithBearing average = track.getRawEstimatedSpeed(t2);
assertEquals(0, average.getBearing().getDegrees(), 0.00001); PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing(), /* deg delta */ 0.1);
} }
/** /**
@@ -560,14 +562,14 @@ public class TrackTest {
Iterator<GPSFixMoving> subsetIter = subset.iterator(); Iterator<GPSFixMoving> subsetIter = subset.iterator();
// start iteration // start iteration
GPSFixMoving firstOfSubset = subsetIter.next(); GPSFixMoving firstOfSubset = subsetIter.next();
assertEquals(gpsFix2, firstOfSubset); PositionAssert.assertGPSFixEquals(gpsFix2, firstOfSubset, /* pos deg delta */ 0.00001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01);
// now add a fix: // now add a fix:
TimePoint now6 = addMillisToTimepoint(gpsFix5.getTimePoint(), 3); TimePoint now6 = addMillisToTimepoint(gpsFix5.getTimePoint(), 3);
track.addGPSFix(new GPSFixMovingImpl( track.addGPSFix(new GPSFixMovingImpl(
new DegreePosition(6, 5), now6, new KnotSpeedWithBearingImpl(2, new DegreeBearingImpl(0)))); new DegreePosition(6, 5), now6, new KnotSpeedWithBearingImpl(2, new DegreeBearingImpl(0))));
try { try {
GPSFixMoving secondOfSubset = subsetIter.next(); GPSFixMoving secondOfSubset = subsetIter.next();
assertEquals(gpsFix3, secondOfSubset); PositionAssert.assertGPSFixEquals(gpsFix3, secondOfSubset, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.01, /* knot delta */ 0.01);
fail("adding a fix interferes with iteration over subSet of track's fixes"); fail("adding a fix interferes with iteration over subSet of track's fixes");
} catch (ConcurrentModificationException e) { } catch (ConcurrentModificationException e) {
// this is what we expected // this is what we expected
@@ -675,7 +677,7 @@ public class TrackTest {
TimePoint afterTimePoint = new MillisecondsTimePoint(millis + (millis-lastMillis)); TimePoint afterTimePoint = new MillisecondsTimePoint(millis + (millis-lastMillis));
Position extrapolatedPosition = track.getEstimatedPosition(afterTimePoint, true); Position extrapolatedPosition = track.getEstimatedPosition(afterTimePoint, true);
assertEquals(0.5, fix.getPosition().getDistance(extrapolatedPosition).getMeters() assertEquals(0.5, fix.getPosition().getDistance(extrapolatedPosition).getMeters()
/ lastFix.getPosition().getDistance(extrapolatedPosition).getMeters(), 0.00001); / lastFix.getPosition().getDistance(extrapolatedPosition).getMeters(), 0.01);
} }
@Test @Test
@@ -792,7 +794,7 @@ public class TrackTest {
bearing = new DegreeBearingImpl(bearing.getDegrees() + 1); bearing = new DegreeBearingImpl(bearing.getDegrees() + 1);
} }
invalidationCalls.clear(); invalidationCalls.clear();
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.01); assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullIntervalCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start); final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullIntervalCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start);
assertNotNull(fullIntervalCacheEntry); // no more entry for "to"-value start in cache assertNotNull(fullIntervalCacheEntry); // no more entry for "to"-value start in cache
assertEquals(start, fullIntervalCacheEntry.getA()); assertEquals(start, fullIntervalCacheEntry.getA());
@@ -808,7 +810,7 @@ public class TrackTest {
invalidationCalls.clear(); invalidationCalls.clear();
final TimePoint timePointOfLastOriginalFix = track.getLastRawFix().getTimePoint(); final TimePoint timePointOfLastOriginalFix = track.getLastRawFix().getTimePoint();
assertEquals(speed.getMetersPerSecond() * (steps - 1), assertEquals(speed.getMetersPerSecond() * (steps - 1),
track.getDistanceTraveled(now, timePointOfLastOriginalFix).getMeters(), 0.01); track.getDistanceTraveled(now, timePointOfLastOriginalFix).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> newFullIntervalCacheEntry = distanceCache final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> newFullIntervalCacheEntry = distanceCache
.getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix); .getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix);
assertNotNull(newFullIntervalCacheEntry); // no more entry for "to"-value start in cache assertNotNull(newFullIntervalCacheEntry); // no more entry for "to"-value start in cache
@@ -861,7 +863,7 @@ public class TrackTest {
track.unlockAfterRead(); track.unlockAfterRead();
} }
GPSFix polishedLastFix2 = track.getLastFixBefore(new MillisecondsTimePoint(Long.MAX_VALUE)); // get the last smoothened fix... GPSFix polishedLastFix2 = track.getLastFixBefore(new MillisecondsTimePoint(Long.MAX_VALUE)); // get the last smoothened fix...
assertEquals(fix, polishedLastFix2); PositionAssert.assertGPSFixEquals(fix, polishedLastFix2, /* pos deg delta */ 0.000001);
assertEquals(speed.getMetersPerSecond()*steps, track.getDistanceTraveled(now, start).getMeters(), 0.01); assertEquals(speed.getMetersPerSecond()*steps, track.getDistanceTraveled(now, start).getMeters(), 0.01);
} }
@@ -1003,7 +1005,7 @@ public class TrackTest {
final TimePoint stripTo = start.minus(2*timeBetweenFixesInMillis); final TimePoint stripTo = start.minus(2*timeBetweenFixesInMillis);
assertEquals(speed.getMetersPerSecond()*(steps-3), assertEquals(speed.getMetersPerSecond()*(steps-3),
track.getDistanceTraveled(stripFrom, stripTo).getMeters(), 0.01); track.getDistanceTraveled(stripFrom, stripTo).getMeters(), 0.01);
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.01); assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
assertTrue(invalidationCalls.isEmpty()); assertTrue(invalidationCalls.isEmpty());
// expect a cache entry exactly for the strip's boundaries // expect a cache entry exactly for the strip's boundaries
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stripCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(stripFrom, stripTo); com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stripCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(stripFrom, stripTo);
@@ -1054,8 +1056,7 @@ public class TrackTest {
assertEquals(gpsFix2.getPosition().getLatDeg(), estimatedPosNew.getLatDeg(), 0.5); assertEquals(gpsFix2.getPosition().getLatDeg(), estimatedPosNew.getLatDeg(), 0.5);
assertEquals(gpsFix2.getPosition().getLngDeg(), estimatedPosNew.getLngDeg(), 0.5); assertEquals(gpsFix2.getPosition().getLngDeg(), estimatedPosNew.getLngDeg(), 0.5);
SpeedWithBearing estimatedSpeedNew = track.getEstimatedSpeed(normalFixesTime); SpeedWithBearing estimatedSpeedNew = track.getEstimatedSpeed(normalFixesTime);
assertEquals(estimatedSpeed.getKnots(), estimatedSpeedNew.getKnots(), 0.001); PositionAssert.assertSpeedEquals(estimatedSpeed, estimatedSpeedNew, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
assertEquals(estimatedSpeed.getBearing().getDegrees(), estimatedSpeedNew.getBearing().getDegrees(), 0.001);
} }
@Test @Test
@@ -1262,21 +1263,21 @@ public class TrackTest {
@Test @Test
public void testFrequency() { public void testFrequency() {
assertEquals(3, track.getAverageIntervalBetweenFixes().asMillis()); assertEquals(MILLIS_BETWEEN_FIXES, track.getAverageIntervalBetweenFixes().asMillis());
assertEquals(3, track.getAverageIntervalBetweenRawFixes().asMillis()); assertEquals(MILLIS_BETWEEN_FIXES, track.getAverageIntervalBetweenRawFixes().asMillis());
} }
@Test @Test
public void testEstimatedSpeedCaching() { public void testEstimatedSpeedCaching() throws CompactionNotPossibleException {
GPSFixMoving originalFix = new GPSFixMovingImpl(new DegreePosition(12, 34), MillisecondsTimePoint.now(), GPSFixMoving originalFix = new GPSFixMovingImpl(new DegreePosition(12, 34), MillisecondsTimePoint.now(),
new KnotSpeedWithBearingImpl(9, new DegreeBearingImpl(123))); new KnotSpeedWithBearingImpl(9, new DegreeBearingImpl(123)));
GPSFixMoving fix = new CompactGPSFixMovingImpl(originalFix); GPSFixMoving fix = new VeryCompactGPSFixMovingImpl(originalFix);
assertFalse(fix.isEstimatedSpeedCached()); assertFalse(fix.isEstimatedSpeedCached());
final KnotSpeedWithBearingImpl estimatedSpeed = new KnotSpeedWithBearingImpl(9.1, new DegreeBearingImpl(124)); final KnotSpeedWithBearingImpl estimatedSpeed = new KnotSpeedWithBearingImpl(9.1, new DegreeBearingImpl(124));
fix.cacheEstimatedSpeed(estimatedSpeed); fix.cacheEstimatedSpeed(estimatedSpeed);
assertTrue(fix.isEstimatedSpeedCached()); assertTrue(fix.isEstimatedSpeedCached());
SpeedWithBearing cachedEstimatedSpeed = fix.getCachedEstimatedSpeed(); SpeedWithBearing cachedEstimatedSpeed = fix.getCachedEstimatedSpeed();
assertEquals(estimatedSpeed, cachedEstimatedSpeed); PositionAssert.assertSpeedEquals(estimatedSpeed, cachedEstimatedSpeed, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
fix.invalidateEstimatedSpeedCache(); fix.invalidateEstimatedSpeedCache();
assertFalse(fix.isEstimatedSpeedCached()); assertFalse(fix.isEstimatedSpeedCached());
} }
@@ -1287,8 +1288,9 @@ public class TrackTest {
assertFalse(compactFix3.isEstimatedSpeedCached()); assertFalse(compactFix3.isEstimatedSpeedCached());
SpeedWithBearing fix3EstimatedSpeed = track.getEstimatedSpeed(gpsFix3.getTimePoint()); SpeedWithBearing fix3EstimatedSpeed = track.getEstimatedSpeed(gpsFix3.getTimePoint());
assertTrue(compactFix3.isEstimatedSpeedCached()); assertTrue(compactFix3.isEstimatedSpeedCached());
assertEquals(fix3EstimatedSpeed, compactFix3.getCachedEstimatedSpeed()); PositionAssert.assertSpeedEquals(fix3EstimatedSpeed, compactFix3.getCachedEstimatedSpeed(), /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
assertEquals(fix3EstimatedSpeed, track.getEstimatedSpeed(gpsFix3.getTimePoint())); // fetch again from the cache PositionAssert.assertSpeedEquals(fix3EstimatedSpeed, track.getEstimatedSpeed(gpsFix3.getTimePoint()),
/* bearing deg delta */ 0.1, /* knot speed delta */ 0.1); // fetch again from the cache
// assuming that all test fixes are within a few milliseconds and the averaging interval is much larger than that, // assuming that all test fixes are within a few milliseconds and the averaging interval is much larger than that,
// adding a single fix in the middle should invalidate the cache // adding a single fix in the middle should invalidate the cache
track.add(new GPSFixMovingImpl(gpsFix3.getPosition(), gpsFix3.getTimePoint().plus(1), gpsFix3.getSpeed())); track.add(new GPSFixMovingImpl(gpsFix3.getPosition(), gpsFix3.getTimePoint().plus(1), gpsFix3.getSpeed()));
@@ -68,7 +68,9 @@ public class UnicodeCharactersInCompetitorNamesTest {
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory,
"tracTest", "tracTest", "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); "tracTest", "tracTest", "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true,
/* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS),
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
Iterable<Competitor> competitors = fourtyninerYellow_2.getRaceHandle().getRace().getCompetitors(); Iterable<Competitor> competitors = fourtyninerYellow_2.getRaceHandle().getRace().getCompetitors();
for (Competitor competitor : competitors) { for (Competitor competitor : competitors) {
@@ -186,9 +186,9 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
}; };
Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime); Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirection); assertNotNull(estimatedWindDirection);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
assertFalse(cachedFixes.isEmpty()); assertFalse(cachedFixes.isEmpty());
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.00000001); assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.1);
// now invert leg's type by moving the top mark along the wind from the leeward gate: // now invert leg's type by moving the top mark along the wind from the leeward gate:
Iterator<Waypoint> waypointsIter = getTrackedRace().getRace().getCourse().getWaypoints().iterator(); Iterator<Waypoint> waypointsIter = getTrackedRace().getRace().getCourse().getWaypoints().iterator();
Waypoint leewardMark = waypointsIter.next(); Waypoint leewardMark = waypointsIter.next();
@@ -202,7 +202,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
assertEquals(LegType.DOWNWIND, firstLeg.getLegType(fixTime)); assertEquals(LegType.DOWNWIND, firstLeg.getLegType(fixTime));
Wind estimatedWindDirectionDownwind = track.getAveragedWind(/* position */ null, checkTime); Wind estimatedWindDirectionDownwind = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirectionDownwind); assertNotNull(estimatedWindDirectionDownwind);
assertEquals(185., estimatedWindDirectionDownwind.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirectionDownwind.getBearing().getDegrees(), 0.1);
} }
@Test @Test
@@ -229,15 +229,15 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
}; };
Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime); Wind estimatedWindDirection = track.getAveragedWind(/* position */ null, checkTime);
assertNotNull(estimatedWindDirection); assertNotNull(estimatedWindDirection);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
assertFalse(cachedFixes.isEmpty()); assertFalse(cachedFixes.isEmpty());
assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.00000001); assertEquals(185., cachedFixes.values().iterator().next().getObject().getBearing().getDegrees(), 0.1);
// now clear set of cached fixes, ask again and ensure nothing is cached again: // now clear set of cached fixes, ask again and ensure nothing is cached again:
cachedFixes.clear(); cachedFixes.clear();
Wind estimatedWindDirectionCached = track.getAveragedWind(/* position */ null, checkTime); Wind estimatedWindDirectionCached = track.getAveragedWind(/* position */ null, checkTime);
assertTrue(cachedFixes.isEmpty()); assertTrue(cachedFixes.isEmpty());
assertNotNull(estimatedWindDirectionCached); assertNotNull(estimatedWindDirectionCached);
assertEquals(185., estimatedWindDirectionCached.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirectionCached.getBearing().getDegrees(), 0.1);
// now add a GPS fix and make sure the cache is invalidated by adding it inside the averaging interval // now add a GPS fix and make sure the cache is invalidated by adding it inside the averaging interval
now = new MillisecondsTimePoint(checkTime.asMillis() + track.getMillisecondsOverWhichToAverageWind()/2); now = new MillisecondsTimePoint(checkTime.asMillis() + track.getMillisecondsOverWhichToAverageWind()/2);
setBearingForCompetitor(competitors.get(0), now, 330); setBearingForCompetitor(competitors.get(0), now, 330);
@@ -257,7 +257,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(0), now, 320); setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 50); setBearingForCompetitor(competitors.get(1), now, 50);
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now); Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
} }
@Test @Test
@@ -268,7 +268,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(1), now, 50); setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), new MillisecondsTimePoint(0), 100); // this shouldn't disturb the estimation because it's too old setBearingForCompetitor(competitors.get(2), new MillisecondsTimePoint(0), 100); // this shouldn't disturb the estimation because it's too old
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now); Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
} }
@Test @Test
@@ -280,7 +280,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(1), now, 50); setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), markPassingTimePoint, 100); // this shouldn't disturb the estimation because it's too old setBearingForCompetitor(competitors.get(2), markPassingTimePoint, 100); // this shouldn't disturb the estimation because it's too old
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now); Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(185., estimatedWindDirection.getBearing().getDegrees(), 0.1);
} }
@Test @Test
@@ -318,7 +318,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
setBearingForCompetitor(competitors.get(2), now, 135); setBearingForCompetitor(competitors.get(2), now, 135);
setBearingForCompetitor(competitors.get(3), now, 220); // on the same tack, should give no read-out setBearingForCompetitor(competitors.get(3), now, 220); // on the same tack, should give no read-out
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now); Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
assertEquals(177.5, estimatedWindDirection.getBearing().getDegrees(), 0.00000001); assertEquals(177.5, estimatedWindDirection.getBearing().getDegrees(), 0.1);
} }
@Test @Test
@@ -43,10 +43,12 @@ import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher; import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl; import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager; import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory; import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
@@ -86,7 +88,7 @@ public class WindTest {
track.add(wind1); track.add(wind1);
track.add(wind2); track.add(wind2);
Wind average = track.getAveragedWind(pos, middle); Wind average = track.getAveragedWind(pos, middle);
assertEquals(0., average.getBearing().getDifferenceTo(new DegreeBearingImpl(0)).getDegrees(), 0.0001); PositionAssert.assertBearingEquals(new DegreeBearingImpl(0), average.getBearing().getDifferenceTo(new DegreeBearingImpl(0)), 0.01);
} }
@Test @Test
@@ -109,14 +111,14 @@ public class WindTest {
for (Wind wind : track.getFixes()) { for (Wind wind : track.getFixes()) {
actualWind.add(wind); actualWind.add(wind);
} }
assertEquals(expectedWind, actualWind); PositionAssert.assertWindEquals(expectedWind, actualWind, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
} finally { } finally {
track.unlockAfterRead(); track.unlockAfterRead();
} }
} }
@Test @Test
public void testWindEquality() { public void testWindEquality() throws CompactionNotPossibleException {
Position p1 = new DegreePosition(1, 2); Position p1 = new DegreePosition(1, 2);
Position p2 = new DegreePosition(4, 5); Position p2 = new DegreePosition(4, 5);
TimePoint t1 = MillisecondsTimePoint.now(); TimePoint t1 = MillisecondsTimePoint.now();
@@ -124,8 +126,8 @@ public class WindTest {
SpeedWithBearing s1 = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(3)); SpeedWithBearing s1 = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(3));
SpeedWithBearing s2 = new KnotSpeedWithBearingImpl(22, new DegreeBearingImpl(123)); SpeedWithBearing s2 = new KnotSpeedWithBearingImpl(22, new DegreeBearingImpl(123));
Wind w1 = new WindImpl(p1, t1, s1); Wind w1 = new WindImpl(p1, t1, s1);
Wind w2 = new CompactWindImpl(w1); Wind w2 = new VeryCompactWindImpl(w1);
assertEquals(w1, w2); PositionAssert.assertWindEquals(w1, w2, /* pos deg delta */ 0.000001, /* bearing deg delta */ 0.1, /* knot speed delta */ 0.1);
assertEquals(w1.hashCode(), w2.hashCode()); assertEquals(w1.hashCode(), w2.hashCode());
Wind w3 = new WindImpl(p2, t2, s2); Wind w3 = new WindImpl(p2, t2, s2);
assertFalse(w1.equals(w3)); assertFalse(w1.equals(w3));
@@ -163,9 +165,9 @@ public class WindTest {
track.add(wind7); track.add(wind7);
// interval does bearely reach 20's burst because 0 has 0 length and 1000..30000 has 29000 length // interval does bearely reach 20's burst because 0 has 0 length and 1000..30000 has 29000 length
assertEquals(20, track.getAveragedWind(pos, new MillisecondsTimePoint(1)).getKnots(), 0.00000001); PositionAssert.assertSpeedEquals(new KnotSpeedImpl(20), track.getAveragedWind(pos, new MillisecondsTimePoint(1)), 0.02);
// interval uses the two fixes to the left (0, 1000)=1000 and three to the right (2000, 10000, 30000)=28000 // interval uses the two fixes to the left (0, 1000)=1000 and three to the right (2000, 10000, 30000)=28000
assertEquals(20, track.getAveragedWind(pos, new MillisecondsTimePoint(1001)).getKnots(), 0.00000001); PositionAssert.assertSpeedEquals(new KnotSpeedImpl(20), track.getAveragedWind(pos, new MillisecondsTimePoint(1001)), 0.02);
// in the middle of the "hole", fetches (0, 1000, 2000, 10000)=10000 and (30000, 40000)=10000, so 20000ms worth of wind // in the middle of the "hole", fetches (0, 1000, 2000, 10000)=10000 and (30000, 40000)=10000, so 20000ms worth of wind
final double averageFor20000 = track.getAveragedWind(pos, new MillisecondsTimePoint(20000)).getKnots(); final double averageFor20000 = track.getAveragedWind(pos, new MillisecondsTimePoint(20000)).getKnots();
// value is hard to predict exactly because time difference-based confidences rate fixes closer to 20000ms higher than those further away // value is hard to predict exactly because time difference-based confidences rate fixes closer to 20000ms higher than those further away
@@ -183,8 +185,7 @@ public class WindTest {
Wind wind = new WindImpl(pos, new MillisecondsTimePoint(0), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123))); Wind wind = new WindImpl(pos, new MillisecondsTimePoint(0), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)));
track.add(wind); track.add(wind);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(0)); Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(0));
assertEquals(10, estimate.getKnots(), 0.000000001); PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001);
} }
@Test @Test
@@ -195,8 +196,8 @@ public class WindTest {
track.add(wind); track.add(wind);
// we only have one measurement; this should be extrapolated because it's our best guess // we only have one measurement; this should be extrapolated because it's our best guess
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(1000)); Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(1000));
assertEquals(10, estimate.getKnots(), 0.000000001); PositionAssert.assertSpeedEquals(new KnotSpeedImpl(10), estimate, 0.01);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001); PositionAssert.assertBearingEquals(new DegreeBearingImpl(123), estimate.getBearing(), 0.01);
} }
@Test @Test
@@ -208,8 +209,7 @@ public class WindTest {
// we only have one measurement; this should be extrapolated because it's our best guess even if // we only have one measurement; this should be extrapolated because it's our best guess even if
// the last measurement was longer ago than our smoothening interval // the last measurement was longer ago than our smoothening interval
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(AVERAGING_INTERVAL_MILLIS+1000)); Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(AVERAGING_INTERVAL_MILLIS+1000));
assertEquals(10, estimate.getKnots(), 0.000000001); PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(123)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
assertEquals(123, estimate.getBearing().getDegrees(), 0.000000001);
} }
@Test @Test
@@ -221,8 +221,7 @@ public class WindTest {
Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(20, new DegreeBearingImpl(100))); Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(20, new DegreeBearingImpl(100)));
track.add(wind2); track.add(wind2);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(500)); Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(500));
assertEquals(15, estimate.getKnots(), 0.000000001); PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(15, new DegreeBearingImpl(100)), estimate, /* deg bearing delta */ 0.1, /* knot speed delta */ 0.01);
assertEquals(100, estimate.getBearing().getDegrees(), 0.00000001);
} }
@Test @Test
@@ -234,8 +233,7 @@ public class WindTest {
Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(100))); Wind wind2 = new WindImpl(pos, new MillisecondsTimePoint(1000), new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(100)));
track.add(wind2); track.add(wind2);
Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(2000)); Wind estimate = track.getAveragedWind(pos, new MillisecondsTimePoint(2000));
assertEquals(10, estimate.getKnots(), 0.000000001); PositionAssert.assertSpeedEquals(new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(105)), estimate, /* deg bearing delta, some tolerance needed because of time-based confidence */ 0.6, /* knot speed delta */ 0.01);
assertEquals(105, estimate.getBearing().getDegrees(), 0.6); // some tolerance needed because of time-based confidence
} }
@Test @Test
@@ -266,7 +264,7 @@ public class WindTest {
TimePoint timePoint = new MillisecondsTimePoint(df.parse("2009-07-11T17:31:38").getTime()); TimePoint timePoint = new MillisecondsTimePoint(df.parse("2009-07-11T17:31:38").getTime());
Wind result = track.getAveragedWind(null, timePoint); Wind result = track.getAveragedWind(null, timePoint);
// expectation: take two from left (because they are closer than AVERAGING_INTERVAL_MILLIS apart), one from right side: // expectation: take two from left (because they are closer than AVERAGING_INTERVAL_MILLIS apart), one from right side:
assertEquals((wind1.getKnots() + wind2.getKnots() + wind3.getKnots()) / 3, result.getKnots(), 0.000000001); assertEquals((wind1.getKnots() + wind2.getKnots() + wind3.getKnots()) / 3, result.getKnots(), 0.01);
assertEquals(80., result.getBearing().getDegrees(), 5); assertEquals(80., result.getBearing().getDegrees(), 5);
} }
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.junit.Test; import org.junit.Test;
import com.sap.sse.common.Duration;
import com.tractrac.model.lib.api.ModelLocator; import com.tractrac.model.lib.api.ModelLocator;
import com.tractrac.model.lib.api.data.IControlPassing; import com.tractrac.model.lib.api.data.IControlPassing;
import com.tractrac.model.lib.api.data.IControlPassings; import com.tractrac.model.lib.api.data.IControlPassings;
@@ -66,7 +67,7 @@ public class JorgesTracTracParallelLoadingTest {
threads.add(new Thread(()->{ threads.add(new Thread(()->{
try { try {
// Download the parameters file and load the race // Download the parameters file and load the race
IRace race = ModelLocator.getEventFactory().createRace(new URI(paramsURIs.get(index))); IRace race = ModelLocator.getEventFactory().createRace(new URI(paramsURIs.get(index)), (int) /* timeout in milliseconds */ Duration.ONE_MINUTE.asMillis());
// Create the subscriptions // Create the subscriptions
SubscriptionLogger subscriptionLogger = new SubscriptionLogger(race); SubscriptionLogger subscriptionLogger = new SubscriptionLogger(race);
@@ -1,10 +1,9 @@
package com.sap.sailing.domain.tracking.impl; package com.sap.sailing.domain.tracking.impl;
import static org.junit.Assert.assertEquals;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -22,7 +21,7 @@ public abstract class PositionInterpolationAndExtrapolationTest<FixType extends
} }
protected void assertPos(Position p, boolean extrapolate) { protected void assertPos(Position p, boolean extrapolate) {
assertEquals(p, getTrack().getEstimatedPosition(now, extrapolate)); PositionAssert.assertPositionEquals(p, getTrack().getEstimatedPosition(now, extrapolate), /* deg delta */ 0.0005);
} }
protected GPSFixTrack<Object, FixType> getTrack() { protected GPSFixTrack<Object, FixType> getTrack() {
@@ -7,6 +7,7 @@ import java.util.Map;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler; import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler;
import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -88,7 +89,8 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne
map.get(TRAC_TRAC_PASSWORD)==null?null:map.get(TRAC_TRAC_PASSWORD).toString(), map.get(TRAC_TRAC_PASSWORD)==null?null:map.get(TRAC_TRAC_PASSWORD).toString(),
map.get(RACE_STATUS)==null?null:map.get(RACE_STATUS).toString(), map.get(RACE_STATUS)==null?null:map.get(RACE_STATUS).toString(),
map.get(RACE_VISIBILITY)==null?null:map.get(RACE_VISIBILITY).toString(), isTrackWind(map), map.get(RACE_VISIBILITY)==null?null:map.get(RACE_VISIBILITY).toString(), isTrackWind(map),
isCorrectWindDirectionByMagneticDeclination(map), /* preferReplayIfAvailable */ true); isCorrectWindDirectionByMagneticDeclination(map), /* preferReplayIfAvailable */ true,
/* default timeout for obtaining IRace object from params URL */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
} }
@Override @Override
@@ -11,6 +11,7 @@ import java.text.ParseException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Boat;
@@ -169,28 +170,42 @@ public interface DomainFactory {
IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds); IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds);
/** /**
* Creates a {@link RaceDefinition} from a TracTrac {@link IRace} and a domain {@link Course} definition. The * Creates a {@link RaceDefinition} from a TracTrac {@link IRace} and a
* resulting {@link RaceDefinition} is added to the {@link com.sap.sailing.domain.base.Regatta} to which * domain {@link Course} definition. The resulting {@link RaceDefinition} is
* <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race * added to the {@link com.sap.sailing.domain.base.Regatta} to which
* cache. The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on * <code>trackedRegatta</code> belongs (see
* the race cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the * {@link TrackedRegatta#getRegatta()}). It is added to the internal race
* tracked race has been created and the {@link RaceDefinition} was * cache. The corresponding {@link TrackedRace} object is also created, and
* {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added} to the domain event. This ensures that * the notification of threads waiting on the race cache such as a blocking
* waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non-<code>null</code> tracked race * {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em>
* already immediately after the notification was sent, and that the {@link RaceDefinition} is already * the tracked race has been created and the {@link RaceDefinition} was
* {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its containing * {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added}
* {@link com.sap.sailing.domain.base.Regatta}. * to the domain event. This ensures that waiters for the
* @param raceDefinitionSetToUpdate * {@link RaceDefinition} are guaranteed to obtain a valid,
* if not <code>null</code>, after creating the {@link TrackedRace}, the {@link RaceDefinition} is * non-<code>null</code> tracked race already immediately after the
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that * notification was sent, and that the {@link RaceDefinition} is already
* object. * {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its
* @param raceLogResolver TODO * containing {@link com.sap.sailing.domain.base.Regatta}.
*/ *
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId, * @param raceDefinitionSetToUpdate
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass, Map<Competitor, Boat> competitorBoats, * if not <code>null</code>, after creating the
Course course, Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis, * {@link TrackedRace}, the {@link RaceDefinition} is
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, * {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace)
URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver); * added} to that object.
* @param runBeforeExposingRace
* if not {@code null} then this consumer will be passed the
* {@link DynamicTrackedRace} if it was actually created by this
* call. This happens while still in the
* {@code synchronized(raceCache)} block, therefore before calls
* waiting for the race (e.g.,
* {@link #getAndWaitForRaceDefinition(UUID)}) return the race.
*/
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass,
Map<Competitor, Boat> competitorBoats, Course course, Iterable<Sideline> sidelines, WindStore windStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid,
String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver, Consumer<DynamicTrackedRace> runBeforeExposingRace);
/** /**
* The record may be for a single mark or a gate. If for a gate, the * The record may be for a single mark or a gate. If for a gate, the
@@ -256,12 +271,13 @@ public interface DomainFactory {
* {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file
* instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring
* races if since the last connection the race was migrated to a replay file format. * races if since the last connection the race was migrated to a replay file format.
* @param timeoutInMillis TODO
*/ */
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI, RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination,
boolean preferReplayIfAvailable) throws Exception; boolean preferReplayIfAvailable, int timeoutInMillis) throws Exception;
/** /**
* Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the * Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the
* {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding * {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding
@@ -48,13 +48,12 @@ public interface TracTracAdapter {
* @param timeoutInMilliseconds * @param timeoutInMilliseconds
* if the race definition is not received for the race within this time, the race tracker for that race * if the race definition is not received for the race within this time, the race tracker for that race
* is stopped; use -1 to wait forever * is stopped; use -1 to wait forever
* @param trackWind TODO
* @param correctWindDirectionByMagneticDeclination TODO
*/ */
RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI, RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus, long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, int timeoutInMillis)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
/** /**
* Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are * Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are
@@ -12,12 +12,12 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.LoadingQueueDoneCallBack; import com.sap.sailing.domain.tractracadapter.LoadingQueueDoneCallBack;
import com.sap.sailing.domain.tractracadapter.Receiver; import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Triple; import com.sap.sse.common.Util.Triple;
import com.tractrac.model.lib.api.event.IEvent; import com.tractrac.model.lib.api.event.IEvent;
@@ -35,7 +35,15 @@ import com.tractrac.subscription.lib.api.IRaceSubscriber;
*/ */
public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Receiver { public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Receiver {
private static Logger logger = Logger.getLogger(AbstractReceiverWithQueue.class.getName()); private static Logger logger = Logger.getLogger(AbstractReceiverWithQueue.class.getName());
/**
* The timeout after which to re-try looking for the race definition to have appeared in
* {@link #getTrackedRace(IRace)} in case an infinite timeout (-1) was specified. During this
* short break, {@link #getTrackedRace(IRace)} will check for {@link #hasBeenStoppedPreemptively()}
* and will stop trying to look for the race if this received was stopped preemptively.
*/
private static final long RETRY_TIMEOUT_IN_MILLIS = Duration.ONE_MINUTE.asMillis();
private final LinkedBlockingDeque<Util.Triple<A, B, C>> queue; private final LinkedBlockingDeque<Util.Triple<A, B, C>> queue;
private final DomainFactory domainFactory; private final DomainFactory domainFactory;
private final IEvent tractracEvent; private final IEvent tractracEvent;
@@ -53,6 +61,14 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
*/ */
private boolean receivedEventDuringTimeout; private boolean receivedEventDuringTimeout;
/**
* Set by {@link #stopPreemptively()}. Invocations of {@link #handleEvent(Triple)} waiting for something
* should wait repeatedly with some timeout, such as, say, one minute, and keep re-trying until what they
* are waiting for has appeared, or this flag has been set in which case they can abort and assume they
* received a stop event.
*/
private boolean stoppedPreemptively;
public AbstractReceiverWithQueue(DomainFactory domainFactory, IEvent tractracEvent, public AbstractReceiverWithQueue(DomainFactory domainFactory, IEvent tractracEvent,
DynamicTrackedRegatta trackedRegatta, Simulator simulator, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, DynamicTrackedRegatta trackedRegatta, Simulator simulator, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber,
long timeoutInMilliseconds) { long timeoutInMilliseconds) {
@@ -97,6 +113,11 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
// mark the end and hence terminate the thread by adding a null/null/null event to the queue // mark the end and hence terminate the thread by adding a null/null/null event to the queue
queue.clear(); queue.clear();
stopAfterProcessingQueuedEvents(); stopAfterProcessingQueuedEvents();
stoppedPreemptively = true;
}
protected boolean hasBeenStoppedPreemptively() {
return stoppedPreemptively;
} }
@Override @Override
@@ -195,15 +216,19 @@ public abstract class AbstractReceiverWithQueue<A, B, C> implements Runnable, Re
/** /**
* Tries to find a {@link TrackedRace} for <code>race</code> in the {@link com.sap.sailing.domain.base.Regatta} * Tries to find a {@link TrackedRace} for <code>race</code> in the {@link com.sap.sailing.domain.base.Regatta}
* corresponding to {@link #tractracEvent}, as keyed by the {@link #domainFactory}. Waits for * corresponding to {@link #tractracEvent}, as keyed by the {@link #domainFactory}. Waits for
* {@link RaceTracker#TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS} milliseconds for the * {@link #timeoutInMilliseconds} milliseconds for the {@link RaceDefinition} to show up, or infinitely if
* {@link RaceDefinition} to show up. If it doesn't, <code>null</code> is returned. If the {@link RaceDefinition} * {@link #timeoutInMilliseconds} is -1, except this receiver is {@link #hasBeenStoppedPreemptively() stopped
* for <code>race</code> is not found in the {@link com.sap.sailing.domain.base.Regatta}, <code>null</code> is * preemptively}. If the race doesn't show up under these boundary conditions, <code>null</code> is returned. If the
* returned. If the {@link TrackedRace} for <code>race</code> isn't found in the {@link TrackedRegatta}, * {@link RaceDefinition} for <code>race</code> is not found in the {@link com.sap.sailing.domain.base.Regatta},
* <code>null</code> is returned, too. * <code>null</code> is returned. If the {@link TrackedRace} for <code>race</code> isn't found in the
* {@link TrackedRegatta}, <code>null</code> is returned, too.
*/ */
protected DynamicTrackedRace getTrackedRace(IRace race) { protected DynamicTrackedRace getTrackedRace(IRace race) {
DynamicTrackedRace result = null; DynamicTrackedRace result = null;
RaceDefinition raceDefinition = getDomainFactory().getAndWaitForRaceDefinition(race.getId(), timeoutInMilliseconds); final long effectiveTimeoutInMilliseconds = timeoutInMilliseconds == -1 ? RETRY_TIMEOUT_IN_MILLIS : timeoutInMilliseconds;
RaceDefinition raceDefinition;
while ((raceDefinition = getDomainFactory().getAndWaitForRaceDefinition(race.getId(), effectiveTimeoutInMilliseconds)) == null &&
timeoutInMilliseconds == -1 && !hasBeenStoppedPreemptively());
if (raceDefinition != null) { if (raceDefinition != null) {
com.sap.sailing.domain.base.Regatta domainRegatta = trackedRegatta.getRegatta(); com.sap.sailing.domain.base.Regatta domainRegatta = trackedRegatta.getRegatta();
if (domainRegatta.getRaceByName(raceDefinition.getName()) != null) { if (domainRegatta.getRaceByName(raceDefinition.getName()) != null) {
@@ -18,8 +18,10 @@ import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Stream;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Boat;
@@ -386,9 +388,9 @@ public class DomainFactoryImpl implements DomainFactory {
private Pair<String, BoatClass> getDefaultRegattaNameAndBoatClass(IRace race) { private Pair<String, BoatClass> getDefaultRegattaNameAndBoatClass(IRace race) {
Collection<ICompetitorClass> competitorClassList = new ArrayList<>(); Collection<ICompetitorClass> competitorClassList = new ArrayList<>();
for (IRaceCompetitor competitor : race.getRaceCompetitors()) { getCompetingCompetitors(race).forEach(competitor->{
competitorClassList.add(competitor.getCompetitor().getCompetitorClass()); competitorClassList.add(competitor.getCompetitor().getCompetitorClass());
} });
Pair<String, BoatClass> defaultRegattaNameAndBoatClass = new Pair<String, BoatClass>(race.getEvent().getName(), Pair<String, BoatClass> defaultRegattaNameAndBoatClass = new Pair<String, BoatClass>(race.getEvent().getName(),
getDominantBoatClass(competitorClassList)); getDominantBoatClass(competitorClassList));
return defaultRegattaNameAndBoatClass; return defaultRegattaNameAndBoatClass;
@@ -464,10 +466,6 @@ public class DomainFactoryImpl implements DomainFactory {
} }
} }
if (raceDefinition != null) { if (raceDefinition != null) {
Collection<ICompetitorClass> competitorClassList = new ArrayList<ICompetitorClass>();
for (IRaceCompetitor c : tractracRace.getRaceCompetitors()) {
competitorClassList.add(c.getCompetitor().getCompetitorClass());
}
// FIXME this only removes the race from its default regatta, not any explicit regatta // FIXME this only removes the race from its default regatta, not any explicit regatta
Pair<String, BoatClass> defaultRegattaNameAndBoatClass = getDefaultRegattaNameAndBoatClass(tractracRace); Pair<String, BoatClass> defaultRegattaNameAndBoatClass = getDefaultRegattaNameAndBoatClass(tractracRace);
Pair<String, String> key = new Pair<String, String>(defaultRegattaNameAndBoatClass.getA(), Pair<String, String> key = new Pair<String, String>(defaultRegattaNameAndBoatClass.getA(),
@@ -510,11 +508,13 @@ public class DomainFactoryImpl implements DomainFactory {
} }
@Override @Override
public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId, public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<Competitor> competitors, BoatClass boatClass, Map<Competitor, Boat> competitorBoats, Course course, String raceName, Iterable<Competitor> competitors, BoatClass boatClass,
Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis, Map<Competitor, Boat> competitorBoats, Course course, Iterable<Sideline> sidelines, WindStore windStore,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
URI tracTracUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver) { DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI tracTracUpdateURI, UUID tracTracEventUuid,
String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver,
Consumer<DynamicTrackedRace> runBeforeExposingRace) {
synchronized (raceCache) { synchronized (raceCache) {
RaceDefinition raceDefinition = raceCache.get(raceId); RaceDefinition raceDefinition = raceCache.get(raceId);
if (raceDefinition == null) { if (raceDefinition == null) {
@@ -532,14 +532,25 @@ public class DomainFactoryImpl implements DomainFactory {
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, ignoreTracTracMarkPassings, delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, ignoreTracTracMarkPassings,
raceLogResolver); raceLogResolver);
logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()); logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta());
if (runBeforeExposingRace != null) {
logger.fine("Running callback for tracked race creation for "+trackedRace.getRace());
runBeforeExposingRace.accept(trackedRace);
}
addTracTracUpdateHandlers(tracTracUpdateURI, tracTracEventUuid, tracTracUsername, tracTracPassword, addTracTracUpdateHandlers(tracTracUpdateURI, tracTracEventUuid, tracTracUsername, tracTracPassword,
raceDefinition, trackedRace); raceDefinition, trackedRace);
raceCache.put(raceId, raceDefinition); raceCache.put(raceId, raceDefinition);
raceCache.notifyAll(); raceCache.notifyAll();
} else { } else {
logger.warning("Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta() final String reasonForNotAddingRaceToRegatta = "Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()
+ " because boat class " + raceDefinition.getBoatClass() + " because boat class " + raceDefinition.getBoatClass()
+ " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass()); + " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass();
logger.warning(reasonForNotAddingRaceToRegatta);
try {
raceDefinitionSetToUpdate.raceNotLoaded(reasonForNotAddingRaceToRegatta);
} catch (Exception e) {
logger.log(Level.INFO, "Something else went wrong while trying to notify the RaceDefinition set that the race "+
raceDefinition+" could not be added to the the regatta "+trackedRegatta.getRegatta(), e);
}
} }
} else { } else {
logger.info("Found existing tracked race for race "+raceName+" with ID "+raceId); logger.info("Found existing tracked race for race "+raceName+" with ID "+raceId);
@@ -580,7 +591,7 @@ public class DomainFactoryImpl implements DomainFactory {
@Override @Override
public Map<Competitor, Boat> getBoatsInfoForCompetitors(IRace race, BoatClass defaultBoatClass) { public Map<Competitor, Boat> getBoatsInfoForCompetitors(IRace race, BoatClass defaultBoatClass) {
final Map<Competitor, Boat> competitorBoatInfos = new HashMap<>(); final Map<Competitor, Boat> competitorBoatInfos = new HashMap<>();
for (IRaceCompetitor rc : race.getRaceCompetitors()) { getCompetingCompetitors(race).forEach(rc->{
Util.Triple<String, String, String> competitorBoatInfo = getMetadataParser().parseCompetitorBoat(rc); Util.Triple<String, String, String> competitorBoatInfo = getMetadataParser().parseCompetitorBoat(rc);
Competitor existingCompetitor = getOrCreateCompetitor(rc.getCompetitor()); Competitor existingCompetitor = getOrCreateCompetitor(rc.getCompetitor());
if (existingCompetitor != null && competitorBoatInfo != null) { if (existingCompetitor != null && competitorBoatInfo != null) {
@@ -588,21 +599,28 @@ public class DomainFactoryImpl implements DomainFactory {
competitorBoatInfo.getB(), AbstractColor.getCssColor(competitorBoatInfo.getC())); competitorBoatInfo.getB(), AbstractColor.getCssColor(competitorBoatInfo.getC()));
competitorBoatInfos.put(existingCompetitor, boatOfCompetitor); competitorBoatInfos.put(existingCompetitor, boatOfCompetitor);
} }
} });
return competitorBoatInfos; return competitorBoatInfos;
} }
/**
* Obtains those {@link IRace#getRaceCompetitors() competitors} that are actually competing in the race
* ({@link ICompetitor#isNonCompeting()}=={@code false})
*/
private Stream<IRaceCompetitor> getCompetingCompetitors(IRace race) {
return race.getRaceCompetitors().stream().filter(rc->!rc.getCompetitor().isNonCompeting());
}
@Override @Override
public Util.Pair<Iterable<Competitor>, BoatClass> getCompetitorsAndDominantBoatClass(IRace race) { public Util.Pair<Iterable<Competitor>, BoatClass> getCompetitorsAndDominantBoatClass(IRace race) {
List<ICompetitorClass> competitorClasses = new ArrayList<ICompetitorClass>(); List<ICompetitorClass> competitorClasses = new ArrayList<ICompetitorClass>();
final List<Competitor> competitors = new ArrayList<Competitor>(); final List<Competitor> competitors = new ArrayList<Competitor>();
for (IRaceCompetitor rc : race.getRaceCompetitors()) { getCompetingCompetitors(race).forEach(rc->{
// also add those whose race class doesn't match the dominant one (such as camera boats) // also add those whose race class doesn't match the dominant one (such as camera boats)
// because they may still send data that we would like to record in some tracks // because they may still send data that we would like to record in some tracks
competitors.add(getOrCreateCompetitor(rc.getCompetitor())); competitors.add(getOrCreateCompetitor(rc.getCompetitor()));
competitorClasses.add(rc.getCompetitor().getCompetitorClass()); competitorClasses.add(rc.getCompetitor().getCompetitorClass());
} });
BoatClass dominantBoatClass = getDominantBoatClass(competitorClasses); BoatClass dominantBoatClass = getDominantBoatClass(competitorClasses);
Util.Pair<Iterable<Competitor>, BoatClass> competitorsAndDominantBoatClass = new com.sap.sse.common.Util.Pair<Iterable<Competitor>, BoatClass>( Util.Pair<Iterable<Competitor>, BoatClass> competitorsAndDominantBoatClass = new com.sap.sse.common.Util.Pair<Iterable<Competitor>, BoatClass>(
competitors, dominantBoatClass); competitors, dominantBoatClass);
@@ -688,11 +706,11 @@ public class DomainFactoryImpl implements DomainFactory {
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus, RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable) throws Exception { String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable, int timeoutInMillis) throws Exception {
return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI, return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination,
preferReplayIfAvailable); preferReplayIfAvailable, timeoutInMillis);
} }
@Override @Override
@@ -7,7 +7,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -56,48 +55,49 @@ public class MarkPassingReceiver extends AbstractReceiverWithQueue<IRaceCompetit
} }
protected void handleEvent(Util.Triple<IRaceCompetitor, IControlPassings, Void> event) { protected void handleEvent(Util.Triple<IRaceCompetitor, IControlPassings, Void> event) {
System.out.print("L"); // as in "Leg" if (!event.getA().getCompetitor().isNonCompeting()) {
DynamicTrackedRace trackedRace = getTrackedRace(event.getA().getRace()); System.out.print("L"); // as in "Leg"
if (trackedRace != null) { DynamicTrackedRace trackedRace = getTrackedRace(event.getA().getRace());
Course course = trackedRace.getRace().getCourse(); if (trackedRace != null) {
Iterator<Waypoint> waypointsIter = course.getWaypoints().iterator(); Course course = trackedRace.getRace().getCourse();
Map<Waypoint, MarkPassing> passingsByWaypoint = new HashMap<Waypoint, MarkPassing>(); Iterator<Waypoint> waypointsIter = course.getWaypoints().iterator();
// Note: the entries always describe all mark passings for the competitor so far in the current race in Map<Waypoint, MarkPassing> passingsByWaypoint = new HashMap<Waypoint, MarkPassing>();
// order // Note: the entries always describe all mark passings for the competitor so far in the current race in
for (IControlPassing passing : event.getB().getPassings()) { // order
IControl controlPointPassed = passing.getControl(); for (IControlPassing passing : event.getB().getPassings()) {
com.sap.sailing.domain.base.ControlPoint domainControlPoint = getDomainFactory() IControl controlPointPassed = passing.getControl();
.getOrCreateControlPoint(new ControlPointAdapter(controlPointPassed)); com.sap.sailing.domain.base.ControlPoint domainControlPoint = getDomainFactory()
Waypoint passed = findWaypointForControlPoint(trackedRace, waypointsIter, domainControlPoint, .getOrCreateControlPoint(new ControlPointAdapter(controlPointPassed));
getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor())); Waypoint passed = findWaypointForControlPoint(trackedRace, waypointsIter, domainControlPoint);
if (passed != null) { if (passed != null) {
TimePoint time = new MillisecondsTimePoint(passing.getTimestamp()); TimePoint time = new MillisecondsTimePoint(passing.getTimestamp());
MarkPassing markPassing = getDomainFactory().createMarkPassing(time, passed, MarkPassing markPassing = getDomainFactory().createMarkPassing(time, passed,
getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor())); getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()));
passingsByWaypoint.put(passed, markPassing); passingsByWaypoint.put(passed, markPassing);
} else { } else {
logger.warning("Didn't find waypoint in course " + course + " for mark passing around " logger.warning("Didn't find waypoint in course " + course + " for mark passing around "
+ passing.getControl()); + passing.getControl());
} }
} }
List<MarkPassing> markPassings = new ArrayList<MarkPassing>(); List<MarkPassing> markPassings = new ArrayList<MarkPassing>();
for (Waypoint waypoint : course.getWaypoints()) { for (Waypoint waypoint : course.getWaypoints()) {
MarkPassing passing = passingsByWaypoint.get(waypoint); MarkPassing passing = passingsByWaypoint.get(waypoint);
if (passing != null) { if (passing != null) {
markPassings.add(passing); markPassings.add(passing);
} }
} }
logger.fine("Received mark passings in race "+trackedRace.getRace().getName()+": "+markPassings); logger.fine("Received mark passings in race "+trackedRace.getRace().getName()+": "+markPassings);
if (getSimulator() != null) { if (getSimulator() != null) {
getSimulator().delayMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), markPassings); getSimulator().delayMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), markPassings);
} else { } else {
trackedRace.updateMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), trackedRace.updateMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()),
markPassings); markPassings);
} }
} else { } else {
logger.warning("Couldn't find tracked race for race " + event.getA().getRace().getName() logger.warning("Couldn't find tracked race for race " + event.getA().getRace().getName()
+ ". Dropping mark passing event " + event); + ". Dropping mark passing event " + event);
} }
}
} }
/** /**
@@ -106,7 +106,7 @@ public class MarkPassingReceiver extends AbstractReceiverWithQueue<IRaceCompetit
* returns <code>false</code>. * returns <code>false</code>.
*/ */
private Waypoint findWaypointForControlPoint(TrackedRace trackedRace, Iterator<Waypoint> waypointsIter, private Waypoint findWaypointForControlPoint(TrackedRace trackedRace, Iterator<Waypoint> waypointsIter,
com.sap.sailing.domain.base.ControlPoint domainControlPoint, Competitor competitor) { com.sap.sailing.domain.base.ControlPoint domainControlPoint) {
while (waypointsIter.hasNext()) { while (waypointsIter.hasNext()) {
Waypoint waypoint = waypointsIter.next(); Waypoint waypoint = waypointsIter.next();
if (waypoint.getControlPoint() == domainControlPoint) { if (waypoint.getControlPoint() == domainControlPoint) {
@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -135,7 +136,6 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
DynamicTrackedRace trackedRace = null; DynamicTrackedRace trackedRace = null;
// When the tracked race is created, we noted that for REPLAY races the TracAPI transmission of race times is not reliable. // When the tracked race is created, we noted that for REPLAY races the TracAPI transmission of race times is not reliable.
// Therefore, we poke the tracking start/end times and the race start time into the TrackedRace here when it's created here. // Therefore, we poke the tracking start/end times and the race start time into the TrackedRace here when it's created here.
boolean needToUpdateRaceTimes = false;
if (existingRaceDefinitionForRace != null) { if (existingRaceDefinitionForRace != null) {
logger.log(Level.INFO, "Received course update for existing race "+tractracRace.getName()+": "+ logger.log(Level.INFO, "Received course update for existing race "+tractracRace.getName()+": "+
event.getA().getControls()); event.getA().getControls());
@@ -145,8 +145,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
try { try {
getDomainFactory().updateCourseWaypoints(existingRaceDefinitionForRace.getCourse(), ttControlPoints); getDomainFactory().updateCourseWaypoints(existingRaceDefinitionForRace.getCourse(), ttControlPoints);
if (getTrackedRegatta().getExistingTrackedRace(existingRaceDefinitionForRace) == null) { if (getTrackedRegatta().getExistingTrackedRace(existingRaceDefinitionForRace) == null) {
trackedRace = createTrackedRace(existingRaceDefinitionForRace, sidelines); trackedRace = createTrackedRace(existingRaceDefinitionForRace, sidelines, tr->updateRaceTimes(tractracRace, tr));
needToUpdateRaceTimes = true;
} }
} catch (PatchFailedException e) { } catch (PatchFailedException e) {
logger.log(Level.SEVERE, "Internal error updating race course "+course+": "+e.getMessage()); logger.log(Level.SEVERE, "Internal error updating race course "+course+": "+e.getMessage());
@@ -161,15 +160,11 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
getTrackedRegatta(), tractracRace.getId(), tractracRace.getName(), competitorsAndDominantBoatClass.getA(), getTrackedRegatta(), tractracRace.getId(), tractracRace.getName(), competitorsAndDominantBoatClass.getA(),
competitorsAndDominantBoatClass.getB(), competitorBoats, course, sidelines, windStore, delayToLiveInMillis, competitorsAndDominantBoatClass.getB(), competitorBoats, course, sidelines, windStore, delayToLiveInMillis,
millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, tracTracUpdateURI, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, tracTracUpdateURI,
getTracTracEvent().getId(), tracTracUsername, tracTracPassword, useInternalMarkPassingAlgorithm, raceLogResolver); getTracTracEvent().getId(), tracTracUsername, tracTracPassword, useInternalMarkPassingAlgorithm, raceLogResolver, tr->updateRaceTimes(tractracRace, tr));
needToUpdateRaceTimes = true;
if (getSimulator() != null) { if (getSimulator() != null) {
getSimulator().setTrackedRace(trackedRace); getSimulator().setTrackedRace(trackedRace);
} }
} }
if (needToUpdateRaceTimes) {
updateRaceTimes(tractracRace, trackedRace);
}
} }
private void updateRaceTimes(IRace tractracRace, DynamicTrackedRace trackedRace) { private void updateRaceTimes(IRace tractracRace, DynamicTrackedRace trackedRace) {
@@ -222,11 +217,14 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
} }
} }
private DynamicTrackedRace createTrackedRace(RaceDefinition race, Iterable<Sideline> sidelines) { private DynamicTrackedRace createTrackedRace(RaceDefinition race, Iterable<Sideline> sidelines, Consumer<DynamicTrackedRace> runAfterCreatingTrackedRace) {
DynamicTrackedRace trackedRace = getTrackedRegatta().createTrackedRace(race, sidelines, DynamicTrackedRace trackedRace = getTrackedRegatta().createTrackedRace(race, sidelines,
windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver); raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver);
if (runAfterCreatingTrackedRace != null) {
runAfterCreatingTrackedRace.accept(trackedRace);
}
getDomainFactory().addTracTracUpdateHandlers(tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, race, trackedRace); getDomainFactory().addTracTracUpdateHandlers(tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, race, trackedRace);
return trackedRace; return trackedRace;
} }
@@ -5,6 +5,7 @@ import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
@@ -54,17 +55,24 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
* {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file
* instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring
* races if since the last connection the race was migrated to a replay file format. * races if since the last connection the race was migrated to a replay file format.
* @param timeoutInMillis
* -1 means no timeout; otherwise, this is the timeout for waiting for the {@link IRace} to be obtained
* from the {@code paramURL} document. A {@link TimeoutException} will result if the timeout applied.
*/ */
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility,
boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable) boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable, int timeoutInMillis)
throws Exception { throws Exception {
super(trackWind, correctWindDirectionByMagneticDeclination); super(trackWind, correctWindDirectionByMagneticDeclination);
this.paramURL = paramURL; this.paramURL = paramURL;
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())); if (timeoutInMillis == -1) {
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()));
} else {
this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()), timeoutInMillis);
}
if (preferReplayIfAvailable && isReplayRace(tractracRace) && if (preferReplayIfAvailable && isReplayRace(tractracRace) &&
(!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI()))) { (!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI()))) {
logger.info("Replay format available and preferred for race " + tractracRace.getName() logger.info("Replay format available and preferred for race " + tractracRace.getName()
@@ -48,25 +48,27 @@ public class RawPositionReceiver extends AbstractReceiverWithQueue<IRaceCompetit
@Override @Override
protected void handleEvent(Triple<IRaceCompetitor, IPosition, Void> event) { protected void handleEvent(Triple<IRaceCompetitor, IPosition, Void> event) {
if (received++ % 1000 == 0) { if (!event.getA().getCompetitor().isNonCompeting()) {
System.out.print("P"); if (received++ % 1000 == 0) {
if ((received / 1000 + 1) % 80 == 0) { System.out.print("P");
System.out.println(); if ((received / 1000 + 1) % 80 == 0) {
} System.out.println();
} }
IRace race = event.getA().getRace(); }
DynamicTrackedRace trackedRace = getTrackedRace(race); IRace race = event.getA().getRace();
if (trackedRace != null) { DynamicTrackedRace trackedRace = getTrackedRace(race);
final GPSFixMoving fix = getDomainFactory().createGPSFixMoving(event.getB()); if (trackedRace != null) {
Competitor competitor = getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()); final GPSFixMoving fix = getDomainFactory().createGPSFixMoving(event.getB());
if (getSimulator() != null) { Competitor competitor = getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor());
getSimulator().scheduleCompetitorPosition(competitor, fix); if (getSimulator() != null) {
} else { getSimulator().scheduleCompetitorPosition(competitor, fix);
trackedRace.recordFix(competitor, fix); } else {
} trackedRace.recordFix(competitor, fix);
} else { }
logger.warning("Couldn't find tracked race for race " + race.getName() } else {
+ ". Dropping raw position event " + event); logger.warning("Couldn't find tracked race for race " + race.getName()
} + ". Dropping raw position event " + event);
}
}
} }
} }
@@ -49,7 +49,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
public RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI, public RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus, long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws Exception { String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, int timeoutInMillis) throws Exception {
return trackerManager.addRace( return trackerManager.addRace(
/* regattaToAddTo */null, /* regattaToAddTo */null,
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
@@ -57,7 +57,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
/* startOfTracking */null, /* startOfTracking */null,
/* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false, /* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false,
raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination,
/* preferReplayIfAvailable */ false), /* preferReplayIfAvailable */ false, timeoutInMillis),
timeoutInMilliseconds); timeoutInMilliseconds);
} }
@@ -73,7 +73,8 @@ public class TracTracAdapterImpl implements TracTracAdapter {
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false), timeoutInMilliseconds); raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false,
(int) timeoutInMilliseconds), timeoutInMilliseconds);
} }
@Override @Override
@@ -334,10 +334,12 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
eventSubscriber.subscribeCompetitors(new ICompetitorsListener() { eventSubscriber.subscribeCompetitors(new ICompetitorsListener() {
@Override @Override
public void updateCompetitor(ICompetitor competitor) { public void updateCompetitor(ICompetitor competitor) {
final Competitor domainCompetitor = TracTracRaceTrackerImpl.this.domainFactory.getOrCreateCompetitor(competitor); if (!competitor.isNonCompeting()) {
logger.info("Competitor "+competitor+" was updated on TracTrac side. Maybe consider updating in competitor store as well. "+ final Competitor domainCompetitor = TracTracRaceTrackerImpl.this.domainFactory.getOrCreateCompetitor(competitor);
"TracTrac competitor maps to "+domainCompetitor.getName()+" with sail ID "+domainCompetitor.getBoat().getSailID()+ logger.info("Competitor "+competitor+" was updated on TracTrac side. Maybe consider updating in competitor store as well. "+
" and boat class "+domainCompetitor.getBoat().getBoatClass().getName()); "TracTrac competitor maps to "+domainCompetitor.getName()+" with sail ID "+domainCompetitor.getBoat().getSailID()+
" and boat class "+domainCompetitor.getBoat().getBoatClass().getName());
}
} }
@Override @Override
@@ -647,6 +649,16 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
notifyRaceCreationListeners(); notifyRaceCreationListeners();
} }
/**
* When this tracker is informed that the race cannot be loaded, e.g., because its boat class does
* not match the regatta's boat class, the tracker will {@link #stop} preemptively.
*/
@Override
public void raceNotLoaded(String reason) throws MalformedURLException, IOException, InterruptedException {
logger.severe("Race for tracker "+this+" with ID "+getID()+" did not load: "+reason+". Stopping tracker.");
trackedRegattaRegistry.stopTracking(regatta, race);
}
@Override @Override
public void gotLiveDataEvent(ILiveDataEvent liveDataEvent) { public void gotLiveDataEvent(ILiveDataEvent liveDataEvent) {
logger.info("Status change in tracker "+getID()+" for race(s) "+getRace()+": "+liveDataEvent); logger.info("Status change in tracker "+getID()+" for race(s) "+getRace()+": "+liveDataEvent);
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding//src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedWithBearingImpl.java=UTF-8
@@ -43,6 +43,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOver
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets1LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets1LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEight; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsEight;
@@ -124,6 +125,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
return new HighPointFirstGets10Or8AndLastBreaksTie(); return new HighPointFirstGets10Or8AndLastBreaksTie();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT: case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT:
return new HighPointFirstGets12Or8AndLastBreaksTie(); return new HighPointFirstGets12Or8AndLastBreaksTie();
case HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017:
return new HighPointFirstGets12Or8AndLastBreaksTie2017();
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07: case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
return new LowPointWithEliminationsAndRoundsWinnerGets07(); return new LowPointWithEliminationsAndRoundsWinnerGets07();
case LOW_POINT_LEAGUE_OVERALL: case LOW_POINT_LEAGUE_OVERALL:
@@ -46,7 +46,7 @@ public class KilometersPerHourSpeedWithBearingImpl extends KilometersPerHourSpee
@Override @Override
public String toString() { public String toString() {
return super.toString()+" to "+getBearing().getDegrees()+""; return super.toString()+" to "+getBearing().getDegrees()+"°";
} }
@Override @Override
@@ -175,7 +175,7 @@ public class LeaderboardCacheManager {
} }
@Override @Override
public void correctedScoreChanced(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) { public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) {
removeFromCache(leaderboard); removeFromCache(leaderboard);
} }
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.leaderboard; package com.sap.sailing.domain.leaderboard;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
import com.sap.sse.common.TimePoint;
/** /**
* Computing the competitors can be a bit expensive, particularly if the fleet is large and there may be suppressed * Computing the competitors can be a bit expensive, particularly if the fleet is large and there may be suppressed
@@ -23,4 +25,5 @@ import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
*/ */
public interface NumberOfCompetitorsInLeaderboardFetcher { public interface NumberOfCompetitorsInLeaderboardFetcher {
int getNumberOfCompetitorsInLeaderboard(); int getNumberOfCompetitorsInLeaderboard();
int getNumberOfCompetitorsWithoutMaxPointReason(RaceColumn column, TimePoint timePoint);
} }
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
public interface ScoreCorrectionListener { public interface ScoreCorrectionListener {
void correctedScoreChanced(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore); void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore);
void maxPointsReasonChanced(Competitor competitor, RaceColumn raceColumn, MaxPointsReason oldMaxPointsReason, MaxPointsReason newMaxPointsReason); void maxPointsReasonChanced(Competitor competitor, RaceColumn raceColumn, MaxPointsReason oldMaxPointsReason, MaxPointsReason newMaxPointsReason);
@@ -66,9 +66,12 @@ public interface ScoringScheme extends Serializable {
* otherwise, this parameter tells the number of competitors in the same race as <code>competitor</code>, * otherwise, this parameter tells the number of competitors in the same race as <code>competitor</code>,
* not in the entire <code>raceColumn</code> (those may be more in case of split fleets). The scoring scheme * not in the entire <code>raceColumn</code> (those may be more in case of split fleets). The scoring scheme
* may use this number, if available, to infer a penalty score. * may use this number, if available, to infer a penalty score.
* @param timePoint
* an optional timePoint that may help the scheme to determine the penalty related to a certain point in time only.
*/ */
Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher); Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
TimePoint timePoint);
/** /**
* @param competitor1Scores scores of the first competitor, in the order of race columns in the leaderboard * @param competitor1Scores scores of the first competitor, in the order of race columns in the leaderboard
@@ -252,6 +252,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
*/ */
public class NumberOfCompetitorsFetcherImpl implements NumberOfCompetitorsInLeaderboardFetcher { public class NumberOfCompetitorsFetcherImpl implements NumberOfCompetitorsInLeaderboardFetcher {
private int numberOfCompetitors = -1; private int numberOfCompetitors = -1;
private int numberOfCompetitorsWithoutMaxPointReason = -1;
@Override @Override
public int getNumberOfCompetitorsInLeaderboard() { public int getNumberOfCompetitorsInLeaderboard() {
@@ -260,6 +261,18 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} }
return numberOfCompetitors; return numberOfCompetitors;
} }
@Override
public int getNumberOfCompetitorsWithoutMaxPointReason(RaceColumn column, TimePoint timePoint) {
if (numberOfCompetitorsWithoutMaxPointReason == -1) {
numberOfCompetitorsWithoutMaxPointReason = 0;
for (Competitor competitor : getCompetitors()) {
MaxPointsReason maxPointReason = getScoreCorrection().getMaxPointsReason(competitor, column, timePoint);
numberOfCompetitorsWithoutMaxPointReason += maxPointReason == MaxPointsReason.NONE ? 1 : 0;
}
}
return numberOfCompetitorsWithoutMaxPointReason;
}
} }
/** /**
@@ -1200,6 +1213,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override @Override
public void setSuppressed(Competitor competitor, boolean suppressed) { public void setSuppressed(Competitor competitor, boolean suppressed) {
if (competitor == null) {
throw new IllegalArgumentException("Cannot change suppression for a null competitor");
}
LockUtil.lockForWrite(suppressedCompetitorsLock); LockUtil.lockForWrite(suppressedCompetitorsLock);
try { try {
if (suppressed) { if (suppressed) {
@@ -64,7 +64,7 @@ public class HighPoint extends AbstractScoringSchemeImpl {
@Override @Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace, public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) { NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
return 0.0; return 0.0;
} }
@@ -0,0 +1,61 @@
package com.sap.sailing.domain.leaderboard.impl;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sse.common.TimePoint;
/**
* {@link HighPointFirstGetsFixedScore} scheme that in most cases applies
* 12 points for the winner. Iff for one race column a {@link RaceLogAdditionalScoringInformationEvent}
* is found then 8 points are applied for the winner.
*
* From Phil, Race Director, on 12.09.2014: "If there is a tie in the regatta score between two or more boats at any time, the tie
* shall be broken in favour of the boat that has won the most races. If a tie still remains, it shall be broken in
* favour of the boat that had the better place at the last race sailed."
*
* 13.1.2 A Boat that did not start, did not finish, or retired shall receive 1 point less than the last boat to complete the race.
* 13.1.3 A Boat that is disqualified from a race, including OCS, shall receive 2 points less than the last boat to complete the race.
*
* @author Simon Marcel Pamies
*/
public class HighPointFirstGets12Or8AndLastBreaksTie2017 extends AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie {
private static final long serialVersionUID = 1L;
public HighPointFirstGets12Or8AndLastBreaksTie2017() {
super(12.0);
}
@Override
public ScoringSchemeType getType() {
return ScoringSchemeType.HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017;
}
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
Double result;
/**
* 1. Get all competitors without a max point reason.
* 2. This list of competitors is assumed to be all competitors having successfully taken part in the race.
* 3. The score of the last competitor will be 12-size(competitorList)
*
* XXX How about two competitors not finishing a race? They will get the same score. NOR does not state
* anything on how to handle such a situation except that it implicates that this will lead to a tie. No
* update from OC Sport on that question so far (Mar 2017).
*/
int competitorCountWithoutMaxPointReason = numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsWithoutMaxPointReason(raceColumn, timePoint);
if (maxPointsReason == MaxPointsReason.DNS || maxPointsReason == MaxPointsReason.DNF || maxPointsReason == MaxPointsReason.RET) {
result = (12 - competitorCountWithoutMaxPointReason) * 1.0;
} else if (maxPointsReason == MaxPointsReason.DSQ || maxPointsReason == MaxPointsReason.OCS) {
result = (12 - competitorCountWithoutMaxPointReason - 1) * 1.0;
} else {
result = 0.0;
}
return result;
}
}
@@ -24,7 +24,7 @@ public class HighPointFirstGets1LastBreaksTie extends HighPointFirstGetsFixedSco
@Override @Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace, public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, Integer numberOfCompetitorsInRace,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) { NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
return -1.0; return -1.0;
} }
@@ -57,7 +57,7 @@ public class LowPoint extends AbstractScoringSchemeImpl {
@Override @Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason, public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) { Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
Double result; Double result;
if (numberOfCompetitorsInRace == null || raceColumn.hasSplitFleetContiguousScoring()) { if (numberOfCompetitorsInRace == null || raceColumn.hasSplitFleetContiguousScoring()) {
result = (double) (numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsInLeaderboard()+1); result = (double) (numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsInLeaderboard()+1);
@@ -99,7 +99,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
protected void notifyListeners(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) { protected void notifyListeners(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) {
for (ScoreCorrectionListener listener : getScoreCorrectionListeners()) { for (ScoreCorrectionListener listener : getScoreCorrectionListeners()) {
listener.correctedScoreChanced(competitor, raceColumn, oldCorrectedScore, newCorrectedScore); listener.correctedScoreChanged(competitor, raceColumn, oldCorrectedScore, newCorrectedScore);
} }
} }
@@ -330,7 +330,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
} }
} }
private AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { protected AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
MaxPointsReason maxPointsReason = maxPointsReasons.get(raceColumn.getKey(competitor)); MaxPointsReason maxPointsReason = maxPointsReasons.get(raceColumn.getKey(competitor));
boolean maxPointsReasonExistsButIsNotApplicableForTimePoint; boolean maxPointsReasonExistsButIsNotApplicableForTimePoint;
if (maxPointsReason == null) { if (maxPointsReason == null) {
@@ -390,7 +390,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
if (correctedNonMaxedScore == null) { if (correctedNonMaxedScore == null) {
result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason.getMaxPointsReason(), result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason.getMaxPointsReason(),
getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher), getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher),
numberOfCompetitorsInLeaderboardFetcher); numberOfCompetitorsInLeaderboardFetcher, timePoint);
} else { } else {
result = correctedNonMaxedScore; result = correctedNonMaxedScore;
} }
@@ -69,7 +69,7 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
private static final long serialVersionUID = 915433462154943441L; private static final long serialVersionUID = 915433462154943441L;
@Override @Override
public void correctedScoreChanced(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) { public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) {
getScoreCorrection().notifyListeners(competitor, raceColumn, oldCorrectedScore, newCorrectedScore); getScoreCorrection().notifyListeners(competitor, raceColumn, oldCorrectedScore, newCorrectedScore);
} }
@@ -286,7 +286,6 @@ public class TopLevelMasterData implements Serializable {
for (RegattaAndRaceIdentifier raceIdentifier : mediaTrack.assignedRaces) { for (RegattaAndRaceIdentifier raceIdentifier : mediaTrack.assignedRaces) {
if (raceIdentitifiersForMediaExport.contains(raceIdentifier)) { if (raceIdentitifiersForMediaExport.contains(raceIdentifier)) {
filteredMediaTracks.add(mediaTrack); filteredMediaTracks.add(mediaTrack);
continue;
} }
} }
} }
@@ -18,4 +18,11 @@ import com.sap.sailing.domain.base.RaceDefinition;
@FunctionalInterface @FunctionalInterface
public interface DynamicRaceDefinitionSet { public interface DynamicRaceDefinitionSet {
void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace); void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace);
/**
* Called when the race has not or can not be loaded. A {@code reason} may be provided or may be left {@code null}.
* This default implementation does nothing. Implementing classes may choose, e.g., to stop all further efforts
* trying to track the race.
*/
default void raceNotLoaded(String reason) throws Exception {}
} }
@@ -9,8 +9,8 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
/** /**
* Centerpiece of a tracking adapter. A tracker is responsible for receiving tracking data for one or more * Centerpiece of a tracking adapter. A tracker is responsible for receiving tracking data for one
* {@link RaceDefinition races} that are {@link Regatta#getAllRaces() part of} a common {@link #getRegatta() Event}. Some * {@link RaceDefinition race} that is {@link Regatta#getAllRaces() part of} a common {@link #getRegatta() Event}. Some
* tracker architectures may not be able to deliver all data for the {@link RaceDefinition} when created or started. * tracker architectures may not be able to deliver all data for the {@link RaceDefinition} when created or started.
* Therefore, {@link #getRace()} may return <code>null</code> if the race information hasn't been received by the * Therefore, {@link #getRace()} may return <code>null</code> if the race information hasn't been received by the
* tracker yet. Through the {@link RaceHandle} returned by {@link #getRaceHandle()} it is also possible to perform a * tracker yet. Through the {@link RaceHandle} returned by {@link #getRaceHandle()} it is also possible to perform a
@@ -43,7 +43,7 @@ public interface RaceTracker {
static long TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS = 60000; static long TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS = 60000;
/** /**
* Stops tracking the races. * Stops tracking the race.
* *
* @param preemptive * @param preemptive
* if <code>false</code>, the tracker will continue to process data already received but will stop * if <code>false</code>, the tracker will continue to process data already received but will stop
@@ -55,7 +55,7 @@ public interface RaceTracker {
com.sap.sailing.domain.base.Regatta getRegatta(); com.sap.sailing.domain.base.Regatta getRegatta();
/** /**
* Returns the races being tracked by this tracker, in a "volatile" way, meaning that if another thread belonging to * Returns the race being tracked by this tracker, in a "volatile" way, meaning that if another thread belonging to
* this tracker has established the {@link RaceDefinition} for this tracker, other threads will immediately see this * this tracker has established the {@link RaceDefinition} for this tracker, other threads will immediately see this
* object. Non-blocking call that returns <code>null</code> if the {@link RaceDefinition} hasn't been created yet, * object. Non-blocking call that returns <code>null</code> if the {@link RaceDefinition} hasn't been created yet,
* e.g., because the course definition hasn't been received yet or the listener for receiving course information * e.g., because the course definition hasn't been received yet or the listener for receiving course information
@@ -25,7 +25,13 @@ public interface RaceTrackingConnectivityParameters {
* {@link Regatta} is provided, this will first look up a regatta for the race from * {@link Regatta} is provided, this will first look up a regatta for the race from
* {@link TrackedRegattaRegistry#getRememberedRegattaForRace(java.io.Serializable)} and if not found will look up or * {@link TrackedRegattaRegistry#getRememberedRegattaForRace(java.io.Serializable)} and if not found will look up or
* create a default regatta based on race data such as an event name and the boat class. * create a default regatta based on race data such as an event name and the boat class.
* @param timeoutInMilliseconds TODO *
* @param timeoutInMilliseconds
* gives the tracker a possibility to abort tracking the race after so many milliseconds of
* unsuccessfully waiting for the connection to be established. Support is optional for
* implementations, and there is no exact specification what must have happened before
* this timeout in order for tracking to continue. So, consider this as a "hint" to the
* tracker.
*/ */
RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception; RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception;
@@ -34,6 +34,9 @@ public interface RaceTrackingConnectivityParametersHandler {
* finding} this service instance in the first place. The object returned is equivalent to one that has * finding} this service instance in the first place. The object returned is equivalent to one that has
* produced {@code map} by passing it to this service's {@link #mapFrom(RaceTrackingConnectivityParameters)} * produced {@code map} by passing it to this service's {@link #mapFrom(RaceTrackingConnectivityParameters)}
* method. * method.
*
* @return {@code null} in case the object cannot be produced, e.g., because the leaderboard to which a
* race log-tracked race belonged has been deleted in the meantime.
*/ */
RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws Exception; RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws Exception;
@@ -16,13 +16,6 @@ import com.sap.sse.common.WithID;
public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix> public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix>
extends MappedTrack<ItemType, FixT> { extends MappedTrack<ItemType, FixT> {
/**
* Value names to be used when reading values from {@link SensorFix}es by calling {@link SensorFix#get(String)}.
*
* @return the value names that fixes contained in this track support
*/
Iterable<String> getValueNames();
void addListener(SensorFixTrackListener<ItemType, FixT> listener); void addListener(SensorFixTrackListener<ItemType, FixT> listener);
void removeListener(SensorFixTrackListener<ItemType, FixT> listener); void removeListener(SensorFixTrackListener<ItemType, FixT> listener);
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.Serializable; import java.io.Serializable;
import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix; import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.tracking.BravoFixTrack; import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
@@ -28,8 +27,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
* the name of the track by which it can be obtained from the {@link TrackedRace}. * the name of the track by which it can be obtained from the {@link TrackedRace}.
*/ */
public BravoFixTrackImpl(ItemType trackedItem, String trackName) { public BravoFixTrackImpl(ItemType trackedItem, String trackName) {
super(trackedItem, trackName, BravoSensorDataMetadata.INSTANCE.getFileColumns(), super(trackedItem, trackName, BravoFixTrack.TRACK_NAME + " for " + trackedItem);
BravoFixTrack.TRACK_NAME + " for " + trackedItem);
} }
@Override @Override
@@ -3,6 +3,8 @@ package com.sap.sailing.domain.tracking.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence;
@@ -13,6 +15,7 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster; import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
@@ -22,23 +25,62 @@ import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFixMoving> implements DynamicGPSFixTrack<ItemType, GPSFixMoving> { public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFixMoving> implements DynamicGPSFixTrack<ItemType, GPSFixMoving> {
private static final Logger logger = Logger.getLogger(DynamicGPSFixMovingTrackImpl.class.getName());
private static final long serialVersionUID = 9111448573301259784L; private static final long serialVersionUID = 9111448573301259784L;
private static final double MAX_SPEED_FACTOR_COMPARED_TO_MEASURED_SPEED_FOR_FILTERING = 2; private static final double MAX_SPEED_FACTOR_COMPARED_TO_MEASURED_SPEED_FOR_FILTERING = 2;
/**
* Uses lossy compaction of fixes. See {@link CompactPositionHelper}. Use {@link #DynamicGPSFixMovingTrackImpl(Object, long, boolean)}
* to configure lossless compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) { public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage); this(trackedItem, millisecondsOverWhichToAverage, /* losslessCompaction */ false);
} }
/** /**
* @param maxSpeedForSmoothening pass <code>null</code> if you don't want speed-based smoothening * Uses lossy compaction. Use {@link #DynamicGPSFixMovingTrackImpl(Object, long, Speed, boolean)} to configure
* lossless compaction.
*
* @param maxSpeedForSmoothening
* pass <code>null</code> if you don't want speed-based smoothening
*/ */
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) { public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) {
super(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening); this(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening, /* losslessCompaction */ false);
}
/**
* @param maxSpeedForSmoothening
* pass <code>null</code> if you don't want speed-based smoothening
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, maxSpeedForSmoothening, losslessCompaction);
}
/**
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
public DynamicGPSFixMovingTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, losslessCompaction);
} }
/** /**
@@ -57,7 +99,14 @@ public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<Item
@Override @Override
public boolean add(GPSFixMoving fix, boolean replace) { public boolean add(GPSFixMoving fix, boolean replace) {
return super.add(new CompactGPSFixMovingImpl(fix), replace); GPSFixMoving compactFix;
try {
compactFix = isLosslessCompaction() ? new PreciseCompactGPSFixMovingImpl(fix) : new VeryCompactGPSFixMovingImpl(fix);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINE, "Couldn't compact fix "+fix+" for track for "+getTrackedItem()+". Using losslessly-compacted fix instead.", e);
compactFix = new PreciseCompactGPSFixMovingImpl(fix);
}
return super.add(compactFix, replace);
} }
/** /**
@@ -1,14 +1,23 @@
package com.sap.sailing.domain.tracking.impl; package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFix> implements DynamicGPSFixTrack<ItemType, GPSFix> { public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType, GPSFix> implements DynamicGPSFixTrack<ItemType, GPSFix> {
private static final long serialVersionUID = 4035953954507697564L; private static final long serialVersionUID = 4035953954507697564L;
/**
* By default uses lossy compaction. See {@link CompactPositionHelper}.
*/
public DynamicGPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) { public DynamicGPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage); this(trackedItem, millisecondsOverWhichToAverage, /* losslessCompaction */ false);
}
public DynamicGPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
super(trackedItem, millisecondsOverWhichToAverage, losslessCompaction);
} }
public void addGPSFix(GPSFix gpsFix) { public void addGPSFix(GPSFix gpsFix) {
@@ -22,7 +31,8 @@ public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType,
@Override @Override
public boolean add(GPSFix fix, boolean replace) { public boolean add(GPSFix fix, boolean replace) {
return super.add(new CompactGPSFixImpl(fix), replace); return super.add(
isLosslessCompaction() ? new PreciseCompactGPSFixImpl(fix) : new VeryCompactGPSFixImpl(fix), replace);
} }
@Override @Override
@@ -273,6 +273,9 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
final TimePoint fixTimePoint = fix.getTimePoint(); final TimePoint fixTimePoint = fix.getTimePoint();
if (!onlyWhenInTrackingTimeInterval || isWithinStartAndEndOfTracking(fixTimePoint)) { if (!onlyWhenInTrackingTimeInterval || isWithinStartAndEndOfTracking(fixTimePoint)) {
getOrCreateTrack(mark).addGPSFix(fix); getOrCreateTrack(mark).addGPSFix(fix);
} else {
logger.finer(() -> "Dropped fix " + fix + " because it is outside the tracking interval "
+ getStartOfTracking() + ".." + getEndOfTracking());
} }
} }
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster; import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
@@ -34,6 +35,7 @@ import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.WithValidityCache; import com.sap.sailing.domain.common.tracking.WithValidityCache;
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.GPSTrackListener; import com.sap.sailing.domain.tracking.GPSTrackListener;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -45,7 +47,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.TimeRangeImpl; import com.sap.sse.common.impl.TimeRangeImpl;
import com.sap.sse.util.impl.ArrayListNavigableSet; import com.sap.sse.util.impl.ArrayListNavigableSet;
public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTrackImpl<ItemType, FixType> public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTrackImpl<ItemType, FixType>
implements GPSFixTrack<ItemType, FixType> { implements GPSFixTrack<ItemType, FixType> {
private static final Logger logger = Logger.getLogger(GPSFixTrackImpl.class.getName()); private static final Logger logger = Logger.getLogger(GPSFixTrackImpl.class.getName());
private static final long serialVersionUID = 2115321069242514378L; private static final long serialVersionUID = 2115321069242514378L;
@@ -121,8 +123,25 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
private int estimatedSpeedCacheHits; private int estimatedSpeedCacheHits;
private int estimatedSpeedCacheMisses; private int estimatedSpeedCacheMisses;
public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) { /**
this(trackedItem, millisecondsOverWhichToAverage, DEFAULT_MAX_SPEED_FOR_SMOOTHING); * If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely
* equal to their original, without rounding or range limitations different from those of the
* original {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that,
* however, has stricter limits on ranges and precisions. See {@link CompactPositionHelper} for
* details on lossy compaction.
*/
private final boolean losslessCompaction;
/**
* @param losslessCompaction
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely equal to
* their original, without rounding or range limitations different from those of the original
* {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that, however, has
* stricter limits on ranges and precisions. See {@link CompactPositionHelper} for details on lossy
* compaction.
*/
protected GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, boolean losslessCompaction) {
this(trackedItem, millisecondsOverWhichToAverage, DEFAULT_MAX_SPEED_FOR_SMOOTHING, losslessCompaction);
} }
/** /**
@@ -130,7 +149,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
* if <code>null</code>, any fix speed will be accepted as long as fix-provided speed (if any) and * if <code>null</code>, any fix speed will be accepted as long as fix-provided speed (if any) and
* inferred speed can be matched. * inferred speed can be matched.
*/ */
public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) { protected GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening, boolean losslessCompaction) {
super(trackedItem, /* nameForReadWriteLock */ GPSFixTrackImpl.class.getSimpleName() super(trackedItem, /* nameForReadWriteLock */ GPSFixTrackImpl.class.getSimpleName()
+ (trackedItem == null ? "" : (" for " + trackedItem.toString()))); + (trackedItem == null ? "" : (" for " + trackedItem.toString())));
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage; this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
@@ -139,8 +158,13 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTra
this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString()); this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString());
this.maxSpeedCache = createMaxSpeedCache(); this.maxSpeedCache = createMaxSpeedCache();
this.validityCachingSuspended = false; this.validityCachingSuspended = false;
this.losslessCompaction = losslessCompaction;
} }
protected boolean isLosslessCompaction() {
return losslessCompaction;
}
@Override @Override
public void suspendValidityCaching() { public void suspendValidityCaching() {
validityCachingSuspended = true; validityCachingSuspended = true;
@@ -21,21 +21,17 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
private static final long serialVersionUID = 6383421895429843002L; private static final long serialVersionUID = 6383421895429843002L;
private final Iterable<String> valueNames;
private final String trackName; private final String trackName;
private final TrackListenerCollection<ItemType, FixT, SensorFixTrackListener<ItemType, FixT>> listeners; private final TrackListenerCollection<ItemType, FixT, SensorFixTrackListener<ItemType, FixT>> listeners;
/** /**
* @param trackedItem the item this track is mapped to * @param trackedItem the item this track is mapped to
* @param trackName the name of the track by which it can be obtained from the {@link TrackedRace}. * @param trackName the name of the track by which it can be obtained from the {@link TrackedRace}.
* @param valueNames the name of the values that can be obtained by fixes contained in the track
* @param nameForReadWriteLock the name to use for the lock object that is used internally * @param nameForReadWriteLock the name to use for the lock object that is used internally
*/ */
public SensorFixTrackImpl(ItemType trackedItem, String trackName, Iterable<String> valueNames, public SensorFixTrackImpl(ItemType trackedItem, String trackName, String nameForReadWriteLock) {
String nameForReadWriteLock) {
super(trackedItem, nameForReadWriteLock); super(trackedItem, nameForReadWriteLock);
this.trackName = trackName; this.trackName = trackName;
this.valueNames = valueNames;
this.listeners = new TrackListenerCollection<>(); this.listeners = new TrackListenerCollection<>();
} }
@@ -57,11 +53,6 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
listeners.getListeners().forEach(notification); listeners.getListeners().forEach(notification);
} }
@Override
public Iterable<String> getValueNames() {
return valueNames;
}
@Override @Override
public void addListener(SensorFixTrackListener<ItemType, FixT> listener) { public void addListener(SensorFixTrackListener<ItemType, FixT> listener) {
this.listeners.addListener(listener); this.listeners.addListener(listener);
@@ -790,7 +790,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
boolean startOfTrackingFound = false; boolean startOfTrackingFound = false;
boolean endOfTrackingFound = false; boolean endOfTrackingFound = false;
// check race log // check race log
if (trackingTimesFromRaceLog != null){ if (trackingTimesFromRaceLog != null) {
if (trackingTimesFromRaceLog.getA() != null) { if (trackingTimesFromRaceLog.getA() != null) {
startOfTracking = trackingTimesFromRaceLog.getA().getTimePoint(); startOfTracking = trackingTimesFromRaceLog.getA().getTimePoint();
startOfTrackingFound = true; startOfTrackingFound = true;
@@ -971,8 +971,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
} }
startTime = newStartTime; startTime = newStartTime;
startTimeWithoutInferenceFromStartMarkPassings = newStartTimeWithoutInferenceFromStartMarkPassings; startTimeWithoutInferenceFromStartMarkPassings = newStartTimeWithoutInferenceFromStartMarkPassings;
}
} }
}
/** /**
* Calculates the end time of the race from the mark passings of the last course waypoint * Calculates the end time of the race from the mark passings of the last course waypoint
@@ -2852,8 +2852,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
} }
private <T extends Timed> List<T> extractInterval(TimePoint from, TimePoint to, List<T> listOfTimed) { private <T extends Timed> List<T> extractInterval(TimePoint from, TimePoint to, List<T> listOfTimed) {
List<T> result; List<T> result = new LinkedList<T>();
result = new LinkedList<T>();
for (T timed : listOfTimed) { for (T timed : listOfTimed) {
if (timed.getTimePoint().compareTo(from) >= 0 && timed.getTimePoint().compareTo(to) <= 0) { if (timed.getTimePoint().compareTo(from) >= 0 && timed.getTimePoint().compareTo(to) <= 0) {
result.add(timed); result.add(timed);
@@ -19,7 +19,10 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher; import com.sap.sailing.domain.common.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl; import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactWindImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager; import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory; import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.tracking.WindListener; import com.sap.sailing.domain.tracking.WindListener;
@@ -56,11 +59,23 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
*/ */
private transient Set<WindListener> listeners; private transient Set<WindListener> listeners;
/**
* If {@code true}, {@link Wind} fixes will be compacted in a way that they remain precisely
* equal to their original, without rounding or range limitations different from those of the
* original {@link Wind} fix. Lossy compaction, in contrast, may use a more compact form that,
* however, has stricter limits on ranges and precisions. See {@link CompactPositionHelper} for
* details on lossy compaction.
*/
private final boolean losslessCompaction;
public WindTrackImpl(long millisecondsOverWhichToAverage, boolean useSpeed, String nameForReadWriteLock) { public WindTrackImpl(long millisecondsOverWhichToAverage, boolean useSpeed, String nameForReadWriteLock) {
this(millisecondsOverWhichToAverage, DEFAULT_BASE_CONFIDENCE, useSpeed, nameForReadWriteLock); this(millisecondsOverWhichToAverage, DEFAULT_BASE_CONFIDENCE, useSpeed, nameForReadWriteLock);
} }
/** /**
* Uses lossy compaction. See {@link CompactPositionHelper}. Use {@link #WindTrackImpl(long, double, boolean, String, boolean)}
* to select lossless compaction.
*
* @param baseConfidence * @param baseConfidence
* the confidence to attribute to the raw wind fixes in this track * the confidence to attribute to the raw wind fixes in this track
* @param useSpeed * @param useSpeed
@@ -69,11 +84,29 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
* value for the speed * value for the speed
*/ */
public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence, boolean useSpeed, String nameForReadWriteLock) { public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence, boolean useSpeed, String nameForReadWriteLock) {
this(millisecondsOverWhichToAverage, baseConfidence, useSpeed, nameForReadWriteLock, /* losslessCompaction */ false);
}
/**
* Uses lossy compaction. See {@link CompactPositionHelper}.
*
* @param baseConfidence
* the confidence to attribute to the raw wind fixes in this track
* @param useSpeed
* whether the wind speed described by the fixes in this track are usable at all; example for an unusable
* wind speed would be that of an estimation that only estimates the wind direction and uses some default
* value for the speed
* @param losslessCompaction
* whether or not to use lossless compaction; see {@link CompactPositionHelper} for details on lossy
* compaction
*/
public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence, boolean useSpeed, String nameForReadWriteLock, boolean losslessCompaction) {
super(new ArrayListNavigableSet<Timed>(WindComparator.INSTANCE), nameForReadWriteLock); super(new ArrayListNavigableSet<Timed>(WindComparator.INSTANCE), nameForReadWriteLock);
this.baseConfidence = baseConfidence; this.baseConfidence = baseConfidence;
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage; this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
listeners = new HashSet<WindListener>(); listeners = new HashSet<WindListener>();
this.useSpeed = useSpeed; this.useSpeed = useSpeed;
this.losslessCompaction = losslessCompaction;
} }
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
@@ -114,12 +147,23 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
@Override @Override
public boolean add(Wind wind, boolean replace) { public boolean add(Wind wind, boolean replace) {
final CompactWindImpl compactWind = new CompactWindImpl(wind); final Wind compactWind = compactify(wind);
final boolean result = super.add(compactWind, replace); final boolean result = super.add(compactWind, replace);
notifyListenersAboutReceive(compactWind); notifyListenersAboutReceive(compactWind);
return result; return result;
} }
private Wind compactify(Wind wind) {
Wind compactWind;
try {
compactWind = losslessCompaction ? new PreciseCompactWindImpl(wind) : new VeryCompactWindImpl(wind);
} catch (CompactionNotPossibleException e) {
logger.log(Level.FINE, "Couldn't compact wind fix "+wind+". Using lossless compactification instead.", e);
compactWind = new PreciseCompactWindImpl(wind);
}
return compactWind;
}
private void notifyListenersAboutReceive(Wind wind) { private void notifyListenersAboutReceive(Wind wind) {
synchronized (listeners) { synchronized (listeners) {
for (WindListener listener : listeners) { for (WindListener listener : listeners) {
@@ -420,7 +464,8 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
public void remove(Wind wind) { public void remove(Wind wind) {
lockForWrite(); lockForWrite();
try { try {
getInternalRawFixes().remove(wind); // map through "compactify" to account for possible rounding / accuracy differences and still achieve "equals"
getInternalRawFixes().remove(compactify(wind));
} finally { } finally {
unlockAfterWrite(); unlockAfterWrite();
} }
+10 -10
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src"/> <classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/> <classpathentry kind="src" path="resources"/>
<classpathentry exported="true" kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/> <classpathentry exported="true" kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
<classpathentry exported="true" kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry exported="true" kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry exported="true" kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
@@ -4,7 +4,7 @@ Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.gwt.ui.test Bundle-SymbolicName: com.sap.sailing.gwt.ui.test
Bundle-Version: 1.0.0.qualifier Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Fragment-Host: com.sap.sailing.gwt.ui Fragment-Host: com.sap.sailing.gwt.ui
Require-Bundle: com.sap.sailing.server, Require-Bundle: com.sap.sailing.server,
com.sap.sailing.expeditionconnector, com.sap.sailing.expeditionconnector,
@@ -60,7 +60,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.AutoPlay"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.ui.YoutubePopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m -Dgwt.watchFileChanges=false"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m -Dgwt.watchFileChanges=false"/>
</launchConfiguration> </launchConfiguration>
@@ -3,7 +3,7 @@
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> <booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/> <stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> <booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/> <stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9876"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/> <booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES"> <listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/> <listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
@@ -69,7 +69,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false -Dgwt.watchFileChanges=false"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false -Dgwt.watchFileChanges=false"/>
</launchConfiguration> </launchConfiguration>
@@ -1,5 +1,7 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl; package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import java.util.logging.Logger;
import com.google.gwt.user.client.rpc.CustomFieldSerializer; import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamReader;
@@ -8,46 +10,54 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactWindImpl.CompactPosition; import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl.VeryCompactPosition;
import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException;
public final class CompactWindImpl { public final class CompactWindImpl {
public static final class CompactPosition_CustomFieldSerializer extends CustomFieldSerializer<CompactPosition> { private static final Logger logger = Logger.getLogger(CompactWindImpl.class.getName());
public static final class CompactPosition_CustomFieldSerializer extends CustomFieldSerializer<VeryCompactPosition> {
@Override @Override
public boolean hasCustomInstantiateInstance() { public boolean hasCustomInstantiateInstance() {
return true; return true;
} }
@Override @Override
public CompactPosition instantiateInstance(SerializationStreamReader streamReader) public VeryCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException { throws SerializationException {
return instantiate(streamReader); return instantiate(streamReader);
} }
public static CompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException { public static VeryCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble(); final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble(); final double lngDeg = streamReader.readDouble();
return (CompactPosition) new com.sap.sailing.domain.common.tracking.impl.CompactWindImpl(new WindImpl( try {
new DegreePosition(latDeg, lngDeg), /* timePoint */null, new KnotSpeedWithBearingImpl(0, return (VeryCompactPosition) new com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl(new WindImpl(
new DegreeBearingImpl(0)))).getPosition(); new DegreePosition(latDeg, lngDeg), /* timePoint */null, new KnotSpeedWithBearingImpl(0,
new DegreeBearingImpl(0)))).getPosition();
} catch (CompactionNotPossibleException e) {
logger.severe("Internal error: Cannot de-serialize compact position: "+e.getMessage()+"; throwing runtime exception");
throw new RuntimeException(e);
}
} }
@Override @Override
public void deserializeInstance(SerializationStreamReader streamReader, CompactPosition instance) public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactPosition instance)
throws SerializationException { throws SerializationException {
deserialize(streamReader, instance); deserialize(streamReader, instance);
} }
public static void deserialize(SerializationStreamReader streamReader, CompactPosition instance) { public static void deserialize(SerializationStreamReader streamReader, VeryCompactPosition instance) {
// handled by instantiate // handled by instantiate
} }
@Override @Override
public void serializeInstance(SerializationStreamWriter streamWriter, CompactPosition instance) public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException { throws SerializationException {
serialize(streamWriter, instance); serialize(streamWriter, instance);
} }
public static void serialize(SerializationStreamWriter streamWriter, CompactPosition instance) public static void serialize(SerializationStreamWriter streamWriter, VeryCompactPosition instance)
throws SerializationException { throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg()); streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg()); streamWriter.writeDouble(instance.getLngDeg());
@@ -0,0 +1,52 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl.PreciseCompactPosition;
public final class PreciseCompactGPSFixImpl {
public static final class PreciseCompactPosition_CustomFieldSerializer extends CustomFieldSerializer<PreciseCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public PreciseCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static PreciseCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
return (PreciseCompactPosition) new com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null).getPosition();
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, PreciseCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, PreciseCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
}
}
}
@@ -0,0 +1,52 @@
package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl.PreciseCompactPosition;
public final class PreciseCompactGPSFixMovingImpl {
public static final class PreciseCompactPosition_CustomFieldSerializer extends CustomFieldSerializer<PreciseCompactPosition> {
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public PreciseCompactPosition instantiateInstance(SerializationStreamReader streamReader)
throws SerializationException {
return instantiate(streamReader);
}
public static PreciseCompactPosition instantiate(SerializationStreamReader streamReader) throws SerializationException {
final double latDeg = streamReader.readDouble();
final double lngDeg = streamReader.readDouble();
return (PreciseCompactPosition) new com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl(
new DegreePosition(latDeg, lngDeg), /* timePoint */null, /* speedWithBearing */ null).getPosition();
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, PreciseCompactPosition instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, PreciseCompactPosition instance) {
// handled by instantiate
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, PreciseCompactPosition instance)
throws SerializationException {
streamWriter.writeDouble(instance.getLatDeg());
streamWriter.writeDouble(instance.getLngDeg());
}
}
}

Some files were not shown because too many files have changed in this diff Show More