e76cb09f1c313667c3b8af57da364d1c0a7c7172
.github/workflows/create-docker-image.yml
| ... | ... | @@ -17,6 +17,18 @@ jobs: |
| 17 | 17 | if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} |
| 18 | 18 | runs-on: ubuntu-latest |
| 19 | 19 | steps: |
| 20 | + - name: Convert repo name to lowercase GHCR package |
|
| 21 | + id: ghcr |
|
| 22 | + env: |
|
| 23 | + GITHUB_REPOSITORY: ${{ github.repository }} |
|
| 24 | + run: | |
|
| 25 | + # $GITHUB_REPOSITORY is in format owner/repo |
|
| 26 | + OWNER=${GITHUB_REPOSITORY%%/*} # extract owner |
|
| 27 | + REPO=${GITHUB_REPOSITORY##*/} # extract repo |
|
| 28 | + OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') # lowercase owner |
|
| 29 | + REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') # lowercase repo |
|
| 30 | + PACKAGE="$OWNER_LOWER/$REPO_LOWER" |
|
| 31 | + echo "PACKAGE=${PACKAGE}" >> $GITHUB_OUTPUT |
|
| 20 | 32 | - name: Checkout |
| 21 | 33 | uses: actions/checkout@v4 |
| 22 | 34 | with: |
| ... | ... | @@ -50,7 +62,7 @@ jobs: |
| 50 | 62 | - name: Download release |
| 51 | 63 | shell: bash |
| 52 | 64 | run: | |
| 53 | - RELEASE_TAR_GZ_FILENAME=$( configuration/github-download-release-assets.sh ${{ secrets.GITHUB_TOKEN }} ${{ env.RELEASE_PREFIX }} ) |
|
| 65 | + RELEASE_TAR_GZ_FILENAME=$( configuration/github-download-release-assets.sh ${{ secrets.GITHUB_TOKEN }} ${{ env.RELEASE_PREFIX }} ${{ github.repository }} ) |
|
| 54 | 66 | RELEASE=$( echo ${RELEASE_TAR_GZ_FILENAME} | sed -e 's/\.tar\.gz$//' ) |
| 55 | 67 | if [ -n ${RELEASE_TAR_GZ_FILENAME} ]; then |
| 56 | 68 | mv ${RELEASE_TAR_GZ_FILENAME} docker/ |
| ... | ... | @@ -60,7 +72,7 @@ jobs: |
| 60 | 72 | uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 |
| 61 | 73 | with: |
| 62 | 74 | build-args: RELEASE=${{ env.RELEASE }} |
| 63 | - tags: ghcr.io/sap/sailing-analytics:${{ env.RELEASE }}${{ env.BRANCH == 'main' && github.event.inputs.release == '' && ',ghcr.io/sap/sailing-analytics:latest' || env.BRANCH == 'docker-17' && github.event.inputs.release == '' && ',ghcr.io/sap/sailing-analytics:latest-17' || env.BRANCH == 'docker-21' && github.event.inputs.release == '' && ',ghcr.io/sap/sailing-analytics:latest-21' || env.BRANCH == 'docker-24' && github.event.inputs.release == '' && ',ghcr.io/sap/sailing-analytics:latest-24' || '' }} |
|
| 75 | + tags: ghcr.io/${{steps.ghcr.outputs.PACKAGE}}:${{ env.RELEASE }}${{ env.BRANCH == 'main' && github.event.inputs.release == '' && format(',ghcr.io/{0}:latest', steps.ghcr.outputs.PACKAGE) || env.BRANCH == 'docker-17' && github.event.inputs.release == '' && format(',ghcr.io/{0}:latest-17', steps.ghcr.outputs.PACKAGE) || env.BRANCH == 'docker-21' && github.event.inputs.release == '' && format(',ghcr.io/{0}:latest-21', steps.ghcr.outputs.PACKAGE) || env.BRANCH == 'docker-24' && github.event.inputs.release == '' && format(',ghcr.io/{0}:latest-24', steps.ghcr.outputs.PACKAGE) || '' }} |
|
| 64 | 76 | annotations: | |
| 65 | 77 | maintainer=axel.uhl@sap.com |
| 66 | 78 | index:org.opencontainers.image.title=Sailing Analytics |
.github/workflows/merge-main-into-docker-17.yml
| ... | ... | @@ -1,29 +0,0 @@ |
| 1 | -name: Merge main branch into docker-24 after successful build |
|
| 2 | -on: |
|
| 3 | - workflow_run: |
|
| 4 | - workflows: [release] |
|
| 5 | - types: [completed] |
|
| 6 | - branches: [main] |
|
| 7 | - workflow_dispatch: {} |
|
| 8 | -jobs: |
|
| 9 | - merge-main-into-docker-24: |
|
| 10 | - permissions: |
|
| 11 | - contents: write |
|
| 12 | - if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} |
|
| 13 | - runs-on: ubuntu-latest |
|
| 14 | - steps: |
|
| 15 | - - name: Checkout |
|
| 16 | - uses: actions/checkout@v4 |
|
| 17 | - with: |
|
| 18 | - ref: docker-24 |
|
| 19 | - fetch-depth: 0 # fetch the whole thing to make sure the histories merge |
|
| 20 | - - name: Merge main into docker-24 |
|
| 21 | - uses: devmasx/merge-branch@854d3ac71ed1e9deb668e0074781b81fdd6e771f # v1.4.0 |
|
| 22 | - env: |
|
| 23 | - GH_TOKEN: ${{ secrets.REPO_TOKEN_FOR_MERGE_AND_PUSH }} |
|
| 24 | - with: |
|
| 25 | - type: now |
|
| 26 | - from_branch: main |
|
| 27 | - target_branch: docker-24 |
|
| 28 | - message: Auto-merging main into docker-24 after successful release build |
|
| 29 | - github_token: ${{ secrets.REPO_TOKEN_FOR_MERGE_AND_PUSH }} |
.github/workflows/merge-main-into-docker-24.yml
| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | +name: Merge main branch into docker-24 after successful build |
|
| 2 | +on: |
|
| 3 | + workflow_run: |
|
| 4 | + workflows: [release] |
|
| 5 | + types: [completed] |
|
| 6 | + branches: [main] |
|
| 7 | + workflow_dispatch: {} |
|
| 8 | +jobs: |
|
| 9 | + merge-main-into-docker-24: |
|
| 10 | + permissions: |
|
| 11 | + contents: write |
|
| 12 | + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} |
|
| 13 | + runs-on: ubuntu-latest |
|
| 14 | + steps: |
|
| 15 | + - name: Checkout |
|
| 16 | + uses: actions/checkout@v4 |
|
| 17 | + with: |
|
| 18 | + ref: docker-24 |
|
| 19 | + fetch-depth: 0 # fetch the whole thing to make sure the histories merge |
|
| 20 | + - name: Merge main into docker-24 |
|
| 21 | + uses: devmasx/merge-branch@854d3ac71ed1e9deb668e0074781b81fdd6e771f # v1.4.0 |
|
| 22 | + env: |
|
| 23 | + GH_TOKEN: ${{ secrets.REPO_TOKEN_FOR_MERGE_AND_PUSH }} |
|
| 24 | + with: |
|
| 25 | + type: now |
|
| 26 | + from_branch: main |
|
| 27 | + target_branch: docker-24 |
|
| 28 | + message: Auto-merging main into docker-24 after successful release build |
|
| 29 | + github_token: ${{ secrets.REPO_TOKEN_FOR_MERGE_AND_PUSH }} |
.github/workflows/release.yml
| ... | ... | @@ -5,6 +5,7 @@ on: |
| 5 | 5 | - 'wiki/**' |
| 6 | 6 | - '.github/workflows/*' |
| 7 | 7 | - '**/*.md' |
| 8 | + - 'README.md' |
|
| 8 | 9 | workflow_dispatch: |
| 9 | 10 | inputs: |
| 10 | 11 | skip_tests: |
| ... | ... | @@ -14,8 +15,8 @@ on: |
| 14 | 15 | description: Build with a local target platform (set to "true" to use local target platform) |
| 15 | 16 | default: 'false' |
| 16 | 17 | runner_cpus: |
| 17 | - description: Default is 16, but can be 4, 8, and 64 as well |
|
| 18 | - default: '16' |
|
| 18 | + description: Empty means ubuntu-latest, usually 4 CPUs with little disk/memory, but if you have runners named ubuntu-latest-Xcpu, it can be 4, 8, 16, and 64 as well, using, e.g., ubuntu-latest-16cpu; default is ubuntu-latest |
|
| 19 | + default: '' |
|
| 19 | 20 | firefox_version: |
| 20 | 21 | description: Version to use for Selenium tests; default is 137.0, but could, e.g., be "141.0" or "latest" |
| 21 | 22 | default: '140.0' |
| ... | ... | @@ -25,16 +26,36 @@ jobs: |
| 25 | 26 | permissions: |
| 26 | 27 | contents: write |
| 27 | 28 | checks: write |
| 28 | - runs-on: ubuntu-latest-${{ github.event.inputs.runner_cpus == '' && '16' || github.event.inputs.runner_cpus }}cpu |
|
| 29 | + actions: read |
|
| 30 | + runs-on: ${{ github.event.inputs.runner_cpus != '' && format('ubuntu-latest-{0}cpu', github.event.inputs.runner_cpus) || 'ubuntu-latest' }} |
|
| 29 | 31 | steps: |
| 32 | + - name: Free Disk Space (Ubuntu) |
|
| 33 | + uses: jlumbroso/free-disk-space@v1.3.1 |
|
| 34 | + with: |
|
| 35 | + # this might remove tools that are actually needed, |
|
| 36 | + # if set to "true" but frees about 6 GB |
|
| 37 | + tool-cache: false |
|
| 38 | + # all of these default to true, but feel free to set to |
|
| 39 | + # "false" if necessary for your workflow |
|
| 40 | + android: false |
|
| 41 | + dotnet: true |
|
| 42 | + haskell: true |
|
| 43 | + large-packages: true |
|
| 44 | + docker-images: false |
|
| 45 | + swap-storage: true |
|
| 30 | 46 | - name: Allocate swap space |
| 31 | 47 | shell: bash |
| 32 | 48 | run: | |
| 33 | - sudo dd if=/dev/zero of=/mnt/large-swapfile bs=1G count=10 |
|
| 49 | + sudo dd if=/dev/zero of=/mnt/large-swapfile bs=1G count=30 |
|
| 34 | 50 | sudo chmod 600 /mnt/large-swapfile |
| 35 | 51 | sudo mkswap /mnt/large-swapfile |
| 36 | 52 | sudo chown root:root /mnt/large-swapfile |
| 37 | 53 | sudo swapon /mnt/large-swapfile |
| 54 | + - name: Create TMP space under /mnt/tmp |
|
| 55 | + shell: bash |
|
| 56 | + run: | |
|
| 57 | + sudo mkdir /mnt/tmp |
|
| 58 | + sudo chmod 777 /mnt/tmp |
|
| 38 | 59 | - name: Collect Workflow Telemetry |
| 39 | 60 | uses: catchpoint/workflow-telemetry-action@94c3c3d9567a0205de6da68a76c428ce4e769af1 # v2.0.0 |
| 40 | 61 | with: |
| ... | ... | @@ -79,10 +100,18 @@ jobs: |
| 79 | 100 | ports: '5672:5672' |
| 80 | 101 | - shell: bash |
| 81 | 102 | env: # Or as an environment variable |
| 82 | - APP_PARAMETERS: "-Daws.region=eu-west-1 -Dgoogle.maps.authenticationparams=${{ secrets.GOOGLE_MAPS_AUTHENTICATIONPARAMS }} -Daws.s3.test.s3AccessId=${{ secrets.AWS_S3_TEST_S3ACCESSID }} -Daws.s3.test.s3AccessKey=${{ secrets.AWS_S3_TEST_S3ACCESSKEY }} -Dgeonames.org.usernames=${{ secrets.GEONAMES_ORG_USERNAMES }}" |
|
| 103 | + TMP: /mnt/tmp |
|
| 104 | + AWS_S3_TEST_S3ACCESSID: ${{ secrets.AWS_S3_TEST_S3ACCESSID }} |
|
| 105 | + AWS_S3_TEST_S3ACCESSKEY: ${{ secrets.AWS_S3_TEST_S3ACCESSKEY }} |
|
| 106 | + GEONAMES_ORG_USERNAMES: ${{ secrets.GEONAMES_ORG_USERNAMES }} |
|
| 107 | + GOOGLE_MAPS_AUTHENTICATION_PARAMS: ${{ secrets.GOOGLE_MAPS_AUTHENTICATION_PARAMS }} |
|
| 108 | + APP_PARAMETERS: "-Daws.region=eu-west-1" |
|
| 83 | 109 | JAVA8_HOME: ${{env.JAVA_HOME_8_X64}} |
| 84 | 110 | run: | |
| 85 | - ./configuration/buildAndUpdateProduct.sh -x ${{ github.event.inputs.runner_cpus == '' && '16' || github.event.inputs.runner_cpus }} ${{ github.event.inputs.skip_tests == 'true' && '-t' || '' }} ${{ github.event.inputs.local_target_platform == 'true' && '-v' || '' }} build 2>&1 |
|
| 111 | + ./configuration/buildAndUpdateProduct.sh -x ${{ github.event.inputs.runner_cpus == '' && '4' || github.event.inputs.runner_cpus }} ${{ github.event.inputs.skip_tests == 'true' && '-t' || '' }} ${{ github.event.inputs.local_target_platform == 'true' && '-v' || '' }} build 2>&1 |
|
| 112 | + - name: show disk stats |
|
| 113 | + if: always() |
|
| 114 | + run: df -h |
|
| 86 | 115 | - name: Upload build log |
| 87 | 116 | uses: actions/upload-artifact@v4 |
| 88 | 117 | if: always() |
| ... | ... | @@ -156,7 +185,11 @@ jobs: |
| 156 | 185 | asset_name: release-notes.txt |
| 157 | 186 | asset_content_type: text/plain |
| 158 | 187 | - name: Trigger Hudson job |
| 159 | - if: always() |
|
| 188 | + env: |
|
| 189 | + HUDSON_JOB_USERNAME: ${{ secrets.HUDSON_JOB_USERNAME }} |
|
| 190 | + HUDSON_JOB_PASSWORD: ${{ secrets.HUDSON_JOB_PASSWORD }} |
|
| 191 | + HUDSON_JOB_TOKEN: ${{ secrets.HUDSON_JOB_TOKEN }} |
|
| 192 | + if: ${{ always() && env.HUDSON_JOB_USERNAME != '' && env.HUDSON_JOB_PASSWORD != '' && env.HUDSON_JOB_TOKEN != '' }} |
|
| 160 | 193 | shell: bash |
| 161 | 194 | run: | |
| 162 | 195 | if [ "${{ github.ref }}" = "refs/heads/main" ]; then |
Gemfile.lock
| ... | ... | @@ -70,7 +70,7 @@ GEM |
| 70 | 70 | faraday_middleware (1.2.1) |
| 71 | 71 | faraday (~> 1.0) |
| 72 | 72 | fastimage (2.4.0) |
| 73 | - fastlane (2.227.1) |
|
| 73 | + fastlane (2.228.0) |
|
| 74 | 74 | CFPropertyList (>= 2.3, < 4.0.0) |
| 75 | 75 | addressable (>= 2.8, < 3.0.0) |
| 76 | 76 | artifactory (~> 3.0) |
| ... | ... | @@ -134,12 +134,12 @@ GEM |
| 134 | 134 | google-apis-core (>= 0.11.0, < 2.a) |
| 135 | 135 | google-apis-storage_v1 (0.31.0) |
| 136 | 136 | google-apis-core (>= 0.11.0, < 2.a) |
| 137 | - google-cloud-core (1.8.0) |
|
| 137 | + google-cloud-core (1.7.1) |
|
| 138 | 138 | google-cloud-env (>= 1.0, < 3.a) |
| 139 | 139 | google-cloud-errors (~> 1.0) |
| 140 | 140 | google-cloud-env (1.6.0) |
| 141 | 141 | faraday (>= 0.17.3, < 3.0) |
| 142 | - google-cloud-errors (1.5.0) |
|
| 142 | + google-cloud-errors (1.4.0) |
|
| 143 | 143 | google-cloud-storage (1.47.0) |
| 144 | 144 | addressable (~> 2.8) |
| 145 | 145 | digest-crc (~> 0.4) |
| ... | ... | @@ -176,14 +176,14 @@ GEM |
| 176 | 176 | optparse (0.6.0) |
| 177 | 177 | os (1.1.4) |
| 178 | 178 | plist (3.7.2) |
| 179 | - public_suffix (6.0.1) |
|
| 179 | + public_suffix (5.1.1) |
|
| 180 | 180 | rake (13.2.1) |
| 181 | 181 | representable (3.2.0) |
| 182 | 182 | declarative (< 0.1.0) |
| 183 | 183 | trailblazer-option (>= 0.1.1, < 0.2.0) |
| 184 | 184 | uber (< 0.2.0) |
| 185 | 185 | retriable (3.1.2) |
| 186 | - rexml (3.4.1) |
|
| 186 | + rexml (3.4.2) |
|
| 187 | 187 | rouge (3.28.0) |
| 188 | 188 | ruby2_keywords (0.0.5) |
| 189 | 189 | rubyzip (2.4.1) |
| ... | ... | @@ -231,4 +231,4 @@ DEPENDENCIES |
| 231 | 231 | json (> 0) |
| 232 | 232 | |
| 233 | 233 | BUNDLED WITH |
| 234 | - 2.6.1 |
|
| 234 | + 2.4.21 |
Home.md
| ... | ... | @@ -62,6 +62,7 @@ SAP is at the center of today’s technology revolution, developing innovations |
| 62 | 62 | * [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]] |
| 63 | 63 | * [[Upgrading an EC2 image|wiki/info/landscape/upgrading-ec2-image]] |
| 64 | 64 | * [[Creating a webserver EC2 image from scratch|wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch]] |
| 65 | + * [[Upgrading Operating System Across Landscape|wiki/info/landscape/operating-system-upgrade]] |
|
| 65 | 66 | * [[EC2 mail relaying vs. Amazon Simple E-Mail Service (SES)|wiki/info/landscape/mail-relaying]] |
| 66 | 67 | * [[Establishing support@sapsailing.com with AWS SES, SNS, and Lambda|wiki/info/landscape/support-email]] |
| 67 | 68 | * [[Creating an EC2 image for a MongoDB Replica Set from scratch|wiki/info/landscape/creating-ec2-mongodb-image-from-scratch]] |
| ... | ... | @@ -157,10 +158,8 @@ SAP is at the center of today’s technology revolution, developing innovations |
| 157 | 158 | * [[Monitoring Apache and RabbitMQ|wiki/misc/monitoring-apache-and-rabbitmq]] |
| 158 | 159 | |
| 159 | 160 | ## Projects |
| 160 | -* [[Analytics on a stick|wiki/projects/analytics-on-a-stick]] |
|
| 161 | -* [[Consolidating User Stores (bug 4006 / 4018)|wiki/projects/consolidating-user-stores]] |
|
| 162 | -* [[Cloud Infrastructure Orchestration|wiki/projects/cloud-orchestrator]] |
|
| 163 | 161 | * [[Management Console for Easier Administration|wiki/howto/development/management-console]] |
| 162 | +* [[Cloud Infrastructure Orchestration|wiki/projects/cloud-orchestrator]] |
|
| 164 | 163 | |
| 165 | 164 | ## Events and Planning |
| 166 | 165 | * [[Project Planning (bigger development)|wiki/events/planning]] |
README.md
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | ## About this Project |
| 4 | 4 | |
| 5 | -The Sailing Analytics, formerly known as the "SAP Sailing Analytics," are a solution for portraying and analyzing sailing regattas, supporting training scenarios, and powering the vast archive at https://sapsailing.com. The solution consists of a cloud application with a web-based user interface, as well as three companion apps that integrate with the cloud application. This repository has the code for the cloud-based web application, and two of the three mobile apps (Buoy Pinger and Race Manager). The third companion app (Sail Insight) is found in [another repository](https://github.com/SailTracks/sailinsight). |
|
| 5 | +The Sailing Analytics, formerly known as the "SAP Sailing Analytics," are a solution for portraying and analyzing sailing regattas, supporting training scenarios, and powering the vast archive at https://sapsailing.com. The solution consists of a cloud application with a web-based user interface, as well as three companion apps that integrate with the cloud application. This repository has the code for the cloud-based web application, and two of the three mobile apps (Buoy Pinger and Race Manager). The third companion app (Sail Insight) is found in [another repository](https://github.com/SAP/sailing-analytics-sail-insight). |
|
| 6 | 6 | |
| 7 | 7 | ## Description |
| 8 | 8 | |
| ... | ... | @@ -32,11 +32,11 @@ Based on the ``docker/docker-compose.yml`` definition you should end up with thr |
| 32 | 32 | |
| 33 | 33 | Try a request to [``http://127.0.0.1:8888/index.html``](http://127.0.0.1:8888/index.html) or [``http://127.0.0.1:8888/gwt/status``](http://127.0.0.1:8888/gwt/status) to see if things worked. |
| 34 | 34 | |
| 35 | -To use Java 17, use the ``docker-compose-17.yml`` file instead: |
|
| 35 | +To use Java 24, use the ``docker-compose-24.yml`` file instead: |
|
| 36 | 36 | |
| 37 | 37 | ``` |
| 38 | 38 | cd docker |
| 39 | - docker-compose -f docker-compose-17.yml up |
|
| 39 | + docker-compose -f docker-compose-24.yml up |
|
| 40 | 40 | ``` |
| 41 | 41 | |
| 42 | 42 | ## Requirements |
| ... | ... | @@ -74,12 +74,123 @@ See [here](https://www.sapsailing.com/gwt/Home.html#/imprint/:) for a list of co |
| 74 | 74 | |
| 75 | 75 | ## Building and Running |
| 76 | 76 | |
| 77 | -This assumes you have completed the onboarding (see again [here](https://wiki.sapsailing.com/wiki/howto/onboarding)) successfully. To build, then invoke |
|
| 77 | +Builds usually run on [GitHub Actions](https://github.com/SAP/sailing-analytics/actions/workflows/release.yml) upon every push. A few repository secrets ensure that the build process has the permissions it needs. Pushes to the ``main``, ``docker-24`` and ``releases/*`` branches also publish a [release](https://github.com/SAP/sailing-analytics/releases) after a successful build. |
|
| 78 | + |
|
| 79 | +There are two options for building, detailed below; for both, you need to fulfill a few prerequisites. |
|
| 80 | + |
|
| 81 | +### Prerequisites |
|
| 82 | + |
|
| 83 | +If you have forked the repository and would like to run your own build, you will need the following prerequisites: |
|
| 84 | + |
|
| 85 | +#### Google Maps API Key |
|
| 86 | + |
|
| 87 | +Create or log on to your Google account and go to the [Google Cloud Console](https://console.cloud.google.com/apis/) and create an API key at least for the Maps JavaScript API. To later run the full product, while you're here, you can also create an API key for the YouTube Data API v3. |
|
| 88 | + |
|
| 89 | +#### AWS S3 Bucket for Upload Tests |
|
| 90 | + |
|
| 91 | +The build runs integration tests using the AWS API, requiring an access key with permission to upload to a bucket called ``sapsailing-automatic-upload-test``. Create that bucket in your AWS S3 environment. You can create an IAM user with CLI access and a permission policy that restricts write permissions to just that test bucket. You have to create the bucked in AWS region ``eu-west-1``. A permission policy for that user can look like this: |
|
| 92 | + |
|
| 93 | +``` |
|
| 94 | +{ |
|
| 95 | + "Version": "2012-10-17", |
|
| 96 | + "Statement": [ |
|
| 97 | + { |
|
| 98 | + "Sid": "Stmt1422015883000", |
|
| 99 | + "Effect": "Allow", |
|
| 100 | + "Action": [ |
|
| 101 | + "s3:AbortMultipartUpload", |
|
| 102 | + "s3:DeleteObject", |
|
| 103 | + "s3:DeleteObjectVersion", |
|
| 104 | + "s3:GetBucketAcl", |
|
| 105 | + "s3:GetBucketCORS", |
|
| 106 | + "s3:GetBucketLocation", |
|
| 107 | + "s3:GetBucketLogging", |
|
| 108 | + "s3:GetBucketNotification", |
|
| 109 | + "s3:GetBucketPolicy", |
|
| 110 | + "s3:GetBucketTagging", |
|
| 111 | + "s3:GetBucketVersioning", |
|
| 112 | + "s3:GetBucketWebsite", |
|
| 113 | + "s3:GetLifecycleConfiguration", |
|
| 114 | + "s3:GetObject", |
|
| 115 | + "s3:GetObjectAcl", |
|
| 116 | + "s3:GetObjectTorrent", |
|
| 117 | + "s3:GetObjectVersion", |
|
| 118 | + "s3:GetObjectVersionAcl", |
|
| 119 | + "s3:GetObjectVersionTorrent", |
|
| 120 | + "s3:ListAllMyBuckets", |
|
| 121 | + "s3:ListBucket", |
|
| 122 | + "s3:ListBucketMultipartUploads", |
|
| 123 | + "s3:ListBucketVersions", |
|
| 124 | + "s3:ListMultipartUploadParts", |
|
| 125 | + "s3:PutBucketLogging", |
|
| 126 | + "s3:PutBucketNotification", |
|
| 127 | + "s3:PutBucketPolicy", |
|
| 128 | + "s3:PutBucketTagging", |
|
| 129 | + "s3:PutBucketVersioning", |
|
| 130 | + "s3:PutBucketWebsite", |
|
| 131 | + "s3:PutLifecycleConfiguration", |
|
| 132 | + "s3:PutObject", |
|
| 133 | + "s3:PutObjectAcl", |
|
| 134 | + "s3:PutObjectVersionAcl", |
|
| 135 | + "s3:RestoreObject" |
|
| 136 | + ], |
|
| 137 | + "Resource": [ |
|
| 138 | + "arn:aws:s3:::sapsailing-automatic-upload-test/*", |
|
| 139 | + "arn:aws:s3:::sapsailing-automatic-upload-test" |
|
| 140 | + ] |
|
| 141 | + } |
|
| 142 | + ] |
|
| 143 | +} |
|
| 144 | +``` |
|
| 145 | + |
|
| 146 | +Create an access key for that user and note key ID and secret. |
|
| 147 | + |
|
| 148 | +#### Account(s) for geonames.org |
|
| 149 | + |
|
| 150 | +The build runs integration tests against [geonames.org](https://geonames.org). Use their login/sign-up form to create your user account and note its username. |
|
| 151 | + |
|
| 152 | +### Get the GitHub Actions build to work in your forked repository |
|
| 153 | + |
|
| 154 | +Assign the IDs and secrets from the prerequisites to repository secrets in your forked repository as follows: |
|
| 155 | + |
|
| 156 | +``` |
|
| 157 | +AWS_S3_TEST_S3ACCESSID: {your-S3-test-bucket-upload-token-ID} |
|
| 158 | +AWS_S3_TEST_S3ACCESSKEY: {key-for-your-S3-token} |
|
| 159 | +GEONAMES_ORG_USERNAMES: {comma-separated-list-of-geonames.org-usernames} |
|
| 160 | +GOOGLE_MAPS_AUTHENTICATION_PARAMS: key={your-Google-Maps-API-key} |
|
| 161 | +``` |
|
| 162 | + |
|
| 163 | +Then, manually trigger the ``release`` workflow with default options. You find the "Run workflow" drop-down in your forked repository under ``https://github.com/{your-github-user}/[your-repository-name}/actions/workflows/release.yml``. |
|
| 164 | + |
|
| 165 | +Release builds will trigger the ``create-docker-image`` workflow which will produce a Docker image of your release and publish it as a "ghcr" package in your repository. Note that package names are computed from the repository name by converting the latter to all lowercase characters. If you want to use your packages in the docker-compose configurations from the ``docker/`` folder, make sure to adjust the package name so it points to your own fork's package registry. |
|
| 166 | + |
|
| 167 | +If you want automatic validation of your changes to the ``main`` branch also for newer Java versions, you can use the ``merge-main-into-docker-24`` workflow. It requires another secret: |
|
| 168 | + |
|
| 169 | +``` |
|
| 170 | +REPO_TOKEN_FOR_MERGE_AND_PUSH: {a-GitHub-token-enabled-for-push} |
|
| 171 | +``` |
|
| 172 | + |
|
| 173 | +The workflow will launch automatically after a release has been performed for the ``main`` branch and will try to merge the latest ``main`` branch into ``docker-24`` and pushing the merge result if the merge was successful. This will then trigger a build for the ``docker-24`` branch which, if successul, will in turn produce a release and a docker image for use with Java 24. |
|
| 174 | + |
|
| 175 | +### Run a build locally |
|
| 176 | + |
|
| 177 | +This assumes you have completed the onboarding (see again [here](https://wiki.sapsailing.com/wiki/howto/onboarding)) successfully, including the Maven-specific parts such as having Maven installed (>= 3.9.x) and having a valid ``toolchains.xml`` file in your ~/.m2 folder. Furthermore, set and export the following environment variables: |
|
| 178 | + |
|
| 179 | +``` |
|
| 180 | +export AWS_S3_TEST_S3ACCESSID={your-S3-test-bucket-upload-token-ID} |
|
| 181 | +export AWS_S3_TEST_S3ACCESSKEY={key-for-your-S3-token} |
|
| 182 | +export GEONAMES_ORG_USERNAMES={comma-separated-list-of-geonames.org-usernames} |
|
| 183 | +export GOOGLE_MAPS_AUTHENTICATION_PARAMS=key={your-Google-Maps-API-key} |
|
| 184 | +export JAVA8_HOME={location-of-your-JDK8} |
|
| 185 | +export JAVA_HOME={location-of-your-JDK17-or-newer} |
|
| 186 | +``` |
|
| 187 | + |
|
| 188 | +To build, then invoke |
|
| 78 | 189 | |
| 79 | 190 | ``` |
| 80 | 191 | configuration/buildAndUpdateProduct.sh build |
| 81 | 192 | ``` |
| 82 | -This will build the Android companion apps first, then the web application. If the build was successful you can install the product locally by invoking |
|
| 193 | +This will build the Android companion apps first, then the web application. If you lack a proper Android SDK set-up, consider using the ``-a`` option to skip building the Android apps. If the build was successful you can install the product locally by invoking |
|
| 83 | 194 | |
| 84 | 195 | ``` |
| 85 | 196 | configuration/buildAndUpdateProduct.sh install [ -s <server-name> ] |
| ... | ... | @@ -99,9 +210,9 @@ Run the ``buildAndUpdateProduct.sh`` without any arguments to see the sub-comman |
| 99 | 210 | |
| 100 | 211 | ## Downloading, Installing and Running an Official Release |
| 101 | 212 | |
| 102 | -You need to have Java 8 installed. Get one from [here](https://tools.eu1.hana.ondemand.com/#cloud). Either ensure that this JVM's ``java`` executable in on the ``PATH`` or set ``JAVA_HOME`` appropriately. |
|
| 213 | +You need to have Java 8 installed. Get one from, e.g., [here](https://tools.eu1.hana.ondemand.com/#cloud). Either ensure that this JVM's ``java`` executable in on the ``PATH`` or set ``JAVA_HOME`` appropriately. |
|
| 103 | 214 | |
| 104 | -At [https://releases.sapsailing.com](https://releases.sapsailing.com) you find official product builds. To fetch and install one of them, make an empty directory, change into it and run the ``refreshInstance.sh`` command, e.g., like this: |
|
| 215 | +At [https://github.com/SAP/sailing-analytics/releases](https://github.com/SAP/sailing-analytics/releases) you find official product builds. To fetch and install one of them, make an empty directory, change into it and run the ``refreshInstance.sh`` command, e.g., like this: |
|
| 105 | 216 | ``` |
| 106 | 217 | mkdir sailinganalytics |
| 107 | 218 | cd sailinganalytics |
| ... | ... | @@ -113,7 +224,6 @@ This will download and install the latest release and configure it such that it |
| 113 | 224 | In addition to the necessary ``MONGODB_URI`` variable you may need to inject a few secrets into your runtime environment: |
| 114 | 225 | |
| 115 | 226 | - ``MANAGE2SAIL_ACCESS_TOKEN`` access token for result and regatta structure import from the Manage2Sail regatta management system |
| 116 | -- ``IGTIMI_CLIENT_ID`` / ``IGTIMI_CLIENT_SECRET`` credentials for ``igtimi.com`` in case you have one or more WindBot devices that you would like to integrate with |
|
| 117 | 227 | - ``GOOGLE_MAPS_AUTHENTICATION_PARAMS`` as in ``"key=..."`` or ``"client=..."``, required to display the Google Map in the race viewer. Obtain a Google Maps key from the Google Cloud Developer console, e.g., [here](https://console.cloud.google.com/apis/dashboard). |
| 118 | 228 | - ``YOUTUBE_API_KEY`` as in ``"key=..."``, required to analyze time stamps and durations of YouTube videos when linking to races. Obtain a YouTube API key from the Google Cloud Developer console, e.g., [here](https://console.cloud.google.com/apis/dashboard). |
| 119 | 229 |
REUSE.toml
| ... | ... | @@ -2,6 +2,7 @@ version = 1 |
| 2 | 2 | SPDX-PackageName = "sailing-analytics" |
| 3 | 3 | SPDX-PackageSupplier = "SAP Open Source Program Office ospo@sap.com" |
| 4 | 4 | SPDX-PackageDownloadLocation = "https://github.com/SAP/sailing-analytics" |
| 5 | +SPDX-PackageComment = "The code in this project may include calls to APIs (\"API Calls\") of\n SAP or third-party products or services developed outside of this project\n (\"External Products\").\n \"APIs\" means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project's code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." |
|
| 5 | 6 | |
| 6 | 7 | [[annotations]] |
| 7 | 8 | path = "java/com.google.gwt.servlet/**" |
configuration/buildAndUpdateProduct.sh
| ... | ... | @@ -222,7 +222,7 @@ echo TMP will be used for java.io.tmpdir and is $TMP |
| 222 | 222 | if [ "$TMP" = "" ]; then |
| 223 | 223 | export TMP=/tmp |
| 224 | 224 | fi |
| 225 | -extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP" |
|
| 225 | +extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP -Dgwt.workDir=$TMP" |
|
| 226 | 226 | extra="${extra} -Djdk.xml.maxGeneralEntitySizeLimit=0 -Djdk.xml.totalEntitySizeLimit=0" |
| 227 | 227 | |
| 228 | 228 | shift $((OPTIND-1)) |
configuration/environments_scripts/repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -28,7 +28,8 @@ if [ "${curl_exit_code}" = "0" ]; then |
| 28 | 28 | logger -t sailing "New SSH key changes for landscape managers (${last_change_millis} newer than ${PREVIOUS_CHANGE})" |
| 29 | 29 | if update_authorized_keys_for_landscape_managers "${BEARER_TOKEN}" "${BASE_URL}" "${LOGON_USER_HOME}" ; then |
| 30 | 30 | logger -t sailing "Updating SSH keys for landscape managers successful; updating ${LAST_CHANGE_FILE}" |
| 31 | - echo ${last_change_millis} >${LAST_CHANGE_FILE} |
|
| 31 | + # /var/run is writable only for root, so we need to be able to sudo: |
|
| 32 | + sudo bash -c "echo ${last_change_millis} >${LAST_CHANGE_FILE}" |
|
| 32 | 33 | else |
| 33 | 34 | logger -t sailing "Updating SSH keys for landscape managers failed with exit code $?; not updating ${LAST_CHANGE_FILE}" |
| 34 | 35 | fi |
configuration/environments_scripts/reverse_proxy/setup-disposable-reverse-proxy.sh
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | #!/bin/bash |
| 2 | 2 | |
| 3 | -# Setup script for Amazon Linux 2023. May need to update macro definitions for the archive IP. |
|
| 3 | +# Setup script for Amazon Linux 2023. Use 50g root partition. |
|
| 4 | +# May need to update macro definitions for the archive IP. |
|
| 4 | 5 | # Parameter 1 is the IP and parameter 2 is the bearer token to be installed in the root home dir. |
| 5 | 6 | # Ensure that the security for requesting the metadata uses IMDSv1 |
| 6 | 7 | if [[ "$#" -ne 2 ]]; then |
configuration/github-copy-release-to-sapsailing-com.sh
| ... | ... | @@ -17,7 +17,7 @@ |
| 17 | 17 | # produce false matches for the "main-" prefix. |
| 18 | 18 | BEARER_TOKEN="${1}" |
| 19 | 19 | RELEASE_NAME_PREFIX="${2}" |
| 20 | -RELEASE_TAR_GZ_FILE_NAME=$( `dirname "${0}"`/github-download-release-assets.sh "${BEARER_TOKEN}" "${RELEASE_NAME_PREFIX}" ) |
|
| 20 | +RELEASE_TAR_GZ_FILE_NAME=$( `dirname "${0}"`/github-download-release-assets.sh "${BEARER_TOKEN}" "${RELEASE_NAME_PREFIX}" SAP/sailing-analytics ) |
|
| 21 | 21 | if [ "${RELEASE_TAR_GZ_FILE_NAME}" != "" ]; then |
| 22 | 22 | RELEASE_NAME=$( echo ${RELEASE_TAR_GZ_FILE_NAME} | sed -e 's/^\(.*\)-\([0-9]*\).tar.gz$/\1/' ) |
| 23 | 23 | RELEASE_TIMESTAMP=$( echo ${RELEASE_TAR_GZ_FILE_NAME} | sed -e 's/^\(.*\)-\([0-9]*\).tar.gz$/\2/' ) |
configuration/github-download-release-assets.sh
| ... | ... | @@ -5,9 +5,9 @@ |
| 5 | 5 | # always be 0. |
| 6 | 6 | # |
| 7 | 7 | # Usage: |
| 8 | -# ./github-download-release-assets.sh {BEARER_TOKEN} {release-name-prefix} |
|
| 8 | +# ./github-download-release-assets.sh {BEARER_TOKEN} {release-name-prefix} {repository-name} |
|
| 9 | 9 | # For example: |
| 10 | -# ./github-download-release-assets.sh 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 main- |
|
| 10 | +# ./github-download-release-assets.sh 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 main- SAP/sailing-analytics |
|
| 11 | 11 | # which will download the latest release tar.gz and release-notes.txt of the main branch (main-xxxxxxxxxxx). |
| 12 | 12 | # Note the "-" at the end of the "main-" prefix specifier; this way we're making name |
| 13 | 13 | # clashes with releases whose name happens to start with "main" unlikely. This |
| ... | ... | @@ -15,7 +15,8 @@ |
| 15 | 15 | # produce false matches for the "main-" prefix. |
| 16 | 16 | BEARER_TOKEN="${1}" |
| 17 | 17 | RELEASE_NAME_PREFIX="${2}" |
| 18 | -RELEASES=$( curl -L -H 'Authorization: Bearer '${BEARER_TOKEN} https://api.github.com/repos/SAP/sailing-analytics/releases 2>/dev/null ) |
|
| 18 | +GITHUB_REPOSITORY="${3}" |
|
| 19 | +RELEASES=$( curl -L -H 'Authorization: Bearer '${BEARER_TOKEN} https://api.github.com/repos/${GITHUB_REPOSITORY}/releases 2>/dev/null ) |
|
| 19 | 20 | RELEASE_NOTES_TXT_ASSET_ID=$( echo "${RELEASES}" | jq -r 'sort_by(.published_at) | reverse | map(select(.name | startswith("'${RELEASE_NAME_PREFIX}'")))[0].assets[] | select(.content_type=="text/plain").id' 2>/dev/null) |
| 20 | 21 | if [ "$?" -ne "0" ]; then |
| 21 | 22 | echo "No release with prefix ${RELEASE_NAME_PREFIX} found. Not trying to download/upload anything." >&2 |
| ... | ... | @@ -26,7 +27,7 @@ else |
| 26 | 27 | RELEASE_TIMESTAMP=$( echo ${RELEASE_FULL_NAME} | sed -e 's/^\(.*\)-\([0-9]*\)$/\2/' ) |
| 27 | 28 | echo "Found release ${RELEASE_FULL_NAME} with name ${RELEASE_NAME} and time stamp ${RELEASE_TIMESTAMP}, notes ID is ${RELEASE_NOTES_TXT_ASSET_ID}, tarball ID is ${RELEASE_TAR_GZ_ASSET_ID}" >&2 |
| 28 | 29 | RELEASE_TAR_GZ_FILE_NAME="${RELEASE_FULL_NAME}.tar.gz" |
| 29 | - curl -o "${RELEASE_TAR_GZ_FILE_NAME}" -L -H 'Accept: application/octet-stream' -H 'Authorization: Bearer '${BEARER_TOKEN} 'https://api.github.com/repos/SAP/sailing-analytics/releases/assets/'${RELEASE_TAR_GZ_ASSET_ID} |
|
| 30 | - curl -o release-notes.txt -L -H 'Accept: application/octet-stream' -H 'Authorization: Bearer '${BEARER_TOKEN} 'https://api.github.com/repos/SAP/sailing-analytics/releases/assets/'${RELEASE_NOTES_TXT_ASSET_ID} |
|
| 30 | + curl -o "${RELEASE_TAR_GZ_FILE_NAME}" -L -H 'Accept: application/octet-stream' -H 'Authorization: Bearer '${BEARER_TOKEN} 'https://api.github.com/repos/'${GITHUB_REPOSITORY}'/releases/assets/'${RELEASE_TAR_GZ_ASSET_ID} |
|
| 31 | + curl -o release-notes.txt -L -H 'Accept: application/octet-stream' -H 'Authorization: Bearer '${BEARER_TOKEN} 'https://api.github.com/repos/'${GITHUB_REPOSITORY}'/releases/assets/'${RELEASE_NOTES_TXT_ASSET_ID} |
|
| 31 | 32 | echo "${RELEASE_TAR_GZ_FILE_NAME}" |
| 32 | 33 | fi |
configuration/github-download-workflow-artifacts.sh
| ... | ... | @@ -12,12 +12,13 @@ |
| 12 | 12 | # an exit status of 2 is returned. |
| 13 | 13 | BRANCH="${1}" |
| 14 | 14 | BEARER_TOKEN="${2}" |
| 15 | +GITHUB_REPOSITORY="${3}" |
|
| 15 | 16 | UNIX_TIME=$( date +%s ) |
| 16 | 17 | UNIX_DATE=$( date --iso-8601=second ) |
| 17 | 18 | UNIX_TIME_YESTERDAY=$(( UNIX_TIME - 10*24*3600 )) # look back ten days in time, trying to catch even re-runs of older jobs |
| 18 | 19 | DATE_YESTERDAY=$( date --iso-8601=second -d @${UNIX_TIME_YESTERDAY} ) |
| 19 | 20 | HEADERS_FILE=$( mktemp headersXXXXX ) |
| 20 | -NEXT_PAGE="https://api.github.com/repos/SAP/sailing-analytics/actions/runs?created=${DATE_YESTERDAY/+/%2B}..${UNIX_DATE/+/%2B}&per_page=100" |
|
| 21 | +NEXT_PAGE="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/runs?created=${DATE_YESTERDAY/+/%2B}..${UNIX_DATE/+/%2B}&per_page=100" |
|
| 21 | 22 | ARTIFACTS_JSON="" |
| 22 | 23 | LATEST_RUN_STARTED_AT="0000-00-00T00:00:00Z" |
| 23 | 24 | # Now go through the pages as long as we have a non-empty NEXT_PAGE URL and find the completed "release" workflow that was started last |
docker/makeImageForLatestRelease
| ... | ... | @@ -1,5 +1,11 @@ |
| 1 | 1 | #!/bin/bash |
| 2 | -release_prefix=$1 |
|
| 2 | +release_prefix="$1" |
|
| 3 | +GITHUB_REPOSITORY="$2" |
|
| 4 | +OWNER=${GITHUB_REPOSITORY%%/*} |
|
| 5 | +OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') |
|
| 6 | +REPO=${GITHUB_REPOSITORY##*/} |
|
| 7 | +REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') |
|
| 8 | +PACKAGE="$OWNER_LOWER/$REPO_LOWER" |
|
| 3 | 9 | GITROOT="`dirname $0`/.." |
| 4 | 10 | DOCKERDIR="${GITROOT}/docker" |
| 5 | 11 | DOCKERFILE="$DOCKERDIR/Dockerfile" |
| ... | ... | @@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then |
| 8 | 14 | release_prefix="main-" |
| 9 | 15 | fi |
| 10 | 16 | pushd "${DOCKERDIR}" |
| 11 | -RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" ) |
|
| 17 | +RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" ) |
|
| 12 | 18 | if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then |
| 13 | 19 | echo "No release with prefix ${release_prefix} found" >&2 |
| 14 | 20 | else |
| ... | ... | @@ -20,7 +26,7 @@ else |
| 20 | 26 | cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR" |
| 21 | 27 | cd "$DOCKERDIR" |
| 22 | 28 | docker buildx create --name=container --driver=docker-container --use --bootstrap |
| 23 | - docker buildx build --builder=container --platform=linux/amd64,linux/arm64 --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} --push . |
|
| 29 | + docker buildx build --builder=container --platform=linux/amd64,linux/arm64 --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} --push . |
|
| 24 | 30 | echo "Cleaning up..." |
| 25 | 31 | rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt |
| 26 | 32 | fi |
docker/makeImageForLatestRelease-arm
| ... | ... | @@ -1,5 +1,11 @@ |
| 1 | 1 | #!/bin/bash |
| 2 | -release_prefix=$1 |
|
| 2 | +release_prefix="$1" |
|
| 3 | +GITHUB_REPOSITORY="$2" |
|
| 4 | +OWNER=${GITHUB_REPOSITORY%%/*} |
|
| 5 | +OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') |
|
| 6 | +REPO=${GITHUB_REPOSITORY##*/} |
|
| 7 | +REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') |
|
| 8 | +PACKAGE="$OWNER_LOWER/$REPO_LOWER" |
|
| 3 | 9 | GITROOT="`dirname $0`/.." |
| 4 | 10 | DOCKERDIR="${GITROOT}/docker" |
| 5 | 11 | DOCKERFILE="$DOCKERDIR/Dockerfile" |
| ... | ... | @@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then |
| 8 | 14 | release_prefix="main-" |
| 9 | 15 | fi |
| 10 | 16 | pushd "${DOCKERDIR}" |
| 11 | -RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" ) |
|
| 17 | +RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" ) |
|
| 12 | 18 | if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then |
| 13 | 19 | echo "No release with prefix ${release_prefix} found" >&2 |
| 14 | 20 | else |
| ... | ... | @@ -19,13 +25,13 @@ else |
| 19 | 25 | cp "$GITROOT/java/target/start" "$DOCKERDIR" |
| 20 | 26 | cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR" |
| 21 | 27 | cd "$DOCKERDIR" |
| 22 | - docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release}-arm . |
|
| 28 | + docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release}-arm . |
|
| 23 | 29 | echo "Cleaning up..." |
| 24 | 30 | rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt |
| 25 | - docker push ghcr.io/sap/sailing-analytics:${release}-arm |
|
| 31 | + docker push ghcr.io/${PACKAGE}:${release}-arm |
|
| 26 | 32 | if [ "$SET_LATEST" = "1" ]; then |
| 27 | - docker tag ghcr.io/sap/sailing-analytics:${release}-arm ghcr.io/sap/sailing-analytics:latest-arm |
|
| 28 | - docker push ghcr.io/sap/sailing-analytics:latest-arm |
|
| 33 | + docker tag ghcr.io/${PACKAGE}:${release}-arm ghcr.io/${PACKAGE}:latest-arm |
|
| 34 | + docker push ghcr.io/${PACKAGE}:latest-arm |
|
| 29 | 35 | fi |
| 30 | 36 | fi |
| 31 | 37 | popd |
docker/makeImageForLatestRelease-on-sapmachine11
| ... | ... | @@ -1,5 +1,11 @@ |
| 1 | 1 | #!/bin/bash |
| 2 | -release_prefix=$1 |
|
| 2 | +release_prefix="$1" |
|
| 3 | +GITHUB_REPOSITORY="$2" |
|
| 4 | +OWNER=${GITHUB_REPOSITORY%%/*} |
|
| 5 | +OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') |
|
| 6 | +REPO=${GITHUB_REPOSITORY##*/} |
|
| 7 | +REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') |
|
| 8 | +PACKAGE="$OWNER_LOWER/$REPO_LOWER" |
|
| 3 | 9 | GITROOT="`dirname $0`/.." |
| 4 | 10 | DOCKERDIR="${GITROOT}/docker" |
| 5 | 11 | DOCKERFILE="$DOCKERDIR/Dockerfile" |
| ... | ... | @@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then |
| 8 | 14 | release_prefix="docker-11-" |
| 9 | 15 | fi |
| 10 | 16 | pushd "${DOCKERDIR}" |
| 11 | -RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" ) |
|
| 17 | +RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" ) |
|
| 12 | 18 | if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then |
| 13 | 19 | echo "No release with prefix ${release_prefix} found" >&2 |
| 14 | 20 | else |
| ... | ... | @@ -19,13 +25,13 @@ else |
| 19 | 25 | cp "$GITROOT/java/target/start" "$DOCKERDIR" |
| 20 | 26 | cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR" |
| 21 | 27 | cd "$DOCKERDIR" |
| 22 | - docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} -f Dockerfile_sapsailing_on_sapmachine11 . |
|
| 28 | + docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} -f Dockerfile_sapsailing_on_sapmachine11 . |
|
| 23 | 29 | echo "Cleaning up..." |
| 24 | 30 | rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt |
| 25 | - docker push ghcr.io/sap/sailing-analytics:${release} |
|
| 31 | + docker push ghcr.io/${PACKAGE}:${release} |
|
| 26 | 32 | if [ "$SET_LATEST" = "1" ]; then |
| 27 | - docker tag ghcr.io/sap/sailing-analytics:${release} ghcr.io/sap/sailing-analytics:latest-11 |
|
| 28 | - docker push ghcr.io/sap/sailing-analytics:latest-11 |
|
| 33 | + docker tag ghcr.io/${PACKAGE}:${release} ghcr.io/${PACKAGE}:latest-11 |
|
| 34 | + docker push ghcr.io/${PACKAGE}:latest-11 |
|
| 29 | 35 | fi |
| 30 | 36 | fi |
| 31 | 37 | popd |
docker/makeImageForLatestRelease-on-sapmachine17
| ... | ... | @@ -1,5 +1,11 @@ |
| 1 | 1 | #!/bin/bash |
| 2 | -release_prefix=$1 |
|
| 2 | +release_prefix="$1" |
|
| 3 | +GITHUB_REPOSITORY="$2" |
|
| 4 | +OWNER=${GITHUB_REPOSITORY%%/*} |
|
| 5 | +OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') |
|
| 6 | +REPO=${GITHUB_REPOSITORY##*/} |
|
| 7 | +REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') |
|
| 8 | +PACKAGE="$OWNER_LOWER/$REPO_LOWER" |
|
| 3 | 9 | GITROOT="`dirname $0`/.." |
| 4 | 10 | DOCKERDIR="${GITROOT}/docker" |
| 5 | 11 | DOCKERFILE="$DOCKERDIR/Dockerfile" |
| ... | ... | @@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then |
| 8 | 14 | release_prefix="docker-17-" |
| 9 | 15 | fi |
| 10 | 16 | pushd "${DOCKERDIR}" |
| 11 | -RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" ) |
|
| 17 | +RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" ) |
|
| 12 | 18 | if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then |
| 13 | 19 | echo "No release with prefix ${release_prefix} found" >&2 |
| 14 | 20 | else |
| ... | ... | @@ -19,13 +25,13 @@ else |
| 19 | 25 | cp "$GITROOT/java/target/start" "$DOCKERDIR" |
| 20 | 26 | cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR" |
| 21 | 27 | cd "$DOCKERDIR" |
| 22 | - docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} -f Dockerfile_sapsailing_on_sapmachine17 . |
|
| 28 | + docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} -f Dockerfile_sapsailing_on_sapmachine17 . |
|
| 23 | 29 | echo "Cleaning up..." |
| 24 | 30 | rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt |
| 25 | - docker push ghcr.io/sap/sailing-analytics:${release} |
|
| 31 | + docker push ghcr.io/${PACKAGE}:${release} |
|
| 26 | 32 | if [ "$SET_LATEST" = "1" ]; then |
| 27 | - docker tag ghcr.io/sap/sailing-analytics:${release} ghcr.io/sap/sailing-analytics:latest-17 |
|
| 28 | - docker push ghcr.io/sap/sailing-analytics:latest-17 |
|
| 33 | + docker tag ghcr.io/${PACKAGE}:${release} ghcr.io/${PACKAGE}:latest-17 |
|
| 34 | + docker push ghcr.io/${PACKAGE}:latest-17 |
|
| 29 | 35 | fi |
| 30 | 36 | fi |
| 31 | 37 | popd |
java/com.sap.sailing.aiagent/META-INF/MANIFEST.MF
| ... | ... | @@ -10,6 +10,10 @@ Automatic-Module-Name: com.sap.sailing.aiagent |
| 10 | 10 | Import-Package: com.sap.sailing.aiagent.interfaces, |
| 11 | 11 | com.sap.sailing.domain.abstractlog.race, |
| 12 | 12 | com.sap.sailing.domain.abstractlog.regatta, |
| 13 | + com.sap.sailing.domain.leaderboard, |
|
| 14 | + com.sap.sailing.domain.tracking, |
|
| 15 | + com.sap.sailing.domain.tracking.impl, |
|
| 16 | + com.sap.sailing.domain.shared.tracking, |
|
| 13 | 17 | com.sap.sse.common, |
| 14 | 18 | com.sap.sse.concurrent, |
| 15 | 19 | com.sap.sse.security.shared, |
java/com.sap.sailing.aiagent/src/com/sap/sailing/aiagent/impl/RaceListener.java
| ... | ... | @@ -27,7 +27,7 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 28 | 28 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 29 | 29 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 30 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 30 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 31 | 31 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 32 | 32 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 33 | 33 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startanalysis/StartAnalysisDTOFactory.java
| ... | ... | @@ -22,7 +22,7 @@ import com.sap.sailing.domain.common.NoWindException; |
| 22 | 22 | import com.sap.sailing.domain.common.Position; |
| 23 | 23 | import com.sap.sailing.domain.common.Wind; |
| 24 | 24 | import com.sap.sailing.domain.common.racelog.RacingProcedureType; |
| 25 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 25 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 26 | 26 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 27 | 27 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 28 | 28 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/util/actions/startlineadvantage/precalculation/StartlineAdvantageCalculationDataRetriever.java
| ... | ... | @@ -15,8 +15,8 @@ import com.sap.sailing.domain.common.confidence.BearingWithConfidence; |
| 15 | 15 | import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 17 | 17 | import com.sap.sailing.domain.polars.PolarDataService; |
| 18 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 18 | 19 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 19 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 20 | 20 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 21 | 21 | import com.sap.sse.common.Speed; |
| 22 | 22 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java
| ... | ... | @@ -29,15 +29,15 @@ import com.sap.sailing.domain.common.Tack; |
| 29 | 29 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 30 | 30 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 31 | 31 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 32 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 33 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 32 | 34 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 33 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 34 | 35 | import com.sap.sailing.domain.tracking.Maneuver; |
| 35 | 36 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 36 | 37 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 37 | 38 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 38 | 39 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 39 | 40 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 40 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 41 | 41 | import com.sap.sse.common.Bearing; |
| 42 | 42 | import com.sap.sse.common.Distance; |
| 43 | 43 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java
| ... | ... | @@ -34,8 +34,8 @@ import com.sap.sailing.domain.common.NoWindException; |
| 34 | 34 | import com.sap.sailing.domain.common.Position; |
| 35 | 35 | import com.sap.sailing.domain.common.WindSource; |
| 36 | 36 | import com.sap.sailing.domain.common.WindSourceType; |
| 37 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 38 | -import com.sap.sailing.domain.tracking.Track; |
|
| 37 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 38 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 39 | 39 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 40 | 40 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 41 | 41 | import com.sap.sailing.geocoding.ReverseGeocoder; |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java
| ... | ... | @@ -31,6 +31,7 @@ public enum BoatClassMasterdata { |
| 31 | 31 | BAVARIA_CRUISER_46 ("Bavaria Cruiser 46", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B46", "B 46", "BAVARIACRUISER46"), |
| 32 | 32 | BB10M ("BB 10m", true, 10.00, 2.30, BoatHullType.MONOHULL, true, "Dansk BB10M klub"), |
| 33 | 33 | BENETEAU_FIRST_35 ("Benetau First 35", true, 10.66, 3.636, BoatHullType.MONOHULL, true, "First 35"), |
| 34 | + BENETEAU_FIRST_36 ("Benetau First 36", true, 11.98, 3.80, BoatHullType.MONOHULL, true, "First 36"), |
|
| 34 | 35 | BENETEAU_FIRST_45 ("Benetau First 45", true, 13.68, 4.202, BoatHullType.MONOHULL, true, "First 45"), |
| 35 | 36 | BRASSFAHRT_I ("Brassfahrt I", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 1"), |
| 36 | 37 | BRASSFAHRT_II ("Brassfahrt II", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 2"), |
| ... | ... | @@ -49,6 +50,7 @@ public enum BoatClassMasterdata { |
| 49 | 50 | DELPHIA_24 ("Delphia 24", true, 7.70, 2.50, BoatHullType.MONOHULL, true, "Delphia 24 One Design", "Delphia 24 OD"), |
| 50 | 51 | DYAS("Dyas", true, 7.15, 1.95, BoatHullType.MONOHULL, true), |
| 51 | 52 | ELAN350("Elan 350", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan 350 Performance"), |
| 53 | + ELAN_E4("Elan E4", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan E4"), |
|
| 52 | 54 | EXTREME_40 ("Extreme 40", false, 12.2, 6.60, BoatHullType.CATAMARAN, true, "Extreme-40", "Extreme40", "ESS40", "ess"), |
| 53 | 55 | D_35 ("D35", false, 10.81, 6.89, BoatHullType.CATAMARAN, false), |
| 54 | 56 | ELLIOTT_6M ("Elliott 6m", true, 6.0, 2.35, BoatHullType.MONOHULL, true, "Elliott6m"), |
java/com.sap.sailing.domain.igtimiadapter.persistence/src/com/sap/sailing/domain/igtimiadapter/persistence/impl/DomainObjectFactoryImpl.java
| ... | ... | @@ -96,8 +96,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { |
| 96 | 96 | final Document query = new Document(); |
| 97 | 97 | appendMultiTimeRangeQuery(query, timeRanges); |
| 98 | 98 | query.append(FieldNames.IGTIMI_MESSAGES_DEVICE_SERIAL_NUMBER.name(), deviceSerialNumber); |
| 99 | - final Iterable<Document> queryResult = clientSessionOrNull == null ? messagesCollection.find(query) |
|
| 100 | - : messagesCollection.find(clientSessionOrNull, query); |
|
| 99 | + final Iterable<Document> queryResult = (clientSessionOrNull == null ? messagesCollection.find(query) |
|
| 100 | + : messagesCollection.find(clientSessionOrNull, query)).sort(Sorts.ascending(FieldNames.IGTIMI_MESSAGES_TIMESTAMP.name())); |
|
| 101 | 101 | result = Util.filter(Util.map(queryResult, |
| 102 | 102 | doc->{ |
| 103 | 103 | try { |
java/com.sap.sailing.domain.igtimiadapter.server/src/com/sap/sailing/domain/igtimiadapter/server/riot/RiotServer.java
| ... | ... | @@ -53,9 +53,9 @@ import com.sap.sse.replication.Replicable; |
| 53 | 53 | * can be registered with this server using the {@link #addListener(BulkFixReceiver)} method. |
| 54 | 54 | * <p> |
| 55 | 55 | * |
| 56 | - * When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver}, |
|
| 56 | + * When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver}), |
|
| 57 | 57 | * {@link IgtimiWindListener}s can be registered on the wind receiver, just as they can |
| 58 | - * for a websocket connection. |
|
| 58 | + * for a websocket connection.<p> |
|
| 59 | 59 | * |
| 60 | 60 | * The server uses <tt>java.nio</tt> and {@link ServerSocketChannel}s, avoiding the creation |
| 61 | 61 | * of a thread per connection.<p> |
java/com.sap.sailing.domain.igtimiadapter.server/src/com/sap/sailing/domain/igtimiadapter/server/riot/impl/RiotServerImpl.java
| ... | ... | @@ -337,24 +337,26 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl |
| 337 | 337 | // obtain the securityService only if connections exist; otherwise, we may still be in start-up mode |
| 338 | 338 | final SecurityService securityService = liveWebSocketConnections.isEmpty() ? null : getSecurityService(); |
| 339 | 339 | for (final RiotWebsocketHandler webSocketClient : liveWebSocketConnections) { |
| 340 | - final User user = webSocketClient.getAuthenticatedUser(); |
|
| 341 | - final OwnershipAnnotation deviceOwnership = securityService.getOwnership(device.getIdentifier()); |
|
| 342 | - final AccessControlListAnnotation deviceAccessControlList = securityService.getAccessControlList(device.getIdentifier()); |
|
| 343 | - if (!Util.isEmpty(Util.filter(daws, daw->{ |
|
| 344 | - final OwnershipAnnotation dawOownership = securityService.getOwnership(daw.getIdentifier()); |
|
| 345 | - final AccessControlListAnnotation dawAccessControlList = securityService.getAccessControlList(daw.getIdentifier()); |
|
| 346 | - return PermissionChecker.isPermitted( |
|
| 347 | - daw.getIdentifier().getPermission(DefaultActions.READ), |
|
| 340 | + if (webSocketClient.getDeviceSerialNumbers().contains(deviceSerialNumber)) { |
|
| 341 | + final User user = webSocketClient.getAuthenticatedUser(); |
|
| 342 | + final OwnershipAnnotation deviceOwnership = securityService.getOwnership(device.getIdentifier()); |
|
| 343 | + final AccessControlListAnnotation deviceAccessControlList = securityService.getAccessControlList(device.getIdentifier()); |
|
| 344 | + if (!Util.isEmpty(Util.filter(daws, daw->{ |
|
| 345 | + final OwnershipAnnotation dawOownership = securityService.getOwnership(daw.getIdentifier()); |
|
| 346 | + final AccessControlListAnnotation dawAccessControlList = securityService.getAccessControlList(daw.getIdentifier()); |
|
| 347 | + return PermissionChecker.isPermitted( |
|
| 348 | + daw.getIdentifier().getPermission(DefaultActions.READ), |
|
| 349 | + user, securityService.getAllUser(), |
|
| 350 | + dawOownership==null?null:dawOownership.getAnnotation(), |
|
| 351 | + dawAccessControlList==null?null:dawAccessControlList.getAnnotation()); |
|
| 352 | + })) |
|
| 353 | + && PermissionChecker.isPermitted( |
|
| 354 | + device.getIdentifier().getPermission(DefaultActions.READ), |
|
| 348 | 355 | user, securityService.getAllUser(), |
| 349 | - dawOownership==null?null:dawOownership.getAnnotation(), |
|
| 350 | - dawAccessControlList==null?null:dawAccessControlList.getAnnotation()); |
|
| 351 | - })) |
|
| 352 | - && PermissionChecker.isPermitted( |
|
| 353 | - device.getIdentifier().getPermission(DefaultActions.READ), |
|
| 354 | - user, securityService.getAllUser(), |
|
| 355 | - deviceOwnership==null?null:deviceOwnership.getAnnotation(), |
|
| 356 | - deviceAccessControlList==null?null:deviceAccessControlList.getAnnotation()) ) { |
|
| 357 | - webSocketClient.sendBytesByFuture(ByteBuffer.wrap(messageAsBytes)); |
|
| 356 | + deviceOwnership==null?null:deviceOwnership.getAnnotation(), |
|
| 357 | + deviceAccessControlList==null?null:deviceAccessControlList.getAnnotation()) ) { |
|
| 358 | + webSocketClient.sendBytesByFuture(ByteBuffer.wrap(messageAsBytes)); |
|
| 359 | + } |
|
| 358 | 360 | } |
| 359 | 361 | } |
| 360 | 362 | } |
java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/IgtimiFixTrackTest.java
| ... | ... | @@ -23,8 +23,8 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.AWS; |
| 23 | 23 | import com.sap.sailing.domain.igtimiadapter.datatypes.Fix; |
| 24 | 24 | import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM; |
| 25 | 25 | import com.sap.sailing.domain.igtimiadapter.datatypes.Type; |
| 26 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 27 | -import com.sap.sailing.domain.tracking.Track; |
|
| 28 | 28 | import com.sap.sse.common.TimePoint; |
| 29 | 29 | import com.sap.sse.common.Util; |
| 30 | 30 | import com.sap.sse.common.impl.DegreeBearingImpl; |
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnection.java
| ... | ... | @@ -13,9 +13,9 @@ import org.json.simple.parser.ParseException; |
| 13 | 13 | import com.igtimi.IgtimiStream.Msg; |
| 14 | 14 | import com.sap.sailing.domain.igtimiadapter.datatypes.Fix; |
| 15 | 15 | import com.sap.sailing.domain.igtimiadapter.datatypes.Type; |
| 16 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 16 | 17 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 17 | 18 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 18 | -import com.sap.sailing.domain.tracking.Track; |
|
| 19 | 19 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 20 | 20 | import com.sap.sse.common.TimePoint; |
| 21 | 21 | import com.sap.sse.security.shared.HasPermissions; |
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java
| ... | ... | @@ -188,8 +188,9 @@ public class IgtimiWindReceiver implements BulkFixReceiver { |
| 188 | 188 | } |
| 189 | 189 | |
| 190 | 190 | /** |
| 191 | - * Returns a wind fix produced out of an AWS/AWA and other fixes, as well as the fixes used for this, including a {@link BatteryLevel} |
|
| 192 | - * fix, if any was found, although this did technically not contribute to the production of the {@link Wind} object. |
|
| 191 | + * Returns a wind fix produced out of an AWS/AWA and other fixes, as well as the fixes used for this (in case of |
|
| 192 | + * interpolation between two fixes, the older one of the two is returned), including a {@link BatteryLevel} fix, if |
|
| 193 | + * any was found, although this did technically not contribute to the production of the {@link Wind} object. |
|
| 193 | 194 | */ |
| 194 | 195 | private Pair<Wind, Set<Fix>> getWind(final TimePoint timePoint, String deviceSerialNumber) throws ClassNotFoundException, IOException, ParseException { |
| 195 | 196 | final Wind result; |
| ... | ... | @@ -213,7 +214,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver { |
| 213 | 214 | final SpeedWithBearing apparentWindSpeedWithDirection = new KnotSpeedWithBearingImpl(aws.getKnots(), apparentWindDirection); |
| 214 | 215 | /* |
| 215 | 216 | * Hint from Brent Russell from Igtimi, at 2013-12-05 on the question whether to use GpsLatLong to |
| 216 | - * improve precision of boat speed / coarse over SOG/COG measurements: |
|
| 217 | + * improve precision of boat speed / course over SOG/COG measurements: |
|
| 217 | 218 | * |
| 218 | 219 | * "Personally I would use COG/SOG exclusively, and if unhappy with the result add a small amount of |
| 219 | 220 | * smoothing and consider dropping samples as outliers if they cause a SOG discontinuity. The latter |
| ... | ... | @@ -260,7 +261,11 @@ public class IgtimiWindReceiver implements BulkFixReceiver { |
| 260 | 261 | /** |
| 261 | 262 | * Searches for the last fix at or before {@code timePoint} in the given {@link trackToCleanUp}. If such a fix is |
| 262 | 263 | * found, adds the given fix to {@code fixesUsed} and clears all fixes from the {@code trackToCleanUp} that are |
| 263 | - * earlier than or at the {@code timePoint} given. |
|
| 264 | + * earlier than the {@code timePoint} given. This in particular preserves at least one live fix which will "hide" |
|
| 265 | + * older, e.g., buffered fixes that may be received when a WindBot device sends them out-of-order. For old, buffered |
|
| 266 | + * fixes this may still mean that could be dropped prior to being converted into a {@link Wind} object with an older |
|
| 267 | + * time stamp, but usually---and particularly with buffered data from previous days---this shouldn't be a problem. |
|
| 268 | + * Also, old data can still be imported at a later point in time. |
|
| 264 | 269 | * <p> |
| 265 | 270 | * |
| 266 | 271 | * This assumes that the {@code fix} passed has been "consumed" now, and earlier fixes will not be relevant anymore. |
| ... | ... | @@ -270,7 +275,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver { |
| 270 | 275 | final FixType fix = trackToCleanUp.getLastFixAtOrBefore(timePoint); |
| 271 | 276 | if (fix != null) { |
| 272 | 277 | fixesUsed.add(fix); |
| 273 | - trackToCleanUp.removeAllUpToAndIncluding(fix); |
|
| 278 | + trackToCleanUp.removeAllUpToExcluding(fix); |
|
| 274 | 279 | } |
| 275 | 280 | } |
| 276 | 281 |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java
| ... | ... | @@ -45,8 +45,8 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServi |
| 45 | 45 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 46 | 46 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 47 | 47 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 48 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 48 | 49 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 49 | -import com.sap.sailing.domain.tracking.Track; |
|
| 50 | 50 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
| 51 | 51 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 52 | 52 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
| ... | ... | @@ -68,10 +68,10 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 68 | 68 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager; |
| 69 | 69 | import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper; |
| 70 | 70 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 71 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 71 | 72 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 72 | 73 | import com.sap.sailing.domain.tracking.RaceHandle; |
| 73 | 74 | import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler; |
| 74 | -import com.sap.sailing.domain.tracking.Track; |
|
| 75 | 75 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 76 | 76 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 77 | 77 | import com.sap.sailing.server.impl.RacingEventServiceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java
| ... | ... | @@ -88,13 +88,13 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 88 | 88 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker; |
| 89 | 89 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 90 | 90 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 91 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 91 | 92 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 92 | 93 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 93 | 94 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 94 | 95 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 95 | 96 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 96 | 97 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 97 | -import com.sap.sailing.domain.tracking.Track; |
|
| 98 | 98 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 99 | 99 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 100 | 100 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java
| ... | ... | @@ -38,7 +38,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 38 | 38 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl; |
| 39 | 39 | import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker; |
| 40 | 40 | import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest; |
| 41 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 41 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 42 | 42 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 43 | 43 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 44 | 44 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java
| ... | ... | @@ -64,6 +64,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 64 | 64 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 65 | 65 | import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; |
| 66 | 66 | import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 67 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 67 | 68 | import com.sap.sailing.domain.tracking.AbstractRaceTrackerBaseImpl; |
| 68 | 69 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 69 | 70 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| ... | ... | @@ -73,7 +74,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 73 | 74 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 74 | 75 | import com.sap.sailing.domain.tracking.WindStore; |
| 75 | 76 | import com.sap.sailing.domain.tracking.WindTrack; |
| 76 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 77 | 77 | import com.sap.sse.common.Util; |
| 78 | 78 | import com.sap.sse.common.Util.Pair; |
| 79 | 79 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
| ... | ... | @@ -44,19 +44,19 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper; |
| 44 | 44 | import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 45 | 45 | import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory; |
| 46 | 46 | import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 47 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 48 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 47 | 49 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 48 | 50 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 49 | 51 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 50 | 52 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 51 | 53 | import com.sap.sailing.domain.tracking.Maneuver; |
| 52 | 54 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 53 | -import com.sap.sailing.domain.tracking.Track; |
|
| 54 | 55 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 55 | 56 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 56 | 57 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 57 | 58 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 58 | 59 | import com.sap.sailing.domain.tracking.impl.OutlierFilter; |
| 59 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 60 | 60 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 61 | 61 | import com.sap.sse.common.Duration; |
| 62 | 62 | import com.sap.sse.common.MultiTimeRange; |
java/com.sap.sailing.domain.shared.android/META-INF/MANIFEST.MF
| ... | ... | @@ -55,9 +55,9 @@ Export-Package: com.sap.sailing.domain.abstractlog, |
| 55 | 55 | com.sap.sailing.domain.racelogtracking, |
| 56 | 56 | com.sap.sailing.domain.racelogtracking.impl, |
| 57 | 57 | com.sap.sailing.domain.resultimport, |
| 58 | + com.sap.sailing.domain.shared.tracking, |
|
| 59 | + com.sap.sailing.domain.shared.tracking.impl, |
|
| 58 | 60 | com.sap.sailing.domain.statistics, |
| 59 | - com.sap.sailing.domain.statistics.impl, |
|
| 60 | - com.sap.sailing.domain.tracking, |
|
| 61 | - com.sap.sailing.domain.tracking.impl |
|
| 61 | + com.sap.sailing.domain.statistics.impl |
|
| 62 | 62 | Automatic-Module-Name: com.sap.sailing.domain.shared.android |
| 63 | 63 | Import-Package: com.sap.sse.security.shared |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLog.java
| ... | ... | @@ -7,7 +7,7 @@ import java.util.NavigableSet; |
| 7 | 7 | import java.util.UUID; |
| 8 | 8 | |
| 9 | 9 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 10 | -import com.sap.sailing.domain.tracking.Track; |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 11 | 11 | import com.sap.sse.common.TimePoint; |
| 12 | 12 | import com.sap.sse.common.WithID; |
| 13 | 13 |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
| ... | ... | @@ -24,9 +24,9 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 24 | 24 | import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventComparator; |
| 25 | 25 | import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; |
| 26 | 26 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 27 | -import com.sap.sailing.domain.tracking.Track; |
|
| 28 | -import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView; |
|
| 29 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 27 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 28 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 29 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 30 | 30 | import com.sap.sse.common.Timed; |
| 31 | 31 | import com.sap.sse.common.Util; |
| 32 | 32 | import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/LogEventComparator.java
| ... | ... | @@ -6,7 +6,7 @@ import java.util.Comparator; |
| 6 | 6 | import com.sap.sailing.domain.abstractlog.AbstractLogEvent; |
| 7 | 7 | import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 9 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 10 | 10 | import com.sap.sse.common.Timed; |
| 11 | 11 | import com.sap.sse.common.Util; |
| 12 | 12 |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java
| ... | ... | @@ -7,7 +7,7 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl; |
| 7 | 7 | import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 9 | 9 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 10 | -import com.sap.sailing.domain.tracking.Track; |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 11 | 11 | |
| 12 | 12 | public abstract class RaceLogAnalyzer<ResultType> extends BaseLogAnalyzer |
| 13 | 13 | <RaceLog, RaceLogEvent, RaceLogEventVisitor, ResultType> { |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/NoAddingRaceLogWrapper.java
| ... | ... | @@ -16,7 +16,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 16 | 16 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 17 | 17 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 18 | 18 | import com.sap.sailing.domain.common.abstractlog.NotRevokableException; |
| 19 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 19 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 20 | 20 | import com.sap.sse.common.Duration; |
| 21 | 21 | import com.sap.sse.common.TimePoint; |
| 22 | 22 | import com.sap.sse.common.scalablevalue.ScalableValue; |
| ... | ... | @@ -273,8 +273,8 @@ public class NoAddingRaceLogWrapper implements RaceLog { |
| 273 | 273 | |
| 274 | 274 | @Override |
| 275 | 275 | public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, |
| 276 | - com.sap.sailing.domain.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache, |
|
| 277 | - com.sap.sailing.domain.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) { |
|
| 276 | + com.sap.sailing.domain.shared.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache, |
|
| 277 | + com.sap.sailing.domain.shared.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) { |
|
| 278 | 278 | return innerRaceLog.getValueSum(from, to, nullElement, adder, cache, valueCalculator); |
| 279 | 279 | } |
| 280 | 280 | } |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java
| ... | ... | @@ -8,8 +8,8 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl; |
| 8 | 8 | import com.sap.sailing.domain.abstractlog.race.RaceLog; |
| 9 | 9 | import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; |
| 10 | 10 | import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; |
| 11 | -import com.sap.sailing.domain.tracking.Track; |
|
| 12 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | 15 | * {@link Track} implementation for {@link RaceLogEvent}s. |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java
| ... | ... | @@ -5,7 +5,7 @@ import java.util.Locale; |
| 5 | 5 | import java.util.Map; |
| 6 | 6 | import java.util.Set; |
| 7 | 7 | |
| 8 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 8 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 9 | 9 | import com.sap.sse.common.NamedWithID; |
| 10 | 10 | import com.sap.sse.common.Renamable; |
| 11 | 11 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java
| ... | ... | @@ -6,7 +6,7 @@ import java.util.UUID; |
| 6 | 6 | import com.sap.sailing.domain.base.EventBase; |
| 7 | 7 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 8 | 8 | import com.sap.sailing.domain.base.Venue; |
| 9 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 10 | 10 | import com.sap.sse.common.TimePoint; |
| 11 | 11 | |
| 12 | 12 | /** |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/AddResult.java
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import javax.swing.plaf.basic.BasicSliderUI.TrackListener; |
|
| 4 | + |
|
| 5 | +/** |
|
| 6 | + * The result of trying to add a fix to a {@link Track}. Used when notifying {@link TrackListener}s. |
|
| 7 | + * This allows listeners, in particular, to distinguish between the add and replace scenario. |
|
| 8 | + * |
|
| 9 | + * @author Axel Uhl (D043530) |
|
| 10 | + * |
|
| 11 | + */ |
|
| 12 | +public enum AddResult { |
|
| 13 | + NOT_ADDED, ADDED, REPLACED; |
|
| 14 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/FixAcceptancePredicate.java
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +/** |
|
| 4 | + * A predicate for a fix, for use in |
|
| 5 | + * {@link Track#getInterpolatedValue(com.sap.sse.common.TimePoint, com.sap.sse.common.Util.Function)}, used, e.g., to |
|
| 6 | + * provide a rule for when a fix shall be accepted during the search for surrounding fixes. |
|
| 7 | + * |
|
| 8 | + * @author Axel Uhl (d043530) |
|
| 9 | + * |
|
| 10 | + * @param <FixType> |
|
| 11 | + */ |
|
| 12 | +public interface FixAcceptancePredicate<FixType> { |
|
| 13 | + boolean isAcceptFix(FixType fix); |
|
| 14 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/LineDetails.java
| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.base.Mark; |
|
| 4 | +import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | +import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | +import com.sap.sailing.domain.common.Position; |
|
| 7 | +import com.sap.sse.common.Bearing; |
|
| 8 | +import com.sap.sse.common.Distance; |
|
| 9 | +import com.sap.sse.common.TimePoint; |
|
| 10 | + |
|
| 11 | +/** |
|
| 12 | + * For a line such as a start or a finish line, tells the line's length at a given time, which side is |
|
| 13 | + * {@link NauticalSide#PORT port} and which is {@link NauticalSide#STARBOARD starboard} when approaching the line, |
|
| 14 | + * and---if wind information is available---its angle to a true wind direction and the advantageous side in approaching |
|
| 15 | + * direction as well as how much the advantageous side is ahead. The wind-dependent information |
|
| 16 | + * will all be <code>null</code> if no wind data is available. |
|
| 17 | + * |
|
| 18 | + * @author Axel Uhl (d043530) |
|
| 19 | + * |
|
| 20 | + */ |
|
| 21 | +public interface LineDetails { |
|
| 22 | + TimePoint getTimePoint(); |
|
| 23 | + |
|
| 24 | + Waypoint getWaypoint(); |
|
| 25 | + |
|
| 26 | + Distance getLength(); |
|
| 27 | + |
|
| 28 | + Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); |
|
| 29 | + |
|
| 30 | + NauticalSide getAdvantageousSideWhileApproachingLine(); |
|
| 31 | + |
|
| 32 | + Mark getStarboardMarkWhileApproachingLine(); |
|
| 33 | + |
|
| 34 | + Mark getPortMarkWhileApproachingLine(); |
|
| 35 | + |
|
| 36 | + Distance getAdvantage(); |
|
| 37 | + |
|
| 38 | + Position getPortMarkPosition(); |
|
| 39 | + |
|
| 40 | + Position getStarboardMarkPosition(); |
|
| 41 | + |
|
| 42 | + default Mark getAdvantageousMark() { |
|
| 43 | + return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkWhileApproachingLine() : getStarboardMarkWhileApproachingLine(); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + default Position getAdvantageousMarkPosition() { |
|
| 47 | + return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkPosition() : getStarboardMarkPosition(); |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + default Bearing getBearingFromStarboardToPortWhenApproachingLine() { |
|
| 51 | + return getStarboardMarkPosition().getBearingGreatCircle(getPortMarkPosition()); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + default Bearing getBearingFromPortToStarboardWhenApproachingLine() { |
|
| 55 | + return getPortMarkPosition().getBearingGreatCircle(getStarboardMarkPosition()); |
|
| 56 | + } |
|
| 57 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/MappedTrack.java
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import com.sap.sse.common.Timed; |
|
| 4 | + |
|
| 5 | +/** |
|
| 6 | + * {@link Track} specialization, which is mapped to a specific type of items. |
|
| 7 | + * |
|
| 8 | + * @param <ItemType> |
|
| 9 | + * the type of item this track is mapped to |
|
| 10 | + * @param <FixType> |
|
| 11 | + * the type of fix that is contained in this track |
|
| 12 | + */ |
|
| 13 | +public interface MappedTrack<ItemType, FixType extends Timed> extends Track<FixType> { |
|
| 14 | + |
|
| 15 | + /** |
|
| 16 | + * @return the item this track is mapped to. |
|
| 17 | + */ |
|
| 18 | + ItemType getTrackedItem(); |
|
| 19 | + |
|
| 20 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/Track.java
| ... | ... | @@ -0,0 +1,259 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | +import java.util.concurrent.locks.ReadWriteLock; |
|
| 6 | +import java.util.function.Function; |
|
| 7 | + |
|
| 8 | +import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 9 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 10 | +import com.sap.sse.common.Duration; |
|
| 11 | +import com.sap.sse.common.TimePoint; |
|
| 12 | +import com.sap.sse.common.Timed; |
|
| 13 | +import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 14 | + |
|
| 15 | +/** |
|
| 16 | + * A track records {@link Timed} items for an object of type <code>ItemType</code>. It allows clients to ask for a value |
|
| 17 | + * close to a given {@link TimePoint}. The track manages a time-based set of raw fixes. An implementation may have an |
|
| 18 | + * understanding of how to eliminate outliers. For example, if a track implementation knows it's tracking boats, it may |
|
| 19 | + * consider fixes that the boat cannot possibly have reached due to its speed and direction change limitations as |
|
| 20 | + * outliers. The set of fixes with outliers filtered out can be obtained using {@link #getFixes} whereas |
|
| 21 | + * {@link #getRawFixes()} returns the unfiltered, raw fixes. If an implementation has no idea what an outlier is, |
|
| 22 | + * both methods will return the same fix sequence.<p> |
|
| 23 | + * |
|
| 24 | + * With tracks, concurrency is an important issue. Threads may want to modify a track while other threads may want to |
|
| 25 | + * read from it. Several methods such as {@link #getLastFixAtOrBefore(TimePoint)} return a single fix and can manage |
|
| 26 | + * concurrency internally. However, those methods returning a collection of fixes, such as {@link #getFixes()} or an |
|
| 27 | + * iterator over a collection of fixes, such as {@link #getFixesIterator(TimePoint, boolean)}, need special treatment. |
|
| 28 | + * Until we internalize such iterations (see bug 824, http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=824), |
|
| 29 | + * callers need to manage a read lock which is part of a {@link ReadWriteLock} managed by this track. Callers do so |
|
| 30 | + * by calling {@link #lockForRead} and {@link #unlockAfterRead}. |
|
| 31 | + * |
|
| 32 | + * @author Axel Uhl (d043530) |
|
| 33 | + */ |
|
| 34 | +public interface Track<FixType extends Timed> extends Serializable { |
|
| 35 | + /** |
|
| 36 | + * An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}. |
|
| 37 | + * |
|
| 38 | + * @author Axel Uhl (D043530) |
|
| 39 | + * |
|
| 40 | + * @param <T> |
|
| 41 | + */ |
|
| 42 | + static interface Adder<T> { |
|
| 43 | + /** |
|
| 44 | + * Adds two elements of type {@code T}. Neither argument must be {@code null}. |
|
| 45 | + */ |
|
| 46 | + T add(T t1, T t2); |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + static interface TimeRangeValueCalculator<T> { |
|
| 50 | + /** |
|
| 51 | + * Calculates a value for fixes across a time range. When the method is called, |
|
| 52 | + * a read lock will previously have been {@link Track#lockForRead obtained} before, |
|
| 53 | + * so an implementing class does not need to worry about acquiring the lock. |
|
| 54 | + */ |
|
| 55 | + T calculate(TimePoint from, TimePoint to); |
|
| 56 | + } |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * Locks this track for reading by the calling thread. If the thread already holds the lock for this track, |
|
| 60 | + * the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a <code>finally</code> |
|
| 61 | + * block to release the lock under all possible circumstances. Failure to do so will inevitably lead to |
|
| 62 | + * deadlocks! |
|
| 63 | + */ |
|
| 64 | + void lockForRead(); |
|
| 65 | + |
|
| 66 | + /** |
|
| 67 | + * Decrements the hold count for this track's read lock for the calling thread. If it goes to zero, the lock will be |
|
| 68 | + * released and other readers or a writer can obtain the lock. Make sure to call this method in a |
|
| 69 | + * <code>finally</code> block for each {@link #lockForRead()} invocation. |
|
| 70 | + */ |
|
| 71 | + void unlockAfterRead(); |
|
| 72 | + |
|
| 73 | + /** |
|
| 74 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 75 | + * will be thrown in case the caller has failed to do so. |
|
| 76 | + * |
|
| 77 | + * @return the smoothened fixes |
|
| 78 | + */ |
|
| 79 | + Iterable<FixType> getFixes(); |
|
| 80 | + |
|
| 81 | + /** |
|
| 82 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 83 | + * will be thrown in case the caller has failed to do so. |
|
| 84 | + * |
|
| 85 | + * @return The smoothened fixes between from and to. |
|
| 86 | + */ |
|
| 87 | + Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive); |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 91 | + * will be thrown in case the caller has failed to do so. |
|
| 92 | + */ |
|
| 93 | + Iterable<FixType> getRawFixes(); |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Returns <code>null</code> if no such fix exists. |
|
| 97 | + */ |
|
| 98 | + FixType getLastFixAtOrBefore(TimePoint timePoint); |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * Returns <code>null</code> if no such fix exists. |
|
| 102 | + */ |
|
| 103 | + FixType getLastFixBefore(TimePoint timePoint); |
|
| 104 | + |
|
| 105 | + /** |
|
| 106 | + * Returns <code>null</code> if no such fix exists. |
|
| 107 | + */ |
|
| 108 | + FixType getLastRawFixAtOrBefore(TimePoint timePoint); |
|
| 109 | + |
|
| 110 | + /** |
|
| 111 | + * Returns <code>null</code> if no such fix exists. |
|
| 112 | + */ |
|
| 113 | + FixType getFirstFixAtOrAfter(TimePoint timePoint); |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Returns <code>null</code> if no such fix exists. |
|
| 117 | + */ |
|
| 118 | + FixType getFirstRawFixAtOrAfter(TimePoint timePoint); |
|
| 119 | + |
|
| 120 | + /** |
|
| 121 | + * Returns <code>null</code> if no such fix exists. |
|
| 122 | + */ |
|
| 123 | + FixType getLastRawFixBefore(TimePoint timePoint); |
|
| 124 | + |
|
| 125 | + /** |
|
| 126 | + * Returns <code>null</code> if no such fix exists. |
|
| 127 | + */ |
|
| 128 | + FixType getFirstRawFixAfter(TimePoint timePoint); |
|
| 129 | + |
|
| 130 | + /** |
|
| 131 | + * Returns <code>null</code> if no such fix exists. |
|
| 132 | + */ |
|
| 133 | + FixType getFirstFixAfter(TimePoint timePoint); |
|
| 134 | + |
|
| 135 | + /** |
|
| 136 | + * The first fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 137 | + * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 138 | + */ |
|
| 139 | + FixType getFirstRawFix(); |
|
| 140 | + |
|
| 141 | + /** |
|
| 142 | + * The last fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 143 | + * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 144 | + */ |
|
| 145 | + FixType getLastRawFix(); |
|
| 146 | + |
|
| 147 | + /** |
|
| 148 | + * Interpolates an aspect of the fixes in this track for a given {@code timePoint}. If {@code timePoint} matches |
|
| 149 | + * exactly a fix in this track, that fix is used. If this track is empty, {@code null} is returned. If the |
|
| 150 | + * {@code timePoint} is after the last fix of this track, the last fix is used; if before the first fix, the first |
|
| 151 | + * fix is used. |
|
| 152 | + * <p> |
|
| 153 | + * |
|
| 154 | + * The fix(es) are converted to {@link ScalableValue}s using the {@code converter} which gives callers a choice |
|
| 155 | + * which aspect of the fixes to project and interpolate. If more than one value results because two fixes (one |
|
| 156 | + * before, one after) are used, linear interpolation based on the fixes' time points takes place. |
|
| 157 | + * <p> |
|
| 158 | + * |
|
| 159 | + * Example: for a track of {@link GPSFixMoving} fixes the course over ground shall be determined for a given time |
|
| 160 | + * point. The call would look like this: |
|
| 161 | + * {@code getInterpolatedValue(timePoint, f->new ScalableBearing(f.getSpeed().getBearing()))} |
|
| 162 | + * |
|
| 163 | + * @return the projected interpolated value, typed by what the {@link ScalableValue#divide(double)} method returns. |
|
| 164 | + */ |
|
| 165 | + <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 166 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter); |
|
| 167 | + |
|
| 168 | + /** |
|
| 169 | + * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 170 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator exclude outliers (see |
|
| 171 | + * also {@link #getFixes()} and returns the remaining fixes without any smoothening or dampening applied. |
|
| 172 | + * |
|
| 173 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 174 | + * will be thrown in case the caller has failed to do so. |
|
| 175 | + */ |
|
| 176 | + Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 177 | + |
|
| 178 | + /** |
|
| 179 | + * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 180 | + * <code>inclusive</code> is <code>true</code>) and that ends at the <code>endingAt</code> time point or just before |
|
| 181 | + * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the smoothened fixes (see |
|
| 182 | + * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 183 | + * |
|
| 184 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an |
|
| 185 | + * exception will be thrown in case the caller has failed to do so. |
|
| 186 | + * |
|
| 187 | + * @param startingAt |
|
| 188 | + * if <code>null</code>, starts with the first fix available |
|
| 189 | + * @param endingAt |
|
| 190 | + * if <code>null</code>., ends with the last fix available |
|
| 191 | + */ |
|
| 192 | + Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 193 | + |
|
| 194 | + /** |
|
| 195 | + * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 196 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 197 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 198 | + * |
|
| 199 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 200 | + * will be thrown in case the caller has failed to do so. |
|
| 201 | + */ |
|
| 202 | + Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 203 | + |
|
| 204 | + /** |
|
| 205 | + * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 206 | + * <code>startingAtInclusive</code> is <code>true</code>) and ending at the <code>endingAt</code> time point or just before |
|
| 207 | + * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the raw fixes (see also |
|
| 208 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 209 | + * |
|
| 210 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 211 | + * will be thrown in case the caller has failed to do so. |
|
| 212 | + */ |
|
| 213 | + Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 214 | + |
|
| 215 | + /** |
|
| 216 | + * Returns a descending iterator starting at the first fix before <code>startingAt</code> (or "at or before" in case |
|
| 217 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the smoothened fixes (see |
|
| 218 | + * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 219 | + * |
|
| 220 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 221 | + * will be thrown in case the caller has failed to do so. |
|
| 222 | + */ |
|
| 223 | + Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 224 | + |
|
| 225 | + /** |
|
| 226 | + * Returns a descending iterator starting at the first raw fix before <code>startingAt</code> (or "at or before" in case |
|
| 227 | + * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 228 | + * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 229 | + * |
|
| 230 | + * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 231 | + * will be thrown in case the caller has failed to do so. |
|
| 232 | + */ |
|
| 233 | + Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 234 | + |
|
| 235 | + /** |
|
| 236 | + * @return the average duration between two fixes (outliers removed) in this track or <code>null</code> if there is not |
|
| 237 | + * more than one fix in the track |
|
| 238 | + */ |
|
| 239 | + Duration getAverageIntervalBetweenFixes(); |
|
| 240 | + |
|
| 241 | + /** |
|
| 242 | + * @return the average duration between two fixes (outliers <em>not</em> removed) in this track or <code>null</code> if there is not |
|
| 243 | + * more than one raw fix in the track |
|
| 244 | + */ |
|
| 245 | + Duration getAverageIntervalBetweenRawFixes(); |
|
| 246 | + |
|
| 247 | + <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator); |
|
| 248 | + |
|
| 249 | + /** |
|
| 250 | + * @return the number of raw fixes contained in the Track. |
|
| 251 | + */ |
|
| 252 | + int size(); |
|
| 253 | + |
|
| 254 | + /** |
|
| 255 | + * Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty |
|
| 256 | + */ |
|
| 257 | + boolean isEmpty(); |
|
| 258 | + |
|
| 259 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/TrackingConnectorInfo.java
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | + |
|
| 2 | +package com.sap.sailing.domain.shared.tracking; |
|
| 3 | + |
|
| 4 | +import java.io.Serializable; |
|
| 5 | + |
|
| 6 | +/** |
|
| 7 | + * Identifies the tracking connector that was used to create a TrackedRace. |
|
| 8 | + * Further the Connector can provide a webUrl, that leads to an event web page. |
|
| 9 | + */ |
|
| 10 | +public interface TrackingConnectorInfo extends Serializable { |
|
| 11 | + |
|
| 12 | + /** |
|
| 13 | + * gets the name associated with the tracking technology used for the Race |
|
| 14 | + */ |
|
| 15 | + String getTrackingConnectorName(); |
|
| 16 | + |
|
| 17 | + /** |
|
| 18 | + * gets a {@link String} representation of the default web-URL associated with the tracking technology used for the Race. |
|
| 19 | + * may be {@code null} if there is none provided in the adapter. |
|
| 20 | + */ |
|
| 21 | + String getTrackingConnectorDefaultUrl(); |
|
| 22 | + |
|
| 23 | + /** |
|
| 24 | + * gets a {@link String} representation of the web-URL associated with the Event. |
|
| 25 | + * may be {@code null} if the API of the respective Tracking-Service does not provide a URL. |
|
| 26 | + */ |
|
| 27 | + String getWebUrl(); |
|
| 28 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/LineDetailsImpl.java
| ... | ... | @@ -0,0 +1,91 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.base.Mark; |
|
| 4 | +import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | +import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | +import com.sap.sailing.domain.common.Position; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 8 | +import com.sap.sse.common.Bearing; |
|
| 9 | +import com.sap.sse.common.Distance; |
|
| 10 | +import com.sap.sse.common.TimePoint; |
|
| 11 | + |
|
| 12 | +public class LineDetailsImpl implements LineDetails { |
|
| 13 | + private final TimePoint timePoint; |
|
| 14 | + private final Waypoint waypoint; |
|
| 15 | + private final Distance length; |
|
| 16 | + private final Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 17 | + private final NauticalSide advantageousSidewhileApproachingLine; |
|
| 18 | + private final Distance advantage; |
|
| 19 | + private final Mark portMarkWhileApproachingLine; |
|
| 20 | + private final Mark starboardMarkWhileApproachingLine; |
|
| 21 | + private final Position portMarkPosition; |
|
| 22 | + private final Position starboardMarkPosition; |
|
| 23 | + |
|
| 24 | + public LineDetailsImpl(TimePoint timePoint, Waypoint waypoint, Distance length, |
|
| 25 | + Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind, NauticalSide advantageousSideWhileApproachingLine, |
|
| 26 | + Distance advantage, Mark portMarkWhileApproachingLine, Mark starboardMarkWhileApproachingLine, |
|
| 27 | + Position portMarkPosition, Position starboardMarkPosition) { |
|
| 28 | + super(); |
|
| 29 | + this.timePoint = timePoint; |
|
| 30 | + this.waypoint = waypoint; |
|
| 31 | + this.length = length; |
|
| 32 | + this.angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 33 | + this.advantageousSidewhileApproachingLine = advantageousSideWhileApproachingLine; |
|
| 34 | + this.advantage = advantage; |
|
| 35 | + this.portMarkWhileApproachingLine = portMarkWhileApproachingLine; |
|
| 36 | + this.starboardMarkWhileApproachingLine = starboardMarkWhileApproachingLine; |
|
| 37 | + this.portMarkPosition = portMarkPosition; |
|
| 38 | + this.starboardMarkPosition = starboardMarkPosition; |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | + @Override |
|
| 42 | + public TimePoint getTimePoint() { |
|
| 43 | + return timePoint; |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + @Override |
|
| 47 | + public Waypoint getWaypoint() { |
|
| 48 | + return waypoint; |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + @Override |
|
| 52 | + public Distance getLength() { |
|
| 53 | + return length; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + @Override |
|
| 57 | + public Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind() { |
|
| 58 | + return angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + @Override |
|
| 62 | + public NauticalSide getAdvantageousSideWhileApproachingLine() { |
|
| 63 | + return advantageousSidewhileApproachingLine; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + @Override |
|
| 67 | + public Distance getAdvantage() { |
|
| 68 | + return advantage; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + @Override |
|
| 72 | + public Mark getStarboardMarkWhileApproachingLine() { |
|
| 73 | + return starboardMarkWhileApproachingLine; |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public Mark getPortMarkWhileApproachingLine() { |
|
| 78 | + return portMarkWhileApproachingLine; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + @Override |
|
| 82 | + public Position getPortMarkPosition() { |
|
| 83 | + return portMarkPosition; |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + @Override |
|
| 87 | + public Position getStarboardMarkPosition() { |
|
| 88 | + return starboardMarkPosition; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/MappedTrackImpl.java
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 4 | +import com.sap.sse.common.Timed; |
|
| 5 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 6 | + |
|
| 7 | +/** |
|
| 8 | + * Default implementation of {@link MappedTrack} interface. |
|
| 9 | + * |
|
| 10 | + * @param <ItemType> |
|
| 11 | + * the type of item this track is mapped to |
|
| 12 | + * @param <FixType> |
|
| 13 | + * the type of fix that is contained in this track |
|
| 14 | + */ |
|
| 15 | +public class MappedTrackImpl<ItemType, FixType extends Timed> extends TrackImpl<FixType> |
|
| 16 | + implements MappedTrack<ItemType, FixType> { |
|
| 17 | + |
|
| 18 | + private static final long serialVersionUID = 6165693342087329096L; |
|
| 19 | + |
|
| 20 | + private final ItemType trackedItem; |
|
| 21 | + |
|
| 22 | + /** @see TrackImpl#TrackImpl(String) */ |
|
| 23 | + public MappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) { |
|
| 24 | + super(nameForReadWriteLock); |
|
| 25 | + this.trackedItem = trackedItem; |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + /** @see TrackImpl#TrackImpl(ArrayListNavigableSet, String) */ |
|
| 29 | + protected MappedTrackImpl(ItemType trackedItem, ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 30 | + super(fixes, nameForReadWriteLock); |
|
| 31 | + this.trackedItem = trackedItem; |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + @Override |
|
| 35 | + public ItemType getTrackedItem() { |
|
| 36 | + return trackedItem; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/PartialNavigableSetView.java
| ... | ... | @@ -0,0 +1,387 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.util.ArrayList; |
|
| 4 | +import java.util.Collection; |
|
| 5 | +import java.util.Comparator; |
|
| 6 | +import java.util.Iterator; |
|
| 7 | +import java.util.List; |
|
| 8 | +import java.util.NavigableSet; |
|
| 9 | +import java.util.NoSuchElementException; |
|
| 10 | +import java.util.SortedSet; |
|
| 11 | +import java.util.TreeSet; |
|
| 12 | + |
|
| 13 | +/** |
|
| 14 | + * A view on a {@link NavigableSet} which suppresses some entries based on some configurable rule. |
|
| 15 | + * The {@link #size()} operation is expensive because it requires a full scan. {@link #isEmpty()} is |
|
| 16 | + * much cheaper because it suffices to find one element passing the filter rule. The filtering rule |
|
| 17 | + * has to be expressed by subclsses implementing the {@link #isValid(Object)} method. |
|
| 18 | + * |
|
| 19 | + * @author Axel Uhl (d043530) |
|
| 20 | + * |
|
| 21 | + * @param <E> |
|
| 22 | + */ |
|
| 23 | +public abstract class PartialNavigableSetView<E> implements NavigableSet<E> { |
|
| 24 | + private final NavigableSet<E> set; |
|
| 25 | + |
|
| 26 | + private class PartialNavigableSetViewWithSameValidityAsEnclosing extends PartialNavigableSetView<E> { |
|
| 27 | + public PartialNavigableSetViewWithSameValidityAsEnclosing(NavigableSet<E> set) { |
|
| 28 | + super(set); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + @Override |
|
| 32 | + protected boolean isValid(E e) { |
|
| 33 | + return PartialNavigableSetView.this.isValid(e); |
|
| 34 | + } |
|
| 35 | + } |
|
| 36 | + |
|
| 37 | + private class FilteringIterator implements Iterator<E> { |
|
| 38 | + /** |
|
| 39 | + * The iterator is always kept one step "ahead" in order to know whether there really is a next element. The |
|
| 40 | + * next valid element is fetched and stored in {@link #nextValid} and {@link #hasNext} is set to |
|
| 41 | + * <code>true</code>. |
|
| 42 | + */ |
|
| 43 | + private Iterator<E> iter; |
|
| 44 | + |
|
| 45 | + private E nextValid; |
|
| 46 | + |
|
| 47 | + private boolean hasNext; |
|
| 48 | + |
|
| 49 | + private boolean hasLastNext; |
|
| 50 | + |
|
| 51 | + private E lastNext; |
|
| 52 | + |
|
| 53 | + public FilteringIterator() { |
|
| 54 | + iter = getSet().iterator(); |
|
| 55 | + hasLastNext = false; |
|
| 56 | + advance(); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + private void advance() { |
|
| 60 | + if (iter.hasNext()) { |
|
| 61 | + E next = iter.next(); |
|
| 62 | + while (!isValid(next) && iter.hasNext()) { |
|
| 63 | + next = iter.next(); |
|
| 64 | + } |
|
| 65 | + if (isValid(next)) { |
|
| 66 | + nextValid = next; |
|
| 67 | + hasNext = true; |
|
| 68 | + } else { |
|
| 69 | + hasNext = false; |
|
| 70 | + } |
|
| 71 | + } else { |
|
| 72 | + hasNext = false; |
|
| 73 | + } |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public boolean hasNext() { |
|
| 78 | + return hasNext; |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + @Override |
|
| 82 | + public E next() { |
|
| 83 | + if (hasNext) { |
|
| 84 | + E result = nextValid; |
|
| 85 | + advance(); |
|
| 86 | + hasLastNext = true; |
|
| 87 | + lastNext = result; |
|
| 88 | + return result; |
|
| 89 | + } else { |
|
| 90 | + throw new NoSuchElementException(); |
|
| 91 | + } |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + @Override |
|
| 95 | + public void remove() { |
|
| 96 | + if (!hasLastNext) { |
|
| 97 | + throw new IllegalStateException("next() was not called before remove()"); |
|
| 98 | + } else { |
|
| 99 | + PartialNavigableSetView.this.remove(lastNext); |
|
| 100 | + hasLastNext = false; |
|
| 101 | + } |
|
| 102 | + } |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + public PartialNavigableSetView(NavigableSet<E> set) { |
|
| 106 | + this.set = set; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + /** |
|
| 110 | + * Subclasses need to implement this method. For elements to be eliminated from the view represented by this |
|
| 111 | + * object, return <code>false</code> for such an element being passed to this method. |
|
| 112 | + */ |
|
| 113 | + abstract protected boolean isValid(E e); |
|
| 114 | + |
|
| 115 | + @Override |
|
| 116 | + public Comparator<? super E> comparator() { |
|
| 117 | + return getSet().comparator(); |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + public NavigableSet<E> descendingSet() { |
|
| 121 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().descendingSet()); |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + @Override |
|
| 125 | + public Iterator<E> descendingIterator() { |
|
| 126 | + return descendingSet().iterator(); |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + @Override |
|
| 130 | + public E first() { |
|
| 131 | + E first = getSet().first(); |
|
| 132 | + while (first != null && !isValid(first)) { |
|
| 133 | + first = getSet().higher(first); |
|
| 134 | + } |
|
| 135 | + if (first == null) { |
|
| 136 | + throw new NoSuchElementException(); |
|
| 137 | + } else { |
|
| 138 | + return first; |
|
| 139 | + } |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + @Override |
|
| 143 | + public E last() { |
|
| 144 | + E last = getSet().last(); |
|
| 145 | + while (last != null && !isValid(last)) { |
|
| 146 | + last = getSet().lower(last); |
|
| 147 | + } |
|
| 148 | + if (last == null) { |
|
| 149 | + throw new NoSuchElementException(); |
|
| 150 | + } else { |
|
| 151 | + return last; |
|
| 152 | + } |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + @Override |
|
| 156 | + public int size() { |
|
| 157 | + int size = 0; |
|
| 158 | + for (E e : getSet()) { |
|
| 159 | + if (isValid(e)) { |
|
| 160 | + size++; |
|
| 161 | + } |
|
| 162 | + } |
|
| 163 | + return size; |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + @Override |
|
| 167 | + public boolean isEmpty() { |
|
| 168 | + for (E e : getSet()) { |
|
| 169 | + if (isValid(e)) { |
|
| 170 | + return false; |
|
| 171 | + } |
|
| 172 | + } |
|
| 173 | + return true; |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + @SuppressWarnings("unchecked") |
|
| 177 | + @Override |
|
| 178 | + public boolean contains(Object o) { |
|
| 179 | + return getSet().contains(o) && isValid((E) o); |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + @Override |
|
| 183 | + public Object[] toArray() { |
|
| 184 | + List<E> l = new ArrayList<E>(); |
|
| 185 | + for (E e : getSet()) { |
|
| 186 | + if (isValid(e)) { |
|
| 187 | + l.add(e); |
|
| 188 | + } |
|
| 189 | + } |
|
| 190 | + return l.toArray(); |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + @SuppressWarnings("unchecked") |
|
| 194 | + @Override |
|
| 195 | + public <T> T[] toArray(T[] a) { |
|
| 196 | + List<T> l = new ArrayList<T>(); |
|
| 197 | + for (E e : getSet()) { |
|
| 198 | + if (isValid(e)) { |
|
| 199 | + l.add((T) e); |
|
| 200 | + } |
|
| 201 | + } |
|
| 202 | + return l.toArray(a); |
|
| 203 | + |
|
| 204 | + } |
|
| 205 | + |
|
| 206 | + @Override |
|
| 207 | + public boolean add(E e) { |
|
| 208 | + return getSet().add(e); |
|
| 209 | + } |
|
| 210 | + |
|
| 211 | + @Override |
|
| 212 | + public boolean remove(Object o) { |
|
| 213 | + return getSet().remove(o); |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + @SuppressWarnings("unchecked") |
|
| 217 | + @Override |
|
| 218 | + public boolean containsAll(Collection<?> c) { |
|
| 219 | + for (Object o : c) { |
|
| 220 | + if (!isValid((E) o) || !getSet().contains(o)) { |
|
| 221 | + return false; |
|
| 222 | + } |
|
| 223 | + } |
|
| 224 | + return true; |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + @Override |
|
| 228 | + public boolean addAll(Collection<? extends E> c) { |
|
| 229 | + return getSet().addAll(c); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + @Override |
|
| 233 | + public boolean retainAll(Collection<?> c) { |
|
| 234 | + return getSet().retainAll(c); |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + @Override |
|
| 238 | + public boolean removeAll(Collection<?> c) { |
|
| 239 | + return getSet().removeAll(c); |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + @Override |
|
| 243 | + public void clear() { |
|
| 244 | + getSet().clear(); |
|
| 245 | + } |
|
| 246 | + |
|
| 247 | + @Override |
|
| 248 | + public E lower(E e) { |
|
| 249 | + E result = getSet().lower(e); |
|
| 250 | + while (result != null && !isValid(result)) { |
|
| 251 | + result = getSet().lower(result); |
|
| 252 | + } |
|
| 253 | + return result; |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + /** |
|
| 257 | + * goes one left on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 258 | + * <code>false</code> |
|
| 259 | + */ |
|
| 260 | + protected E lowerInternal(E e) { |
|
| 261 | + return getSet().lower(e); |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + /** |
|
| 265 | + * goes one right on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 266 | + * <code>false</code> |
|
| 267 | + */ |
|
| 268 | + protected E higherInternal(E e) { |
|
| 269 | + return getSet().higher(e); |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + @Override |
|
| 273 | + public E floor(E e) { |
|
| 274 | + E result = getSet().floor(e); |
|
| 275 | + while (result != null && !isValid(result)) { |
|
| 276 | + result = getSet().lower(result); |
|
| 277 | + } |
|
| 278 | + return result; |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + @Override |
|
| 282 | + public E ceiling(E e) { |
|
| 283 | + E result = getSet().ceiling(e); |
|
| 284 | + while (result != null && !isValid(result)) { |
|
| 285 | + result = getSet().higher(result); |
|
| 286 | + } |
|
| 287 | + return result; |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + @Override |
|
| 291 | + public E higher(E e) { |
|
| 292 | + E result = getSet().higher(e); |
|
| 293 | + while (result != null && !isValid(result)) { |
|
| 294 | + result = getSet().higher(result); |
|
| 295 | + } |
|
| 296 | + return result; |
|
| 297 | + } |
|
| 298 | + |
|
| 299 | + /** |
|
| 300 | + * Removes all raw fixes that have {@link #isValid(Object)}==<code>false</code> and the first element to have |
|
| 301 | + * {@link #isValid(Object)}==<code>true</code>. This latter element is returned. If no such element exists, |
|
| 302 | + * <code>null</code> is returned. It is hence possible that invalid raw fixes are removed but stil <code>null</code> |
|
| 303 | + * is returned. |
|
| 304 | + */ |
|
| 305 | + @Override |
|
| 306 | + public E pollFirst() { |
|
| 307 | + E result = getSet().first(); |
|
| 308 | + while (result != null && !isValid(result)) { |
|
| 309 | + getSet().remove(result); |
|
| 310 | + result = getSet().first(); |
|
| 311 | + } |
|
| 312 | + return result; |
|
| 313 | + } |
|
| 314 | + |
|
| 315 | + @Override |
|
| 316 | + public E pollLast() { |
|
| 317 | + E result = getSet().last(); |
|
| 318 | + while (result != null && !isValid(result)) { |
|
| 319 | + getSet().remove(result); |
|
| 320 | + result = getSet().last(); |
|
| 321 | + } |
|
| 322 | + return result; |
|
| 323 | + } |
|
| 324 | + |
|
| 325 | + @Override |
|
| 326 | + public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { |
|
| 327 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().subSet(fromElement, fromInclusive, toElement, toInclusive)); |
|
| 328 | + } |
|
| 329 | + |
|
| 330 | + @Override |
|
| 331 | + public NavigableSet<E> headSet(E toElement, boolean inclusive) { |
|
| 332 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().headSet(toElement, inclusive)); |
|
| 333 | + } |
|
| 334 | + |
|
| 335 | + @Override |
|
| 336 | + public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { |
|
| 337 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().tailSet(fromElement, inclusive)); |
|
| 338 | + } |
|
| 339 | + |
|
| 340 | + @Override |
|
| 341 | + public NavigableSet<E> subSet(E fromElement, E toElement) { |
|
| 342 | + SortedSet<E> subSet = set.subSet(fromElement, toElement); |
|
| 343 | + if (subSet instanceof NavigableSet<?>) { |
|
| 344 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) subSet); |
|
| 345 | + } else { |
|
| 346 | + TreeSet<E> result = new TreeSet<E>(subSet); |
|
| 347 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 348 | + } |
|
| 349 | + } |
|
| 350 | + |
|
| 351 | + @Override |
|
| 352 | + public NavigableSet<E> headSet(E toElement) { |
|
| 353 | + SortedSet<E> headSet = set.headSet(toElement); |
|
| 354 | + if (headSet instanceof NavigableSet<?>) { |
|
| 355 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) headSet); |
|
| 356 | + } else { |
|
| 357 | + TreeSet<E> result = new TreeSet<E>(headSet); |
|
| 358 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 359 | + } |
|
| 360 | + } |
|
| 361 | + |
|
| 362 | + @Override |
|
| 363 | + public NavigableSet<E> tailSet(E fromElement) { |
|
| 364 | + SortedSet<E> tailSet = set.tailSet(fromElement); |
|
| 365 | + if (tailSet instanceof NavigableSet<?>) { |
|
| 366 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) tailSet); |
|
| 367 | + } else { |
|
| 368 | + TreeSet<E> result = new TreeSet<E>(tailSet); |
|
| 369 | + return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 370 | + } |
|
| 371 | + } |
|
| 372 | + |
|
| 373 | + |
|
| 374 | + @Override |
|
| 375 | + public Iterator<E> iterator() { |
|
| 376 | + return new FilteringIterator(); |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + @Override |
|
| 380 | + public String toString() { |
|
| 381 | + return new ArrayList<E>(this).toString(); |
|
| 382 | + } |
|
| 383 | + |
|
| 384 | + protected NavigableSet<E> getSet() { |
|
| 385 | + return set; |
|
| 386 | + } |
|
| 387 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TimeRangeCache.java
| ... | ... | @@ -0,0 +1,221 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.util.Comparator; |
|
| 4 | +import java.util.Iterator; |
|
| 5 | +import java.util.LinkedHashMap; |
|
| 6 | +import java.util.Map.Entry; |
|
| 7 | +import java.util.NavigableSet; |
|
| 8 | + |
|
| 9 | +import com.sap.sse.common.TimePoint; |
|
| 10 | +import com.sap.sse.common.Util; |
|
| 11 | +import com.sap.sse.common.Util.Pair; |
|
| 12 | +import com.sap.sse.concurrent.LockUtil; |
|
| 13 | +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 14 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 15 | + |
|
| 16 | +/** |
|
| 17 | + * This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used in |
|
| 18 | + * a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of |
|
| 19 | + * pairs where the first pair component represents a <code>from</code> parameter used in the calculation's time range |
|
| 20 | + * and the second pair component represents the result of the calculation for this parameter combination. |
|
| 21 | + * <p> |
|
| 22 | + * |
|
| 23 | + * For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order |
|
| 24 | + * to be able to efficiently extend a cache entry for a single <code>to</code> fix, the navigable sets containing the |
|
| 25 | + * <code>from</code> fixes and results are ordered such that earlier fixes come later in the set. This way, extending |
|
| 26 | + * the cache entry for a <code>to</code> fix to an earlier <code>from</code> fix only requires appending to the set. |
|
| 27 | + * <p> |
|
| 28 | + * |
|
| 29 | + * <b>Invalidation</b>: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's |
|
| 30 | + * time point are removed from this cache. Additionally, the fix insertion may have an impact on the |
|
| 31 | + * {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and |
|
| 32 | + * therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the |
|
| 33 | + * cache entries for the time points at or after the previous fix also need to be removed. |
|
| 34 | + * <p> |
|
| 35 | + * |
|
| 36 | + * <b>Cache use</b>: When a result across a time range is to be computed the calculating method should first look for a |
|
| 37 | + * cache entry for the <code>to</code> parameter. If one is found, the earliest entry in the navigable set for the |
|
| 38 | + * navigable set of <code>from</code> and result values that is at or after the requested <code>from</code> time point |
|
| 39 | + * is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the |
|
| 40 | + * <code>from</code> value found in the cache as the new <code>to</code> value, and the <code>from</code> value |
|
| 41 | + * originally passed to the calculating method as <code>from</code> again. If no entry is found in the cache entry for |
|
| 42 | + * <code>to</code> that is at or after the requested <code>from</code> time, the result has to be computed "from |
|
| 43 | + * scratch." |
|
| 44 | + * <p> |
|
| 45 | + * |
|
| 46 | + * If a cache entry for <code>to</code> is not found, the latest cache entry before it is looked up. If one is found, |
|
| 47 | + * the result for the time range between the <code>to</code> time point requested and the <code>to</code> time point |
|
| 48 | + * found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is |
|
| 49 | + * computed by iterating backwards all the way to <code>from</code>. |
|
| 50 | + * <p> |
|
| 51 | + * |
|
| 52 | + * Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the |
|
| 53 | + * result to the cache. |
|
| 54 | + * |
|
| 55 | + * @author Axel Uhl (D043530) |
|
| 56 | + */ |
|
| 57 | +public class TimeRangeCache<T> { |
|
| 58 | + public static final int MAX_SIZE = 100; |
|
| 59 | + |
|
| 60 | + private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> timeRangeCache; |
|
| 61 | + |
|
| 62 | + /** |
|
| 63 | + * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is |
|
| 64 | + * defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or |
|
| 65 | + * having been added by {@link #cache(TimePoint, TimePoint, Object)}. |
|
| 66 | + * <p> |
|
| 67 | + * |
|
| 68 | + * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements, |
|
| 69 | + * the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading |
|
| 70 | + * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash |
|
| 71 | + * map that counts access as "use" has a modifying effect on its internal structures. |
|
| 72 | + * <p> |
|
| 73 | + * |
|
| 74 | + * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the |
|
| 75 | + * alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.<p> |
|
| 76 | + * |
|
| 77 | + * Read access is to be <code>synchronized<code> using this field's mutex; write access only happens under the |
|
| 78 | + * {@link #lock write lock} and therefore will have no contenders. |
|
| 79 | + */ |
|
| 80 | + private final LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void> lruCache; |
|
| 81 | + |
|
| 82 | + private final NamedReentrantReadWriteLock lock; |
|
| 83 | + |
|
| 84 | + private static final Comparator<Util.Pair<TimePoint, ?>> timePointInPairComparator = new Comparator<Util.Pair<TimePoint, ?>>() { |
|
| 85 | + @Override |
|
| 86 | + public int compare(Util.Pair<TimePoint, ?> o1, Util.Pair<TimePoint, ?> o2) { |
|
| 87 | + return o1.getA().compareTo(o2.getA()); |
|
| 88 | + } |
|
| 89 | + }; |
|
| 90 | + |
|
| 91 | + public TimeRangeCache(String nameForLockLogging) { |
|
| 92 | + lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true); |
|
| 93 | + this.timeRangeCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>>(timePointInPairComparator); |
|
| 94 | + this.lruCache = new LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void>(/* initial capacity */ 10, /* load factor */ 0.75f, |
|
| 95 | + /* access-based ordering */ true) { |
|
| 96 | + private static final long serialVersionUID = -6568235517111733193L; |
|
| 97 | + |
|
| 98 | + @Override |
|
| 99 | + protected boolean removeEldestEntry(Entry<Pair<TimePoint, TimePoint>, Void> eldest) { |
|
| 100 | + final boolean expunge = size() > MAX_SIZE; |
|
| 101 | + if (expunge) { |
|
| 102 | + removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB()); |
|
| 103 | + } |
|
| 104 | + return expunge; |
|
| 105 | + } |
|
| 106 | + }; |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + public int size() { |
|
| 110 | + return lruCache.size(); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + private void removeCacheEntry(TimePoint from, TimePoint to) { |
|
| 114 | + assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock |
|
| 115 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 116 | + if (entryForTo.getA().equals(to)) { |
|
| 117 | + Pair<TimePoint, T> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 118 | + if (entryForFrom.getA().equals(from)) { |
|
| 119 | + entryForTo.getB().remove(entryForFrom); |
|
| 120 | + if (entryForTo.getB().isEmpty()) { |
|
| 121 | + timeRangeCache.remove(entryForTo); |
|
| 122 | + } |
|
| 123 | + } |
|
| 124 | + } |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * Looks up the entry closest to but no later than <code>to</code>. If not found, <code>null</code> is returned. If |
|
| 129 | + * found, the earliest pair of from/result that is at or after <code>from</code> will be returned, together with |
|
| 130 | + * the <code>to</code> value of the entry. If there is no entry that is at or after <code>from</code>, |
|
| 131 | + * <code>null</code> is returned. |
|
| 132 | + */ |
|
| 133 | + public Util.Pair<TimePoint, Util.Pair<TimePoint, T>> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) { |
|
| 134 | + LockUtil.lockForRead(lock); |
|
| 135 | + try { |
|
| 136 | + Util.Pair<TimePoint, Util.Pair<TimePoint, T>> result = null; |
|
| 137 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 138 | + if (entryForTo != null) { |
|
| 139 | + final Util.Pair<TimePoint, T> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 140 | + if (fromCeiling != null) { |
|
| 141 | + result = new Util.Pair<TimePoint, Util.Pair<TimePoint, T>>(entryForTo.getA(), fromCeiling); |
|
| 142 | + } |
|
| 143 | + } |
|
| 144 | + // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using |
|
| 145 | + // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry |
|
| 146 | + // modifies the internal parts of the data structure which is not thread safe. |
|
| 147 | + synchronized (lruCache) { // ping the "perfect match" although it may not even have existed in the cache |
|
| 148 | + lruCache.get(new Util.Pair<TimePoint, TimePoint>(from, to)); |
|
| 149 | + } |
|
| 150 | + return result; |
|
| 151 | + } finally { |
|
| 152 | + LockUtil.unlockAfterRead(lock); |
|
| 153 | + } |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + /** |
|
| 157 | + * Removes all cache entries that have a <code>to</code> time point that is at or after <code>timePoint</code>. |
|
| 158 | + */ |
|
| 159 | + public void invalidateAllAtOrLaterThan(TimePoint timePoint) { |
|
| 160 | + LockUtil.lockForWrite(lock); |
|
| 161 | + try { |
|
| 162 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummy = createDummy(timePoint); |
|
| 163 | + NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true); |
|
| 164 | + for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> i=toRemove.iterator(); i.hasNext(); ) { |
|
| 165 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryToRemove = i.next(); |
|
| 166 | + assert entryToRemove.getA().compareTo(timePoint) >= 0; |
|
| 167 | + for (Pair<TimePoint, T> fromAndResult : entryToRemove.getB()) { |
|
| 168 | + lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA())); |
|
| 169 | + } |
|
| 170 | + i.remove(); |
|
| 171 | + } |
|
| 172 | + } finally { |
|
| 173 | + LockUtil.unlockAfterWrite(lock); |
|
| 174 | + } |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + private NavigableSet<Util.Pair<TimePoint, T>> getEntryForTo(TimePoint to) { |
|
| 178 | + NavigableSet<Util.Pair<TimePoint, T>> result = null; |
|
| 179 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummyForTo = createDummy(to); |
|
| 180 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(dummyForTo); |
|
| 181 | + if (entryForTo != null && entryForTo.getA().equals(to)) { |
|
| 182 | + result = entryForTo.getB(); |
|
| 183 | + } |
|
| 184 | + return result; |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + public void cache(TimePoint from, TimePoint to, T result) { |
|
| 188 | + LockUtil.lockForWrite(lock); |
|
| 189 | + try { |
|
| 190 | + NavigableSet<Util.Pair<TimePoint, T>> entryForTo = getEntryForTo(to); |
|
| 191 | + if (entryForTo == null) { |
|
| 192 | + entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, T>>(timePointInPairComparator); |
|
| 193 | + Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>( |
|
| 194 | + to, entryForTo); |
|
| 195 | + timeRangeCache.add(pairForTo); |
|
| 196 | + } |
|
| 197 | + entryForTo.add(new Util.Pair<TimePoint, T>(from, result)); |
|
| 198 | + lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null); |
|
| 199 | + } finally { |
|
| 200 | + LockUtil.unlockAfterWrite(lock); |
|
| 201 | + } |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> createDummy(TimePoint to) { |
|
| 205 | + return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(to, null); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + /** |
|
| 209 | + * Removes all contents from this cache |
|
| 210 | + */ |
|
| 211 | + public void clear() { |
|
| 212 | + LockUtil.lockForWrite(lock); |
|
| 213 | + try { |
|
| 214 | + timeRangeCache.clear(); |
|
| 215 | + lruCache.clear(); |
|
| 216 | + } finally { |
|
| 217 | + LockUtil.unlockAfterWrite(lock); |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + } |
|
| 221 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TimedComparator.java
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | +import java.util.Comparator; |
|
| 5 | + |
|
| 6 | +import com.sap.sse.common.Timed; |
|
| 7 | + |
|
| 8 | +public class TimedComparator implements Comparator<Timed>, Serializable { |
|
| 9 | + private static final long serialVersionUID = 1604511471599854988L; |
|
| 10 | + public static final Comparator<Timed> INSTANCE = new TimedComparator(); |
|
| 11 | + |
|
| 12 | + @Override |
|
| 13 | + public int compare(Timed o1, Timed o2) { |
|
| 14 | + return o1.getTimePoint().compareTo(o2.getTimePoint()); |
|
| 15 | + } |
|
| 16 | +} |
|
| 17 | + |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TrackImpl.java
| ... | ... | @@ -0,0 +1,549 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import java.io.IOException; |
|
| 4 | +import java.io.ObjectOutputStream; |
|
| 5 | +import java.util.ConcurrentModificationException; |
|
| 6 | +import java.util.Iterator; |
|
| 7 | +import java.util.NavigableSet; |
|
| 8 | +import java.util.function.Function; |
|
| 9 | + |
|
| 10 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.FixAcceptancePredicate; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 13 | +import com.sap.sse.common.Duration; |
|
| 14 | +import com.sap.sse.common.TimePoint; |
|
| 15 | +import com.sap.sse.common.Timed; |
|
| 16 | +import com.sap.sse.common.Util; |
|
| 17 | +import com.sap.sse.common.Util.Pair; |
|
| 18 | +import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 19 | +import com.sap.sse.concurrent.LockUtil; |
|
| 20 | +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 21 | +import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 22 | +import com.sap.sse.shared.util.impl.UnmodifiableNavigableSet; |
|
| 23 | + |
|
| 24 | +public class TrackImpl<FixType extends Timed> implements Track<FixType> { |
|
| 25 | + private static final long serialVersionUID = -4075853657857657528L; |
|
| 26 | + /** |
|
| 27 | + * The fixes, ordered by their time points |
|
| 28 | + */ |
|
| 29 | + private final ArrayListNavigableSet<Timed> fixes; |
|
| 30 | + |
|
| 31 | + private final NamedReentrantReadWriteLock readWriteLock; |
|
| 32 | + |
|
| 33 | + protected static class DummyTimed implements Timed { |
|
| 34 | + private static final long serialVersionUID = 6047311973718918856L; |
|
| 35 | + private final TimePoint timePoint; |
|
| 36 | + public DummyTimed(TimePoint timePoint) { |
|
| 37 | + super(); |
|
| 38 | + this.timePoint = timePoint; |
|
| 39 | + } |
|
| 40 | + @Override |
|
| 41 | + public TimePoint getTimePoint() { |
|
| 42 | + return timePoint; |
|
| 43 | + } |
|
| 44 | + @Override |
|
| 45 | + public String toString() { |
|
| 46 | + return timePoint.toString(); |
|
| 47 | + } |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + public TrackImpl(String nameForReadWriteLock) { |
|
| 51 | + this(new ArrayListNavigableSet<Timed>(TimedComparator.INSTANCE), nameForReadWriteLock); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + protected TrackImpl(ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 55 | + this.readWriteLock = new NamedReentrantReadWriteLock(nameForReadWriteLock, /* fair */ false); |
|
| 56 | + this.fixes = fixes; |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + /** |
|
| 60 | + * Synchronize the serialization such that no fixes are added while serializing |
|
| 61 | + */ |
|
| 62 | + private void writeObject(ObjectOutputStream s) throws IOException { |
|
| 63 | + lockForRead(); |
|
| 64 | + try { |
|
| 65 | + s.defaultWriteObject(); |
|
| 66 | + } finally { |
|
| 67 | + unlockAfterRead(); |
|
| 68 | + } |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + @Override |
|
| 72 | + public void lockForRead() { |
|
| 73 | + LockUtil.lockForRead(readWriteLock); |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + @Override |
|
| 77 | + public void unlockAfterRead() { |
|
| 78 | + LockUtil.unlockAfterRead(readWriteLock); |
|
| 79 | + } |
|
| 80 | + |
|
| 81 | + protected void lockForWrite() { |
|
| 82 | + LockUtil.lockForWrite(readWriteLock); |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + protected void unlockAfterWrite() { |
|
| 86 | + LockUtil.unlockAfterWrite(readWriteLock); |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and {@link #unlockAfterRead()} |
|
| 91 | + * to avoid {@link ConcurrentModificationException}s. Should they modify the structure returned, they have to use |
|
| 92 | + * {@link #lockForWrite()} and {@link #unlockAfterWrite()}, respectively. |
|
| 93 | + */ |
|
| 94 | + protected NavigableSet<FixType> getInternalRawFixes() { |
|
| 95 | + @SuppressWarnings("unchecked") |
|
| 96 | + NavigableSet<FixType> result = (NavigableSet<FixType>) fixes; |
|
| 97 | + return result; |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * asserts that the calling thread holds at least one of read and write lock |
|
| 102 | + */ |
|
| 103 | + protected void assertReadLock() { |
|
| 104 | + if (readWriteLock.getReadHoldCount() < 1 && readWriteLock.getWriteHoldCount() < 1) { |
|
| 105 | + throw new IllegalStateException("Caller must obtain read lock using lockForRead() before calling this method"); |
|
| 106 | + } |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + protected void assertWriteLock() { |
|
| 110 | + if (readWriteLock.getWriteHoldCount() < 1) { |
|
| 111 | + throw new IllegalStateException("Caller must obtain write lock using lockForWrite() before calling this method"); |
|
| 112 | + } |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and |
|
| 117 | + * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s. |
|
| 118 | + * |
|
| 119 | + * @return the smoothened fixes ordered by their time points; this implementation simply delegates to |
|
| 120 | + * {@link #getInternalRawFixes()} because for only {@link Timed} fixes we can't know how to remove outliers. |
|
| 121 | + * Subclasses that constrain the <code>FixType</code> may provide smoothening implementations. |
|
| 122 | + */ |
|
| 123 | + protected NavigableSet<FixType> getInternalFixes() { |
|
| 124 | + NavigableSet<FixType> result = getInternalRawFixes(); |
|
| 125 | + return result; |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + /** |
|
| 129 | + * Iterates the fixes with outliers getting skipped, in the order of their time points. |
|
| 130 | + * Relies on {@link #getInternalFixes()} to void the track view from outliers. |
|
| 131 | + */ |
|
| 132 | + @Override |
|
| 133 | + public NavigableSet<FixType> getFixes() { |
|
| 134 | + assertReadLock(); |
|
| 135 | + return new UnmodifiableNavigableSet<FixType>(getInternalFixes()); |
|
| 136 | + } |
|
| 137 | + |
|
| 138 | + @Override |
|
| 139 | + public Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive) { |
|
| 140 | + return getFixes().subSet(getDummyFix(from), fromInclusive, getDummyFix(to), toInclusive); |
|
| 141 | + } |
|
| 142 | + |
|
| 143 | + /** |
|
| 144 | + * Iterates over the raw sequence of fixes, all potential outliers included |
|
| 145 | + */ |
|
| 146 | + @Override |
|
| 147 | + public NavigableSet<FixType> getRawFixes() { |
|
| 148 | + assertReadLock(); |
|
| 149 | + return new UnmodifiableNavigableSet<FixType>(getInternalRawFixes()); |
|
| 150 | + } |
|
| 151 | + |
|
| 152 | + @Override |
|
| 153 | + public FixType getLastFixAtOrBefore(TimePoint timePoint) { |
|
| 154 | + return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null); |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 158 | + lockForRead(); |
|
| 159 | + try { |
|
| 160 | + final NavigableSet<FixType> headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 161 | + for (final Iterator<FixType> i=headSet.descendingIterator(); i.hasNext(); ) { |
|
| 162 | + final FixType next = i.next(); |
|
| 163 | + if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 164 | + return next; |
|
| 165 | + } |
|
| 166 | + } |
|
| 167 | + return null; |
|
| 168 | + } finally { |
|
| 169 | + unlockAfterRead(); |
|
| 170 | + } |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + @Override |
|
| 174 | + public FixType getLastFixBefore(TimePoint timePoint) { |
|
| 175 | + lockForRead(); |
|
| 176 | + try { |
|
| 177 | + return (FixType) getInternalFixes().lower(getDummyFix(timePoint)); |
|
| 178 | + } finally { |
|
| 179 | + unlockAfterRead(); |
|
| 180 | + } |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + @Override |
|
| 184 | + public FixType getLastRawFixAtOrBefore(TimePoint timePoint) { |
|
| 185 | + lockForRead(); |
|
| 186 | + try { |
|
| 187 | + return (FixType) getInternalRawFixes().floor(getDummyFix(timePoint)); |
|
| 188 | + } finally { |
|
| 189 | + unlockAfterRead(); |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + @Override |
|
| 194 | + public FixType getFirstRawFixAtOrAfter(TimePoint timePoint) { |
|
| 195 | + lockForRead(); |
|
| 196 | + try { |
|
| 197 | + return (FixType) getInternalRawFixes().ceiling(getDummyFix(timePoint)); |
|
| 198 | + } finally { |
|
| 199 | + unlockAfterRead(); |
|
| 200 | + } |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + @Override |
|
| 204 | + public FixType getFirstFixAtOrAfter(TimePoint timePoint) { |
|
| 205 | + return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 209 | + lockForRead(); |
|
| 210 | + try { |
|
| 211 | + final NavigableSet<FixType> tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 212 | + for (final FixType next : tailSet) { |
|
| 213 | + if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 214 | + return next; |
|
| 215 | + } |
|
| 216 | + } |
|
| 217 | + return null; |
|
| 218 | + } finally { |
|
| 219 | + unlockAfterRead(); |
|
| 220 | + } |
|
| 221 | + } |
|
| 222 | + |
|
| 223 | + @Override |
|
| 224 | + public FixType getLastRawFixBefore(TimePoint timePoint) { |
|
| 225 | + lockForRead(); |
|
| 226 | + try { |
|
| 227 | + return (FixType) getInternalRawFixes().lower(getDummyFix(timePoint)); |
|
| 228 | + } finally { |
|
| 229 | + unlockAfterRead(); |
|
| 230 | + } |
|
| 231 | + } |
|
| 232 | + |
|
| 233 | + @Override |
|
| 234 | + public FixType getFirstFixAfter(TimePoint timePoint) { |
|
| 235 | + lockForRead(); |
|
| 236 | + try { |
|
| 237 | + return (FixType) getInternalFixes().higher(getDummyFix(timePoint)); |
|
| 238 | + } finally { |
|
| 239 | + unlockAfterRead(); |
|
| 240 | + } |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + @Override |
|
| 244 | + public FixType getFirstRawFixAfter(TimePoint timePoint) { |
|
| 245 | + lockForRead(); |
|
| 246 | + try { |
|
| 247 | + return (FixType) getInternalRawFixes().higher(getDummyFix(timePoint)); |
|
| 248 | + } finally { |
|
| 249 | + unlockAfterRead(); |
|
| 250 | + } |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + @Override |
|
| 254 | + public FixType getFirstRawFix() { |
|
| 255 | + lockForRead(); |
|
| 256 | + try { |
|
| 257 | + if (getInternalFixes().isEmpty()) { |
|
| 258 | + return null; |
|
| 259 | + } else { |
|
| 260 | + return (FixType) getInternalFixes().first(); |
|
| 261 | + } |
|
| 262 | + } finally { |
|
| 263 | + unlockAfterRead(); |
|
| 264 | + } |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + @Override |
|
| 268 | + public FixType getLastRawFix() { |
|
| 269 | + lockForRead(); |
|
| 270 | + try { |
|
| 271 | + if (getInternalRawFixes().isEmpty()) { |
|
| 272 | + return null; |
|
| 273 | + } else { |
|
| 274 | + return (FixType) getInternalRawFixes().last(); |
|
| 275 | + } |
|
| 276 | + } finally { |
|
| 277 | + unlockAfterRead(); |
|
| 278 | + } |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + /** |
|
| 282 | + * @param fixAcceptancePredicate |
|
| 283 | + * if not {@code null}, adjacent fixes will be skipped as long as this predicate does not |
|
| 284 | + * {@link FixAcceptancePredicate#isAcceptFix(Object) accept} the fix. This can, e.g., be used to skip |
|
| 285 | + * fixes that don't have values in a dimension required. If {@code null}, the next fixes left and right |
|
| 286 | + * (including the exact {@code timePoint} if a fix exists there) will be used without further check. |
|
| 287 | + */ |
|
| 288 | + private Pair<FixType, FixType> getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 289 | + FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate); |
|
| 290 | + FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate); |
|
| 291 | + com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right); |
|
| 292 | + return result; |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + /** |
|
| 296 | + * Calculates a linear interpolation of values based on their time points and a target time point that is expected |
|
| 297 | + * to be in between (inclusive) the two time points for the two values. If the two time points for the two values |
|
| 298 | + * are equal, the average of the two values is returned. |
|
| 299 | + */ |
|
| 300 | + private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) { |
|
| 301 | + final T acc; |
|
| 302 | + if (timePoint1.equals(timePoint2)) { |
|
| 303 | + acc = value1.add(value2).divide(2); |
|
| 304 | + } else { |
|
| 305 | + long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis()); |
|
| 306 | + long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis()); |
|
| 307 | + acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2); |
|
| 308 | + } |
|
| 309 | + return acc; |
|
| 310 | + } |
|
| 311 | + |
|
| 312 | + @Override |
|
| 313 | + public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 314 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter) { |
|
| 315 | + return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null); |
|
| 316 | + } |
|
| 317 | + |
|
| 318 | + protected <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 319 | + Function<FixType, ScalableValue<InternalType, ValueType>> converter, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 320 | + final ValueType result; |
|
| 321 | + Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate); |
|
| 322 | + if (fixPair.getA() == null) { |
|
| 323 | + if (fixPair.getB() == null) { |
|
| 324 | + result = null; |
|
| 325 | + } else { |
|
| 326 | + result = converter.apply(fixPair.getB()).divide(1); |
|
| 327 | + } |
|
| 328 | + } else { |
|
| 329 | + if (fixPair.getB() == null || fixPair.getA() == fixPair.getB()) { |
|
| 330 | + result = converter.apply(fixPair.getA()).divide(1); |
|
| 331 | + } else { |
|
| 332 | + result = timeBasedAverage(timePoint, |
|
| 333 | + converter.apply(fixPair.getA()), fixPair.getA().getTimePoint(), |
|
| 334 | + converter.apply(fixPair.getB()), fixPair.getB().getTimePoint()); |
|
| 335 | + } |
|
| 336 | + } |
|
| 337 | + return result; |
|
| 338 | + } |
|
| 339 | + |
|
| 340 | + @Override |
|
| 341 | + public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 342 | + assertReadLock(); |
|
| 343 | + return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 344 | + } |
|
| 345 | + |
|
| 346 | + @Override |
|
| 347 | + public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, |
|
| 348 | + boolean endingAtInclusive) { |
|
| 349 | + assertReadLock(); |
|
| 350 | + return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 351 | + } |
|
| 352 | + |
|
| 353 | + @Override |
|
| 354 | + public Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 355 | + assertReadLock(); |
|
| 356 | + Iterator<FixType> result = (Iterator<FixType>) getInternalFixes().headSet( |
|
| 357 | + getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 358 | + return result; |
|
| 359 | + } |
|
| 360 | + |
|
| 361 | + /** |
|
| 362 | + * Creates a dummy fix that conforms to <code>FixType</code>. This in particular means that subclasses |
|
| 363 | + * instantiating <code>FixType</code> with a specific class need to redefine this method so as to return |
|
| 364 | + * a dummy fix complying with their instantiation type used for <code>FixType</code>. Otherwise, a |
|
| 365 | + * {@link ClassCastException} may result upon certain operations performed with the fix returned by |
|
| 366 | + * this method. |
|
| 367 | + */ |
|
| 368 | + protected FixType getDummyFix(TimePoint timePoint) { |
|
| 369 | + @SuppressWarnings("unchecked") |
|
| 370 | + FixType result = (FixType) new DummyTimed(timePoint); |
|
| 371 | + return result; |
|
| 372 | + } |
|
| 373 | + |
|
| 374 | + @Override |
|
| 375 | + public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 376 | + assertReadLock(); |
|
| 377 | + return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 378 | + } |
|
| 379 | + |
|
| 380 | + private Iterator<FixType> getTimeConstrainedFixesIterator(NavigableSet<FixType> set, TimePoint startingAt, boolean startingAtInclusive, |
|
| 381 | + TimePoint endingAt, boolean endingAtInclusive) { |
|
| 382 | + assertReadLock(); |
|
| 383 | + if (startingAt != null && endingAt != null) { |
|
| 384 | + set = set.subSet(getDummyFix(startingAt), startingAtInclusive, getDummyFix(endingAt), endingAtInclusive); |
|
| 385 | + } else if (endingAt != null) { |
|
| 386 | + set = set.headSet(getDummyFix(endingAt), endingAtInclusive); |
|
| 387 | + } else if (startingAt != null) { |
|
| 388 | + set = set.tailSet(getDummyFix(startingAt), startingAtInclusive); |
|
| 389 | + } |
|
| 390 | + Iterator<FixType> result = set.iterator(); |
|
| 391 | + return result; |
|
| 392 | + } |
|
| 393 | + |
|
| 394 | + @Override |
|
| 395 | + public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, |
|
| 396 | + TimePoint endingAt, boolean endingAtInclusive) { |
|
| 397 | + assertReadLock(); |
|
| 398 | + return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + @Override |
|
| 402 | + public Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 403 | + assertReadLock(); |
|
| 404 | + Iterator<FixType> result = (Iterator<FixType>) getInternalRawFixes().headSet( |
|
| 405 | + getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 406 | + return result; |
|
| 407 | + } |
|
| 408 | + |
|
| 409 | + protected boolean add(FixType fix) { |
|
| 410 | + return add(fix, /* replace */ false); |
|
| 411 | + } |
|
| 412 | + |
|
| 413 | + /** |
|
| 414 | + * @return {@code true} if the fix was added or replaced; {@code false} in case no change was performed |
|
| 415 | + */ |
|
| 416 | + protected boolean add(FixType fix, boolean replace) { |
|
| 417 | + lockForWrite(); |
|
| 418 | + try { |
|
| 419 | + final AddResult addResult = addWithoutLocking(fix, replace); |
|
| 420 | + return addResult == AddResult.ADDED || addResult == AddResult.REPLACED; |
|
| 421 | + } finally { |
|
| 422 | + unlockAfterWrite(); |
|
| 423 | + } |
|
| 424 | + } |
|
| 425 | + |
|
| 426 | + /** |
|
| 427 | + * The caller must ensure to hold the write lock for this track when calling this method. |
|
| 428 | + * |
|
| 429 | + * @param replace |
|
| 430 | + * whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the |
|
| 431 | + * comparator used for the {@link #fixes} set. By default this is a comparator only comparing the |
|
| 432 | + * fixes' time stamps. Subclasses may use different comparator implementations. |
|
| 433 | + */ |
|
| 434 | + protected AddResult addWithoutLocking(FixType fix, boolean replace) { |
|
| 435 | + final AddResult result; |
|
| 436 | + final boolean added = getInternalRawFixes().add(fix); |
|
| 437 | + if (!added && replace) { |
|
| 438 | + getInternalRawFixes().remove(fix); |
|
| 439 | + result = getInternalRawFixes().add(fix) ? AddResult.REPLACED : AddResult.NOT_ADDED; |
|
| 440 | + } else { |
|
| 441 | + result = added ? AddResult.ADDED : AddResult.NOT_ADDED; |
|
| 442 | + } |
|
| 443 | + return result; |
|
| 444 | + } |
|
| 445 | + |
|
| 446 | + @Override |
|
| 447 | + public Duration getAverageIntervalBetweenFixes() { |
|
| 448 | + lockForRead(); |
|
| 449 | + try { |
|
| 450 | + final Duration result; |
|
| 451 | + final int size = getRawFixes().size(); |
|
| 452 | + if (size > 1) { |
|
| 453 | + result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 454 | + } else { |
|
| 455 | + result = null; |
|
| 456 | + } |
|
| 457 | + return result; |
|
| 458 | + } finally { |
|
| 459 | + unlockAfterRead(); |
|
| 460 | + } |
|
| 461 | + } |
|
| 462 | + |
|
| 463 | + @Override |
|
| 464 | + public Duration getAverageIntervalBetweenRawFixes() { |
|
| 465 | + lockForRead(); |
|
| 466 | + try { |
|
| 467 | + final Duration result; |
|
| 468 | + final int size = getRawFixes().size(); |
|
| 469 | + if (size > 1) { |
|
| 470 | + result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 471 | + } else { |
|
| 472 | + result = null; |
|
| 473 | + } |
|
| 474 | + return result; |
|
| 475 | + } finally { |
|
| 476 | + unlockAfterRead(); |
|
| 477 | + } |
|
| 478 | + } |
|
| 479 | + |
|
| 480 | + @Override |
|
| 481 | + public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 482 | + return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator); |
|
| 483 | + } |
|
| 484 | + |
|
| 485 | + private <T> T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement, |
|
| 486 | + Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 487 | + T result; |
|
| 488 | + if (!from.before(to)) { |
|
| 489 | + result = nullElement; |
|
| 490 | + } else { |
|
| 491 | + boolean perfectCacheHit = false; |
|
| 492 | + lockForRead(); |
|
| 493 | + try { |
|
| 494 | + Util.Pair<TimePoint, Util.Pair<TimePoint, T>> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to); |
|
| 495 | + if (bestCacheEntry != null) { |
|
| 496 | + perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below |
|
| 497 | + // compute the missing stretches between best cache entry's "from" and our "from" and the cache |
|
| 498 | + // entry's "to" and our "to" |
|
| 499 | + T valueFromFromToBeginningOfCacheEntry = nullElement; |
|
| 500 | + T valueFromEndOfCacheEntryToTo = nullElement; |
|
| 501 | + if (!bestCacheEntry.getB().getA().equals(from)) { |
|
| 502 | + assert bestCacheEntry.getB().getA().after(from); |
|
| 503 | + perfectCacheHit = false; |
|
| 504 | + valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry |
|
| 505 | + .getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 506 | + } |
|
| 507 | + if (!bestCacheEntry.getA().equals(to)) { |
|
| 508 | + assert bestCacheEntry.getA().before(to); |
|
| 509 | + perfectCacheHit = false; |
|
| 510 | + valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to, |
|
| 511 | + recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 512 | + } |
|
| 513 | + if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) { |
|
| 514 | + result = null; |
|
| 515 | + } else { |
|
| 516 | + result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()), |
|
| 517 | + valueFromEndOfCacheEntryToTo); |
|
| 518 | + } |
|
| 519 | + } else { |
|
| 520 | + if (from.compareTo(to) < 0) { |
|
| 521 | + result = valueCalculator.calculate(from, to); |
|
| 522 | + } else { |
|
| 523 | + result = nullElement; |
|
| 524 | + } |
|
| 525 | + } |
|
| 526 | + // run the cache update while still holding the read lock; this avoids bug4629 where a cache invalidation |
|
| 527 | + // caused by fix insertions can come after the result calculation and before the cache update |
|
| 528 | + if (!perfectCacheHit && recursionDepth == 0) { |
|
| 529 | + cache.cache(from, to, result); |
|
| 530 | + } |
|
| 531 | + } finally { |
|
| 532 | + unlockAfterRead(); |
|
| 533 | + } |
|
| 534 | + } |
|
| 535 | + return result; |
|
| 536 | + } |
|
| 537 | + |
|
| 538 | + |
|
| 539 | + |
|
| 540 | + @Override |
|
| 541 | + public int size() { |
|
| 542 | + return fixes.size(); |
|
| 543 | + } |
|
| 544 | + |
|
| 545 | + @Override |
|
| 546 | + public boolean isEmpty() { |
|
| 547 | + return fixes.isEmpty(); |
|
| 548 | + } |
|
| 549 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/shared/tracking/impl/TrackingConnectorInfoImpl.java
| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | +package com.sap.sailing.domain.shared.tracking.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 4 | + |
|
| 5 | +public class TrackingConnectorInfoImpl implements TrackingConnectorInfo { |
|
| 6 | + private static final long serialVersionUID = 7970268841592389145L; |
|
| 7 | + private final String trackingConnectorName; |
|
| 8 | + private final String TtackingConnectorDefaultUrl; |
|
| 9 | + private final String webUrl; |
|
| 10 | + |
|
| 11 | + public TrackingConnectorInfoImpl(String trackingConnectorName, String trackingConnectorDefaultUrl, String webUrl) { |
|
| 12 | + super(); |
|
| 13 | + this.trackingConnectorName = trackingConnectorName; |
|
| 14 | + TtackingConnectorDefaultUrl = trackingConnectorDefaultUrl; |
|
| 15 | + this.webUrl = webUrl; |
|
| 16 | + } |
|
| 17 | + |
|
| 18 | + public String getTrackingConnectorDefaultUrl() { |
|
| 19 | + return TtackingConnectorDefaultUrl; |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + public String getTrackingConnectorName() { |
|
| 23 | + return trackingConnectorName; |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public String getWebUrl() { |
|
| 27 | + return webUrl; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + @Override |
|
| 31 | + public int hashCode() { |
|
| 32 | + final int prime = 31; |
|
| 33 | + int result = 1; |
|
| 34 | + result = prime * result + ((TtackingConnectorDefaultUrl == null) ? 0 : TtackingConnectorDefaultUrl.hashCode()); |
|
| 35 | + result = prime * result + ((trackingConnectorName == null) ? 0 : trackingConnectorName.hashCode()); |
|
| 36 | + result = prime * result + ((webUrl == null) ? 0 : webUrl.hashCode()); |
|
| 37 | + return result; |
|
| 38 | + } |
|
| 39 | + |
|
| 40 | + @Override |
|
| 41 | + public boolean equals(Object obj) { |
|
| 42 | + if (this == obj) |
|
| 43 | + return true; |
|
| 44 | + if (obj == null) |
|
| 45 | + return false; |
|
| 46 | + if (getClass() != obj.getClass()) |
|
| 47 | + return false; |
|
| 48 | + TrackingConnectorInfoImpl other = (TrackingConnectorInfoImpl) obj; |
|
| 49 | + if (TtackingConnectorDefaultUrl == null) { |
|
| 50 | + if (other.TtackingConnectorDefaultUrl != null) |
|
| 51 | + return false; |
|
| 52 | + } else if (!TtackingConnectorDefaultUrl.equals(other.TtackingConnectorDefaultUrl)) |
|
| 53 | + return false; |
|
| 54 | + if (trackingConnectorName == null) { |
|
| 55 | + if (other.trackingConnectorName != null) |
|
| 56 | + return false; |
|
| 57 | + } else if (!trackingConnectorName.equals(other.trackingConnectorName)) |
|
| 58 | + return false; |
|
| 59 | + if (webUrl == null) { |
|
| 60 | + if (other.webUrl != null) |
|
| 61 | + return false; |
|
| 62 | + } else if (!webUrl.equals(other.webUrl)) |
|
| 63 | + return false; |
|
| 64 | + return true; |
|
| 65 | + } |
|
| 66 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/AddResult.java
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import javax.swing.plaf.basic.BasicSliderUI.TrackListener; |
|
| 4 | - |
|
| 5 | -/** |
|
| 6 | - * The result of trying to add a fix to a {@link Track}. Used when notifying {@link TrackListener}s. |
|
| 7 | - * This allows listeners, in particular, to distinguish between the add and replace scenario. |
|
| 8 | - * |
|
| 9 | - * @author Axel Uhl (D043530) |
|
| 10 | - * |
|
| 11 | - */ |
|
| 12 | -public enum AddResult { |
|
| 13 | - NOT_ADDED, ADDED, REPLACED; |
|
| 14 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/FixAcceptancePredicate.java
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -/** |
|
| 4 | - * A predicate for a fix, for use in |
|
| 5 | - * {@link Track#getInterpolatedValue(com.sap.sse.common.TimePoint, com.sap.sse.common.Util.Function)}, used, e.g., to |
|
| 6 | - * provide a rule for when a fix shall be accepted during the search for surrounding fixes. |
|
| 7 | - * |
|
| 8 | - * @author Axel Uhl (d043530) |
|
| 9 | - * |
|
| 10 | - * @param <FixType> |
|
| 11 | - */ |
|
| 12 | -public interface FixAcceptancePredicate<FixType> { |
|
| 13 | - boolean isAcceptFix(FixType fix); |
|
| 14 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/LineDetails.java
| ... | ... | @@ -1,57 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.base.Mark; |
|
| 4 | -import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | -import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | -import com.sap.sailing.domain.common.Position; |
|
| 7 | -import com.sap.sse.common.Bearing; |
|
| 8 | -import com.sap.sse.common.Distance; |
|
| 9 | -import com.sap.sse.common.TimePoint; |
|
| 10 | - |
|
| 11 | -/** |
|
| 12 | - * For a line such as a start or a finish line, tells the line's length at a given time, which side is |
|
| 13 | - * {@link NauticalSide#PORT port} and which is {@link NauticalSide#STARBOARD starboard} when approaching the line, |
|
| 14 | - * and---if wind information is available---its angle to a true wind direction and the advantageous side in approaching |
|
| 15 | - * direction as well as how much the advantageous side is ahead. The wind-dependent information |
|
| 16 | - * will all be <code>null</code> if no wind data is available. |
|
| 17 | - * |
|
| 18 | - * @author Axel Uhl (d043530) |
|
| 19 | - * |
|
| 20 | - */ |
|
| 21 | -public interface LineDetails { |
|
| 22 | - TimePoint getTimePoint(); |
|
| 23 | - |
|
| 24 | - Waypoint getWaypoint(); |
|
| 25 | - |
|
| 26 | - Distance getLength(); |
|
| 27 | - |
|
| 28 | - Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); |
|
| 29 | - |
|
| 30 | - NauticalSide getAdvantageousSideWhileApproachingLine(); |
|
| 31 | - |
|
| 32 | - Mark getStarboardMarkWhileApproachingLine(); |
|
| 33 | - |
|
| 34 | - Mark getPortMarkWhileApproachingLine(); |
|
| 35 | - |
|
| 36 | - Distance getAdvantage(); |
|
| 37 | - |
|
| 38 | - Position getPortMarkPosition(); |
|
| 39 | - |
|
| 40 | - Position getStarboardMarkPosition(); |
|
| 41 | - |
|
| 42 | - default Mark getAdvantageousMark() { |
|
| 43 | - return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkWhileApproachingLine() : getStarboardMarkWhileApproachingLine(); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - default Position getAdvantageousMarkPosition() { |
|
| 47 | - return getAdvantageousSideWhileApproachingLine() == NauticalSide.PORT ? getPortMarkPosition() : getStarboardMarkPosition(); |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - default Bearing getBearingFromStarboardToPortWhenApproachingLine() { |
|
| 51 | - return getStarboardMarkPosition().getBearingGreatCircle(getPortMarkPosition()); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - default Bearing getBearingFromPortToStarboardWhenApproachingLine() { |
|
| 55 | - return getPortMarkPosition().getBearingGreatCircle(getStarboardMarkPosition()); |
|
| 56 | - } |
|
| 57 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/MappedTrack.java
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import com.sap.sse.common.Timed; |
|
| 4 | - |
|
| 5 | -/** |
|
| 6 | - * {@link Track} specialization, which is mapped to a specific type of items. |
|
| 7 | - * |
|
| 8 | - * @param <ItemType> |
|
| 9 | - * the type of item this track is mapped to |
|
| 10 | - * @param <FixType> |
|
| 11 | - * the type of fix that is contained in this track |
|
| 12 | - */ |
|
| 13 | -public interface MappedTrack<ItemType, FixType extends Timed> extends Track<FixType> { |
|
| 14 | - |
|
| 15 | - /** |
|
| 16 | - * @return the item this track is mapped to. |
|
| 17 | - */ |
|
| 18 | - ItemType getTrackedItem(); |
|
| 19 | - |
|
| 20 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/Track.java
| ... | ... | @@ -1,259 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | -import java.util.Iterator; |
|
| 5 | -import java.util.concurrent.locks.ReadWriteLock; |
|
| 6 | -import java.util.function.Function; |
|
| 7 | - |
|
| 8 | -import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 9 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 10 | -import com.sap.sse.common.Duration; |
|
| 11 | -import com.sap.sse.common.TimePoint; |
|
| 12 | -import com.sap.sse.common.Timed; |
|
| 13 | -import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 14 | - |
|
| 15 | -/** |
|
| 16 | - * A track records {@link Timed} items for an object of type <code>ItemType</code>. It allows clients to ask for a value |
|
| 17 | - * close to a given {@link TimePoint}. The track manages a time-based set of raw fixes. An implementation may have an |
|
| 18 | - * understanding of how to eliminate outliers. For example, if a track implementation knows it's tracking boats, it may |
|
| 19 | - * consider fixes that the boat cannot possibly have reached due to its speed and direction change limitations as |
|
| 20 | - * outliers. The set of fixes with outliers filtered out can be obtained using {@link #getFixes} whereas |
|
| 21 | - * {@link #getRawFixes()} returns the unfiltered, raw fixes. If an implementation has no idea what an outlier is, |
|
| 22 | - * both methods will return the same fix sequence.<p> |
|
| 23 | - * |
|
| 24 | - * With tracks, concurrency is an important issue. Threads may want to modify a track while other threads may want to |
|
| 25 | - * read from it. Several methods such as {@link #getLastFixAtOrBefore(TimePoint)} return a single fix and can manage |
|
| 26 | - * concurrency internally. However, those methods returning a collection of fixes, such as {@link #getFixes()} or an |
|
| 27 | - * iterator over a collection of fixes, such as {@link #getFixesIterator(TimePoint, boolean)}, need special treatment. |
|
| 28 | - * Until we internalize such iterations (see bug 824, http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=824), |
|
| 29 | - * callers need to manage a read lock which is part of a {@link ReadWriteLock} managed by this track. Callers do so |
|
| 30 | - * by calling {@link #lockForRead} and {@link #unlockAfterRead}. |
|
| 31 | - * |
|
| 32 | - * @author Axel Uhl (d043530) |
|
| 33 | - */ |
|
| 34 | -public interface Track<FixType extends Timed> extends Serializable { |
|
| 35 | - /** |
|
| 36 | - * An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}. |
|
| 37 | - * |
|
| 38 | - * @author Axel Uhl (D043530) |
|
| 39 | - * |
|
| 40 | - * @param <T> |
|
| 41 | - */ |
|
| 42 | - static interface Adder<T> { |
|
| 43 | - /** |
|
| 44 | - * Adds two elements of type {@code T}. Neither argument must be {@code null}. |
|
| 45 | - */ |
|
| 46 | - T add(T t1, T t2); |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - static interface TimeRangeValueCalculator<T> { |
|
| 50 | - /** |
|
| 51 | - * Calculates a value for fixes across a time range. When the method is called, |
|
| 52 | - * a read lock will previously have been {@link Track#lockForRead obtained} before, |
|
| 53 | - * so an implementing class does not need to worry about acquiring the lock. |
|
| 54 | - */ |
|
| 55 | - T calculate(TimePoint from, TimePoint to); |
|
| 56 | - } |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * Locks this track for reading by the calling thread. If the thread already holds the lock for this track, |
|
| 60 | - * the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a <code>finally</code> |
|
| 61 | - * block to release the lock under all possible circumstances. Failure to do so will inevitably lead to |
|
| 62 | - * deadlocks! |
|
| 63 | - */ |
|
| 64 | - void lockForRead(); |
|
| 65 | - |
|
| 66 | - /** |
|
| 67 | - * Decrements the hold count for this track's read lock for the calling thread. If it goes to zero, the lock will be |
|
| 68 | - * released and other readers or a writer can obtain the lock. Make sure to call this method in a |
|
| 69 | - * <code>finally</code> block for each {@link #lockForRead()} invocation. |
|
| 70 | - */ |
|
| 71 | - void unlockAfterRead(); |
|
| 72 | - |
|
| 73 | - /** |
|
| 74 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 75 | - * will be thrown in case the caller has failed to do so. |
|
| 76 | - * |
|
| 77 | - * @return the smoothened fixes |
|
| 78 | - */ |
|
| 79 | - Iterable<FixType> getFixes(); |
|
| 80 | - |
|
| 81 | - /** |
|
| 82 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 83 | - * will be thrown in case the caller has failed to do so. |
|
| 84 | - * |
|
| 85 | - * @return The smoothened fixes between from and to. |
|
| 86 | - */ |
|
| 87 | - Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive); |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 91 | - * will be thrown in case the caller has failed to do so. |
|
| 92 | - */ |
|
| 93 | - Iterable<FixType> getRawFixes(); |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Returns <code>null</code> if no such fix exists. |
|
| 97 | - */ |
|
| 98 | - FixType getLastFixAtOrBefore(TimePoint timePoint); |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * Returns <code>null</code> if no such fix exists. |
|
| 102 | - */ |
|
| 103 | - FixType getLastFixBefore(TimePoint timePoint); |
|
| 104 | - |
|
| 105 | - /** |
|
| 106 | - * Returns <code>null</code> if no such fix exists. |
|
| 107 | - */ |
|
| 108 | - FixType getLastRawFixAtOrBefore(TimePoint timePoint); |
|
| 109 | - |
|
| 110 | - /** |
|
| 111 | - * Returns <code>null</code> if no such fix exists. |
|
| 112 | - */ |
|
| 113 | - FixType getFirstFixAtOrAfter(TimePoint timePoint); |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Returns <code>null</code> if no such fix exists. |
|
| 117 | - */ |
|
| 118 | - FixType getFirstRawFixAtOrAfter(TimePoint timePoint); |
|
| 119 | - |
|
| 120 | - /** |
|
| 121 | - * Returns <code>null</code> if no such fix exists. |
|
| 122 | - */ |
|
| 123 | - FixType getLastRawFixBefore(TimePoint timePoint); |
|
| 124 | - |
|
| 125 | - /** |
|
| 126 | - * Returns <code>null</code> if no such fix exists. |
|
| 127 | - */ |
|
| 128 | - FixType getFirstRawFixAfter(TimePoint timePoint); |
|
| 129 | - |
|
| 130 | - /** |
|
| 131 | - * Returns <code>null</code> if no such fix exists. |
|
| 132 | - */ |
|
| 133 | - FixType getFirstFixAfter(TimePoint timePoint); |
|
| 134 | - |
|
| 135 | - /** |
|
| 136 | - * The first fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 137 | - * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 138 | - */ |
|
| 139 | - FixType getFirstRawFix(); |
|
| 140 | - |
|
| 141 | - /** |
|
| 142 | - * The last fix in this track or <code>null</code> if the track is empty. The fix returned may |
|
| 143 | - * be an outlier that is not returned by calls operating on the smoothened version of the track. |
|
| 144 | - */ |
|
| 145 | - FixType getLastRawFix(); |
|
| 146 | - |
|
| 147 | - /** |
|
| 148 | - * Interpolates an aspect of the fixes in this track for a given {@code timePoint}. If {@code timePoint} matches |
|
| 149 | - * exactly a fix in this track, that fix is used. If this track is empty, {@code null} is returned. If the |
|
| 150 | - * {@code timePoint} is after the last fix of this track, the last fix is used; if before the first fix, the first |
|
| 151 | - * fix is used. |
|
| 152 | - * <p> |
|
| 153 | - * |
|
| 154 | - * The fix(es) are converted to {@link ScalableValue}s using the {@code converter} which gives callers a choice |
|
| 155 | - * which aspect of the fixes to project and interpolate. If more than one value results because two fixes (one |
|
| 156 | - * before, one after) are used, linear interpolation based on the fixes' time points takes place. |
|
| 157 | - * <p> |
|
| 158 | - * |
|
| 159 | - * Example: for a track of {@link GPSFixMoving} fixes the course over ground shall be determined for a given time |
|
| 160 | - * point. The call would look like this: |
|
| 161 | - * {@code getInterpolatedValue(timePoint, f->new ScalableBearing(f.getSpeed().getBearing()))} |
|
| 162 | - * |
|
| 163 | - * @return the projected interpolated value, typed by what the {@link ScalableValue#divide(double)} method returns. |
|
| 164 | - */ |
|
| 165 | - <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 166 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter); |
|
| 167 | - |
|
| 168 | - /** |
|
| 169 | - * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 170 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator exclude outliers (see |
|
| 171 | - * also {@link #getFixes()} and returns the remaining fixes without any smoothening or dampening applied. |
|
| 172 | - * |
|
| 173 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 174 | - * will be thrown in case the caller has failed to do so. |
|
| 175 | - */ |
|
| 176 | - Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 177 | - |
|
| 178 | - /** |
|
| 179 | - * Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case |
|
| 180 | - * <code>inclusive</code> is <code>true</code>) and that ends at the <code>endingAt</code> time point or just before |
|
| 181 | - * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the smoothened fixes (see |
|
| 182 | - * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 183 | - * |
|
| 184 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an |
|
| 185 | - * exception will be thrown in case the caller has failed to do so. |
|
| 186 | - * |
|
| 187 | - * @param startingAt |
|
| 188 | - * if <code>null</code>, starts with the first fix available |
|
| 189 | - * @param endingAt |
|
| 190 | - * if <code>null</code>., ends with the last fix available |
|
| 191 | - */ |
|
| 192 | - Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 193 | - |
|
| 194 | - /** |
|
| 195 | - * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 196 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 197 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 198 | - * |
|
| 199 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 200 | - * will be thrown in case the caller has failed to do so. |
|
| 201 | - */ |
|
| 202 | - Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive); |
|
| 203 | - |
|
| 204 | - /** |
|
| 205 | - * Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case |
|
| 206 | - * <code>startingAtInclusive</code> is <code>true</code>) and ending at the <code>endingAt</code> time point or just before |
|
| 207 | - * in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the raw fixes (see also |
|
| 208 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 209 | - * |
|
| 210 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 211 | - * will be thrown in case the caller has failed to do so. |
|
| 212 | - */ |
|
| 213 | - Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive); |
|
| 214 | - |
|
| 215 | - /** |
|
| 216 | - * Returns a descending iterator starting at the first fix before <code>startingAt</code> (or "at or before" in case |
|
| 217 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the smoothened fixes (see |
|
| 218 | - * also {@link #getFixes()}, without any smoothening or dampening applied. |
|
| 219 | - * |
|
| 220 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 221 | - * will be thrown in case the caller has failed to do so. |
|
| 222 | - */ |
|
| 223 | - Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 224 | - |
|
| 225 | - /** |
|
| 226 | - * Returns a descending iterator starting at the first raw fix before <code>startingAt</code> (or "at or before" in case |
|
| 227 | - * <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also |
|
| 228 | - * {@link #getRawFixes()}, without any smoothening or dampening applied. |
|
| 229 | - * |
|
| 230 | - * Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an exception |
|
| 231 | - * will be thrown in case the caller has failed to do so. |
|
| 232 | - */ |
|
| 233 | - Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive); |
|
| 234 | - |
|
| 235 | - /** |
|
| 236 | - * @return the average duration between two fixes (outliers removed) in this track or <code>null</code> if there is not |
|
| 237 | - * more than one fix in the track |
|
| 238 | - */ |
|
| 239 | - Duration getAverageIntervalBetweenFixes(); |
|
| 240 | - |
|
| 241 | - /** |
|
| 242 | - * @return the average duration between two fixes (outliers <em>not</em> removed) in this track or <code>null</code> if there is not |
|
| 243 | - * more than one raw fix in the track |
|
| 244 | - */ |
|
| 245 | - Duration getAverageIntervalBetweenRawFixes(); |
|
| 246 | - |
|
| 247 | - <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator); |
|
| 248 | - |
|
| 249 | - /** |
|
| 250 | - * @return the number of raw fixes contained in the Track. |
|
| 251 | - */ |
|
| 252 | - int size(); |
|
| 253 | - |
|
| 254 | - /** |
|
| 255 | - * Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty |
|
| 256 | - */ |
|
| 257 | - boolean isEmpty(); |
|
| 258 | - |
|
| 259 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/TrackingConnectorInfo.java
| ... | ... | @@ -1,28 +0,0 @@ |
| 1 | - |
|
| 2 | -package com.sap.sailing.domain.tracking; |
|
| 3 | - |
|
| 4 | -import java.io.Serializable; |
|
| 5 | - |
|
| 6 | -/** |
|
| 7 | - * Identifies the tracking connector that was used to create a TrackedRace. |
|
| 8 | - * Further the Connector can provide a webUrl, that leads to an event web page. |
|
| 9 | - */ |
|
| 10 | -public interface TrackingConnectorInfo extends Serializable { |
|
| 11 | - |
|
| 12 | - /** |
|
| 13 | - * gets the name associated with the tracking technology used for the Race |
|
| 14 | - */ |
|
| 15 | - String getTrackingConnectorName(); |
|
| 16 | - |
|
| 17 | - /** |
|
| 18 | - * gets a {@link String} representation of the default web-URL associated with the tracking technology used for the Race. |
|
| 19 | - * may be {@code null} if there is none provided in the adapter. |
|
| 20 | - */ |
|
| 21 | - String getTrackingConnectorDefaultUrl(); |
|
| 22 | - |
|
| 23 | - /** |
|
| 24 | - * gets a {@link String} representation of the web-URL associated with the Event. |
|
| 25 | - * may be {@code null} if the API of the respective Tracking-Service does not provide a URL. |
|
| 26 | - */ |
|
| 27 | - String getWebUrl(); |
|
| 28 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java
| ... | ... | @@ -1,91 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.base.Mark; |
|
| 4 | -import com.sap.sailing.domain.base.Waypoint; |
|
| 5 | -import com.sap.sailing.domain.common.NauticalSide; |
|
| 6 | -import com.sap.sailing.domain.common.Position; |
|
| 7 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 8 | -import com.sap.sse.common.Bearing; |
|
| 9 | -import com.sap.sse.common.Distance; |
|
| 10 | -import com.sap.sse.common.TimePoint; |
|
| 11 | - |
|
| 12 | -public class LineDetailsImpl implements LineDetails { |
|
| 13 | - private final TimePoint timePoint; |
|
| 14 | - private final Waypoint waypoint; |
|
| 15 | - private final Distance length; |
|
| 16 | - private final Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 17 | - private final NauticalSide advantageousSidewhileApproachingLine; |
|
| 18 | - private final Distance advantage; |
|
| 19 | - private final Mark portMarkWhileApproachingLine; |
|
| 20 | - private final Mark starboardMarkWhileApproachingLine; |
|
| 21 | - private final Position portMarkPosition; |
|
| 22 | - private final Position starboardMarkPosition; |
|
| 23 | - |
|
| 24 | - public LineDetailsImpl(TimePoint timePoint, Waypoint waypoint, Distance length, |
|
| 25 | - Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind, NauticalSide advantageousSideWhileApproachingLine, |
|
| 26 | - Distance advantage, Mark portMarkWhileApproachingLine, Mark starboardMarkWhileApproachingLine, |
|
| 27 | - Position portMarkPosition, Position starboardMarkPosition) { |
|
| 28 | - super(); |
|
| 29 | - this.timePoint = timePoint; |
|
| 30 | - this.waypoint = waypoint; |
|
| 31 | - this.length = length; |
|
| 32 | - this.angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 33 | - this.advantageousSidewhileApproachingLine = advantageousSideWhileApproachingLine; |
|
| 34 | - this.advantage = advantage; |
|
| 35 | - this.portMarkWhileApproachingLine = portMarkWhileApproachingLine; |
|
| 36 | - this.starboardMarkWhileApproachingLine = starboardMarkWhileApproachingLine; |
|
| 37 | - this.portMarkPosition = portMarkPosition; |
|
| 38 | - this.starboardMarkPosition = starboardMarkPosition; |
|
| 39 | - } |
|
| 40 | - |
|
| 41 | - @Override |
|
| 42 | - public TimePoint getTimePoint() { |
|
| 43 | - return timePoint; |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - @Override |
|
| 47 | - public Waypoint getWaypoint() { |
|
| 48 | - return waypoint; |
|
| 49 | - } |
|
| 50 | - |
|
| 51 | - @Override |
|
| 52 | - public Distance getLength() { |
|
| 53 | - return length; |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - @Override |
|
| 57 | - public Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind() { |
|
| 58 | - return angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; |
|
| 59 | - } |
|
| 60 | - |
|
| 61 | - @Override |
|
| 62 | - public NauticalSide getAdvantageousSideWhileApproachingLine() { |
|
| 63 | - return advantageousSidewhileApproachingLine; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - @Override |
|
| 67 | - public Distance getAdvantage() { |
|
| 68 | - return advantage; |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - @Override |
|
| 72 | - public Mark getStarboardMarkWhileApproachingLine() { |
|
| 73 | - return starboardMarkWhileApproachingLine; |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public Mark getPortMarkWhileApproachingLine() { |
|
| 78 | - return portMarkWhileApproachingLine; |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - @Override |
|
| 82 | - public Position getPortMarkPosition() { |
|
| 83 | - return portMarkPosition; |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - @Override |
|
| 87 | - public Position getStarboardMarkPosition() { |
|
| 88 | - return starboardMarkPosition; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/MappedTrackImpl.java
| ... | ... | @@ -1,39 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.tracking.MappedTrack; |
|
| 4 | -import com.sap.sse.common.Timed; |
|
| 5 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 6 | - |
|
| 7 | -/** |
|
| 8 | - * Default implementation of {@link MappedTrack} interface. |
|
| 9 | - * |
|
| 10 | - * @param <ItemType> |
|
| 11 | - * the type of item this track is mapped to |
|
| 12 | - * @param <FixType> |
|
| 13 | - * the type of fix that is contained in this track |
|
| 14 | - */ |
|
| 15 | -public class MappedTrackImpl<ItemType, FixType extends Timed> extends TrackImpl<FixType> |
|
| 16 | - implements MappedTrack<ItemType, FixType> { |
|
| 17 | - |
|
| 18 | - private static final long serialVersionUID = 6165693342087329096L; |
|
| 19 | - |
|
| 20 | - private final ItemType trackedItem; |
|
| 21 | - |
|
| 22 | - /** @see TrackImpl#TrackImpl(String) */ |
|
| 23 | - public MappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) { |
|
| 24 | - super(nameForReadWriteLock); |
|
| 25 | - this.trackedItem = trackedItem; |
|
| 26 | - } |
|
| 27 | - |
|
| 28 | - /** @see TrackImpl#TrackImpl(ArrayListNavigableSet, String) */ |
|
| 29 | - protected MappedTrackImpl(ItemType trackedItem, ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 30 | - super(fixes, nameForReadWriteLock); |
|
| 31 | - this.trackedItem = trackedItem; |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - @Override |
|
| 35 | - public ItemType getTrackedItem() { |
|
| 36 | - return trackedItem; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/PartialNavigableSetView.java
| ... | ... | @@ -1,387 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.util.ArrayList; |
|
| 4 | -import java.util.Collection; |
|
| 5 | -import java.util.Comparator; |
|
| 6 | -import java.util.Iterator; |
|
| 7 | -import java.util.List; |
|
| 8 | -import java.util.NavigableSet; |
|
| 9 | -import java.util.NoSuchElementException; |
|
| 10 | -import java.util.SortedSet; |
|
| 11 | -import java.util.TreeSet; |
|
| 12 | - |
|
| 13 | -/** |
|
| 14 | - * A view on a {@link NavigableSet} which suppresses some entries based on some configurable rule. |
|
| 15 | - * The {@link #size()} operation is expensive because it requires a full scan. {@link #isEmpty()} is |
|
| 16 | - * much cheaper because it suffices to find one element passing the filter rule. The filtering rule |
|
| 17 | - * has to be expressed by subclsses implementing the {@link #isValid(Object)} method. |
|
| 18 | - * |
|
| 19 | - * @author Axel Uhl (d043530) |
|
| 20 | - * |
|
| 21 | - * @param <E> |
|
| 22 | - */ |
|
| 23 | -public abstract class PartialNavigableSetView<E> implements NavigableSet<E> { |
|
| 24 | - private final NavigableSet<E> set; |
|
| 25 | - |
|
| 26 | - private class PartialNavigableSetViewWithSameValidityAsEnclosing extends PartialNavigableSetView<E> { |
|
| 27 | - public PartialNavigableSetViewWithSameValidityAsEnclosing(NavigableSet<E> set) { |
|
| 28 | - super(set); |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - @Override |
|
| 32 | - protected boolean isValid(E e) { |
|
| 33 | - return PartialNavigableSetView.this.isValid(e); |
|
| 34 | - } |
|
| 35 | - } |
|
| 36 | - |
|
| 37 | - private class FilteringIterator implements Iterator<E> { |
|
| 38 | - /** |
|
| 39 | - * The iterator is always kept one step "ahead" in order to know whether there really is a next element. The |
|
| 40 | - * next valid element is fetched and stored in {@link #nextValid} and {@link #hasNext} is set to |
|
| 41 | - * <code>true</code>. |
|
| 42 | - */ |
|
| 43 | - private Iterator<E> iter; |
|
| 44 | - |
|
| 45 | - private E nextValid; |
|
| 46 | - |
|
| 47 | - private boolean hasNext; |
|
| 48 | - |
|
| 49 | - private boolean hasLastNext; |
|
| 50 | - |
|
| 51 | - private E lastNext; |
|
| 52 | - |
|
| 53 | - public FilteringIterator() { |
|
| 54 | - iter = getSet().iterator(); |
|
| 55 | - hasLastNext = false; |
|
| 56 | - advance(); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - private void advance() { |
|
| 60 | - if (iter.hasNext()) { |
|
| 61 | - E next = iter.next(); |
|
| 62 | - while (!isValid(next) && iter.hasNext()) { |
|
| 63 | - next = iter.next(); |
|
| 64 | - } |
|
| 65 | - if (isValid(next)) { |
|
| 66 | - nextValid = next; |
|
| 67 | - hasNext = true; |
|
| 68 | - } else { |
|
| 69 | - hasNext = false; |
|
| 70 | - } |
|
| 71 | - } else { |
|
| 72 | - hasNext = false; |
|
| 73 | - } |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public boolean hasNext() { |
|
| 78 | - return hasNext; |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - @Override |
|
| 82 | - public E next() { |
|
| 83 | - if (hasNext) { |
|
| 84 | - E result = nextValid; |
|
| 85 | - advance(); |
|
| 86 | - hasLastNext = true; |
|
| 87 | - lastNext = result; |
|
| 88 | - return result; |
|
| 89 | - } else { |
|
| 90 | - throw new NoSuchElementException(); |
|
| 91 | - } |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - @Override |
|
| 95 | - public void remove() { |
|
| 96 | - if (!hasLastNext) { |
|
| 97 | - throw new IllegalStateException("next() was not called before remove()"); |
|
| 98 | - } else { |
|
| 99 | - PartialNavigableSetView.this.remove(lastNext); |
|
| 100 | - hasLastNext = false; |
|
| 101 | - } |
|
| 102 | - } |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - public PartialNavigableSetView(NavigableSet<E> set) { |
|
| 106 | - this.set = set; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - /** |
|
| 110 | - * Subclasses need to implement this method. For elements to be eliminated from the view represented by this |
|
| 111 | - * object, return <code>false</code> for such an element being passed to this method. |
|
| 112 | - */ |
|
| 113 | - abstract protected boolean isValid(E e); |
|
| 114 | - |
|
| 115 | - @Override |
|
| 116 | - public Comparator<? super E> comparator() { |
|
| 117 | - return getSet().comparator(); |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - public NavigableSet<E> descendingSet() { |
|
| 121 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().descendingSet()); |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - @Override |
|
| 125 | - public Iterator<E> descendingIterator() { |
|
| 126 | - return descendingSet().iterator(); |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - @Override |
|
| 130 | - public E first() { |
|
| 131 | - E first = getSet().first(); |
|
| 132 | - while (first != null && !isValid(first)) { |
|
| 133 | - first = getSet().higher(first); |
|
| 134 | - } |
|
| 135 | - if (first == null) { |
|
| 136 | - throw new NoSuchElementException(); |
|
| 137 | - } else { |
|
| 138 | - return first; |
|
| 139 | - } |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - @Override |
|
| 143 | - public E last() { |
|
| 144 | - E last = getSet().last(); |
|
| 145 | - while (last != null && !isValid(last)) { |
|
| 146 | - last = getSet().lower(last); |
|
| 147 | - } |
|
| 148 | - if (last == null) { |
|
| 149 | - throw new NoSuchElementException(); |
|
| 150 | - } else { |
|
| 151 | - return last; |
|
| 152 | - } |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - @Override |
|
| 156 | - public int size() { |
|
| 157 | - int size = 0; |
|
| 158 | - for (E e : getSet()) { |
|
| 159 | - if (isValid(e)) { |
|
| 160 | - size++; |
|
| 161 | - } |
|
| 162 | - } |
|
| 163 | - return size; |
|
| 164 | - } |
|
| 165 | - |
|
| 166 | - @Override |
|
| 167 | - public boolean isEmpty() { |
|
| 168 | - for (E e : getSet()) { |
|
| 169 | - if (isValid(e)) { |
|
| 170 | - return false; |
|
| 171 | - } |
|
| 172 | - } |
|
| 173 | - return true; |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - @SuppressWarnings("unchecked") |
|
| 177 | - @Override |
|
| 178 | - public boolean contains(Object o) { |
|
| 179 | - return getSet().contains(o) && isValid((E) o); |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - @Override |
|
| 183 | - public Object[] toArray() { |
|
| 184 | - List<E> l = new ArrayList<E>(); |
|
| 185 | - for (E e : getSet()) { |
|
| 186 | - if (isValid(e)) { |
|
| 187 | - l.add(e); |
|
| 188 | - } |
|
| 189 | - } |
|
| 190 | - return l.toArray(); |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - @SuppressWarnings("unchecked") |
|
| 194 | - @Override |
|
| 195 | - public <T> T[] toArray(T[] a) { |
|
| 196 | - List<T> l = new ArrayList<T>(); |
|
| 197 | - for (E e : getSet()) { |
|
| 198 | - if (isValid(e)) { |
|
| 199 | - l.add((T) e); |
|
| 200 | - } |
|
| 201 | - } |
|
| 202 | - return l.toArray(a); |
|
| 203 | - |
|
| 204 | - } |
|
| 205 | - |
|
| 206 | - @Override |
|
| 207 | - public boolean add(E e) { |
|
| 208 | - return getSet().add(e); |
|
| 209 | - } |
|
| 210 | - |
|
| 211 | - @Override |
|
| 212 | - public boolean remove(Object o) { |
|
| 213 | - return getSet().remove(o); |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - @SuppressWarnings("unchecked") |
|
| 217 | - @Override |
|
| 218 | - public boolean containsAll(Collection<?> c) { |
|
| 219 | - for (Object o : c) { |
|
| 220 | - if (!isValid((E) o) || !getSet().contains(o)) { |
|
| 221 | - return false; |
|
| 222 | - } |
|
| 223 | - } |
|
| 224 | - return true; |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - @Override |
|
| 228 | - public boolean addAll(Collection<? extends E> c) { |
|
| 229 | - return getSet().addAll(c); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - @Override |
|
| 233 | - public boolean retainAll(Collection<?> c) { |
|
| 234 | - return getSet().retainAll(c); |
|
| 235 | - } |
|
| 236 | - |
|
| 237 | - @Override |
|
| 238 | - public boolean removeAll(Collection<?> c) { |
|
| 239 | - return getSet().removeAll(c); |
|
| 240 | - } |
|
| 241 | - |
|
| 242 | - @Override |
|
| 243 | - public void clear() { |
|
| 244 | - getSet().clear(); |
|
| 245 | - } |
|
| 246 | - |
|
| 247 | - @Override |
|
| 248 | - public E lower(E e) { |
|
| 249 | - E result = getSet().lower(e); |
|
| 250 | - while (result != null && !isValid(result)) { |
|
| 251 | - result = getSet().lower(result); |
|
| 252 | - } |
|
| 253 | - return result; |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - /** |
|
| 257 | - * goes one left on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 258 | - * <code>false</code> |
|
| 259 | - */ |
|
| 260 | - protected E lowerInternal(E e) { |
|
| 261 | - return getSet().lower(e); |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - /** |
|
| 265 | - * goes one right on the raw, unfiltered set and therefore may return fixes that have {@link #isValid(Object)}== |
|
| 266 | - * <code>false</code> |
|
| 267 | - */ |
|
| 268 | - protected E higherInternal(E e) { |
|
| 269 | - return getSet().higher(e); |
|
| 270 | - } |
|
| 271 | - |
|
| 272 | - @Override |
|
| 273 | - public E floor(E e) { |
|
| 274 | - E result = getSet().floor(e); |
|
| 275 | - while (result != null && !isValid(result)) { |
|
| 276 | - result = getSet().lower(result); |
|
| 277 | - } |
|
| 278 | - return result; |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - @Override |
|
| 282 | - public E ceiling(E e) { |
|
| 283 | - E result = getSet().ceiling(e); |
|
| 284 | - while (result != null && !isValid(result)) { |
|
| 285 | - result = getSet().higher(result); |
|
| 286 | - } |
|
| 287 | - return result; |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - @Override |
|
| 291 | - public E higher(E e) { |
|
| 292 | - E result = getSet().higher(e); |
|
| 293 | - while (result != null && !isValid(result)) { |
|
| 294 | - result = getSet().higher(result); |
|
| 295 | - } |
|
| 296 | - return result; |
|
| 297 | - } |
|
| 298 | - |
|
| 299 | - /** |
|
| 300 | - * Removes all raw fixes that have {@link #isValid(Object)}==<code>false</code> and the first element to have |
|
| 301 | - * {@link #isValid(Object)}==<code>true</code>. This latter element is returned. If no such element exists, |
|
| 302 | - * <code>null</code> is returned. It is hence possible that invalid raw fixes are removed but stil <code>null</code> |
|
| 303 | - * is returned. |
|
| 304 | - */ |
|
| 305 | - @Override |
|
| 306 | - public E pollFirst() { |
|
| 307 | - E result = getSet().first(); |
|
| 308 | - while (result != null && !isValid(result)) { |
|
| 309 | - getSet().remove(result); |
|
| 310 | - result = getSet().first(); |
|
| 311 | - } |
|
| 312 | - return result; |
|
| 313 | - } |
|
| 314 | - |
|
| 315 | - @Override |
|
| 316 | - public E pollLast() { |
|
| 317 | - E result = getSet().last(); |
|
| 318 | - while (result != null && !isValid(result)) { |
|
| 319 | - getSet().remove(result); |
|
| 320 | - result = getSet().last(); |
|
| 321 | - } |
|
| 322 | - return result; |
|
| 323 | - } |
|
| 324 | - |
|
| 325 | - @Override |
|
| 326 | - public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { |
|
| 327 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().subSet(fromElement, fromInclusive, toElement, toInclusive)); |
|
| 328 | - } |
|
| 329 | - |
|
| 330 | - @Override |
|
| 331 | - public NavigableSet<E> headSet(E toElement, boolean inclusive) { |
|
| 332 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().headSet(toElement, inclusive)); |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - @Override |
|
| 336 | - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { |
|
| 337 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(getSet().tailSet(fromElement, inclusive)); |
|
| 338 | - } |
|
| 339 | - |
|
| 340 | - @Override |
|
| 341 | - public NavigableSet<E> subSet(E fromElement, E toElement) { |
|
| 342 | - SortedSet<E> subSet = set.subSet(fromElement, toElement); |
|
| 343 | - if (subSet instanceof NavigableSet<?>) { |
|
| 344 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) subSet); |
|
| 345 | - } else { |
|
| 346 | - TreeSet<E> result = new TreeSet<E>(subSet); |
|
| 347 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 348 | - } |
|
| 349 | - } |
|
| 350 | - |
|
| 351 | - @Override |
|
| 352 | - public NavigableSet<E> headSet(E toElement) { |
|
| 353 | - SortedSet<E> headSet = set.headSet(toElement); |
|
| 354 | - if (headSet instanceof NavigableSet<?>) { |
|
| 355 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) headSet); |
|
| 356 | - } else { |
|
| 357 | - TreeSet<E> result = new TreeSet<E>(headSet); |
|
| 358 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 359 | - } |
|
| 360 | - } |
|
| 361 | - |
|
| 362 | - @Override |
|
| 363 | - public NavigableSet<E> tailSet(E fromElement) { |
|
| 364 | - SortedSet<E> tailSet = set.tailSet(fromElement); |
|
| 365 | - if (tailSet instanceof NavigableSet<?>) { |
|
| 366 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing((NavigableSet<E>) tailSet); |
|
| 367 | - } else { |
|
| 368 | - TreeSet<E> result = new TreeSet<E>(tailSet); |
|
| 369 | - return new PartialNavigableSetViewWithSameValidityAsEnclosing(result); |
|
| 370 | - } |
|
| 371 | - } |
|
| 372 | - |
|
| 373 | - |
|
| 374 | - @Override |
|
| 375 | - public Iterator<E> iterator() { |
|
| 376 | - return new FilteringIterator(); |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - @Override |
|
| 380 | - public String toString() { |
|
| 381 | - return new ArrayList<E>(this).toString(); |
|
| 382 | - } |
|
| 383 | - |
|
| 384 | - protected NavigableSet<E> getSet() { |
|
| 385 | - return set; |
|
| 386 | - } |
|
| 387 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java
| ... | ... | @@ -1,221 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.util.Comparator; |
|
| 4 | -import java.util.Iterator; |
|
| 5 | -import java.util.LinkedHashMap; |
|
| 6 | -import java.util.Map.Entry; |
|
| 7 | -import java.util.NavigableSet; |
|
| 8 | - |
|
| 9 | -import com.sap.sse.common.TimePoint; |
|
| 10 | -import com.sap.sse.common.Util; |
|
| 11 | -import com.sap.sse.common.Util.Pair; |
|
| 12 | -import com.sap.sse.concurrent.LockUtil; |
|
| 13 | -import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 14 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 15 | - |
|
| 16 | -/** |
|
| 17 | - * This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used in |
|
| 18 | - * a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of |
|
| 19 | - * pairs where the first pair component represents a <code>from</code> parameter used in the calculation's time range |
|
| 20 | - * and the second pair component represents the result of the calculation for this parameter combination. |
|
| 21 | - * <p> |
|
| 22 | - * |
|
| 23 | - * For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order |
|
| 24 | - * to be able to efficiently extend a cache entry for a single <code>to</code> fix, the navigable sets containing the |
|
| 25 | - * <code>from</code> fixes and results are ordered such that earlier fixes come later in the set. This way, extending |
|
| 26 | - * the cache entry for a <code>to</code> fix to an earlier <code>from</code> fix only requires appending to the set. |
|
| 27 | - * <p> |
|
| 28 | - * |
|
| 29 | - * <b>Invalidation</b>: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's |
|
| 30 | - * time point are removed from this cache. Additionally, the fix insertion may have an impact on the |
|
| 31 | - * {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and |
|
| 32 | - * therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the |
|
| 33 | - * cache entries for the time points at or after the previous fix also need to be removed. |
|
| 34 | - * <p> |
|
| 35 | - * |
|
| 36 | - * <b>Cache use</b>: When a result across a time range is to be computed the calculating method should first look for a |
|
| 37 | - * cache entry for the <code>to</code> parameter. If one is found, the earliest entry in the navigable set for the |
|
| 38 | - * navigable set of <code>from</code> and result values that is at or after the requested <code>from</code> time point |
|
| 39 | - * is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the |
|
| 40 | - * <code>from</code> value found in the cache as the new <code>to</code> value, and the <code>from</code> value |
|
| 41 | - * originally passed to the calculating method as <code>from</code> again. If no entry is found in the cache entry for |
|
| 42 | - * <code>to</code> that is at or after the requested <code>from</code> time, the result has to be computed "from |
|
| 43 | - * scratch." |
|
| 44 | - * <p> |
|
| 45 | - * |
|
| 46 | - * If a cache entry for <code>to</code> is not found, the latest cache entry before it is looked up. If one is found, |
|
| 47 | - * the result for the time range between the <code>to</code> time point requested and the <code>to</code> time point |
|
| 48 | - * found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is |
|
| 49 | - * computed by iterating backwards all the way to <code>from</code>. |
|
| 50 | - * <p> |
|
| 51 | - * |
|
| 52 | - * Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the |
|
| 53 | - * result to the cache. |
|
| 54 | - * |
|
| 55 | - * @author Axel Uhl (D043530) |
|
| 56 | - */ |
|
| 57 | -public class TimeRangeCache<T> { |
|
| 58 | - public static final int MAX_SIZE = 100; |
|
| 59 | - |
|
| 60 | - private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> timeRangeCache; |
|
| 61 | - |
|
| 62 | - /** |
|
| 63 | - * The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is |
|
| 64 | - * defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or |
|
| 65 | - * having been added by {@link #cache(TimePoint, TimePoint, Object)}. |
|
| 66 | - * <p> |
|
| 67 | - * |
|
| 68 | - * When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements, |
|
| 69 | - * the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading |
|
| 70 | - * and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash |
|
| 71 | - * map that counts access as "use" has a modifying effect on its internal structures. |
|
| 72 | - * <p> |
|
| 73 | - * |
|
| 74 | - * The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the |
|
| 75 | - * alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.<p> |
|
| 76 | - * |
|
| 77 | - * Read access is to be <code>synchronized<code> using this field's mutex; write access only happens under the |
|
| 78 | - * {@link #lock write lock} and therefore will have no contenders. |
|
| 79 | - */ |
|
| 80 | - private final LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void> lruCache; |
|
| 81 | - |
|
| 82 | - private final NamedReentrantReadWriteLock lock; |
|
| 83 | - |
|
| 84 | - private static final Comparator<Util.Pair<TimePoint, ?>> timePointInPairComparator = new Comparator<Util.Pair<TimePoint, ?>>() { |
|
| 85 | - @Override |
|
| 86 | - public int compare(Util.Pair<TimePoint, ?> o1, Util.Pair<TimePoint, ?> o2) { |
|
| 87 | - return o1.getA().compareTo(o2.getA()); |
|
| 88 | - } |
|
| 89 | - }; |
|
| 90 | - |
|
| 91 | - public TimeRangeCache(String nameForLockLogging) { |
|
| 92 | - lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true); |
|
| 93 | - this.timeRangeCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>>(timePointInPairComparator); |
|
| 94 | - this.lruCache = new LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void>(/* initial capacity */ 10, /* load factor */ 0.75f, |
|
| 95 | - /* access-based ordering */ true) { |
|
| 96 | - private static final long serialVersionUID = -6568235517111733193L; |
|
| 97 | - |
|
| 98 | - @Override |
|
| 99 | - protected boolean removeEldestEntry(Entry<Pair<TimePoint, TimePoint>, Void> eldest) { |
|
| 100 | - final boolean expunge = size() > MAX_SIZE; |
|
| 101 | - if (expunge) { |
|
| 102 | - removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB()); |
|
| 103 | - } |
|
| 104 | - return expunge; |
|
| 105 | - } |
|
| 106 | - }; |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - public int size() { |
|
| 110 | - return lruCache.size(); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - private void removeCacheEntry(TimePoint from, TimePoint to) { |
|
| 114 | - assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock |
|
| 115 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 116 | - if (entryForTo.getA().equals(to)) { |
|
| 117 | - Pair<TimePoint, T> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 118 | - if (entryForFrom.getA().equals(from)) { |
|
| 119 | - entryForTo.getB().remove(entryForFrom); |
|
| 120 | - if (entryForTo.getB().isEmpty()) { |
|
| 121 | - timeRangeCache.remove(entryForTo); |
|
| 122 | - } |
|
| 123 | - } |
|
| 124 | - } |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * Looks up the entry closest to but no later than <code>to</code>. If not found, <code>null</code> is returned. If |
|
| 129 | - * found, the earliest pair of from/result that is at or after <code>from</code> will be returned, together with |
|
| 130 | - * the <code>to</code> value of the entry. If there is no entry that is at or after <code>from</code>, |
|
| 131 | - * <code>null</code> is returned. |
|
| 132 | - */ |
|
| 133 | - public Util.Pair<TimePoint, Util.Pair<TimePoint, T>> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) { |
|
| 134 | - LockUtil.lockForRead(lock); |
|
| 135 | - try { |
|
| 136 | - Util.Pair<TimePoint, Util.Pair<TimePoint, T>> result = null; |
|
| 137 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to)); |
|
| 138 | - if (entryForTo != null) { |
|
| 139 | - final Util.Pair<TimePoint, T> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null)); |
|
| 140 | - if (fromCeiling != null) { |
|
| 141 | - result = new Util.Pair<TimePoint, Util.Pair<TimePoint, T>>(entryForTo.getA(), fromCeiling); |
|
| 142 | - } |
|
| 143 | - } |
|
| 144 | - // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using |
|
| 145 | - // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry |
|
| 146 | - // modifies the internal parts of the data structure which is not thread safe. |
|
| 147 | - synchronized (lruCache) { // ping the "perfect match" although it may not even have existed in the cache |
|
| 148 | - lruCache.get(new Util.Pair<TimePoint, TimePoint>(from, to)); |
|
| 149 | - } |
|
| 150 | - return result; |
|
| 151 | - } finally { |
|
| 152 | - LockUtil.unlockAfterRead(lock); |
|
| 153 | - } |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - /** |
|
| 157 | - * Removes all cache entries that have a <code>to</code> time point that is at or after <code>timePoint</code>. |
|
| 158 | - */ |
|
| 159 | - public void invalidateAllAtOrLaterThan(TimePoint timePoint) { |
|
| 160 | - LockUtil.lockForWrite(lock); |
|
| 161 | - try { |
|
| 162 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummy = createDummy(timePoint); |
|
| 163 | - NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true); |
|
| 164 | - for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> i=toRemove.iterator(); i.hasNext(); ) { |
|
| 165 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryToRemove = i.next(); |
|
| 166 | - assert entryToRemove.getA().compareTo(timePoint) >= 0; |
|
| 167 | - for (Pair<TimePoint, T> fromAndResult : entryToRemove.getB()) { |
|
| 168 | - lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA())); |
|
| 169 | - } |
|
| 170 | - i.remove(); |
|
| 171 | - } |
|
| 172 | - } finally { |
|
| 173 | - LockUtil.unlockAfterWrite(lock); |
|
| 174 | - } |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - private NavigableSet<Util.Pair<TimePoint, T>> getEntryForTo(TimePoint to) { |
|
| 178 | - NavigableSet<Util.Pair<TimePoint, T>> result = null; |
|
| 179 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummyForTo = createDummy(to); |
|
| 180 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(dummyForTo); |
|
| 181 | - if (entryForTo != null && entryForTo.getA().equals(to)) { |
|
| 182 | - result = entryForTo.getB(); |
|
| 183 | - } |
|
| 184 | - return result; |
|
| 185 | - } |
|
| 186 | - |
|
| 187 | - public void cache(TimePoint from, TimePoint to, T result) { |
|
| 188 | - LockUtil.lockForWrite(lock); |
|
| 189 | - try { |
|
| 190 | - NavigableSet<Util.Pair<TimePoint, T>> entryForTo = getEntryForTo(to); |
|
| 191 | - if (entryForTo == null) { |
|
| 192 | - entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, T>>(timePointInPairComparator); |
|
| 193 | - Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>( |
|
| 194 | - to, entryForTo); |
|
| 195 | - timeRangeCache.add(pairForTo); |
|
| 196 | - } |
|
| 197 | - entryForTo.add(new Util.Pair<TimePoint, T>(from, result)); |
|
| 198 | - lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null); |
|
| 199 | - } finally { |
|
| 200 | - LockUtil.unlockAfterWrite(lock); |
|
| 201 | - } |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> createDummy(TimePoint to) { |
|
| 205 | - return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(to, null); |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - /** |
|
| 209 | - * Removes all contents from this cache |
|
| 210 | - */ |
|
| 211 | - public void clear() { |
|
| 212 | - LockUtil.lockForWrite(lock); |
|
| 213 | - try { |
|
| 214 | - timeRangeCache.clear(); |
|
| 215 | - lruCache.clear(); |
|
| 216 | - } finally { |
|
| 217 | - LockUtil.unlockAfterWrite(lock); |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - } |
|
| 221 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimedComparator.java
| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.io.Serializable; |
|
| 4 | -import java.util.Comparator; |
|
| 5 | - |
|
| 6 | -import com.sap.sse.common.Timed; |
|
| 7 | - |
|
| 8 | -public class TimedComparator implements Comparator<Timed>, Serializable { |
|
| 9 | - private static final long serialVersionUID = 1604511471599854988L; |
|
| 10 | - public static final Comparator<Timed> INSTANCE = new TimedComparator(); |
|
| 11 | - |
|
| 12 | - @Override |
|
| 13 | - public int compare(Timed o1, Timed o2) { |
|
| 14 | - return o1.getTimePoint().compareTo(o2.getTimePoint()); |
|
| 15 | - } |
|
| 16 | -} |
|
| 17 | - |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java
| ... | ... | @@ -1,544 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import java.io.IOException; |
|
| 4 | -import java.io.ObjectOutputStream; |
|
| 5 | -import java.util.ConcurrentModificationException; |
|
| 6 | -import java.util.Iterator; |
|
| 7 | -import java.util.NavigableSet; |
|
| 8 | -import java.util.function.Function; |
|
| 9 | - |
|
| 10 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 11 | -import com.sap.sailing.domain.tracking.FixAcceptancePredicate; |
|
| 12 | -import com.sap.sailing.domain.tracking.Track; |
|
| 13 | -import com.sap.sse.common.Duration; |
|
| 14 | -import com.sap.sse.common.TimePoint; |
|
| 15 | -import com.sap.sse.common.Timed; |
|
| 16 | -import com.sap.sse.common.Util; |
|
| 17 | -import com.sap.sse.common.Util.Pair; |
|
| 18 | -import com.sap.sse.common.scalablevalue.ScalableValue; |
|
| 19 | -import com.sap.sse.concurrent.LockUtil; |
|
| 20 | -import com.sap.sse.concurrent.NamedReentrantReadWriteLock; |
|
| 21 | -import com.sap.sse.shared.util.impl.ArrayListNavigableSet; |
|
| 22 | -import com.sap.sse.shared.util.impl.UnmodifiableNavigableSet; |
|
| 23 | - |
|
| 24 | -public class TrackImpl<FixType extends Timed> implements Track<FixType> { |
|
| 25 | - private static final long serialVersionUID = -4075853657857657528L; |
|
| 26 | - /** |
|
| 27 | - * The fixes, ordered by their time points |
|
| 28 | - */ |
|
| 29 | - private final ArrayListNavigableSet<Timed> fixes; |
|
| 30 | - |
|
| 31 | - private final NamedReentrantReadWriteLock readWriteLock; |
|
| 32 | - |
|
| 33 | - protected static class DummyTimed implements Timed { |
|
| 34 | - private static final long serialVersionUID = 6047311973718918856L; |
|
| 35 | - private final TimePoint timePoint; |
|
| 36 | - public DummyTimed(TimePoint timePoint) { |
|
| 37 | - super(); |
|
| 38 | - this.timePoint = timePoint; |
|
| 39 | - } |
|
| 40 | - @Override |
|
| 41 | - public TimePoint getTimePoint() { |
|
| 42 | - return timePoint; |
|
| 43 | - } |
|
| 44 | - @Override |
|
| 45 | - public String toString() { |
|
| 46 | - return timePoint.toString(); |
|
| 47 | - } |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - public TrackImpl(String nameForReadWriteLock) { |
|
| 51 | - this(new ArrayListNavigableSet<Timed>(TimedComparator.INSTANCE), nameForReadWriteLock); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - protected TrackImpl(ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) { |
|
| 55 | - this.readWriteLock = new NamedReentrantReadWriteLock(nameForReadWriteLock, /* fair */ false); |
|
| 56 | - this.fixes = fixes; |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * Synchronize the serialization such that no fixes are added while serializing |
|
| 61 | - */ |
|
| 62 | - private void writeObject(ObjectOutputStream s) throws IOException { |
|
| 63 | - lockForRead(); |
|
| 64 | - try { |
|
| 65 | - s.defaultWriteObject(); |
|
| 66 | - } finally { |
|
| 67 | - unlockAfterRead(); |
|
| 68 | - } |
|
| 69 | - } |
|
| 70 | - |
|
| 71 | - @Override |
|
| 72 | - public void lockForRead() { |
|
| 73 | - LockUtil.lockForRead(readWriteLock); |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - @Override |
|
| 77 | - public void unlockAfterRead() { |
|
| 78 | - LockUtil.unlockAfterRead(readWriteLock); |
|
| 79 | - } |
|
| 80 | - |
|
| 81 | - protected void lockForWrite() { |
|
| 82 | - LockUtil.lockForWrite(readWriteLock); |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - protected void unlockAfterWrite() { |
|
| 86 | - LockUtil.unlockAfterWrite(readWriteLock); |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and {@link #unlockAfterRead()} |
|
| 91 | - * to avoid {@link ConcurrentModificationException}s. Should they modify the structure returned, they have to use |
|
| 92 | - * {@link #lockForWrite()} and {@link #unlockAfterWrite()}, respectively. |
|
| 93 | - */ |
|
| 94 | - protected NavigableSet<FixType> getInternalRawFixes() { |
|
| 95 | - @SuppressWarnings("unchecked") |
|
| 96 | - NavigableSet<FixType> result = (NavigableSet<FixType>) fixes; |
|
| 97 | - return result; |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * asserts that the calling thread holds at least one of read and write lock |
|
| 102 | - */ |
|
| 103 | - protected void assertReadLock() { |
|
| 104 | - if (readWriteLock.getReadHoldCount() < 1 && readWriteLock.getWriteHoldCount() < 1) { |
|
| 105 | - throw new IllegalStateException("Caller must obtain read lock using lockForRead() before calling this method"); |
|
| 106 | - } |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - protected void assertWriteLock() { |
|
| 110 | - if (readWriteLock.getWriteHoldCount() < 1) { |
|
| 111 | - throw new IllegalStateException("Caller must obtain write lock using lockForWrite() before calling this method"); |
|
| 112 | - } |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and |
|
| 117 | - * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s. |
|
| 118 | - * |
|
| 119 | - * @return the smoothened fixes ordered by their time points; this implementation simply delegates to |
|
| 120 | - * {@link #getInternalRawFixes()} because for only {@link Timed} fixes we can't know how to remove outliers. |
|
| 121 | - * Subclasses that constrain the <code>FixType</code> may provide smoothening implementations. |
|
| 122 | - */ |
|
| 123 | - protected NavigableSet<FixType> getInternalFixes() { |
|
| 124 | - NavigableSet<FixType> result = getInternalRawFixes(); |
|
| 125 | - return result; |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - /** |
|
| 129 | - * Iterates the fixes with outliers getting skipped, in the order of their time points. |
|
| 130 | - * Relies on {@link #getInternalFixes()} to void the track view from outliers. |
|
| 131 | - */ |
|
| 132 | - @Override |
|
| 133 | - public NavigableSet<FixType> getFixes() { |
|
| 134 | - assertReadLock(); |
|
| 135 | - return new UnmodifiableNavigableSet<FixType>(getInternalFixes()); |
|
| 136 | - } |
|
| 137 | - |
|
| 138 | - @Override |
|
| 139 | - public Iterable<FixType> getFixes(TimePoint from, boolean fromInclusive, TimePoint to, boolean toInclusive) { |
|
| 140 | - return getFixes().subSet(getDummyFix(from), fromInclusive, getDummyFix(to), toInclusive); |
|
| 141 | - } |
|
| 142 | - |
|
| 143 | - /** |
|
| 144 | - * Iterates over the raw sequence of fixes, all potential outliers included |
|
| 145 | - */ |
|
| 146 | - @Override |
|
| 147 | - public NavigableSet<FixType> getRawFixes() { |
|
| 148 | - assertReadLock(); |
|
| 149 | - return new UnmodifiableNavigableSet<FixType>(getInternalRawFixes()); |
|
| 150 | - } |
|
| 151 | - |
|
| 152 | - @Override |
|
| 153 | - public FixType getLastFixAtOrBefore(TimePoint timePoint) { |
|
| 154 | - return getLastFixAtOrBefore(timePoint, /* fixAcceptancePredicate == null means accept all */ null); |
|
| 155 | - } |
|
| 156 | - |
|
| 157 | - private FixType getLastFixAtOrBefore(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 158 | - lockForRead(); |
|
| 159 | - try { |
|
| 160 | - final NavigableSet<FixType> headSet = getInternalFixes().headSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 161 | - for (final Iterator<FixType> i=headSet.descendingIterator(); i.hasNext(); ) { |
|
| 162 | - final FixType next = i.next(); |
|
| 163 | - if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 164 | - return next; |
|
| 165 | - } |
|
| 166 | - } |
|
| 167 | - return null; |
|
| 168 | - } finally { |
|
| 169 | - unlockAfterRead(); |
|
| 170 | - } |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - @Override |
|
| 174 | - public FixType getLastFixBefore(TimePoint timePoint) { |
|
| 175 | - lockForRead(); |
|
| 176 | - try { |
|
| 177 | - return (FixType) getInternalFixes().lower(getDummyFix(timePoint)); |
|
| 178 | - } finally { |
|
| 179 | - unlockAfterRead(); |
|
| 180 | - } |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - @Override |
|
| 184 | - public FixType getLastRawFixAtOrBefore(TimePoint timePoint) { |
|
| 185 | - lockForRead(); |
|
| 186 | - try { |
|
| 187 | - return (FixType) getInternalRawFixes().floor(getDummyFix(timePoint)); |
|
| 188 | - } finally { |
|
| 189 | - unlockAfterRead(); |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - @Override |
|
| 194 | - public FixType getFirstRawFixAtOrAfter(TimePoint timePoint) { |
|
| 195 | - lockForRead(); |
|
| 196 | - try { |
|
| 197 | - return (FixType) getInternalRawFixes().ceiling(getDummyFix(timePoint)); |
|
| 198 | - } finally { |
|
| 199 | - unlockAfterRead(); |
|
| 200 | - } |
|
| 201 | - } |
|
| 202 | - |
|
| 203 | - @Override |
|
| 204 | - public FixType getFirstFixAtOrAfter(TimePoint timePoint) { |
|
| 205 | - return getFirstFixAtOrAfter(timePoint, /* fixAcceptancePredicate==null means accept all fixes */ null); |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - private FixType getFirstFixAtOrAfter(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 209 | - lockForRead(); |
|
| 210 | - try { |
|
| 211 | - final NavigableSet<FixType> tailSet = getInternalFixes().tailSet(getDummyFix(timePoint), /* inclusive */ true); |
|
| 212 | - for (final FixType next : tailSet) { |
|
| 213 | - if (fixAcceptancePredicate == null || fixAcceptancePredicate.isAcceptFix(next)) { |
|
| 214 | - return next; |
|
| 215 | - } |
|
| 216 | - } |
|
| 217 | - return null; |
|
| 218 | - } finally { |
|
| 219 | - unlockAfterRead(); |
|
| 220 | - } |
|
| 221 | - } |
|
| 222 | - |
|
| 223 | - @Override |
|
| 224 | - public FixType getLastRawFixBefore(TimePoint timePoint) { |
|
| 225 | - lockForRead(); |
|
| 226 | - try { |
|
| 227 | - return (FixType) getInternalRawFixes().lower(getDummyFix(timePoint)); |
|
| 228 | - } finally { |
|
| 229 | - unlockAfterRead(); |
|
| 230 | - } |
|
| 231 | - } |
|
| 232 | - |
|
| 233 | - @Override |
|
| 234 | - public FixType getFirstFixAfter(TimePoint timePoint) { |
|
| 235 | - lockForRead(); |
|
| 236 | - try { |
|
| 237 | - return (FixType) getInternalFixes().higher(getDummyFix(timePoint)); |
|
| 238 | - } finally { |
|
| 239 | - unlockAfterRead(); |
|
| 240 | - } |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - @Override |
|
| 244 | - public FixType getFirstRawFixAfter(TimePoint timePoint) { |
|
| 245 | - lockForRead(); |
|
| 246 | - try { |
|
| 247 | - return (FixType) getInternalRawFixes().higher(getDummyFix(timePoint)); |
|
| 248 | - } finally { |
|
| 249 | - unlockAfterRead(); |
|
| 250 | - } |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - @Override |
|
| 254 | - public FixType getFirstRawFix() { |
|
| 255 | - lockForRead(); |
|
| 256 | - try { |
|
| 257 | - if (getInternalFixes().isEmpty()) { |
|
| 258 | - return null; |
|
| 259 | - } else { |
|
| 260 | - return (FixType) getInternalFixes().first(); |
|
| 261 | - } |
|
| 262 | - } finally { |
|
| 263 | - unlockAfterRead(); |
|
| 264 | - } |
|
| 265 | - } |
|
| 266 | - |
|
| 267 | - @Override |
|
| 268 | - public FixType getLastRawFix() { |
|
| 269 | - lockForRead(); |
|
| 270 | - try { |
|
| 271 | - if (getInternalRawFixes().isEmpty()) { |
|
| 272 | - return null; |
|
| 273 | - } else { |
|
| 274 | - return (FixType) getInternalRawFixes().last(); |
|
| 275 | - } |
|
| 276 | - } finally { |
|
| 277 | - unlockAfterRead(); |
|
| 278 | - } |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - /** |
|
| 282 | - * @param fixAcceptancePredicate |
|
| 283 | - * if not {@code null}, adjacent fixes will be skipped as long as this predicate does not |
|
| 284 | - * {@link FixAcceptancePredicate#isAcceptFix(Object) accept} the fix. This can, e.g., be used to skip |
|
| 285 | - * fixes that don't have values in a dimension required. If {@code null}, the next fixes left and right |
|
| 286 | - * (including the exact {@code timePoint} if a fix exists there) will be used without further check. |
|
| 287 | - */ |
|
| 288 | - private Pair<FixType, FixType> getSurroundingFixes(TimePoint timePoint, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 289 | - FixType left = getLastFixAtOrBefore(timePoint, fixAcceptancePredicate); |
|
| 290 | - FixType right = getFirstFixAtOrAfter(timePoint, fixAcceptancePredicate); |
|
| 291 | - com.sap.sse.common.Util.Pair<FixType, FixType> result = new com.sap.sse.common.Util.Pair<>(left, right); |
|
| 292 | - return result; |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) { |
|
| 296 | - final T acc; |
|
| 297 | - if (timePoint1.equals(timePoint2)) { |
|
| 298 | - acc = value1.add(value2).divide(2); |
|
| 299 | - } else { |
|
| 300 | - long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis()); |
|
| 301 | - long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis()); |
|
| 302 | - acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2); |
|
| 303 | - } |
|
| 304 | - return acc; |
|
| 305 | - } |
|
| 306 | - |
|
| 307 | - @Override |
|
| 308 | - public <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 309 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter) { |
|
| 310 | - return getInterpolatedValue(timePoint, converter, /* fixAcceptancePredicate==null means accept all */ null); |
|
| 311 | - } |
|
| 312 | - |
|
| 313 | - protected <InternalType, ValueType> ValueType getInterpolatedValue(TimePoint timePoint, |
|
| 314 | - Function<FixType, ScalableValue<InternalType, ValueType>> converter, FixAcceptancePredicate<FixType> fixAcceptancePredicate) { |
|
| 315 | - final ValueType result; |
|
| 316 | - Pair<FixType, FixType> fixPair = getSurroundingFixes(timePoint, fixAcceptancePredicate); |
|
| 317 | - if (fixPair.getA() == null) { |
|
| 318 | - if (fixPair.getB() == null) { |
|
| 319 | - result = null; |
|
| 320 | - } else { |
|
| 321 | - result = converter.apply(fixPair.getB()).divide(1); |
|
| 322 | - } |
|
| 323 | - } else { |
|
| 324 | - if (fixPair.getB() == null || fixPair.getA() == fixPair.getB()) { |
|
| 325 | - result = converter.apply(fixPair.getA()).divide(1); |
|
| 326 | - } else { |
|
| 327 | - result = timeBasedAverage(timePoint, |
|
| 328 | - converter.apply(fixPair.getA()), fixPair.getA().getTimePoint(), |
|
| 329 | - converter.apply(fixPair.getB()), fixPair.getB().getTimePoint()); |
|
| 330 | - } |
|
| 331 | - } |
|
| 332 | - return result; |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - @Override |
|
| 336 | - public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 337 | - assertReadLock(); |
|
| 338 | - return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 339 | - } |
|
| 340 | - |
|
| 341 | - @Override |
|
| 342 | - public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, |
|
| 343 | - boolean endingAtInclusive) { |
|
| 344 | - assertReadLock(); |
|
| 345 | - return getTimeConstrainedFixesIterator(getInternalFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 346 | - } |
|
| 347 | - |
|
| 348 | - @Override |
|
| 349 | - public Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 350 | - assertReadLock(); |
|
| 351 | - Iterator<FixType> result = (Iterator<FixType>) getInternalFixes().headSet( |
|
| 352 | - getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 353 | - return result; |
|
| 354 | - } |
|
| 355 | - |
|
| 356 | - /** |
|
| 357 | - * Creates a dummy fix that conforms to <code>FixType</code>. This in particular means that subclasses |
|
| 358 | - * instantiating <code>FixType</code> with a specific class need to redefine this method so as to return |
|
| 359 | - * a dummy fix complying with their instantiation type used for <code>FixType</code>. Otherwise, a |
|
| 360 | - * {@link ClassCastException} may result upon certain operations performed with the fix returned by |
|
| 361 | - * this method. |
|
| 362 | - */ |
|
| 363 | - protected FixType getDummyFix(TimePoint timePoint) { |
|
| 364 | - @SuppressWarnings("unchecked") |
|
| 365 | - FixType result = (FixType) new DummyTimed(timePoint); |
|
| 366 | - return result; |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - @Override |
|
| 370 | - public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean inclusive) { |
|
| 371 | - assertReadLock(); |
|
| 372 | - return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, inclusive, /* endingAt */ null, /* endingAtInclusive */ false); |
|
| 373 | - } |
|
| 374 | - |
|
| 375 | - private Iterator<FixType> getTimeConstrainedFixesIterator(NavigableSet<FixType> set, TimePoint startingAt, boolean startingAtInclusive, |
|
| 376 | - TimePoint endingAt, boolean endingAtInclusive) { |
|
| 377 | - assertReadLock(); |
|
| 378 | - if (startingAt != null && endingAt != null) { |
|
| 379 | - set = set.subSet(getDummyFix(startingAt), startingAtInclusive, getDummyFix(endingAt), endingAtInclusive); |
|
| 380 | - } else if (endingAt != null) { |
|
| 381 | - set = set.headSet(getDummyFix(endingAt), endingAtInclusive); |
|
| 382 | - } else if (startingAt != null) { |
|
| 383 | - set = set.tailSet(getDummyFix(startingAt), startingAtInclusive); |
|
| 384 | - } |
|
| 385 | - Iterator<FixType> result = set.iterator(); |
|
| 386 | - return result; |
|
| 387 | - } |
|
| 388 | - |
|
| 389 | - @Override |
|
| 390 | - public Iterator<FixType> getRawFixesIterator(TimePoint startingAt, boolean startingAtInclusive, |
|
| 391 | - TimePoint endingAt, boolean endingAtInclusive) { |
|
| 392 | - assertReadLock(); |
|
| 393 | - return getTimeConstrainedFixesIterator(getInternalRawFixes(), startingAt, startingAtInclusive, endingAt, endingAtInclusive); |
|
| 394 | - } |
|
| 395 | - |
|
| 396 | - @Override |
|
| 397 | - public Iterator<FixType> getRawFixesDescendingIterator(TimePoint startingAt, boolean inclusive) { |
|
| 398 | - assertReadLock(); |
|
| 399 | - Iterator<FixType> result = (Iterator<FixType>) getInternalRawFixes().headSet( |
|
| 400 | - getDummyFix(startingAt), inclusive).descendingIterator(); |
|
| 401 | - return result; |
|
| 402 | - } |
|
| 403 | - |
|
| 404 | - protected boolean add(FixType fix) { |
|
| 405 | - return add(fix, /* replace */ false); |
|
| 406 | - } |
|
| 407 | - |
|
| 408 | - /** |
|
| 409 | - * @return {@code true} if the fix was added or replaced; {@code false} in case no change was performed |
|
| 410 | - */ |
|
| 411 | - protected boolean add(FixType fix, boolean replace) { |
|
| 412 | - lockForWrite(); |
|
| 413 | - try { |
|
| 414 | - final AddResult addResult = addWithoutLocking(fix, replace); |
|
| 415 | - return addResult == AddResult.ADDED || addResult == AddResult.REPLACED; |
|
| 416 | - } finally { |
|
| 417 | - unlockAfterWrite(); |
|
| 418 | - } |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - /** |
|
| 422 | - * The caller must ensure to hold the write lock for this track when calling this method. |
|
| 423 | - * |
|
| 424 | - * @param replace |
|
| 425 | - * whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the |
|
| 426 | - * comparator used for the {@link #fixes} set. By default this is a comparator only comparing the |
|
| 427 | - * fixes' time stamps. Subclasses may use different comparator implementations. |
|
| 428 | - */ |
|
| 429 | - protected AddResult addWithoutLocking(FixType fix, boolean replace) { |
|
| 430 | - final AddResult result; |
|
| 431 | - final boolean added = getInternalRawFixes().add(fix); |
|
| 432 | - if (!added && replace) { |
|
| 433 | - getInternalRawFixes().remove(fix); |
|
| 434 | - result = getInternalRawFixes().add(fix) ? AddResult.REPLACED : AddResult.NOT_ADDED; |
|
| 435 | - } else { |
|
| 436 | - result = added ? AddResult.ADDED : AddResult.NOT_ADDED; |
|
| 437 | - } |
|
| 438 | - return result; |
|
| 439 | - } |
|
| 440 | - |
|
| 441 | - @Override |
|
| 442 | - public Duration getAverageIntervalBetweenFixes() { |
|
| 443 | - lockForRead(); |
|
| 444 | - try { |
|
| 445 | - final Duration result; |
|
| 446 | - final int size = getRawFixes().size(); |
|
| 447 | - if (size > 1) { |
|
| 448 | - result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 449 | - } else { |
|
| 450 | - result = null; |
|
| 451 | - } |
|
| 452 | - return result; |
|
| 453 | - } finally { |
|
| 454 | - unlockAfterRead(); |
|
| 455 | - } |
|
| 456 | - } |
|
| 457 | - |
|
| 458 | - @Override |
|
| 459 | - public Duration getAverageIntervalBetweenRawFixes() { |
|
| 460 | - lockForRead(); |
|
| 461 | - try { |
|
| 462 | - final Duration result; |
|
| 463 | - final int size = getRawFixes().size(); |
|
| 464 | - if (size > 1) { |
|
| 465 | - result = getRawFixes().first().getTimePoint().until(getRawFixes().last().getTimePoint()).divide(size-1); |
|
| 466 | - } else { |
|
| 467 | - result = null; |
|
| 468 | - } |
|
| 469 | - return result; |
|
| 470 | - } finally { |
|
| 471 | - unlockAfterRead(); |
|
| 472 | - } |
|
| 473 | - } |
|
| 474 | - |
|
| 475 | - @Override |
|
| 476 | - public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 477 | - return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator); |
|
| 478 | - } |
|
| 479 | - |
|
| 480 | - private <T> T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement, |
|
| 481 | - Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) { |
|
| 482 | - T result; |
|
| 483 | - if (!from.before(to)) { |
|
| 484 | - result = nullElement; |
|
| 485 | - } else { |
|
| 486 | - boolean perfectCacheHit = false; |
|
| 487 | - lockForRead(); |
|
| 488 | - try { |
|
| 489 | - Util.Pair<TimePoint, Util.Pair<TimePoint, T>> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to); |
|
| 490 | - if (bestCacheEntry != null) { |
|
| 491 | - perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below |
|
| 492 | - // compute the missing stretches between best cache entry's "from" and our "from" and the cache |
|
| 493 | - // entry's "to" and our "to" |
|
| 494 | - T valueFromFromToBeginningOfCacheEntry = nullElement; |
|
| 495 | - T valueFromEndOfCacheEntryToTo = nullElement; |
|
| 496 | - if (!bestCacheEntry.getB().getA().equals(from)) { |
|
| 497 | - assert bestCacheEntry.getB().getA().after(from); |
|
| 498 | - perfectCacheHit = false; |
|
| 499 | - valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry |
|
| 500 | - .getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 501 | - } |
|
| 502 | - if (!bestCacheEntry.getA().equals(to)) { |
|
| 503 | - assert bestCacheEntry.getA().before(to); |
|
| 504 | - perfectCacheHit = false; |
|
| 505 | - valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to, |
|
| 506 | - recursionDepth + 1, nullElement, adder, cache, valueCalculator); |
|
| 507 | - } |
|
| 508 | - if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) { |
|
| 509 | - result = null; |
|
| 510 | - } else { |
|
| 511 | - result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()), |
|
| 512 | - valueFromEndOfCacheEntryToTo); |
|
| 513 | - } |
|
| 514 | - } else { |
|
| 515 | - if (from.compareTo(to) < 0) { |
|
| 516 | - result = valueCalculator.calculate(from, to); |
|
| 517 | - } else { |
|
| 518 | - result = nullElement; |
|
| 519 | - } |
|
| 520 | - } |
|
| 521 | - // run the cache update while still holding the read lock; this avoids bug4629 where a cache invalidation |
|
| 522 | - // caused by fix insertions can come after the result calculation and before the cache update |
|
| 523 | - if (!perfectCacheHit && recursionDepth == 0) { |
|
| 524 | - cache.cache(from, to, result); |
|
| 525 | - } |
|
| 526 | - } finally { |
|
| 527 | - unlockAfterRead(); |
|
| 528 | - } |
|
| 529 | - } |
|
| 530 | - return result; |
|
| 531 | - } |
|
| 532 | - |
|
| 533 | - |
|
| 534 | - |
|
| 535 | - @Override |
|
| 536 | - public int size() { |
|
| 537 | - return fixes.size(); |
|
| 538 | - } |
|
| 539 | - |
|
| 540 | - @Override |
|
| 541 | - public boolean isEmpty() { |
|
| 542 | - return fixes.isEmpty(); |
|
| 543 | - } |
|
| 544 | -} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackingConnectorInfoImpl.java
| ... | ... | @@ -1,66 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking.impl; |
|
| 2 | - |
|
| 3 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 4 | - |
|
| 5 | -public class TrackingConnectorInfoImpl implements TrackingConnectorInfo { |
|
| 6 | - private static final long serialVersionUID = 7970268841592389145L; |
|
| 7 | - private final String trackingConnectorName; |
|
| 8 | - private final String TtackingConnectorDefaultUrl; |
|
| 9 | - private final String webUrl; |
|
| 10 | - |
|
| 11 | - public TrackingConnectorInfoImpl(String trackingConnectorName, String trackingConnectorDefaultUrl, String webUrl) { |
|
| 12 | - super(); |
|
| 13 | - this.trackingConnectorName = trackingConnectorName; |
|
| 14 | - TtackingConnectorDefaultUrl = trackingConnectorDefaultUrl; |
|
| 15 | - this.webUrl = webUrl; |
|
| 16 | - } |
|
| 17 | - |
|
| 18 | - public String getTrackingConnectorDefaultUrl() { |
|
| 19 | - return TtackingConnectorDefaultUrl; |
|
| 20 | - } |
|
| 21 | - |
|
| 22 | - public String getTrackingConnectorName() { |
|
| 23 | - return trackingConnectorName; |
|
| 24 | - } |
|
| 25 | - |
|
| 26 | - public String getWebUrl() { |
|
| 27 | - return webUrl; |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - @Override |
|
| 31 | - public int hashCode() { |
|
| 32 | - final int prime = 31; |
|
| 33 | - int result = 1; |
|
| 34 | - result = prime * result + ((TtackingConnectorDefaultUrl == null) ? 0 : TtackingConnectorDefaultUrl.hashCode()); |
|
| 35 | - result = prime * result + ((trackingConnectorName == null) ? 0 : trackingConnectorName.hashCode()); |
|
| 36 | - result = prime * result + ((webUrl == null) ? 0 : webUrl.hashCode()); |
|
| 37 | - return result; |
|
| 38 | - } |
|
| 39 | - |
|
| 40 | - @Override |
|
| 41 | - public boolean equals(Object obj) { |
|
| 42 | - if (this == obj) |
|
| 43 | - return true; |
|
| 44 | - if (obj == null) |
|
| 45 | - return false; |
|
| 46 | - if (getClass() != obj.getClass()) |
|
| 47 | - return false; |
|
| 48 | - TrackingConnectorInfoImpl other = (TrackingConnectorInfoImpl) obj; |
|
| 49 | - if (TtackingConnectorDefaultUrl == null) { |
|
| 50 | - if (other.TtackingConnectorDefaultUrl != null) |
|
| 51 | - return false; |
|
| 52 | - } else if (!TtackingConnectorDefaultUrl.equals(other.TtackingConnectorDefaultUrl)) |
|
| 53 | - return false; |
|
| 54 | - if (trackingConnectorName == null) { |
|
| 55 | - if (other.trackingConnectorName != null) |
|
| 56 | - return false; |
|
| 57 | - } else if (!trackingConnectorName.equals(other.trackingConnectorName)) |
|
| 58 | - return false; |
|
| 59 | - if (webUrl == null) { |
|
| 60 | - if (other.webUrl != null) |
|
| 61 | - return false; |
|
| 62 | - } else if (!webUrl.equals(other.webUrl)) |
|
| 63 | - return false; |
|
| 64 | - return true; |
|
| 65 | - } |
|
| 66 | -} |
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java
| ... | ... | @@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 41 | 41 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 42 | 42 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 43 | 43 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 44 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 44 | 45 | import com.sap.sailing.domain.swisstimingadapter.Course; |
| 45 | 46 | import com.sap.sailing.domain.swisstimingadapter.DomainFactory; |
| 46 | 47 | import com.sap.sailing.domain.swisstimingadapter.Fix; |
| ... | ... | @@ -70,7 +71,6 @@ import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 70 | 71 | import com.sap.sailing.domain.tracking.WindStore; |
| 71 | 72 | import com.sap.sailing.domain.tracking.WindTrack; |
| 72 | 73 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 73 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 74 | 74 | import com.sap.sailing.domain.tracking.impl.UpdateHandler; |
| 75 | 75 | import com.sap.sse.common.Distance; |
| 76 | 76 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java
| ... | ... | @@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry |
| 41 | 41 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 42 | 42 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 43 | 43 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 44 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 44 | 45 | import com.sap.sailing.domain.swisstimingadapter.DomainFactory; |
| 45 | 46 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; |
| 46 | 47 | import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; |
| ... | ... | @@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindTrack; |
| 60 | 61 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 61 | 62 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 62 | 63 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 63 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 64 | 64 | import com.sap.sse.common.Bearing; |
| 65 | 65 | import com.sap.sse.common.TimePoint; |
| 66 | 66 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain.test/SailingDomainTest (No Proxy, Tunnel).launch
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
| 2 | 2 | <launchConfiguration type="org.eclipse.jdt.junit.launchconfig"> |
| 3 | + <booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/> |
|
| 3 | 4 | <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> |
| 4 | 5 | <listEntry value="/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test"/> |
| 5 | 6 | </listAttribute> |
| ... | ... | @@ -10,7 +11,7 @@ |
| 10 | 11 | <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> |
| 11 | 12 | <listEntry value="org.eclipse.debug.ui.launchGroup.run"/> |
| 12 | 13 | </listAttribute> |
| 13 | - <stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src=/test=/true=/<com.sap.sailing.domain.test"/> |
|
| 14 | + <stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src<com.sap.sailing.domain.test"/> |
|
| 14 | 15 | <booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/> |
| 15 | 16 | <stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/> |
| 16 | 17 | <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/> |
java/com.sap.sailing.domain.test/src/LeaderboardCourseChangeWithEliminationTest.java
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -import com.sap.sailing.domain.base.Regatta; |
|
| 2 | -import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations; |
|
| 3 | -import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; |
|
| 4 | -import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; |
|
| 5 | -import com.sap.sailing.domain.test.LeaderboardCourseChangeTest; |
|
| 6 | - |
|
| 7 | -public class LeaderboardCourseChangeWithEliminationTest extends LeaderboardCourseChangeTest { |
|
| 8 | - @Override |
|
| 9 | - protected RegattaLeaderboardWithEliminations createRegattaLeaderboard(Regatta regatta, |
|
| 10 | - ThresholdBasedResultDiscardingRule discardingRule) { |
|
| 11 | - return new DelegatingRegattaLeaderboardWithCompetitorElimination( |
|
| 12 | - ()->super.createRegattaLeaderboard(regatta, discardingRule), "Test leaderboard with elimination"); |
|
| 13 | - } |
|
| 14 | -} |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/AbstractCandidateFilterTestSupport.java
| ... | ... | @@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach; |
| 12 | 12 | |
| 13 | 13 | import com.sap.sailing.domain.base.Waypoint; |
| 14 | 14 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 15 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 16 | 16 | import com.sap.sse.common.Duration; |
| 17 | 17 | import com.sap.sse.common.TimePoint; |
| 18 | 18 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java
| ... | ... | @@ -25,11 +25,11 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 25 | 25 | import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl; |
| 26 | 26 | import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 28 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 28 | 29 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 29 | 30 | import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; |
| 30 | 31 | import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl; |
| 31 | 32 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 32 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 33 | 33 | import com.sap.sse.common.Distance; |
| 34 | 34 | import com.sap.sse.common.TimePoint; |
| 35 | 35 | import com.sap.sse.common.impl.DegreeBearingImpl; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/FetchTracksAndStoreLocallyTest.java
| ... | ... | @@ -15,7 +15,7 @@ import com.sap.sailing.domain.base.Boat; |
| 15 | 15 | import com.sap.sailing.domain.base.Competitor; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 17 | 17 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 18 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 18 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 19 | 19 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 20 | 20 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 21 | 21 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeWithEliminationTest.java
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +package com.sap.sailing.domain.test; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.base.Regatta; |
|
| 4 | +import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations; |
|
| 5 | +import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; |
|
| 6 | +import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination; |
|
| 7 | + |
|
| 8 | +public class LeaderboardCourseChangeWithEliminationTest extends LeaderboardCourseChangeTest { |
|
| 9 | + @Override |
|
| 10 | + protected RegattaLeaderboardWithEliminations createRegattaLeaderboard(Regatta regatta, |
|
| 11 | + ThresholdBasedResultDiscardingRule discardingRule) { |
|
| 12 | + return new DelegatingRegattaLeaderboardWithCompetitorElimination( |
|
| 13 | + ()->super.createRegattaLeaderboard(regatta, discardingRule), "Test leaderboard with elimination"); |
|
| 14 | + } |
|
| 15 | +} |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTest.java
| ... | ... | @@ -74,12 +74,12 @@ import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard; |
| 74 | 74 | import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; |
| 75 | 75 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 76 | 76 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 77 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 77 | 78 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; |
| 78 | 79 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 79 | 80 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 80 | 81 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 81 | 82 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 82 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 83 | 83 | import com.sap.sse.common.Duration; |
| 84 | 84 | import com.sap.sse.common.TimePoint; |
| 85 | 85 | import com.sap.sse.common.Util; |
| ... | ... | @@ -2918,16 +2918,10 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki |
| 2918 | 2918 | assertTrue(Util.indexOf(rankedCompetitors, lastRaceGoldParticipant) < Util.indexOf(rankedCompetitors, silverParticipant)); |
| 2919 | 2919 | } |
| 2920 | 2920 | } |
| 2921 | - // assert that theUntrackedCompetitorInLastRace ended up between the last race's silver and gold fleet participants |
|
| 2922 | - // based on the "extreme fleet" rule: |
|
| 2921 | + // assert that theUntrackedCompetitorInLastRace ended up ranked worse than all participants of ranked fleets: |
|
| 2923 | 2922 | for (final Competitor c : competitors) { |
| 2924 | 2923 | if (c != theUntrackedCompetitorInLastRace) { |
| 2925 | - if (lastRaceGold.contains(c) || medal.contains(c)) { |
|
| 2926 | - assertTrue(Util.indexOf(rankedCompetitors, c) < Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace)); |
|
| 2927 | - } else { |
|
| 2928 | - assertTrue(silver.contains(c)); |
|
| 2929 | - assertTrue(Util.indexOf(rankedCompetitors, c) > Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace)); |
|
| 2930 | - } |
|
| 2924 | + assertTrue(Util.indexOf(rankedCompetitors, c) < Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace)); |
|
| 2931 | 2925 | } |
| 2932 | 2926 | } |
| 2933 | 2927 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardScoringAndRankingTestBase.java
| ... | ... | @@ -32,12 +32,12 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme; |
| 32 | 32 | import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; |
| 33 | 33 | import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; |
| 34 | 34 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 35 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 35 | 36 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; |
| 36 | 37 | import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks; |
| 37 | 38 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 38 | 39 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 39 | 40 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 40 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 41 | 41 | import com.sap.sse.common.Duration; |
| 42 | 42 | import com.sap.sse.common.TimePoint; |
| 43 | 43 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LineAnalysisTest.java
| ... | ... | @@ -14,9 +14,9 @@ import com.sap.sailing.domain.common.impl.MeterDistance; |
| 14 | 14 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 15 | 15 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 16 | 16 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 17 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 17 | 18 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 18 | 19 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 19 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 20 | 20 | import com.sap.sse.common.Bearing; |
| 21 | 21 | import com.sap.sse.common.Distance; |
| 22 | 22 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java
| ... | ... | @@ -149,12 +149,9 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest i |
| 149 | 149 | case Begin: |
| 150 | 150 | logger.info("Stored data begin"); |
| 151 | 151 | lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0); |
| 152 | - new Thread(()->{ |
|
| 153 | - final RaceDefinition raceDefinition = domainFactory.getAndWaitForRaceDefinition(getTracTracRace().getId(), /* timeout in millis */ 10000); |
|
| 154 | - if (trackedRegatta != null && raceDefinition != null && trackedRegatta.getTrackedRace(raceDefinition) != null) { |
|
| 155 | - trackedRegatta.getTrackedRace(raceDefinition).onStatusChanged(OnlineTracTracBasedTest.this, lastStatus); |
|
| 156 | - } |
|
| 157 | - }, "waiting for race definition for race "+getTracTracRace().getId()).start(); |
|
| 152 | + if (getTrackedRace() != null) { |
|
| 153 | + getTrackedRace().onStatusChanged(OnlineTracTracBasedTest.this, lastStatus); |
|
| 154 | + } |
|
| 158 | 155 | break; |
| 159 | 156 | case End: |
| 160 | 157 | logger.info("Stored data end. Delaying status update on tracked race "+getTrackedRace()+" until all events queued in receivers so far have been processed"); |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PartialNavigableSetViewTest.java
| ... | ... | @@ -12,7 +12,7 @@ import java.util.TreeSet; |
| 12 | 12 | import org.junit.jupiter.api.BeforeEach; |
| 13 | 13 | import org.junit.jupiter.api.Test; |
| 14 | 14 | |
| 15 | -import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 16 | 16 | |
| 17 | 17 | public class PartialNavigableSetViewTest { |
| 18 | 18 | private PartialNavigableSetView<Integer> fullSet; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PassingInstructionParserTest.java
| ... | ... | @@ -47,12 +47,11 @@ public class PassingInstructionParserTest extends OnlineTracTracBasedTest { |
| 47 | 47 | @Test |
| 48 | 48 | public void test() { |
| 49 | 49 | int i = 0; |
| 50 | - for(Waypoint w : getRace().getCourse().getWaypoints()){ |
|
| 51 | - if(w.getPassingInstructions()==PassingInstruction.Starboard){ |
|
| 50 | + for (Waypoint w : getRace().getCourse().getWaypoints()) { |
|
| 51 | + if (w.getPassingInstructions() == PassingInstruction.Starboard) { |
|
| 52 | 52 | i++; |
| 53 | 53 | } |
| 54 | 54 | } |
| 55 | - assertEquals(2,i); |
|
| 55 | + assertEquals(2, i); |
|
| 56 | 56 | } |
| 57 | - |
|
| 58 | 57 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java
| ... | ... | @@ -20,7 +20,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 20 | 20 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 21 | 21 | import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; |
| 22 | 22 | import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; |
| 23 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 23 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 24 | 24 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 25 | 25 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 26 | 26 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ScoreCorrectionForUntrackedRacesTest.java
| ... | ... | @@ -0,0 +1,112 @@ |
| 1 | +package com.sap.sailing.domain.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
|
| 4 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
|
| 5 | +import static org.mockito.Mockito.mock; |
|
| 6 | +import static org.mockito.Mockito.when; |
|
| 7 | + |
|
| 8 | +import java.util.Arrays; |
|
| 9 | +import java.util.UUID; |
|
| 10 | + |
|
| 11 | +import org.junit.jupiter.api.BeforeEach; |
|
| 12 | +import org.junit.jupiter.api.Test; |
|
| 13 | + |
|
| 14 | +import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; |
|
| 15 | +import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; |
|
| 16 | +import com.sap.sailing.domain.abstractlog.race.RaceLog; |
|
| 17 | +import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; |
|
| 18 | +import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RegisteredCompetitorsAnalyzer; |
|
| 19 | +import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl; |
|
| 20 | +import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl; |
|
| 21 | +import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; |
|
| 22 | +import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl; |
|
| 23 | +import com.sap.sailing.domain.base.CompetitorWithBoat; |
|
| 24 | +import com.sap.sailing.domain.base.Fleet; |
|
| 25 | +import com.sap.sailing.domain.base.RaceColumnInSeries; |
|
| 26 | +import com.sap.sailing.domain.base.Series; |
|
| 27 | +import com.sap.sailing.domain.base.impl.FleetImpl; |
|
| 28 | +import com.sap.sailing.domain.base.impl.SeriesImpl; |
|
| 29 | +import com.sap.sailing.domain.leaderboard.Leaderboard; |
|
| 30 | +import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; |
|
| 31 | +import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl; |
|
| 32 | +import com.sap.sse.common.TimePoint; |
|
| 33 | +import com.sap.sse.common.Util.Pair; |
|
| 34 | + |
|
| 35 | +/** |
|
| 36 | + * See bug6168; tests fleet-specific check for score corrections in race columns that suggest that this |
|
| 37 | + * fleet has finished the race. |
|
| 38 | + * |
|
| 39 | + * @author Axel Uhl (d043530) |
|
| 40 | + * |
|
| 41 | + */ |
|
| 42 | +public class ScoreCorrectionForUntrackedRacesTest extends StoredTrackBasedTest { |
|
| 43 | + private RaceColumnInSeries f1; |
|
| 44 | + private RaceColumnInSeries f2; |
|
| 45 | + private Series series; |
|
| 46 | + private Fleet gold; |
|
| 47 | + private Fleet silver; |
|
| 48 | + private RegattaLog regattaLog; |
|
| 49 | + private RaceLog goldRaceLogF1; |
|
| 50 | + private RaceLog goldRaceLogF2; |
|
| 51 | + private RaceLog silverRaceLogF1; |
|
| 52 | + private RaceLog silverRaceLogF2; |
|
| 53 | + private SettableScoreCorrection scoreCorrection; |
|
| 54 | + private CompetitorWithBoat c1; |
|
| 55 | + private CompetitorWithBoat c2; |
|
| 56 | + private AbstractLogEventAuthor author; |
|
| 57 | + private Leaderboard leaderboard; |
|
| 58 | + |
|
| 59 | + @BeforeEach |
|
| 60 | + public void setUp() { |
|
| 61 | + leaderboard = mock(Leaderboard.class); |
|
| 62 | + c1 = createCompetitorWithBoat("C1"); |
|
| 63 | + c2 = createCompetitorWithBoat("C2"); |
|
| 64 | + author = new LogEventAuthorImpl("Test", 1); |
|
| 65 | + gold = new FleetImpl("Gold", 1); |
|
| 66 | + silver = new FleetImpl("Silver", 2); |
|
| 67 | + series = new SeriesImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Arrays.asList(gold, silver), Arrays.asList("F1", "F2"), /* trackedRegattaRegistry */ null); |
|
| 68 | + f1 = mock(RaceColumnInSeries.class); |
|
| 69 | + when(f1.getName()).thenReturn("F1"); |
|
| 70 | + when(f1.getSeries()).thenReturn(series); |
|
| 71 | + when(f1.getKey(c1)).thenReturn(new Pair<>(c1, f1)); |
|
| 72 | + when(f1.getKey(c2)).thenReturn(new Pair<>(c2, f1)); |
|
| 73 | + goldRaceLogF1 = new RaceLogImpl(UUID.randomUUID()); |
|
| 74 | + silverRaceLogF1 = new RaceLogImpl(UUID.randomUUID()); |
|
| 75 | + when(f1.getRaceLog(gold)).thenReturn(goldRaceLogF1); |
|
| 76 | + when(f1.getRaceLog(silver)).thenReturn(silverRaceLogF1); |
|
| 77 | + f2 = mock(RaceColumnInSeries.class); |
|
| 78 | + when(f2.getName()).thenReturn("F2"); |
|
| 79 | + when(f2.getSeries()).thenReturn(series); |
|
| 80 | + when(f2.getKey(c1)).thenReturn(new Pair<>(c1, f2)); |
|
| 81 | + when(f2.getKey(c2)).thenReturn(new Pair<>(c2, f2)); |
|
| 82 | + goldRaceLogF2 = new RaceLogImpl(UUID.randomUUID()); |
|
| 83 | + silverRaceLogF2 = new RaceLogImpl(UUID.randomUUID()); |
|
| 84 | + when(f2.getRaceLog(gold)).thenReturn(goldRaceLogF2); |
|
| 85 | + when(f2.getRaceLog(silver)).thenReturn(silverRaceLogF2); |
|
| 86 | + goldRaceLogF1.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0)); |
|
| 87 | + goldRaceLogF1.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c1)); |
|
| 88 | + goldRaceLogF2.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0)); |
|
| 89 | + goldRaceLogF2.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c1)); |
|
| 90 | + silverRaceLogF1.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0)); |
|
| 91 | + silverRaceLogF1.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c2)); |
|
| 92 | + silverRaceLogF2.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0)); |
|
| 93 | + silverRaceLogF2.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c2)); |
|
| 94 | + regattaLog = new RegattaLogImpl(UUID.randomUUID()); |
|
| 95 | + when(f1.getAllCompetitors(gold)).thenReturn(new RegisteredCompetitorsAnalyzer(goldRaceLogF1, regattaLog).analyze()); |
|
| 96 | + when(f1.getAllCompetitors(silver)).thenReturn(new RegisteredCompetitorsAnalyzer(silverRaceLogF1, regattaLog).analyze()); |
|
| 97 | + when(f2.getAllCompetitors(gold)).thenReturn(new RegisteredCompetitorsAnalyzer(goldRaceLogF2, regattaLog).analyze()); |
|
| 98 | + when(f2.getAllCompetitors(silver)).thenReturn(new RegisteredCompetitorsAnalyzer(silverRaceLogF2, regattaLog).analyze()); |
|
| 99 | + scoreCorrection = new ScoreCorrectionImpl(leaderboard); |
|
| 100 | + scoreCorrection.correctScore(c1, f1, 1.0); |
|
| 101 | + scoreCorrection.correctScore(c1, f2, 1.0); |
|
| 102 | + scoreCorrection.correctScore(c2, f1, 1.0); |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + @Test |
|
| 106 | + public void testNumberOfRacesWithCorrections() { |
|
| 107 | + assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f1, gold)); |
|
| 108 | + assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f1, silver)); |
|
| 109 | + assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f2, gold)); |
|
| 110 | + assertFalse(scoreCorrection.hasCorrectionForNonTrackedFleet(f2, silver)); |
|
| 111 | + } |
|
| 112 | +} |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/StarbordSideOfStartLineRecognitionTest.java
| ... | ... | @@ -26,8 +26,8 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 26 | 26 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 27 | 27 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 28 | 28 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 29 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 29 | 30 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 30 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 31 | 31 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| 32 | 32 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; |
| 33 | 33 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
| ... | ... | @@ -44,14 +44,14 @@ import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleExceptio |
| 44 | 44 | import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; |
| 45 | 45 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 46 | 46 | import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl; |
| 47 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 48 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 47 | 49 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 48 | 50 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 49 | 51 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 50 | 52 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; |
| 51 | 53 | import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl; |
| 52 | 54 | import com.sap.sailing.domain.tracking.impl.MaxSpeedCache; |
| 53 | -import com.sap.sailing.domain.tracking.impl.TimeRangeCache; |
|
| 54 | -import com.sap.sailing.domain.tracking.impl.TrackImpl; |
|
| 55 | 55 | import com.sap.sse.common.AbstractBearing; |
| 56 | 56 | import com.sap.sse.common.Bearing; |
| 57 | 57 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
| ... | ... | @@ -65,6 +65,8 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 65 | 65 | import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 66 | 66 | import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; |
| 67 | 67 | import com.sap.sailing.domain.regattalike.RegattaLikeListener; |
| 68 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 69 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 68 | 70 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 69 | 71 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 70 | 72 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| ... | ... | @@ -72,7 +74,6 @@ import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 72 | 74 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 73 | 75 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 74 | 76 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 75 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 76 | 77 | import com.sap.sailing.domain.tracking.Maneuver; |
| 77 | 78 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 78 | 79 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -87,7 +88,6 @@ import com.sap.sailing.domain.tracking.TrackedLeg; |
| 87 | 88 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 88 | 89 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 89 | 90 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 90 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 91 | 91 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 92 | 92 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 93 | 93 | import com.sap.sailing.domain.tracking.WindPositionMode; |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java
| ... | ... | @@ -53,9 +53,10 @@ import com.sap.sailing.domain.leaderboard.impl.RankAndRankComparable; |
| 53 | 53 | import com.sap.sailing.domain.polars.PolarDataService; |
| 54 | 54 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 55 | 55 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 56 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 57 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 56 | 58 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 57 | 59 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 58 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 59 | 60 | import com.sap.sailing.domain.tracking.Maneuver; |
| 60 | 61 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 61 | 62 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -69,7 +70,6 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 69 | 70 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 70 | 71 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 71 | 72 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 72 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 73 | 73 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 74 | 74 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 75 | 75 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
| ... | ... | @@ -69,6 +69,8 @@ import com.sap.sailing.domain.racelog.RaceLogStore; |
| 69 | 69 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 70 | 70 | import com.sap.sailing.domain.ranking.RankingMetricsFactory; |
| 71 | 71 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 72 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 73 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 72 | 74 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 73 | 75 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 74 | 76 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| ... | ... | @@ -78,13 +80,11 @@ import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; |
| 78 | 80 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 79 | 81 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 80 | 82 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 81 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 82 | 83 | import com.sap.sailing.domain.tracking.WindStore; |
| 83 | 84 | import com.sap.sailing.domain.tracking.WindTrack; |
| 84 | 85 | import com.sap.sailing.domain.tracking.impl.FinishTimeUpdateHandler; |
| 85 | 86 | import com.sap.sailing.domain.tracking.impl.RaceAbortedHandler; |
| 86 | 87 | import com.sap.sailing.domain.tracking.impl.StartTimeUpdateHandler; |
| 87 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 88 | 88 | import com.sap.sailing.domain.tractracadapter.DomainFactory; |
| 89 | 89 | import com.sap.sailing.domain.tractracadapter.JSONService; |
| 90 | 90 | import com.sap.sailing.domain.tractracadapter.MetadataParser; |
java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java
| ... | ... | @@ -23,13 +23,13 @@ import com.sap.sailing.domain.common.PassingInstruction; |
| 23 | 23 | import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 24 | 24 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 25 | 25 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 27 | 28 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 28 | 29 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 29 | 30 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 30 | 31 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 31 | 32 | import com.sap.sailing.domain.tracking.WindStore; |
| 32 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 33 | 33 | import com.sap.sailing.domain.tractracadapter.DomainFactory; |
| 34 | 34 | import com.sap.sailing.domain.tractracadapter.TracTracAdapter; |
| 35 | 35 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain.windfinderadapter/META-INF/MANIFEST.MF
| ... | ... | @@ -7,7 +7,8 @@ Bundle-Version: 1.0.0.qualifier |
| 7 | 7 | Bundle-Activator: com.sap.sailing.domain.windfinderadapter.impl.Activator |
| 8 | 8 | Bundle-Vendor: SAP |
| 9 | 9 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 10 | -Import-Package: com.sap.sse.security, |
|
| 10 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 11 | + com.sap.sse.security, |
|
| 11 | 12 | org.osgi.framework;version="1.3.0", |
| 12 | 13 | org.osgi.util.tracker;version="1.5.1" |
| 13 | 14 | Bundle-ActivationPolicy: lazy |
java/com.sap.sailing.domain.yellowbrickadapter/src/com/sap/sailing/domain/yellowbrickadapter/impl/YellowBrickRaceTrackerImpl.java
| ... | ... | @@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; |
| 47 | 47 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 48 | 48 | import com.sap.sailing.domain.racelog.RaceLogStore; |
| 49 | 49 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 50 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 50 | 51 | import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl; |
| 51 | 52 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 52 | 53 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| ... | ... | @@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindStore; |
| 60 | 61 | import com.sap.sailing.domain.tracking.WindTrack; |
| 61 | 62 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 62 | 63 | import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; |
| 63 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 64 | 64 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRace; |
| 65 | 65 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRaceTrackingConnectivityParams; |
| 66 | 66 | import com.sap.sailing.domain.yellowbrickadapter.YellowBrickTrackingAdapter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java
| ... | ... | @@ -24,8 +24,8 @@ import com.sap.sailing.domain.common.WindSourceType; |
| 24 | 24 | import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition; |
| 25 | 25 | import com.sap.sailing.domain.leaderboard.Leaderboard; |
| 26 | 26 | import com.sap.sailing.domain.leaderboard.LeaderboardGroup; |
| 27 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 27 | 28 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 28 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 29 | 29 | import com.sap.sailing.geocoding.ReverseGeocoder; |
| 30 | 30 | import com.sap.sse.common.TimePoint; |
| 31 | 31 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java
| ... | ... | @@ -4,6 +4,7 @@ import java.io.Serializable; |
| 4 | 4 | import java.util.concurrent.Callable; |
| 5 | 5 | |
| 6 | 6 | import com.sap.sailing.domain.base.Competitor; |
| 7 | +import com.sap.sailing.domain.base.Fleet; |
|
| 7 | 8 | import com.sap.sailing.domain.base.RaceColumn; |
| 8 | 9 | import com.sap.sailing.domain.common.MaxPointsReason; |
| 9 | 10 | import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; |
| ... | ... | @@ -121,8 +122,9 @@ public interface ScoreCorrection extends Serializable { |
| 121 | 122 | * for any competitor who in <code>raceInLeaderboard</code> is not in a tracked race and hence the fleet assignment |
| 122 | 123 | * cannot be determined. This is helpful, e.g., for progress detection. If score corrections are present for such |
| 123 | 124 | * untracked competitors then all untracked fleets need to be assumed as finished. |
| 125 | + * @param fleet TODO |
|
| 124 | 126 | */ |
| 125 | - boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard); |
|
| 127 | + boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard, Fleet fleet); |
|
| 126 | 128 | |
| 127 | 129 | /** |
| 128 | 130 | * @return all race columns for which this score corrections object has at least one correction; note that this |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java
| ... | ... | @@ -290,7 +290,6 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> { |
| 290 | 290 | } |
| 291 | 291 | } |
| 292 | 292 | } |
| 293 | - // TODO bug5877: pass leaderboard and competitors and a totalPointsSupplier (based on totalPointsCache, see call to compareByBetterScore below) to allow for identifying competitors promoted through to later medal race |
|
| 294 | 293 | int result = scoringScheme.compareByMedalRaceParticipation(zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, zeroBasedIndexOfLastMedalSeriesInWhichO2Scored); |
| 295 | 294 | if (result == 0) { |
| 296 | 295 | result = defaultFleetBasedComparisonResult; |
| ... | ... | @@ -482,29 +481,26 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> { |
| 482 | 481 | } |
| 483 | 482 | |
| 484 | 483 | /** |
| 485 | - * If the race column only has one fleet, no decision is made and 0 is returned. Otherwise, if <code>fleet</code> is the |
|
| 486 | - * best fleet with others in the column being worse, return "better" (lesser; -1). If <code>fleet</code> is the worst fleet |
|
| 487 | - * with others in the column being better, return "worse" (greater; 1). Otherwise, return 0. |
|
| 484 | + * If the race column only has one fleet, no decision is made and 0 is returned. Otherwise, if there are other fleets with |
|
| 485 | + * a {@link Fleet#getOrdering() rank} different from that of <code>fleet</code>, we want to sort competitors with no fleet |
|
| 486 | + * assignment to the "worse" end of the leaderboard, therefore returning 1. Otherwise, 0 is returned. |
|
| 488 | 487 | */ |
| 489 | 488 | private int extremeFleetComparison(RaceColumn raceColumn, Fleet fleet) { |
| 490 | - boolean allOthersAreGreater = true; |
|
| 491 | - boolean allOthersAreLess = true; |
|
| 489 | + boolean greaterFleetExists = false; |
|
| 490 | + boolean lesserFleetExists = false; |
|
| 492 | 491 | boolean othersExist = false; |
| 493 | 492 | for (Fleet f : raceColumn.getFleets()) { |
| 494 | 493 | if (f != fleet) { |
| 495 | 494 | othersExist = true; |
| 496 | - allOthersAreGreater = allOthersAreGreater && f.compareTo(fleet) > 0; |
|
| 497 | - allOthersAreLess = allOthersAreLess && f.compareTo(fleet) < 0; |
|
| 495 | + greaterFleetExists = greaterFleetExists || f.compareTo(fleet) > 0; |
|
| 496 | + lesserFleetExists = lesserFleetExists || f.compareTo(fleet) < 0; |
|
| 498 | 497 | } |
| 499 | 498 | } |
| 500 | - int result = 0; |
|
| 501 | - if (othersExist) { |
|
| 502 | - assert !(allOthersAreGreater && allOthersAreLess); |
|
| 503 | - if (allOthersAreGreater) { |
|
| 504 | - result = -1; |
|
| 505 | - } else if (allOthersAreLess) { |
|
| 506 | - result = 1; |
|
| 507 | - } |
|
| 499 | + final int result; |
|
| 500 | + if (othersExist && (greaterFleetExists || lesserFleetExists)) { |
|
| 501 | + result = -1; // the competitor with no fleet is considered worse than a competitor in one of the ranked fleets |
|
| 502 | + } else { |
|
| 503 | + result = 0; |
|
| 508 | 504 | } |
| 509 | 505 | return result; |
| 510 | 506 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.logging.Logger; |
| 14 | 14 | |
| 15 | 15 | import com.sap.sailing.domain.base.Competitor; |
| 16 | 16 | import com.sap.sailing.domain.base.Course; |
| 17 | +import com.sap.sailing.domain.base.Fleet; |
|
| 17 | 18 | import com.sap.sailing.domain.base.RaceColumn; |
| 18 | 19 | import com.sap.sailing.domain.base.RaceColumnInSeries; |
| 19 | 20 | import com.sap.sailing.domain.common.MaxPointsReason; |
| ... | ... | @@ -610,19 +611,17 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { |
| 610 | 611 | |
| 611 | 612 | @Override |
| 612 | 613 | public boolean hasCorrectionFor(RaceColumn raceInLeaderboard) { |
| 613 | - return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ false); |
|
| 614 | + return internalHasScoreCorrectionFor(raceInLeaderboard); |
|
| 614 | 615 | } |
| 615 | 616 | |
| 616 | - private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard, boolean considerOnlyUntrackedRaces) { |
|
| 617 | - for (Pair<Competitor, RaceColumn> correctedScoresKey : correctedScores.keySet()) { |
|
| 618 | - if (correctedScoresKey.getB() == raceInLeaderboard && |
|
| 619 | - (!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(correctedScoresKey.getA()) == null)) { |
|
| 617 | + private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard) { |
|
| 618 | + for (final Pair<Competitor, RaceColumn> correctedScoresKey : correctedScores.keySet()) { |
|
| 619 | + if (correctedScoresKey.getB() == raceInLeaderboard) { |
|
| 620 | 620 | return true; |
| 621 | 621 | } |
| 622 | 622 | } |
| 623 | 623 | for (Pair<Competitor, RaceColumn> maxPointsReasonsKey : maxPointsReasons.keySet()) { |
| 624 | - if (maxPointsReasonsKey.getB() == raceInLeaderboard && |
|
| 625 | - (!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(maxPointsReasonsKey.getA()) == null)) { |
|
| 624 | + if (maxPointsReasonsKey.getB() == raceInLeaderboard) { |
|
| 626 | 625 | return true; |
| 627 | 626 | } |
| 628 | 627 | } |
| ... | ... | @@ -630,8 +629,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { |
| 630 | 629 | } |
| 631 | 630 | |
| 632 | 631 | @Override |
| 633 | - public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard) { |
|
| 634 | - return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ true); |
|
| 632 | + public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard, Fleet fleet) { |
|
| 633 | + boolean result; |
|
| 634 | + if (raceInLeaderboard.getTrackedRace(fleet) == null) { |
|
| 635 | + result = false; |
|
| 636 | + for (final Competitor competitor : raceInLeaderboard.getAllCompetitors(fleet)) { |
|
| 637 | + final Pair<Competitor, RaceColumn> key = raceInLeaderboard.getKey(competitor); |
|
| 638 | + if (correctedScores.containsKey(key) || maxPointsReasons.containsKey(key)) { |
|
| 639 | + result = true; |
|
| 640 | + break; |
|
| 641 | + } |
|
| 642 | + } |
|
| 643 | + } else { |
|
| 644 | + result = false; |
|
| 645 | + } |
|
| 646 | + return result; |
|
| 635 | 647 | } |
| 636 | 648 | |
| 637 | 649 | @Override |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/IncrementalApproximatedFixesCalculatorImpl.java
| ... | ... | @@ -10,10 +10,10 @@ import java.util.NavigableSet; |
| 10 | 10 | import com.sap.sailing.domain.base.Competitor; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 12 | 12 | import com.sap.sailing.domain.maneuverdetection.IncrementalApproximatedFixesCalculator; |
| 13 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 13 | 14 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 14 | 15 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 15 | 16 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 16 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 17 | 17 | import com.sap.sse.common.Duration; |
| 18 | 18 | import com.sap.sse.common.TimePoint; |
| 19 | 19 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java
| ... | ... | @@ -30,9 +30,9 @@ import com.sap.sailing.domain.markpassingcalculation.impl.CandidateFinderImpl; |
| 30 | 30 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint; |
| 31 | 31 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory; |
| 32 | 32 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 33 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 33 | 34 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 34 | 35 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 35 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 36 | 36 | import com.sap.sse.common.TimePoint; |
| 37 | 37 | import com.sap.sse.common.Util; |
| 38 | 38 | import com.sap.sse.common.Util.Pair; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java
| ... | ... | @@ -10,7 +10,7 @@ import com.sap.sailing.domain.base.Mark; |
| 10 | 10 | import com.sap.sailing.domain.base.Waypoint; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 14 | 14 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 15 | 15 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 16 | 16 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java
| ... | ... | @@ -29,13 +29,13 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 29 | 29 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 30 | 30 | import com.sap.sailing.domain.markpassingcalculation.CandidateChooser; |
| 31 | 31 | import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
| 32 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 32 | 33 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 33 | 34 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 34 | 35 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 35 | 36 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 36 | 37 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 37 | 38 | import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; |
| 38 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 39 | 39 | import com.sap.sse.common.Distance; |
| 40 | 40 | import com.sap.sse.common.Duration; |
| 41 | 41 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateFinderImpl.java
| ... | ... | @@ -35,13 +35,13 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 35 | 35 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 36 | 36 | import com.sap.sailing.domain.markpassingcalculation.Candidate; |
| 37 | 37 | import com.sap.sailing.domain.markpassingcalculation.CandidateFinder; |
| 38 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 38 | 39 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 39 | 40 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 40 | 41 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 41 | 42 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| 42 | 43 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 43 | 44 | import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl; |
| 44 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 45 | 45 | import com.sap.sse.common.Bearing; |
| 46 | 46 | import com.sap.sse.common.Distance; |
| 47 | 47 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java
| ... | ... | @@ -21,7 +21,7 @@ import com.sap.sailing.domain.base.Mark; |
| 21 | 21 | import com.sap.sailing.domain.base.Waypoint; |
| 22 | 22 | import com.sap.sailing.domain.common.Position; |
| 23 | 23 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 24 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 24 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 25 | 25 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 26 | 26 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 27 | 27 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DummyTrackedRace.java
| ... | ... | @@ -43,6 +43,8 @@ import com.sap.sailing.domain.leaderboard.impl.RankAndRankComparable; |
| 43 | 43 | import com.sap.sailing.domain.polars.PolarDataService; |
| 44 | 44 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 45 | 45 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 46 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 47 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 46 | 48 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 47 | 49 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 48 | 50 | import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicMappedTrack.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 3 | 4 | import com.sap.sse.common.Timed; |
| 4 | 5 | |
| 5 | 6 | /** |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrack.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 3 | 4 | import com.sap.sse.common.Timed; |
| 4 | 5 | |
| 5 | 6 | public interface DynamicTrack<FixType extends Timed> extends Track<FixType> { |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackWithRemove.java
| ... | ... | @@ -26,5 +26,5 @@ public interface DynamicTrackWithRemove<FixType extends Timed> extends DynamicTr |
| 26 | 26 | */ |
| 27 | 27 | boolean remove(FixType fix); |
| 28 | 28 | |
| 29 | - void removeAllUpToAndIncluding(FixType fix); |
|
| 29 | + void removeAllUpToExcluding(FixType fix); |
|
| 30 | 30 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java
| ... | ... | @@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.confidence.Weigher; |
| 10 | 10 | import com.sap.sailing.domain.common.impl.KnotSpeedImpl; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 13 | 14 | import com.sap.sse.common.Distance; |
| 14 | 15 | import com.sap.sse.common.Duration; |
| 15 | 16 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSTrackListener.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 4 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 4 | 5 | |
| 5 | 6 | public interface GPSTrackListener<ItemType, FixType extends GPSFix> extends TrackListener { |
| 6 | 7 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java
| ... | ... | @@ -17,6 +17,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 17 | 17 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 18 | 18 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 20 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 20 | 21 | import com.sap.sse.common.TimePoint; |
| 21 | 22 | |
| 22 | 23 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingHandler.java
| ... | ... | @@ -21,6 +21,7 @@ import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; |
| 21 | 21 | import com.sap.sailing.domain.maneuverhash.ManeuverRaceFingerprintRegistry; |
| 22 | 22 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 23 | 23 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 24 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 24 | 25 | import com.sap.sse.common.Color; |
| 25 | 26 | import com.sap.sse.common.Duration; |
| 26 | 27 | import com.sap.sse.util.ThreadLocalTransporter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/SensorFixTrack.java
| ... | ... | @@ -3,6 +3,8 @@ package com.sap.sailing.domain.tracking; |
| 3 | 3 | import java.io.Serializable; |
| 4 | 4 | |
| 5 | 5 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 6 | +import com.sap.sailing.domain.shared.tracking.MappedTrack; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 6 | 8 | import com.sap.sse.common.WithID; |
| 7 | 9 | |
| 8 | 10 | /** |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/SensorFixTrackListener.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 4 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | 7 | * Listener to observe a {@link com.sap.sailing.domain.tracking.SensorFixTrack} to get informed whenever a new fix is |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
| ... | ... | @@ -69,6 +69,8 @@ import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 69 | 69 | import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 70 | 70 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 71 | 71 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 72 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 73 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 72 | 74 | import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; |
| 73 | 75 | import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; |
| 74 | 76 | import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java
| ... | ... | @@ -13,6 +13,7 @@ import com.sap.sailing.domain.common.NoWindException; |
| 13 | 13 | import com.sap.sailing.domain.maneuverhash.ManeuverRaceFingerprintRegistry; |
| 14 | 14 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 15 | 15 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 16 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 16 | 17 | import com.sap.sse.common.TimePoint; |
| 17 | 18 | import com.sap.sse.metering.HasCPUMeter; |
| 18 | 19 | import com.sap.sse.util.ThreadLocalTransporter; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java
| ... | ... | @@ -12,7 +12,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 13 | 13 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 14 | 14 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 15 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 16 | 16 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 17 | 17 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 18 | 18 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java
| ... | ... | @@ -10,12 +10,13 @@ import com.sap.sailing.domain.common.scalablevalue.impl.ScalableDistance; |
| 10 | 10 | import com.sap.sailing.domain.common.tracking.BravoExtendedFix; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.BravoFix; |
| 12 | 12 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 13 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 14 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 15 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 14 | 16 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 15 | 17 | import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; |
| 16 | 18 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 17 | 19 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 18 | -import com.sap.sailing.domain.tracking.Track; |
|
| 19 | 20 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 20 | 21 | import com.sap.sse.common.Bearing; |
| 21 | 22 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java
| ... | ... | @@ -17,7 +17,8 @@ import com.sap.sailing.domain.base.Competitor; |
| 17 | 17 | import com.sap.sailing.domain.common.SpeedWithBearing; |
| 18 | 18 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 20 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 20 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 21 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 21 | 22 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 22 | 23 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 23 | 24 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java
| ... | ... | @@ -19,11 +19,12 @@ import com.sap.sailing.domain.common.NoWindException; |
| 19 | 19 | import com.sap.sailing.domain.common.impl.MeterDistance; |
| 20 | 20 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 21 | 21 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 22 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 22 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 23 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 24 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 23 | 25 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 24 | 26 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 25 | 27 | import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 26 | -import com.sap.sailing.domain.tracking.Track; |
|
| 27 | 28 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 28 | 29 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 29 | 30 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicMappedTrackImpl.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking.impl; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.impl.MappedTrackImpl; |
|
| 3 | 4 | import com.sap.sailing.domain.tracking.DynamicMappedTrack; |
| 4 | 5 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 5 | 6 | import com.sap.sse.common.Timed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackImpl.java
| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking.impl; |
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 3 | 4 | import com.sap.sailing.domain.tracking.DynamicTrack; |
| 4 | 5 | import com.sap.sse.common.Timed; |
| 5 | 6 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackWithRemoveImpl.java
| ... | ... | @@ -27,7 +27,7 @@ public class DynamicTrackWithRemoveImpl<FixType extends Timed> extends DynamicTr |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | @Override |
| 30 | - public void removeAllUpToAndIncluding(FixType fix) { |
|
| 31 | - getInternalFixes().removeAllLessOrEqual(fix); |
|
| 30 | + public void removeAllUpToExcluding(FixType fix) { |
|
| 31 | + getInternalFixes().removeAllLessThan(fix); |
|
| 32 | 32 | } |
| 33 | 33 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java
| ... | ... | @@ -49,7 +49,8 @@ import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; |
| 49 | 49 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 50 | 50 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 51 | 51 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 52 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 52 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 53 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 53 | 54 | import com.sap.sailing.domain.tracking.CourseDesignChangedListener; |
| 54 | 55 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 55 | 56 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| ... | ... | @@ -67,7 +68,6 @@ import com.sap.sailing.domain.tracking.TrackFactory; |
| 67 | 68 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 68 | 69 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 69 | 70 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 70 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 71 | 71 | import com.sap.sailing.domain.tracking.TrackingDataLoader; |
| 72 | 72 | import com.sap.sailing.domain.tracking.WindStore; |
| 73 | 73 | import com.sap.sailing.domain.tracking.WindTrack; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
| ... | ... | @@ -33,12 +33,15 @@ import com.sap.sailing.domain.common.tracking.GPSFix; |
| 33 | 33 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 34 | 34 | import com.sap.sailing.domain.common.tracking.WithValidityCache; |
| 35 | 35 | import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper; |
| 36 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 36 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 37 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 38 | +import com.sap.sailing.domain.shared.tracking.impl.MappedTrackImpl; |
|
| 39 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 40 | +import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache; |
|
| 37 | 41 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 38 | 42 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 39 | 43 | import com.sap.sailing.domain.tracking.SpeedWithBearingStep; |
| 40 | 44 | import com.sap.sailing.domain.tracking.SpeedWithBearingStepsIterable; |
| 41 | -import com.sap.sailing.domain.tracking.Track; |
|
| 42 | 45 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 43 | 46 | import com.sap.sse.common.Bearing; |
| 44 | 47 | import com.sap.sse.common.Distance; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/MaxSpeedCache.java
| ... | ... | @@ -9,7 +9,7 @@ import java.util.NavigableSet; |
| 9 | 9 | |
| 10 | 10 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 11 | 11 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 12 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 12 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 13 | 13 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 14 | 14 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 15 | 15 | import com.sap.sse.common.Speed; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/SensorFixTrackImpl.java
| ... | ... | @@ -4,7 +4,7 @@ import java.io.Serializable; |
| 4 | 4 | import java.util.function.Consumer; |
| 5 | 5 | |
| 6 | 6 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 7 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 7 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 8 | 8 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 9 | 9 | import com.sap.sailing.domain.tracking.SensorFixTrack; |
| 10 | 10 | import com.sap.sailing.domain.tracking.SensorFixTrackListener; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java
| ... | ... | @@ -27,7 +27,8 @@ import com.sap.sailing.domain.common.impl.WindImpl; |
| 27 | 27 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 28 | 28 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 29 | 29 | import com.sap.sailing.domain.common.tracking.SensorFix; |
| 30 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 30 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 31 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 31 | 32 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 32 | 33 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 33 | 34 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
| ... | ... | @@ -144,13 +144,17 @@ import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 144 | 144 | import com.sap.sailing.domain.ranking.OneDesignRankingMetric; |
| 145 | 145 | import com.sap.sailing.domain.ranking.RankingMetric; |
| 146 | 146 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 147 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 148 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 149 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 150 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 151 | +import com.sap.sailing.domain.shared.tracking.impl.LineDetailsImpl; |
|
| 152 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 147 | 153 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 148 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 149 | 154 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 150 | 155 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| 151 | 156 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 152 | 157 | import com.sap.sailing.domain.tracking.GPSTrackListener; |
| 153 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 154 | 158 | import com.sap.sailing.domain.tracking.Maneuver; |
| 155 | 159 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 156 | 160 | import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache; |
| ... | ... | @@ -158,7 +162,6 @@ import com.sap.sailing.domain.tracking.RaceChangeListener; |
| 158 | 162 | import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; |
| 159 | 163 | import com.sap.sailing.domain.tracking.RaceListener; |
| 160 | 164 | import com.sap.sailing.domain.tracking.SensorFixTrack; |
| 161 | -import com.sap.sailing.domain.tracking.Track; |
|
| 162 | 165 | import com.sap.sailing.domain.tracking.TrackFactory; |
| 163 | 166 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 164 | 167 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| ... | ... | @@ -166,7 +169,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 166 | 169 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 167 | 170 | import com.sap.sailing.domain.tracking.TrackedRaceWithWindEssentials; |
| 168 | 171 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 169 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 170 | 172 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 171 | 173 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 172 | 174 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java
| ... | ... | @@ -25,12 +25,12 @@ import com.sap.sailing.domain.common.NoWindException; |
| 25 | 25 | import com.sap.sailing.domain.maneuverhash.ManeuverRaceFingerprintRegistry; |
| 26 | 26 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 27 | 27 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 28 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 28 | 29 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 29 | 30 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 30 | 31 | import com.sap.sailing.domain.tracking.RaceListener; |
| 31 | 32 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 32 | 33 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 33 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 34 | 34 | import com.sap.sailing.domain.tracking.WindStore; |
| 35 | 35 | import com.sap.sse.common.TimePoint; |
| 36 | 36 | import com.sap.sse.common.Util; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/VirtualWindTrackImpl.java
| ... | ... | @@ -4,6 +4,7 @@ import java.util.NavigableSet; |
| 4 | 4 | |
| 5 | 5 | import com.sap.sailing.domain.common.Position; |
| 6 | 6 | import com.sap.sailing.domain.common.Wind; |
| 7 | +import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView; |
|
| 7 | 8 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 8 | 9 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
| 9 | 10 | import com.sap.sse.common.TimePoint; |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/WindTrackImpl.java
| ... | ... | @@ -23,6 +23,7 @@ import com.sap.sailing.domain.common.tracking.impl.PreciseCompactWindImpl; |
| 23 | 23 | import com.sap.sailing.domain.common.tracking.impl.VeryCompactWindImpl; |
| 24 | 24 | import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager; |
| 25 | 25 | import com.sap.sailing.domain.confidence.ConfidenceFactory; |
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TrackImpl; |
|
| 26 | 27 | import com.sap.sailing.domain.tracking.WindListener; |
| 27 | 28 | import com.sap.sailing.domain.tracking.WindTrack; |
| 28 | 29 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
java/com.sap.sailing.geocoding/src/com/sap/sailing/geocoding/impl/ReverseGeocoderImpl.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.Collections; |
| 14 | 14 | import java.util.Comparator; |
| 15 | 15 | import java.util.Iterator; |
| 16 | 16 | import java.util.List; |
| 17 | +import java.util.Optional; |
|
| 17 | 18 | import java.util.Random; |
| 18 | 19 | import java.util.TimeZone; |
| 19 | 20 | import java.util.logging.Level; |
| ... | ... | @@ -36,7 +37,21 @@ import com.sap.sse.common.Util; |
| 36 | 37 | import com.sap.sse.util.HttpUrlConnectionHelper; |
| 37 | 38 | |
| 38 | 39 | public class ReverseGeocoderImpl implements ReverseGeocoder { |
| 40 | + /** |
|
| 41 | + * The system property name to get the comma separated list of geonames.org usernames; takes precedence over |
|
| 42 | + * the environment variable whose name is given by {@link #USERNAMES_ENV_VARIABLE_NAME}. |
|
| 43 | + */ |
|
| 39 | 44 | private static final String USERNAMES_SYSTEM_PROPERTY_NAME = "geonames.org.usernames"; |
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * The environment variable name to get the comma separated list of geonames.org usernames. Not evaluated |
|
| 48 | + * if the system property whose name is given by {@link #USERNAMES_SYSTEM_PROPERTY_NAME} is set. |
|
| 49 | + */ |
|
| 50 | + private static final String USERNAMES_ENV_VARIABLE_NAME = "GEONAMES_ORG_USERNAMES"; |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * Default username in case neither system property nor environment variable is set. |
|
| 54 | + */ |
|
| 40 | 55 | private static final String GEONAMES_DEFAULT_USER_NAME = "sailtracking0"; |
| 41 | 56 | |
| 42 | 57 | private static final String DATES = "dates"; |
| ... | ... | @@ -63,7 +78,8 @@ public class ReverseGeocoderImpl implements ReverseGeocoder { |
| 63 | 78 | private static final Logger logger = Logger.getLogger(ReverseGeocoderImpl.class.getName()); |
| 64 | 79 | |
| 65 | 80 | public ReverseGeocoderImpl() { |
| 66 | - final String commaSeparatedUsernames = System.getProperty(USERNAMES_SYSTEM_PROPERTY_NAME, GEONAMES_DEFAULT_USER_NAME); |
|
| 81 | + final String commaSeparatedUsernames = System.getProperty(USERNAMES_SYSTEM_PROPERTY_NAME, |
|
| 82 | + Optional.ofNullable(System.getenv(USERNAMES_ENV_VARIABLE_NAME)).orElse(GEONAMES_DEFAULT_USER_NAME)); |
|
| 67 | 83 | this.usernames = commaSeparatedUsernames.split(","); |
| 68 | 84 | } |
| 69 | 85 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java
| ... | ... | @@ -46,6 +46,7 @@ public class BoatClassImageResolver { |
| 46 | 46 | boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46Icon()); |
| 47 | 47 | boatClassIconsMap.put(BoatClassMasterdata.BB10M.getDisplayName(), imageResources.BB10MIcon()); |
| 48 | 48 | boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_35.getDisplayName(), imageResources.BeneteauFirst35Icon()); |
| 49 | + boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_36.getDisplayName(), imageResources.BeneteauFirst36Icon()); |
|
| 49 | 50 | boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_45.getDisplayName(), imageResources.BeneteauFirst45Icon()); |
| 50 | 51 | boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_I.getDisplayName(), imageResources.Brassfahrt1Icon()); |
| 51 | 52 | boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_II.getDisplayName(), imageResources.Brassfahrt2Icon()); |
| ... | ... | @@ -63,6 +64,7 @@ public class BoatClassImageResolver { |
| 63 | 64 | boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon()); |
| 64 | 65 | boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon()); |
| 65 | 66 | boatClassIconsMap.put(BoatClassMasterdata.ELAN350.getDisplayName(), imageResources.Elan350Icon()); |
| 67 | + boatClassIconsMap.put(BoatClassMasterdata.ELAN_E4.getDisplayName(), imageResources.ElanE4Icon()); |
|
| 66 | 68 | boatClassIconsMap.put(BoatClassMasterdata.ELLIOTT_6M.getDisplayName(), imageResources.Elliott6mIcon()); |
| 67 | 69 | boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon()); |
| 68 | 70 | boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
| ... | ... | @@ -96,6 +96,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 96 | 96 | @ImageOptions(preventInlining = true) |
| 97 | 97 | ImageResource BeneteauFirst35Icon(); |
| 98 | 98 | |
| 99 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png") |
|
| 100 | + @ImageOptions(preventInlining = true) |
|
| 101 | + ImageResource BeneteauFirst36Icon(); |
|
| 102 | + |
|
| 99 | 103 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png") |
| 100 | 104 | @ImageOptions(preventInlining = true) |
| 101 | 105 | ImageResource BeneteauFirst45Icon(); |
| ... | ... | @@ -152,6 +156,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 152 | 156 | @ImageOptions(preventInlining = true) |
| 153 | 157 | ImageResource Elan350Icon(); |
| 154 | 158 | |
| 159 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png") |
|
| 160 | + @ImageOptions(preventInlining = true) |
|
| 161 | + ImageResource ElanE4Icon(); |
|
| 162 | + |
|
| 155 | 163 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EUROPE_INT.png") |
| 156 | 164 | @ImageOptions(preventInlining = true) |
| 157 | 165 | ImageResource EuropeIcon(); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -5,6 +5,19 @@ |
| 5 | 5 | <div id="mainContent"> |
| 6 | 6 | <h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4> |
| 7 | 7 | <div class="innerContent"> |
| 8 | + <h5 class="articleSubheadline">October 2025</h5> |
|
| 9 | + <ul class="bulletList"> |
|
| 10 | + <li>Bug fix: the "Edit Mark Passings" panel showed only up to 15 waypoints.</li> |
|
| 11 | + <li>Competitors not assigned to any fleet will no longer be sorted in between a Gold |
|
| 12 | + and a Silver fleet but will end up "at the bottom" of the leaderboard.</li> |
|
| 13 | + <li>Bug fix: when a split-fleet series had results only entered manually as score |
|
| 14 | + corrections, the progress bars for the series were not shown properly. All |
|
| 15 | + fleets would be considered finished although some may not have seen results.</li> |
|
| 16 | + </ul> |
|
| 17 | + <h5 class="articleSubheadline">September 2025</h5> |
|
| 18 | + <ul class="bulletList"> |
|
| 19 | + <li>Added CSV export for polar data histograms in Data Mining.</li> |
|
| 20 | + </ul> |
|
| 8 | 21 | <h5 class="articleSubheadline">June 2025</h5> |
| 9 | 22 | <ul class="bulletList"> |
| 10 | 23 | <li>Bug fix: the refresh interval in the Races/Tracking list ran into an overflow when races |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java
| ... | ... | @@ -585,7 +585,8 @@ public class RaceContext { |
| 585 | 585 | } |
| 586 | 586 | } |
| 587 | 587 | ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection(); |
| 588 | - if (trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) { |
|
| 588 | + // bug6168: for split fleets we need to check the fleet too |
|
| 589 | + if (trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn, fleet)) { |
|
| 589 | 590 | return RaceViewState.FINISHED; |
| 590 | 591 | } |
| 591 | 592 | if (startTime != null) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java
| ... | ... | @@ -341,6 +341,7 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race |
| 341 | 341 | contentPanel.add(new Label(stringMessages.seeIgtimiTabForAccountSettings())); |
| 342 | 342 | final CheckBox correctByDeclination = new CheckBox(stringMessages.declinationCheckbox()); |
| 343 | 343 | correctByDeclination.setValue(true); // by default this is desirable because the Igtimi connector reads uncorrected magnetic values |
| 344 | + final TextBox optionalBearerTokenBox = new TextBox(); |
|
| 344 | 345 | final Button importButton = new Button(stringMessages.importWindFromIgtimi()); |
| 345 | 346 | importButton.ensureDebugId("ImportWindFromIgtimi"); |
| 346 | 347 | final HTML resultReport = new HTML(); |
| ... | ... | @@ -357,7 +358,9 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race |
| 357 | 358 | if (Window.confirm(warningMessage)) { |
| 358 | 359 | resultReport.setText(stringMessages.loading()); |
| 359 | 360 | sailingServiceWrite.importWindFromIgtimi(new ArrayList<>(refreshableRaceSelectionModel.getSelectedSet()), |
| 360 | - correctByDeclination.getValue(), new AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>>() { |
|
| 361 | + correctByDeclination.getValue(), |
|
| 362 | + Util.hasLength(optionalBearerTokenBox.getValue()) ? optionalBearerTokenBox.getValue().trim() : null, |
|
| 363 | + new AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>>() { |
|
| 361 | 364 | @Override |
| 362 | 365 | public void onFailure(Throwable caught) { |
| 363 | 366 | errorReporter.reportError(stringMessages.errorImportingIgtimiWind(caught.getMessage())); |
| ... | ... | @@ -383,6 +386,11 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race |
| 383 | 386 | } |
| 384 | 387 | }); |
| 385 | 388 | contentPanel.add(correctByDeclination); |
| 389 | + final HorizontalPanel tokenPanel = new HorizontalPanel(); |
|
| 390 | + tokenPanel.setSpacing(5); |
|
| 391 | + tokenPanel.add(new Label(stringMessages.optionalBearerTokenForWindImport())); |
|
| 392 | + tokenPanel.add(optionalBearerTokenBox); |
|
| 393 | + contentPanel.add(tokenPanel); |
|
| 386 | 394 | contentPanel.add(importButton); |
| 387 | 395 | contentPanel.add(resultReport); |
| 388 | 396 | return igtimiWindImportRootPanel; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java
| ... | ... | @@ -243,7 +243,7 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa |
| 243 | 243 | throws NotFoundException, NotDenotableForRaceLogTrackingException; |
| 244 | 244 | |
| 245 | 245 | Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces, |
| 246 | - boolean correctByDeclination) |
|
| 246 | + boolean correctByDeclination, String optionalBearerTokenOrNull) |
|
| 247 | 247 | throws IllegalStateException, Exception; |
| 248 | 248 | |
| 249 | 249 | IgtimiDataAccessWindowWithSecurityDTO addIgtimiDataAccessWindow(String deviceSerialNumber, Date from, Date to); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java
| ... | ... | @@ -586,7 +586,7 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic |
| 586 | 586 | void removeIgtimiDevice(String deviceSerialNumber, AsyncCallback<Void> asyncCallback); |
| 587 | 587 | |
| 588 | 588 | void importWindFromIgtimi(List<RaceDTO> selectedRaces, boolean correctByDeclination, |
| 589 | - AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback); |
|
| 589 | + String optionalBearerTokenOrNull, AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback); |
|
| 590 | 590 | |
| 591 | 591 | /** |
| 592 | 592 | * The boolean result reflects whether a connection to the device identified by {@code serialNumber} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
| ... | ... | @@ -2561,4 +2561,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, |
| 2561 | 2561 | String successfullyCopiedPairings(); |
| 2562 | 2562 | String selectFromRaceColumn(); |
| 2563 | 2563 | String selectToRaceColumn(); |
| 2564 | + String exportTWAHistogramToCsv(); |
|
| 2565 | + String exportWindSpeedHistogramToCsv(); |
|
| 2566 | + String optionalBearerTokenForWindImport(); |
|
| 2564 | 2567 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
| ... | ... | @@ -2598,4 +2598,7 @@ selectRaceColumnsWhosePairingsToCopy=Race columns whose pairings to copy |
| 2598 | 2598 | errorCopyingPairings=Error copying pairings: {0} |
| 2599 | 2599 | successfullyCopiedPairings=Successfully copied pairings |
| 2600 | 2600 | selectFromRaceColumn=Select race column from where to start copying pairings |
| 2601 | -selectToRaceColumn=Select race colunm to where to copy pairings |
|
| ... | ... | \ No newline at end of file |
| 0 | +selectToRaceColumn=Select race colunm to where to copy pairings |
|
| 1 | +exportTWAHistogramToCsv=Export True Wind Angle histogram to CSV |
|
| 2 | +exportWindSpeedHistogramToCsv=Export Wind Speed histogram to CSV |
|
| 3 | +optionalBearerTokenForWindImport=Optional bearer token for wind import |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
| ... | ... | @@ -2592,4 +2592,7 @@ selectRaceColumnsWhosePairingsToCopy=Rennspalten auswählen, deren Zuordnungen k |
| 2592 | 2592 | errorCopyingPairings=Fehler beim Kopieren der Zuordnungen: {0} |
| 2593 | 2593 | successfullyCopiedPairings=Zuordnungen erfolgreich kopiert |
| 2594 | 2594 | selectFromRaceColumn=Spalte auswählen, ab der die Zuordnungen kopiert werden sollen |
| 2595 | -selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen |
|
| ... | ... | \ No newline at end of file |
| 0 | +selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen |
|
| 1 | +exportTWAHistogramToCsv=Histogramm der wahren Windwinkel in CSV exportieren |
|
| 2 | +exportWindSpeedHistogramToCsv=Histogramm der wahren Windgeschwindigkeiten in CSV exportieren |
|
| 3 | +optionalBearerTokenForWindImport=Optionales Bearer Token für Wind-Import |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPassingsPanel.java
| ... | ... | @@ -130,7 +130,7 @@ public class EditMarkPassingsPanel extends AbstractCompositeComponent<AbstractSe |
| 130 | 130 | enableButtons(); |
| 131 | 131 | } |
| 132 | 132 | }); |
| 133 | - wayPointSelectionTable = new BaseCelltable<Util.Pair<Integer, Date>>(); |
|
| 133 | + wayPointSelectionTable = new BaseCelltable<Util.Pair<Integer, Date>>(/* pageSize */10000); |
|
| 134 | 134 | wayPointSelectionTable.addColumn(new Column<Util.Pair<Integer, Date>, SafeHtml>(new AnchorCell()) { |
| 135 | 135 | @Override |
| 136 | 136 | public SafeHtml getValue(final Util.Pair<Integer, Date> object) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java
| ... | ... | @@ -46,15 +46,15 @@ public class BoatClassVectorGraphicsResolver { |
| 46 | 46 | BoatClassMasterdata.HOBIE_WILD_CAT, BoatClassMasterdata.HOBIE_16, BoatClassMasterdata.HOBIE_TIGER, |
| 47 | 47 | BoatClassMasterdata.A_CAT, BoatClassMasterdata.TORNADO, BoatClassMasterdata.FLYING_PHANTOM, BoatClassMasterdata.ORC_MULTIHULL); |
| 48 | 48 | BoatClassVectorGraphics keelBoatWithGennaker = new KeelBoatWithGennakerVectorGraphics(BoatClassMasterdata.ELAN350, |
| 49 | - BoatClassMasterdata.FIRST_CLASS_7_5, |
|
| 49 | + BoatClassMasterdata.ELAN_E4, BoatClassMasterdata.FIRST_CLASS_7_5, |
|
| 50 | 50 | BoatClassMasterdata.J70, BoatClassMasterdata.J80, BoatClassMasterdata.J92, BoatClassMasterdata.J92S, |
| 51 | 51 | BoatClassMasterdata.B_ONE, BoatClassMasterdata.IRC, BoatClassMasterdata.LASER_SB3, BoatClassMasterdata.LONGTZE, |
| 52 | 52 | BoatClassMasterdata.RS_FEVA, BoatClassMasterdata.RS_TERA, BoatClassMasterdata.RS_VENTURE, |
| 53 | 53 | BoatClassMasterdata.RS_VAREO, |
| 54 | 54 | BoatClassMasterdata.RS100, BoatClassMasterdata.RS21, BoatClassMasterdata.TP52, |
| 55 | 55 | BoatClassMasterdata.CLUB_SWAN_50, BoatClassMasterdata.BAVARIA_CRUISER_41S, BoatClassMasterdata.BAVARIA_CRUISER_45, |
| 56 | - BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.BENETEAU_FIRST_35, BoatClassMasterdata.BENETEAU_FIRST_45, |
|
| 57 | - BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99, |
|
| 56 | + BoatClassMasterdata.BAVARIA_CRUISER_46, BoatClassMasterdata.BENETEAU_FIRST_35, BoatClassMasterdata.BENETEAU_FIRST_36, |
|
| 57 | + BoatClassMasterdata.BENETEAU_FIRST_45, BoatClassMasterdata.SPAEKHUGGER, BoatClassMasterdata.SCAN_KAP_99, |
|
| 58 | 58 | BoatClassMasterdata.BB10M, BoatClassMasterdata.WAYFARER, BoatClassMasterdata.X_332, |
| 59 | 59 | BoatClassMasterdata.BRASSFAHRT_I, BoatClassMasterdata.BRASSFAHRT_II, BoatClassMasterdata.BRASSFAHRT_III, |
| 60 | 60 | BoatClassMasterdata.BRASSFAHRT_IV, BoatClassMasterdata.BRASSFAHRT_V, |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarmining/PolarBackendResultsPresenter.java
| ... | ... | @@ -59,7 +59,6 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 59 | 59 | public PolarBackendResultsPresenter(Component<?> parent, ComponentContext<?> context, |
| 60 | 60 | StringMessages stringMessages) { |
| 61 | 61 | super(parent, context, stringMessages); |
| 62 | - |
|
| 63 | 62 | polarChart = ChartFactory.createPolarChart(); |
| 64 | 63 | polarChart.getYAxis().setMin(0); |
| 65 | 64 | polarChartWrapperPanel = new SimpleLayoutPanel() { |
| ... | ... | @@ -70,7 +69,6 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 70 | 69 | } |
| 71 | 70 | }; |
| 72 | 71 | polarChartWrapperPanel.add(polarChart); |
| 73 | - |
|
| 74 | 72 | speedChart = ChartFactory.createSpeedChart(stringMessages); |
| 75 | 73 | angleChart = ChartFactory.createAngleChart(stringMessages); |
| 76 | 74 | speedAndAngleChart = new DockLayoutPanel(Unit.PCT) { |
| ... | ... | @@ -84,16 +82,12 @@ public class PolarBackendResultsPresenter extends AbstractSailingResultsPresente |
| 84 | 82 | }; |
| 85 | 83 | speedAndAngleChart.addNorth(speedChart, 50); |
| 86 | 84 | speedAndAngleChart.addSouth(angleChart, 50); |
| 87 | - |
|
| 88 | 85 | dockLayoutPanel = new DockLayoutPanel(Unit.PCT); |
| 89 | 86 | dockLayoutPanel.addWest(polarChartWrapperPanel, 40); |
| 90 | 87 | dockLayoutPanel.addEast(speedAndAngleChart, 60); |
| 91 | - |
|
| 92 | 88 | ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
| 93 | - |
|
| 94 | 89 | Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
| 95 | 90 | new ClickHandler() { |
| 96 | - |
|
| 97 | 91 | @Override |
| 98 | 92 | public void onClick(ClickEvent event) { |
| 99 | 93 | chartToCsvExporter.exportChartAsCsvToClipboard(polarChart); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarmining/PolarResultsPresenter.java
| ... | ... | @@ -22,7 +22,6 @@ import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions; |
| 22 | 22 | |
| 23 | 23 | import com.google.gwt.core.client.Scheduler; |
| 24 | 24 | import com.google.gwt.dom.client.Style.Unit; |
| 25 | -import com.google.gwt.event.dom.client.ClickEvent; |
|
| 26 | 25 | import com.google.gwt.event.dom.client.ClickHandler; |
| 27 | 26 | import com.google.gwt.user.client.ui.Button; |
| 28 | 27 | import com.google.gwt.user.client.ui.DockLayoutPanel; |
| ... | ... | @@ -104,15 +103,16 @@ public class PolarResultsPresenter extends AbstractSailingResultsPresenter<Setti |
| 104 | 103 | dockLayoutPanel = new DockLayoutPanel(Unit.PCT); |
| 105 | 104 | dockLayoutPanel.addWest(polarChartWrapperPanel, 40); |
| 106 | 105 | dockLayoutPanel.addEast(histogramChartsWrapperPanel, 60); |
| 107 | - ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
|
| 108 | - Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
|
| 109 | - new ClickHandler() { |
|
| 110 | - @Override |
|
| 111 | - public void onClick(ClickEvent event) { |
|
| 112 | - chartToCsvExporter.exportChartAsCsvToClipboard(polarChart); |
|
| 113 | - } |
|
| 114 | - }); |
|
| 106 | + final ChartToCsvExporter chartToCsvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); |
|
| 107 | + final Button exportStatisticsCurveToCsvButton = new Button(stringMessages.exportStatisticsCurveToCsv(), |
|
| 108 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(polarChart)); |
|
| 115 | 109 | addControl(exportStatisticsCurveToCsvButton); |
| 110 | + final Button exportTWAHistogramToCsvButton = new Button(stringMessages.exportTWAHistogramToCsv(), |
|
| 111 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(dataCountHistogramChart)); |
|
| 112 | + addControl(exportTWAHistogramToCsvButton); |
|
| 113 | + final Button exportWindSpeedHistogramToCsvButton = new Button(stringMessages.exportWindSpeedHistogramToCsv(), |
|
| 114 | + (ClickHandler) e->chartToCsvExporter.exportChartAsCsvToClipboard(dataCountPerAngleHistogramChart)); |
|
| 115 | + addControl(exportWindSpeedHistogramToCsvButton); |
|
| 116 | 116 | setSeriesShowAndHideHandler(); |
| 117 | 117 | } |
| 118 | 118 | |
| ... | ... | @@ -230,6 +230,7 @@ public class PolarResultsPresenter extends AbstractSailingResultsPresenter<Setti |
| 230 | 230 | if (point != null) { |
| 231 | 231 | Map<Double, Integer> histogramDataForAngle = histogramData.get(index); |
| 232 | 232 | Series dataCountPerAngleSeries = dataCountPerAngleHistogramChart.createSeries(); |
| 233 | + dataCountPerAngleSeries.setName(key.asString() + " - " + convertedAngle + stringMessages.degreesShort()); |
|
| 233 | 234 | // Iterating over the histogram data without sorting the x coordinates ascending leads |
| 234 | 235 | // to a massive occurrence of Highcharts error 15, freezing the complete UI |
| 235 | 236 | List<Double> sortedAngles = new ArrayList<>(histogramDataForAngle.keySet()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/Activator.java
| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.server; |
| 2 | 2 | |
| 3 | +import java.util.Optional; |
|
| 4 | + |
|
| 3 | 5 | import org.osgi.framework.BundleActivator; |
| 4 | 6 | import org.osgi.framework.BundleContext; |
| 5 | 7 | |
| ... | ... | @@ -10,11 +12,31 @@ public class Activator implements BundleActivator { |
| 10 | 12 | private SailingServiceImpl sailingServiceToStopWhenStopping; |
| 11 | 13 | private static Activator INSTANCE; |
| 12 | 14 | |
| 15 | + /** |
|
| 16 | + * If the system property named after this constant is set, its value is used for Google Maps API authentication. |
|
| 17 | + * It takes precedence over the environment variable named after {@link #GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_ENV_VAR_NAME}. |
|
| 18 | + */ |
|
| 13 | 19 | private final static String GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_PROPERTY_NAME = "google.maps.authenticationparams"; |
| 14 | 20 | |
| 21 | + /** |
|
| 22 | + * If the system property named after {@link #GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_PROPERTY_NAME} is not set, |
|
| 23 | + * this environment variable is checked for Google Maps API authentication parameters. |
|
| 24 | + */ |
|
| 25 | + private final static String GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_ENV_VAR_NAME = "GOOGLE_MAPS_AUTHENTICATION_PARAMS"; |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * The system property named after this constant is expected to provide the YouTube V3 API key. Takes precedence over |
|
| 29 | + * the environment variable named after {@link #YOUTUBE_V3_API_KEY_ENV_VAR_NAME}. |
|
| 30 | + */ |
|
| 15 | 31 | private final static String YOUTUBE_V3_API_KEY_PROPERTY_NAME = "youtube.api.key"; |
| 16 | 32 | |
| 17 | 33 | /** |
| 34 | + * If the system property named after {@link #YOUTUBE_V3_API_KEY_PROPERTY_NAME} is not set, this environment variable |
|
| 35 | + * is checked for the YouTube V3 API key. |
|
| 36 | + */ |
|
| 37 | + private final static String YOUTUBE_V3_API_KEY_ENV_VAR_NAME = "YOUTUBE_V3_API_KEY"; |
|
| 38 | + |
|
| 39 | + /** |
|
| 18 | 40 | * Required by {@link GoogleMapsLoader#load(Runnable, String)} and to be provided through a system property named |
| 19 | 41 | * after {@link GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_PROPERTY_NAME}. The value would be something like |
| 20 | 42 | * {@code client=abcde&channel=fghij}. |
| ... | ... | @@ -34,8 +56,12 @@ public class Activator implements BundleActivator { |
| 34 | 56 | @Override |
| 35 | 57 | public void start(BundleContext context) throws Exception { |
| 36 | 58 | Activator.context = context; |
| 37 | - googleMapsLoaderAuthenticationParams = context.getProperty(GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_PROPERTY_NAME); |
|
| 38 | - youtubeApiKey = context.getProperty(YOUTUBE_V3_API_KEY_PROPERTY_NAME); |
|
| 59 | + googleMapsLoaderAuthenticationParams = Optional |
|
| 60 | + .ofNullable(context.getProperty(GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_PROPERTY_NAME)) |
|
| 61 | + .orElse(System.getenv(GOOGLE_MAPS_LOADER_AUTHENTICATION_PARAMS_ENV_VAR_NAME)); |
|
| 62 | + youtubeApiKey = Optional |
|
| 63 | + .ofNullable(context.getProperty(YOUTUBE_V3_API_KEY_PROPERTY_NAME)) |
|
| 64 | + .orElse(System.getenv(YOUTUBE_V3_API_KEY_ENV_VAR_NAME)); |
|
| 39 | 65 | } |
| 40 | 66 | |
| 41 | 67 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
| ... | ... | @@ -292,6 +292,9 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 292 | 292 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 293 | 293 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 294 | 294 | import com.sap.sailing.domain.sharding.ShardingContext; |
| 295 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 296 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 297 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 295 | 298 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; |
| 296 | 299 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory; |
| 297 | 300 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingArchiveConfiguration; |
| ... | ... | @@ -307,14 +310,11 @@ import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 307 | 310 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 308 | 311 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 309 | 312 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 310 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 311 | 313 | import com.sap.sailing.domain.tracking.Maneuver; |
| 312 | 314 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 313 | -import com.sap.sailing.domain.tracking.Track; |
|
| 314 | 315 | import com.sap.sailing.domain.tracking.TrackedLeg; |
| 315 | 316 | import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; |
| 316 | 317 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 317 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 318 | 318 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 319 | 319 | import com.sap.sailing.domain.tracking.WindTrack; |
| 320 | 320 | import com.sap.sailing.domain.tracking.WindWithConfidence; |
| ... | ... | @@ -6099,7 +6099,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 6099 | 6099 | return Activator.getInstance().getGoogleMapsLoaderAuthenticationParams(); |
| 6100 | 6100 | } |
| 6101 | 6101 | |
| 6102 | - protected IgtimiConnection createIgtimiConnection() { |
|
| 6103 | - return getIgtimiConnectionFactory().getOrCreateConnection(()->getSecurityService().getCurrentUser() != null ? getSecurityService().getAccessToken(getSecurityService().getCurrentUser().getName()) : null); |
|
| 6102 | + /** |
|
| 6103 | + * @param optionalBearerToken |
|
| 6104 | + * if present, this bearer token is used to authenticate the Igtimi connection; otherwise, we try to use |
|
| 6105 | + * Igtimi default credentials provided at startup through the {@code igtimi.bearer.token} system |
|
| 6106 | + * property. Only if no such system property has been set, we look for a logged-in user in the |
|
| 6107 | + * {@link #getSecurityService() security service} and use its access token |
|
| 6108 | + */ |
|
| 6109 | + protected IgtimiConnection createIgtimiConnection(Optional<String> optionalBearerToken) { |
|
| 6110 | + return optionalBearerToken.map(bearerToken->getIgtimiConnectionFactory().getOrCreateConnection(bearerToken)) |
|
| 6111 | + .orElse(getIgtimiConnectionFactory().getOrCreateConnection(()->getSecurityService().getCurrentUser() != null |
|
| 6112 | + ? getSecurityService().getAccessToken(getSecurityService().getCurrentUser().getName()) |
|
| 6113 | + : null)); |
|
| 6104 | 6114 | } |
| 6105 | 6115 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceWriteImpl.java
| ... | ... | @@ -2215,7 +2215,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili |
| 2215 | 2215 | |
| 2216 | 2216 | @Override |
| 2217 | 2217 | public Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces, |
| 2218 | - boolean correctByDeclination) |
|
| 2218 | + boolean correctByDeclination, String optionalBearerTokenOrNull) |
|
| 2219 | 2219 | throws IllegalStateException, ClientProtocolException, IOException, org.json.simple.parser.ParseException { |
| 2220 | 2220 | final List<DynamicTrackedRace> trackedRaces = new ArrayList<>(); |
| 2221 | 2221 | if (selectedRaces != null && !selectedRaces.isEmpty()) { |
| ... | ... | @@ -2238,7 +2238,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili |
| 2238 | 2238 | } |
| 2239 | 2239 | } |
| 2240 | 2240 | Map<RegattaAndRaceIdentifier, Integer> numberOfWindFixesImportedPerRace = new HashMap<RegattaAndRaceIdentifier, Integer>(); |
| 2241 | - final IgtimiConnection conn = createIgtimiConnection(); |
|
| 2241 | + final IgtimiConnection conn = createIgtimiConnection(Optional.ofNullable(optionalBearerTokenOrNull)); |
|
| 2242 | 2242 | // filter account based on used permissions to read account: |
| 2243 | 2243 | Map<TrackedRace, Integer> resultsForAccounts = conn.importWindIntoRace(trackedRaces, correctByDeclination); |
| 2244 | 2244 | for (Entry<TrackedRace, Integer> resultForAccount : resultsForAccounts.entrySet()) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/TrackingConnectorInfoDTO.java
| ... | ... | @@ -2,7 +2,7 @@ package com.sap.sailing.gwt.ui.shared; |
| 2 | 2 | |
| 3 | 3 | import com.google.gwt.core.shared.GwtIncompatible; |
| 4 | 4 | import com.google.gwt.user.client.rpc.IsSerializable; |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | 6 | |
| 7 | 7 | public class TrackingConnectorInfoDTO implements IsSerializable { |
| 8 | 8 | private String trackingConnectorName; |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_36.png differ |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/ELAN_E4.png differ |
java/com.sap.sailing.ingestion/src/main/java/com/sap/sailing/ingestion/FixCombinationLambda.java
| ... | ... | @@ -23,7 +23,7 @@ import com.amazonaws.services.lambda.runtime.Context; |
| 23 | 23 | import com.amazonaws.services.lambda.runtime.RequestStreamHandler; |
| 24 | 24 | import com.sap.sailing.domain.common.DeviceIdentifier; |
| 25 | 25 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 26 | -import com.sap.sailing.domain.tracking.impl.TimedComparator; |
|
| 26 | +import com.sap.sailing.domain.shared.tracking.impl.TimedComparator; |
|
| 27 | 27 | import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer; |
| 28 | 28 | import com.sap.sailing.server.gateway.deserialization.impl.Helpers; |
| 29 | 29 | import com.sap.sailing.server.gateway.serialization.impl.GPSFixMovingJsonSerializer; |
java/com.sap.sailing.nmeaconnector/META-INF/MANIFEST.MF
| ... | ... | @@ -12,7 +12,8 @@ Require-Bundle: com.sap.sailing.domain, |
| 12 | 12 | com.sap.sse.common, |
| 13 | 13 | net.sf.marineapi, |
| 14 | 14 | com.sap.sailing.declination |
| 15 | -Import-Package: org.osgi.framework |
|
| 15 | +Import-Package: org.osgi.framework, |
|
| 16 | + com.sap.sailing.domain.shared.tracking |
|
| 16 | 17 | Bundle-ActivationPolicy: lazy |
| 17 | 18 | Bundle-Activator: com.sap.sailing.nmeaconnector.impl.Activator |
| 18 | 19 | Automatic-Module-Name: com.sap.sailing.nmeaconnector |
java/com.sap.sailing.nmeaconnector/src/com/sap/sailing/nmeaconnector/impl/KnotSpeedWithBearingAndTimepoint.java
| ... | ... | @@ -1,13 +1,12 @@ |
| 1 | 1 | package com.sap.sailing.nmeaconnector.impl; |
| 2 | 2 | |
| 3 | 3 | import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; |
| 4 | -import com.sap.sailing.domain.tracking.Track; |
|
| 5 | 4 | import com.sap.sailing.nmeaconnector.TimedSpeedWithBearing; |
| 6 | 5 | import com.sap.sse.common.Bearing; |
| 7 | 6 | import com.sap.sse.common.TimePoint; |
| 8 | 7 | |
| 9 | 8 | /** |
| 10 | - * Can be used as a wind vector in a {@link Track} because it has a {@link TimePoint}. |
|
| 9 | + * Can be used as a wind vector in a {@code Track} because it has a {@link TimePoint}. |
|
| 11 | 10 | * |
| 12 | 11 | * @author Axel Uhl (d043530) |
| 13 | 12 | * |
java/com.sap.sailing.polars.datamining.shared/src/com/sap/sailing/polars/datamining/shared/PolarAggregation.java
| ... | ... | @@ -15,6 +15,10 @@ public interface PolarAggregation extends Serializable { |
| 15 | 15 | |
| 16 | 16 | PolarDataMiningSettings getSettings(); |
| 17 | 17 | |
| 18 | + /** |
|
| 19 | + * Keys are the angles in degrees (0...359). The {@link Double} key of the inner map is the histogram x-value (e.g. |
|
| 20 | + * 5.0 for wind speed 4.5...5.5 with step 1.0). |
|
| 21 | + */ |
|
| 18 | 22 | Map<Integer, Map<Double, Integer>> getCountHistogramPerAngle(); |
| 19 | 23 | |
| 20 | 24 | } |
java/com.sap.sailing.polars.datamining.shared/src/com/sap/sailing/polars/datamining/shared/PolarAggregationImpl.java
| ... | ... | @@ -11,8 +11,12 @@ public class PolarAggregationImpl implements PolarAggregation { |
| 11 | 11 | private double[] sumSpeedsPerAngle = new double[360]; |
| 12 | 12 | private int[] countPerAngle = new int[360]; |
| 13 | 13 | /** |
| 14 | - * FIXME Right now the histogram data is only valid, if the results are grouped by windrange. |
|
| 15 | - * Otherwise the column-indices of different ranges are mixed in one histogram. |
|
| 14 | + * FIXME Right now the histogram data is only valid, if the results are grouped by windrange. Otherwise the |
|
| 15 | + * column-indices of different ranges are mixed in one histogram. |
|
| 16 | + * <p> |
|
| 17 | + * |
|
| 18 | + * Keys are the angles in degrees (0...359). The {@link Double} key of the inner map is the histogram x-value (e.g. |
|
| 19 | + * 5.0 for wind speed 4.5...5.5 with step 1.0). |
|
| 16 | 20 | */ |
| 17 | 21 | private Map<Integer, Map<Double, Integer>> histogramData; |
| 18 | 22 | private int count = 0; |
| ... | ... | @@ -80,6 +84,4 @@ public class PolarAggregationImpl implements PolarAggregation { |
| 80 | 84 | public Map<Integer, Map<Double, Integer>> getCountHistogramPerAngle() { |
| 81 | 85 | return histogramData; |
| 82 | 86 | } |
| 83 | - |
|
| 84 | - |
|
| 85 | 87 | } |
java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF
| ... | ... | @@ -6,6 +6,8 @@ Bundle-Version: 1.0.0.qualifier |
| 6 | 6 | Bundle-Vendor: SAP |
| 7 | 7 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 8 | 8 | Fragment-Host: com.sap.sailing.polars |
| 9 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 10 | + com.sap.sailing.domain.shared.tracking.impl |
|
| 9 | 11 | Require-Bundle: org.hamcrest;bundle-version="2.2.0", |
| 10 | 12 | org.mockito.mockito-core;bundle-version="1.10.14", |
| 11 | 13 | junit-jupiter-api;bundle-version="5.11.3", |
java/com.sap.sailing.polars/META-INF/MANIFEST.MF
| ... | ... | @@ -39,6 +39,7 @@ Require-Bundle: |
| 39 | 39 | org.apache.httpcomponents.httpcore;bundle-version="4.4.9", |
| 40 | 40 | com.sap.sailing.shared.server |
| 41 | 41 | Import-Package: |
| 42 | + com.sap.sailing.domain.shared.tracking, |
|
| 42 | 43 | com.sap.sailing.util, |
| 43 | 44 | javax.ws.rs;version="1.1.1", |
| 44 | 45 | javax.ws.rs.core;version="1.1.1", |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/core/SeleniumTestInvocationProvider.java
| ... | ... | @@ -2,6 +2,7 @@ package com.sap.sailing.selenium.core; |
| 2 | 2 | |
| 3 | 3 | import java.util.Arrays; |
| 4 | 4 | import java.util.List; |
| 5 | +import java.util.Map.Entry; |
|
| 5 | 6 | import java.util.stream.Stream; |
| 6 | 7 | |
| 7 | 8 | import org.junit.jupiter.api.extension.Extension; |
| ... | ... | @@ -33,6 +34,9 @@ public class SeleniumTestInvocationProvider implements TestTemplateInvocationCon |
| 33 | 34 | @Override |
| 34 | 35 | public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) { |
| 35 | 36 | TestEnvironmentConfiguration config = TestEnvironmentConfiguration.getInstance(); |
| 37 | + for (Entry<String, String> e : config.getSystemProperties().entrySet()) { |
|
| 38 | + System.setProperty(e.getKey(), e.getValue()); |
|
| 39 | + } |
|
| 36 | 40 | return config.getDriverDefinitions().stream().map(driverDef -> { |
| 37 | 41 | return new SeleniumTestContext(driverDef); |
| 38 | 42 | }); |
| ... | ... | @@ -55,4 +59,4 @@ public class SeleniumTestInvocationProvider implements TestTemplateInvocationCon |
| 55 | 59 | return Arrays.asList(new SeleniumTestEnvironmentInjector(driverDefinition)); |
| 56 | 60 | } |
| 57 | 61 | } |
| 58 | -} |
|
| ... | ... | \ No newline at end of file |
| 0 | +} |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/raceboard/SimulatorOverlayTest.java
| ... | ... | @@ -119,7 +119,7 @@ public class SimulatorOverlayTest extends AbstractSeleniumTest { |
| 119 | 119 | gpxInputStream.close(); |
| 120 | 120 | final String routeconverterWindFileName = tmpFile.getAbsolutePath(); |
| 121 | 121 | windPanel.importWindFromRouteconverter(routeconverterWindFileName, /* waiting up to 10 min */ 15 * 60); |
| 122 | - Thread.sleep(10000); // wait for 10s to allow all wind fixes to get processed by the PolarDataService |
|
| 122 | + Thread.sleep(30000); // wait for 30s to allow all wind fixes to get processed by the PolarDataService |
|
| 123 | 123 | } finally { |
| 124 | 124 | tmpFile.delete(); |
| 125 | 125 | } |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/EventBaseJsonDeserializer.java
| ... | ... | @@ -16,7 +16,7 @@ import com.sap.sailing.domain.base.EventBase; |
| 16 | 16 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 17 | 17 | import com.sap.sailing.domain.base.Venue; |
| 18 | 18 | import com.sap.sailing.domain.base.impl.StrippedEventImpl; |
| 19 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 19 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 20 | 20 | import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer; |
| 21 | 21 | import com.sap.sse.common.TimePoint; |
| 22 | 22 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/TrackingConnectorInfoJsonDeserializer.java
| ... | ... | @@ -2,8 +2,8 @@ package com.sap.sailing.server.gateway.deserialization.impl; |
| 2 | 2 | |
| 3 | 3 | import org.json.simple.JSONObject; |
| 4 | 4 | |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 6 | -import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | +import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl; |
|
| 7 | 7 | import com.sap.sailing.server.gateway.serialization.impl.TrackingConnectorInfoJsonSerializer; |
| 8 | 8 | import com.sap.sse.shared.json.JsonDeserializationException; |
| 9 | 9 | import com.sap.sse.shared.json.JsonDeserializer; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java
| ... | ... | @@ -8,7 +8,7 @@ import com.sap.sailing.domain.base.CourseBase; |
| 8 | 8 | import com.sap.sailing.domain.base.Leg; |
| 9 | 9 | import com.sap.sailing.domain.base.Waypoint; |
| 10 | 10 | import com.sap.sailing.domain.common.NauticalSide; |
| 11 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 11 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 12 | 12 | import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseWithGeometryJsonSerializer.CourseGeometry; |
| 13 | 13 | import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; |
| 14 | 14 | import com.sap.sse.common.Bearing; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java
| ... | ... | @@ -10,7 +10,7 @@ import org.json.simple.JSONObject; |
| 10 | 10 | import com.sap.sailing.domain.base.EventBase; |
| 11 | 11 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 12 | 12 | import com.sap.sailing.domain.base.Venue; |
| 13 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 13 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 14 | 14 | import com.sap.sse.shared.json.JsonSerializer; |
| 15 | 15 | import com.sap.sse.shared.media.ImageDescriptor; |
| 16 | 16 | import com.sap.sse.shared.media.VideoDescriptor; |
java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/TrackingConnectorInfoJsonSerializer.java
| ... | ... | @@ -2,7 +2,7 @@ package com.sap.sailing.server.gateway.serialization.impl; |
| 2 | 2 | |
| 3 | 3 | import org.json.simple.JSONObject; |
| 4 | 4 | |
| 5 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 5 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 6 | 6 | import com.sap.sse.shared.json.JsonSerializer; |
| 7 | 7 | |
| 8 | 8 | public class TrackingConnectorInfoJsonSerializer implements JsonSerializer<TrackingConnectorInfo> { |
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
| ... | ... | @@ -115,9 +115,9 @@ import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 115 | 115 | import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; |
| 116 | 116 | import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; |
| 117 | 117 | import com.sap.sailing.domain.sharding.ShardingContext; |
| 118 | +import com.sap.sailing.domain.shared.tracking.LineDetails; |
|
| 118 | 119 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 119 | 120 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 120 | -import com.sap.sailing.domain.tracking.LineDetails; |
|
| 121 | 121 | import com.sap.sailing.domain.tracking.Maneuver; |
| 122 | 122 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 123 | 123 | import com.sap.sailing.domain.tracking.RaceWindCalculator; |
java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java
| ... | ... | @@ -95,6 +95,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier; |
| 95 | 95 | import com.sap.sailing.domain.ranking.RankingMetricConstructor; |
| 96 | 96 | import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 97 | 97 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 98 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 98 | 99 | import com.sap.sailing.domain.statistics.Statistics; |
| 99 | 100 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 100 | 101 | import com.sap.sailing.domain.tracking.RaceHandle; |
| ... | ... | @@ -107,7 +108,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 107 | 108 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 108 | 109 | import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; |
| 109 | 110 | import com.sap.sailing.domain.tracking.TrackerManager; |
| 110 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 111 | 111 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 112 | 112 | import com.sap.sailing.domain.tracking.WindStore; |
| 113 | 113 | import com.sap.sailing.domain.tracking.WindTracker; |
java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java
| ... | ... | @@ -4,10 +4,10 @@ import com.sap.sailing.domain.base.RaceDefinition; |
| 4 | 4 | import com.sap.sailing.domain.base.Regatta; |
| 5 | 5 | import com.sap.sailing.domain.common.RaceIdentifier; |
| 6 | 6 | import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; |
| 7 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 7 | 8 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 8 | 9 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 9 | 10 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 10 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 11 | 11 | import com.sap.sailing.domain.tracking.WindStore; |
| 12 | 12 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore; |
| 13 | 13 | import com.sap.sailing.server.interfaces.RacingEventService; |
java/com.sap.sailing.server.trackfiles.test/src/com/sap/sailing/server/trackfiles/test/JumpyTrackSmootheningTest.java
| ... | ... | @@ -200,7 +200,8 @@ public class JumpyTrackSmootheningTest { |
| 200 | 200 | assertEquals(13, markPassings.size()); |
| 201 | 201 | } |
| 202 | 202 | assertTrue(durationForAdjustedTrack.times(8).compareTo(durationForOriginalTrack) < 0, |
| 203 | - "Expected duration for mark passing analysis on adjusted track to be at least eight times less than for original track"); |
|
| 203 | + "Expected duration for mark passing analysis on adjusted track to be at least eight times less than for original track: "+ |
|
| 204 | + durationForAdjustedTrack+" vs. "+durationForOriginalTrack); |
|
| 204 | 205 | } |
| 205 | 206 | |
| 206 | 207 | private DynamicGPSFixTrack<Competitor, GPSFixMoving> readTrack(String filename) throws Exception { |
java/com.sap.sailing.server.trackfiles/META-INF/MANIFEST.MF
| ... | ... | @@ -21,6 +21,7 @@ Import-Package: com.sap.sailing.domain.abstractlog, |
| 21 | 21 | com.sap.sailing.domain.abstractlog.regatta.events.impl, |
| 22 | 22 | com.sap.sailing.domain.common.sensordata, |
| 23 | 23 | com.sap.sailing.domain.racelogtracking, |
| 24 | + com.sap.sailing.domain.shared.tracking, |
|
| 24 | 25 | com.sap.sse.security.shared, |
| 25 | 26 | javax.xml.bind, |
| 26 | 27 | javax.xml.bind.annotation, |
java/com.sap.sailing.server.trackfiles/src/com/sap/sailing/server/trackfiles/impl/TrackReaderImpl.java
| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.server.trackfiles.impl; |
| 2 | 2 | |
| 3 | -import com.sap.sailing.domain.tracking.Track; |
|
| 3 | +import com.sap.sailing.domain.shared.tracking.Track; |
|
| 4 | 4 | import com.sap.sse.common.Timed; |
| 5 | 5 | |
| 6 | 6 | class TrackReaderImpl<E, T extends Timed> implements TrackReader<E, T>, IterableLocker { |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
| ... | ... | @@ -224,8 +224,9 @@ import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 224 | 224 | import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 225 | 225 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 226 | 226 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 227 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 228 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 227 | 229 | import com.sap.sailing.domain.statistics.Statistics; |
| 228 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 229 | 230 | import com.sap.sailing.domain.tracking.BravoFixTrack; |
| 230 | 231 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 231 | 232 | import com.sap.sailing.domain.tracking.DynamicSensorFixTrack; |
| ... | ... | @@ -247,7 +248,6 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 247 | 248 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 248 | 249 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 249 | 250 | import com.sap.sailing.domain.tracking.TrackedRegattaListener; |
| 250 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 251 | 251 | import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; |
| 252 | 252 | import com.sap.sailing.domain.tracking.WindPositionMode; |
| 253 | 253 | import com.sap.sailing.domain.tracking.WindStore; |
java/com.sap.sailing.server/src/com/sap/sailing/server/security/PermissionAwareRaceTrackingHandler.java
| ... | ... | @@ -28,13 +28,13 @@ import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 28 | 28 | import com.sap.sailing.domain.maneuverhash.ManeuverRaceFingerprintRegistry; |
| 29 | 29 | import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry; |
| 30 | 30 | import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; |
| 31 | +import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo; |
|
| 31 | 32 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| 32 | 33 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 33 | 34 | import com.sap.sailing.domain.tracking.RaceTrackingHandler; |
| 34 | 35 | import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler; |
| 35 | 36 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 36 | 37 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 37 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 38 | 38 | import com.sap.sailing.domain.tracking.WindStore; |
| 39 | 39 | import com.sap.sse.common.Color; |
| 40 | 40 | import com.sap.sse.common.Duration; |
java/com.sap.sailing.server/src/com/sap/sailing/server/simulation/SimulationServiceImpl.java
| ... | ... | @@ -40,7 +40,7 @@ import com.sap.sailing.domain.common.WindSource; |
| 40 | 40 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 41 | 41 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 42 | 42 | import com.sap.sailing.domain.polars.PolarDataService; |
| 43 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 43 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 44 | 44 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 45 | 45 | import com.sap.sailing.domain.tracking.MarkPassing; |
| 46 | 46 | import com.sap.sailing.domain.tracking.RaceListener; |
java/com.sap.sailing.server/src/com/sap/sailing/server/statistics/TrackedRaceStatisticsCacheImpl.java
| ... | ... | @@ -18,8 +18,8 @@ import com.sap.sailing.domain.common.Wind; |
| 18 | 18 | import com.sap.sailing.domain.common.WindSource; |
| 19 | 19 | import com.sap.sailing.domain.common.tracking.GPSFix; |
| 20 | 20 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 21 | +import com.sap.sailing.domain.shared.tracking.AddResult; |
|
| 21 | 22 | import com.sap.sailing.domain.tracking.AbstractTrackedRegattaAndRaceObserver; |
| 22 | -import com.sap.sailing.domain.tracking.AddResult; |
|
| 23 | 23 | import com.sap.sailing.domain.tracking.DynamicTrackedRace; |
| 24 | 24 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 25 | 25 | import com.sap.sailing.domain.tracking.TrackedRace; |
java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF
| ... | ... | @@ -3,6 +3,7 @@ Bundle-ManifestVersion: 2 |
| 3 | 3 | Bundle-Name: Test |
| 4 | 4 | Bundle-SymbolicName: com.sap.sailing.simulator.test |
| 5 | 5 | Bundle-Version: 1.0.0.qualifier |
| 6 | +Import-Package: com.sap.sailing.domain.shared.tracking |
|
| 6 | 7 | Bundle-Vendor: SAP |
| 7 | 8 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 8 | 9 | Require-Bundle: com.sap.sailing.simulator;bundle-version="1.0.0", |
java/com.sap.sailing.simulator/META-INF/MANIFEST.MF
| ... | ... | @@ -14,7 +14,8 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", |
| 14 | 14 | org.apache.httpcomponents.httpclient;bundle-version="4.5.5", |
| 15 | 15 | org.json.simple;bundle-version="1.1.0" |
| 16 | 16 | Bundle-ActivationPolicy: lazy |
| 17 | -Import-Package: org.osgi.framework;version="1.6.0" |
|
| 17 | +Import-Package: com.sap.sailing.domain.shared.tracking, |
|
| 18 | + org.osgi.framework;version="1.6.0" |
|
| 18 | 19 | Bundle-ClassPath: . |
| 19 | 20 | Export-Package: com.sap.sailing.simulator, |
| 20 | 21 | com.sap.sailing.simulator.impl, |
java/com.sap.sailing.www/release_notes_admin.html
| ... | ... | @@ -23,6 +23,25 @@ |
| 23 | 23 | <div class="mainContent"> |
| 24 | 24 | <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
| 25 | 25 | <div class="innerContent"> |
| 26 | + <h2 class="articleSubheadline">October 2025</h2> |
|
| 27 | + <ul class="bulletList"> |
|
| 28 | + <li>When WindBots sent buffered data, e.g., from previous days, there was a possibility that |
|
| 29 | + under some circumstances this data may have interfered with live data, leading, e.g., |
|
| 30 | + to "jumping" WindBot positions. This has now been fixed.<p> |
|
| 31 | + <li>For the following system properties, alternative environment variables have been introduced: |
|
| 32 | + <ul> |
|
| 33 | + <li><tt>AWS_S3_TEST_S3ACCESSID</tt> for <tt>aws.s3.test.s3AccessId</tt></li> |
|
| 34 | + <li><tt>AWS_S3_TEST_S3ACCESSKEY</tt> for <tt>aws.s3.test.s3AccessKey</tt></li> |
|
| 35 | + <li><tt>GOOGLE_MAPS_AUTHENTICATION_PARAMS</tt> for <tt>google.maps.authenticationparams</tt></li> |
|
| 36 | + <li><tt>YOUTUBE_V3_API_KEY</tt> for <tt>youtube.api.key</tt></li> |
|
| 37 | + <li><tt>GEONAMES_ORG_USERNAMES</tt> for <tt>geonames.org.usernames</tt></li> |
|
| 38 | + </ul> |
|
| 39 | + If the system property is set, it takes precedence over the environment variable.</li> |
|
| 40 | + </ul> |
|
| 41 | + <h2 class="articleSubheadline">September 2025</h2> |
|
| 42 | + <ul class="bulletList"> |
|
| 43 | + <li>Added boat classes "Elan E4" and "Beneteau First 36" to the list of boat classes.<p> |
|
| 44 | + </ul> |
|
| 26 | 45 | <h2 class="articleSubheadline">August 2025</h2> |
| 27 | 46 | <ul class="bulletList"> |
| 28 | 47 | <li>Pairings, e.g., in league set-ups, can not be copied across regattas/leaderboards. |
java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sse.datamining.ui.client; |
| 2 | 2 | |
| 3 | -import java.util.ArrayList; |
|
| 4 | -import java.util.List; |
|
| 3 | +import java.util.HashMap; |
|
| 4 | +import java.util.Map; |
|
| 5 | +import java.util.Map.Entry; |
|
| 6 | +import java.util.TreeMap; |
|
| 5 | 7 | |
| 6 | 8 | import org.moxieapps.gwt.highcharts.client.Chart; |
| 7 | 9 | import org.moxieapps.gwt.highcharts.client.Point; |
| ... | ... | @@ -42,38 +44,33 @@ public class ChartToCsvExporter { |
| 42 | 44 | private static String createCsvExportContentForStatisticsCurve(Chart chartToExport) { |
| 43 | 45 | if (chartToExport != null && chartToExport.getSeries().length > 0) { |
| 44 | 46 | final StringBuilder csvStr = new StringBuilder("Series name"); |
| 45 | - int minX = Integer.MAX_VALUE; |
|
| 47 | + final Map<Number, Integer> columnIndexByXValue = new HashMap<>(); |
|
| 46 | 48 | { |
| 47 | - final List<String> columnNames = new ArrayList<>(); |
|
| 49 | + final TreeMap<Number, String> orderedColumnNames = new TreeMap<>((a, b) -> Double.compare(a.doubleValue(), b.doubleValue())); |
|
| 48 | 50 | // collect column names across all series; some may not have points for all columns |
| 49 | 51 | for (final Series series : chartToExport.getSeries()) { |
| 50 | - for (final Point p : series.getPoints()) { |
|
| 51 | - if (p.getX().intValue() < minX) { |
|
| 52 | - minX = p.getX().intValue(); |
|
| 53 | - } |
|
| 54 | - } |
|
| 55 | 52 | for (Point point : series.getPoints()) { |
| 56 | - final String pointName = point.getName(); |
|
| 57 | - final String columnName = pointName != null && !pointName.isEmpty() ? pointName : point.getX().toString(); |
|
| 58 | - while (columnNames.size() <= point.getX().intValue()-minX) { |
|
| 59 | - columnNames.add(columnNames.size(), null); |
|
| 60 | - } |
|
| 61 | - columnNames.set(point.getX().intValue()-minX, columnName); |
|
| 53 | + final String columnName = getColumnName(point); |
|
| 54 | + orderedColumnNames.put(point.getX(), columnName); |
|
| 62 | 55 | } |
| 63 | 56 | } |
| 64 | - for (final String columnName : columnNames) { |
|
| 57 | + for (final String columnName : orderedColumnNames.values()) { |
|
| 65 | 58 | csvStr.append(';'); |
| 66 | 59 | if (columnName != null && !columnName.isEmpty()) { |
| 67 | 60 | csvStr.append(columnName); |
| 68 | 61 | } |
| 69 | 62 | } |
| 63 | + int columnIndex = 0; |
|
| 64 | + for (final Entry<Number, String> e : orderedColumnNames.entrySet()) { |
|
| 65 | + columnIndexByXValue.put(e.getKey(), columnIndex++); |
|
| 66 | + } |
|
| 70 | 67 | } |
| 71 | 68 | csvStr.append("\r\n"); |
| 72 | 69 | for (Series series : chartToExport.getSeries()) { |
| 73 | 70 | csvStr.append(series.getName()); |
| 74 | 71 | int lastXIndex = -1; |
| 75 | 72 | for (Point point : series.getPoints()) { |
| 76 | - while (lastXIndex < point.getX().intValue()-minX) { |
|
| 73 | + while (lastXIndex < columnIndexByXValue.get(point.getX())) { |
|
| 77 | 74 | csvStr.append(';'); |
| 78 | 75 | lastXIndex++; |
| 79 | 76 | } |
| ... | ... | @@ -87,4 +84,10 @@ public class ChartToCsvExporter { |
| 87 | 84 | return "Statistics are empty"; |
| 88 | 85 | } |
| 89 | 86 | |
| 87 | + private static String getColumnName(Point point) { |
|
| 88 | + final String pointName = point.getName(); |
|
| 89 | + final String columnName = pointName != null && !pointName.isEmpty() ? pointName : point.getX().toString(); |
|
| 90 | + return columnName; |
|
| 91 | + } |
|
| 92 | + |
|
| 90 | 93 | } |
java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/testsupport/AmazonS3TestSupport.java
| ... | ... | @@ -8,17 +8,20 @@ import com.sap.sse.filestorage.impl.BaseFileStorageServiceImpl; |
| 8 | 8 | import com.sap.sse.security.SecurityService; |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | - * Provide the S3 credentials for an IAM account that has access to the "sapsailing-automatic-upload-test" bucket |
|
| 12 | - * in the system properties {@code aws.s3.test.s3AccessId} and {@code aws.s3.test.s3AccessKey}. For the build |
|
| 13 | - * script in {@code configuration/buildAndUpdateProduct.sh} this can be done by setting the {@code APP_PARAMETERS} |
|
| 14 | - * environment variable for the script like this: {@code APP_PARAMETERS="-Daws.s3.test.s3AccessId=... -Daws.s3.test.s3AccessKey=..."} |
|
| 11 | + * Provide the S3 credentials for an IAM account that has access to the "sapsailing-automatic-upload-test" bucket in the |
|
| 12 | + * system properties {@code aws.s3.test.s3AccessId} and {@code aws.s3.test.s3AccessKey}. For the build script in |
|
| 13 | + * {@code configuration/buildAndUpdateProduct.sh} this can be done by setting the {@code APP_PARAMETERS} environment |
|
| 14 | + * variable for the script like this: |
|
| 15 | + * {@code APP_PARAMETERS="-Daws.s3.test.s3AccessId=... -Daws.s3.test.s3AccessKey=..."}. Alternatively, e.g., in order to |
|
| 16 | + * avoid system property setting with the "-D" command line option to be shown in log files, you may pass the ID and key |
|
| 17 | + * as environment variables {@code AWS_S3_TEST_S3ACCESSID} and {@code AWS_S3_TEST_S3ACCESSKEY}, respectively. |
|
| 15 | 18 | * |
| 16 | 19 | * @author Axel Uhl (d043530) |
| 17 | 20 | * |
| 18 | 21 | */ |
| 19 | 22 | public class AmazonS3TestSupport { |
| 20 | - public static final String s3AccessId = System.getProperty("aws.s3.test.s3AccessId"); |
|
| 21 | - public static final String s3AccessKey = System.getProperty("aws.s3.test.s3AccessKey"); |
|
| 23 | + public static final String s3AccessId = System.getProperty("aws.s3.test.s3AccessId", System.getenv("AWS_S3_TEST_S3ACCESSID")); |
|
| 24 | + public static final String s3AccessKey = System.getProperty("aws.s3.test.s3AccessKey", System.getenv("AWS_S3_TEST_S3ACCESSKEY")); |
|
| 22 | 25 | private static final String s3BucketName = "sapsailing-automatic-upload-test"; |
| 23 | 26 | |
| 24 | 27 | public static BaseFileStorageServiceImpl createService(final SecurityService securityService) throws InvalidPropertiesException, IOException { |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
| ... | ... | @@ -155,6 +155,7 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.DescribeTarg |
| 155 | 155 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.DescribeTargetGroupsResponse; |
| 156 | 156 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.DescribeTargetHealthRequest; |
| 157 | 157 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.DescribeTargetHealthResponse; |
| 158 | +import software.amazon.awssdk.services.elasticloadbalancingv2.model.IpAddressType; |
|
| 158 | 159 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.Listener; |
| 159 | 160 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancer; |
| 160 | 161 | import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancerAttribute; |
| ... | ... | @@ -348,6 +349,7 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey> |
| 348 | 349 | final CreateLoadBalancerResponse response = client |
| 349 | 350 | .createLoadBalancer(CreateLoadBalancerRequest.builder() |
| 350 | 351 | .name(name) |
| 352 | + .ipAddressType(IpAddressType.DUALSTACK) // IPv4 and IPv6 |
|
| 351 | 353 | .subnetMappings(subnetMappings) |
| 352 | 354 | .securityGroups(getDefaultSecurityGroupForApplicationLoadBalancer(region).getId()) |
| 353 | 355 | .build()); |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java
| ... | ... | @@ -58,9 +58,6 @@ implements UserDataProvider { |
| 58 | 58 | * <li>The {@link #setMailSmtpPort(int) SMTP port} defaults to 25.</li> |
| 59 | 59 | * <li>The {@link #setMailSmtpHost(String) mail SMTP host} defaults to <tt>email-smtp.${region}.amazonaws.com</tt>.</li> |
| 60 | 60 | * <li>The {@link #setMailSmtpAuth(boolean) mail SMTP authentication} is activated by default.</li> |
| 61 | - * <li>The {@link #setMailSmtpUser(String) mail SMTP user} defaults to {@code AKIAIHCQEFAZDLIK7SUQ} which is the |
|
| 62 | - * project's AWS SES (Simple e-Mail Service) username. The {@link #setMailSmtpPassword(String) password}, however, |
|
| 63 | - * must explicitly be provided and does not default to any non-{@code null}, non-empty value.</li> |
|
| 64 | 61 | * <li>The {@link #setMemoryInMegabytes(int) memory size} allocated to the process defaults to what {@link #setMemoryTotalSizeFactor(int)} |
| 65 | 62 | * sets, or, if {@link #setMemoryTotalSizeFactor(int)} isn't used either, to 3/4 of the physical memory available |
| 66 | 63 | * on the host running the application, minus some baseline allocated to the operating system.</li> |
| ... | ... | @@ -148,7 +145,6 @@ implements UserDataProvider { |
| 148 | 145 | * The pattern requires the region ID as {@link String} parameter and produces an AWS SES SMTP hostname for that region. |
| 149 | 146 | */ |
| 150 | 147 | private static final String DEFAULT_SMTP_HOSTNAME_PATTERN = "email-smtp.%s.amazonaws.com"; |
| 151 | - private static final String DEFAULT_SMTP_USER = "AKIAIHCQEFAZDLIK7SUQ"; |
|
| 152 | 148 | private static final Integer DEFAULT_SMTP_PORT = 25; |
| 153 | 149 | private AwsLandscape<ShardingKey> landscape; |
| 154 | 150 | private AwsRegion region; |
| ... | ... | @@ -388,7 +384,9 @@ implements UserDataProvider { |
| 388 | 384 | userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_HOST, mailSmtpHost==null?getDefaultAwsSesMailHostForRegion():mailSmtpHost); |
| 389 | 385 | userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_PORT, mailSmtpPort==null?DEFAULT_SMTP_PORT.toString():mailSmtpPort.toString()); |
| 390 | 386 | userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_AUTH, mailSmtpAuth==null?Boolean.TRUE.toString():mailSmtpAuth.toString()); |
| 391 | - userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_USER, mailSmtpUser==null?DEFAULT_SMTP_USER:mailSmtpUser); |
|
| 387 | + if (mailSmtpUser != null) { |
|
| 388 | + userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_USER, mailSmtpUser); |
|
| 389 | + } |
|
| 392 | 390 | if (mailSmtpPassword != null) { |
| 393 | 391 | userData.put(DefaultProcessConfigurationVariables.MAIL_SMTP_PASSWORD, mailSmtpPassword); |
| 394 | 392 | } |
java/com.tractrac.clientmodule/META-INF/MANIFEST.MF
| ... | ... | @@ -2,7 +2,7 @@ Manifest-Version: 1.0 |
| 2 | 2 | Bundle-ManifestVersion: 2 |
| 3 | 3 | Bundle-Name: TracTrac Client module |
| 4 | 4 | Bundle-SymbolicName: com.tractrac.clientmodule |
| 5 | -Bundle-Version: 4.0.3 |
|
| 5 | +Bundle-Version: 4.0.4 |
|
| 6 | 6 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 7 | 7 | Bundle-ClassPath: ., |
| 8 | 8 | lib/TracAPI.jar |
java/com.tractrac.clientmodule/README.txt
| ... | ... | @@ -16,6 +16,18 @@ It contains also some files: |
| 16 | 16 | - Manifest.txt -> manifest used to create the test.jar file |
| 17 | 17 | |
| 18 | 18 | ******************************************** |
| 19 | + TracAPI 4.0.4 |
|
| 20 | +******************************************** |
|
| 21 | +This is the final version. It keeps the backward compatibility. This version has been compiled with |
|
| 22 | +Java 21 but the target compatibility continues being Java 8. |
|
| 23 | + |
|
| 24 | + Release date: 01/10/2025 |
|
| 25 | + |
|
| 26 | + 1) Bugs |
|
| 27 | + |
|
| 28 | + - The heartbeat monitoring the connection status can stop working (Reported by Jorge Piera, 01/10/2025) |
|
| 29 | + |
|
| 30 | +******************************************** |
|
| 19 | 31 | TracAPI 4.0.3 |
| 20 | 32 | ******************************************** |
| 21 | 33 | This is the final version. It keeps the backward compatibility. The new parameter files will be encoded in UTF-8, |
java/com.tractrac.clientmodule/lib/TracAPI-src.jar
java/com.tractrac.clientmodule/lib/TracAPI.jar
java/com.tractrac.clientmodule/pom.xml
| ... | ... | @@ -8,6 +8,6 @@ |
| 8 | 8 | <version>1.0.0-SNAPSHOT</version> |
| 9 | 9 | </parent> |
| 10 | 10 | <artifactId>com.tractrac.clientmodule</artifactId> |
| 11 | - <version>4.0.3</version> |
|
| 11 | + <version>4.0.4</version> |
|
| 12 | 12 | <packaging>eclipse-plugin</packaging> |
| 13 | 13 | </project> |
java/com.tractrac.clientmodule/src/com/tractrac/subscription/app/tracapi/Main.java
| ... | ... | @@ -62,8 +62,8 @@ public class Main { |
| 62 | 62 | race |
| 63 | 63 | ); |
| 64 | 64 | raceSubscriber.subscribeConnectionStatus(listener); |
| 65 | - raceSubscriber.subscribePositionedItemPositions(listener); |
|
| 66 | - //raceSubscriber.subscribePositions(listener); |
|
| 65 | + //raceSubscriber.subscribePositionedItemPositions(listener); |
|
| 66 | + raceSubscriber.subscribePositions(listener); |
|
| 67 | 67 | //raceSubscriber.subscribePositionsSnapped(listener); |
| 68 | 68 | raceSubscriber.subscribeControlPassings(listener); |
| 69 | 69 | //raceSubscriber.subscribeCompetitorSensorData(listener); |
wiki/howto/eventmanagers/linking-race-videos.md
| ... | ... | @@ -59,4 +59,4 @@ This articel provides a short guidance of how to synchronize races with their Yo |
| 59 | 59 | 8. start again from step 3. of chaper 1 |
| 60 | 60 | |
| 61 | 61 |  |
| 62 | -**Figure 4: Link all races covered by the video** |
|
| ... | ... | \ No newline at end of file |
| 0 | +**Figure 4: Link all races covered by the video** |
wiki/howto/onboarding.md
| ... | ... | @@ -10,7 +10,7 @@ First of all, make sure you've looked at [http://www.amazon.de/Patterns-Elements |
| 10 | 10 | |
| 11 | 11 | 1. Git Account |
| 12 | 12 | |
| 13 | - - The primary Git repository for the project is hosted on Github (see [https://github.com/SAP/sailing-analytics](https://github.com/SAP/sailing-analytics)). To clone, use ``git@github.com:SAP/sailing-analytics.git``. To gain write access you have to become member of the [sailing-analytics-team](https://github.com/orgs/SAP/teams/sailing-analytics-team) organization. For that you need to [link your Github user to the Github SAP organization](https://wiki.one.int.sap/wiki/display/ospodocs/Self-Service+for+Joining+an+SAP+GitHub+Organization). We still have a shadow repository around that, e.g., powers our Wiki at [https://wiki.sapsailing.com](https://wiki.sapsailing.com) and which lives at ``ssh://trac@sapsailing.com/home/trac/git``. |
|
| 13 | + - The primary Git repository for the project is hosted on Github (see [https://github.com/SAP/sailing-analytics](https://github.com/SAP/sailing-analytics)). To clone, use ``git@github.com:SAP/sailing-analytics.git``. To gain write access you have to become member of the [sailing-analytics-team](https://github.com/orgs/SAP/teams/sailing-analytics-team) organization. For that you need to [link your Github user to the Github SAP organization](https://wiki.one.int.sap/wiki/display/ospodocs/Self-Service+for+Joining+an+SAP+GitHub+Organization). For that to work, your Github account needs to have your @sap.com e-mail address assigned and verified. We still have a shadow repository around that, e.g., powers our Wiki at [https://wiki.sapsailing.com](https://wiki.sapsailing.com) and which lives at ``ssh://trac@sapsailing.com/home/trac/git``. |
|
| 14 | 14 | |
| 15 | 15 | - In case you'd like to get access to the external git at `ssh://trac@sapsailing.com/home/trac/git` please send your SSH public key to one of the project maintainers, requesting git access. Make sure to NOT generate the key using Putty. Putty keys don't work reliably under Linux and on Windows/Cygwin environments. Use ssh-keygen in a Cygwin or Linux or MacOS/X environment instead. For further instructions for generating an ssh-key see [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent). |
| 16 | 16 | Note: If you want to use the ssh-key in the context of our solution, it can be an RSA or ED25519 format. Example for creating a key: `ssh-keygen -t ed25519 -b 512 -C "test@test.com"`. Make sure to set a non-empty password for your key. |
wiki/howto/tutorials/home.md
| ... | ... | @@ -91,52 +91,142 @@ |
| 91 | 91 | |
| 92 | 92 | <h3>All Tutorials:</h3> |
| 93 | 93 | |
| 94 | - <table> |
|
| 95 | - <tr> |
|
| 96 | - <td><a href="sailinganalytics/tracking-race-player.md">Tracking Race Player Overview</a></td> |
|
| 97 | - <td><a href="sailinganalytics/sign-up.md">Signing up for a user account</a></td> |
|
| 98 | - <td><a href="sailinganalytics/into-to-backend.md">Introduction to Analytics Backend</a></td> |
|
| 99 | - </tr> |
|
| 100 | - <tr> |
|
| 101 | - <td><a href="sailinganalytics/simple-event-creation.md">Create a simple event</a></td> |
|
| 102 | - <td><a href="sailinganalytics/video-tracking-management.md">Add & align Youtube videos</a></td> |
|
| 103 | - <td><a href="sailinganalytics/create-regattas.md">Create complex regattas</a></td> |
|
| 104 | - </tr> |
|
| 105 | - <tr> |
|
| 106 | - <td><a href="sailinganalytics/competitors-data-management.md">Edit scores/results</a></td> |
|
| 107 | - <td><a href="sailinganalytics/importing-results.md">Importing results</a></td> |
|
| 108 | - <td><a href="sailinganalytics/adjust-mark-position.md">Adjust mark position</a></td> |
|
| 109 | - </tr> |
|
| 110 | - <tr> |
|
| 111 | - <td><a href="sailinganalytics/set-mark-position.md">Set mark position</a></td> |
|
| 112 | - <td><a href="sailinganalytics/configure-wind-settings.md">Configure wind settings</a></td> |
|
| 113 | - <td><a href="sailinganalytics/set-up-regattas.md">Set up multiple regattas</a></td> |
|
| 114 | - </tr> |
|
| 115 | - <tr> |
|
| 116 | - <td><a href="sailinganalytics/leaderboard-groups-explanation.md">Leaderboard groups explained</a></td> |
|
| 117 | - <td><a href="sailinganalytics/split-leaderboard.md">Split a leaderboard</a></td> |
|
| 118 | - <td><a href="sailinganalytics/course-areas.md">Working with course areas</a></td> |
|
| 119 | - </tr> |
|
| 120 | - <tr> |
|
| 121 | - <td><a href="sailinganalytics/sailing-race-manager.md">Work with Race Manager app</a></td> |
|
| 122 | - <td><a href="sailinganalytics/race-manager-connection.md">Connect Race Manager to event</a></td> |
|
| 123 | - <td><a href="sailinganalytics/edit-results.md">Editing results</a></td> |
|
| 124 | - </tr> |
|
| 125 | - <tr> |
|
| 126 | - <td><a href="sailinganalytics/set-matchrace-event.md">Set up matchrace event</a></td> |
|
| 127 | - <td><a href="sailinganalytics/security-system.md">Security system intro</a></td> |
|
| 128 | - <td><a href="sailinganalytics/default-creation-group.md">Default creation group</a></td> |
|
| 129 | - </tr> |
|
| 130 | - <tr> |
|
| 131 | - <td><a href="sailinganalytics/make-group-public.md">Make group public</a></td> |
|
| 132 | - <td><a href="sailinganalytics/publish-event.md">Publish an event</a></td> |
|
| 133 | - <td><a href="sailinganalytics/import-gpx-track.md">Import a GPX track</a></td> |
|
| 134 | - </tr> |
|
| 135 | - <tr> |
|
| 136 | - <td><a href="sailinganalytics/race-fixes.md">Fix device assignment</a></td> |
|
| 137 | - <td><a href="sailinganalytics/data-mining-tool.md">Data Mining Tool intro</a></td> |
|
| 138 | - <td></td> |
|
| 139 | - </tr> |
|
| 94 | + <style> |
|
| 95 | + table.guide-index { width: 100%; border-collapse: collapse; } |
|
| 96 | + .guide-index col:first-child { width: 35%; } |
|
| 97 | + .guide-index col:last-child { width: 65%; } |
|
| 98 | + .guide-index td { padding: 8px 12px; vertical-align: top; } |
|
| 99 | + .guide-index a { font-weight: 600; text-decoration: underline; } |
|
| 100 | + /* optional zebra */ |
|
| 101 | + .guide-index tr:nth-child(odd) { background: #f7f7f7; } |
|
| 102 | + |
|
| 103 | + /* optional: stack on small screens */ |
|
| 104 | + @media (max-width: 640px) { |
|
| 105 | + .guide-index col { width: auto !important; } |
|
| 106 | + .guide-index td { display: block; padding: 6px 12px; } |
|
| 107 | + .guide-index td:first-child { padding-bottom: 0; } |
|
| 108 | + } |
|
| 109 | +</style> |
|
| 110 | + |
|
| 111 | +<table class="guide-index"> |
|
| 112 | + <colgroup> |
|
| 113 | + <col /> |
|
| 114 | + <col /> |
|
| 115 | + </colgroup> |
|
| 116 | + <tbody> |
|
| 117 | + <tr> |
|
| 118 | + <td><a href="sailinganalytics/tracking-race-player.md">Tracking Race Player Overview</a></td> |
|
| 119 | + <td>The video below gives an excellent overview of the main functionality of the SAP Race Player which is accessible on a per-race basis via the “Races/Tracking” tab of a regatta.</td> |
|
| 120 | + </tr> |
|
| 121 | + <tr> |
|
| 122 | + <td><a href="sailinganalytics/sign-up.md">Signing up for a user account</a></td> |
|
| 123 | + <td>This tutorial will show you how to sign up for a user account at the <strong>SAP Sailing Analytics</strong>.</td> |
|
| 124 | + </tr> |
|
| 125 | + <tr> |
|
| 126 | + <td><a href="sailinganalytics/into-to-backend.md">Introduction to Analytics Backend</a></td> |
|
| 127 | + <td>The guide <strong>General Introduction to SAP Sailing Analytics Backend</strong> will give you a look around the SAP Sailing Analytics <em>Admin Console</em>.</td> |
|
| 128 | + </tr> |
|
| 129 | + <tr> |
|
| 130 | + <td><a href="sailinganalytics/simple-event-creation.md">Create a simple event</a></td> |
|
| 131 | + <td>This tutorial shows you how to create an event with the SAP Analytics <em>Administration Console</em> on <a href="https://my.sapsailing.com/gwt/Home.html">my.sapsailing.com</a>.</td> |
|
| 132 | + </tr> |
|
| 133 | + <tr> |
|
| 134 | + <td><a href="sailinganalytics/video-tracking-management.md">Add & align Youtube videos</a></td> |
|
| 135 | + <td>This tutorial shows how to add and align <em>Youtube videos</em> to the <strong>tracking timeline</strong>.</td> |
|
| 136 | + </tr> |
|
| 137 | + <tr> |
|
| 138 | + <td><a href="sailinganalytics/create-regattas.md">Create complex regattas</a></td> |
|
| 139 | + <td>This tutorial shows how to create <strong>more complex regattas</strong>.</td> |
|
| 140 | + </tr> |
|
| 141 | + <tr> |
|
| 142 | + <td><a href="sailinganalytics/competitors-data-management.md">Edit scores/results</a></td> |
|
| 143 | + <td>This tutorial shows how to <em>edit</em> scores/results in leaderboards and <em>change</em> competitor data in <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 144 | + </tr> |
|
| 145 | + <tr> |
|
| 146 | + <td><a href="sailinganalytics/importing-results.md">Importing results</a></td> |
|
| 147 | + <td>The video explains how you can <em>import</em> results/scores from other regatta management systems for which <strong>the SAP Sailing Analytics</strong> have an integration in place, such as <strong>YachtScoring</strong> and <strong>Manage2Sail</strong>.</td> |
|
| 148 | + </tr> |
|
| 149 | + <tr> |
|
| 150 | + <td><a href="sailinganalytics/adjust-mark-position.md">Adjust mark position</a></td> |
|
| 151 | + <td>This episode shows you how to adjust <em>the position of a mark</em> with <strong>SAP Sailing Analytics</strong>.</td> |
|
| 152 | + </tr> |
|
| 153 | + <tr> |
|
| 154 | + <td><a href="sailinganalytics/set-mark-position.md">Set mark position</a></td> |
|
| 155 | + <td>This episode shows you how to set <em>the position of a mark</em> with <strong>SAP Sailing Analytics</strong> in case the tracker failed or you were not able to attach a <em>tracker</em> to the mark.</td> |
|
| 156 | + </tr> |
|
| 157 | + <tr> |
|
| 158 | + <td><a href="sailinganalytics/configure-wind-settings.md">Configure wind settings</a></td> |
|
| 159 | + <td>This tutorial shows how to configure <strong>wind settings.</strong>.</td> |
|
| 160 | + </tr> |
|
| 161 | + <tr> |
|
| 162 | + <td><a href="sailinganalytics/set-up-regattas.md">Set up multiple regattas</a></td> |
|
| 163 | + <td>This tutorial shows how to set up <em>multiple regattas</em> or <em>classes events</em> with the <strong>SAP Sailing Analytics</strong>.</td> |
|
| 164 | + </tr> |
|
| 165 | + <tr> |
|
| 166 | + <td><a href="sailinganalytics/leaderboard-groups-explanation.md">Leaderboard groups explained</a></td> |
|
| 167 | + <td>This tutorial explains how <em>Leaderboard Groups</em> work in <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 168 | + </tr> |
|
| 169 | + <tr> |
|
| 170 | + <td><a href="sailinganalytics/split-leaderboard.md">Split a leaderboard</a></td> |
|
| 171 | + <td>This tutorial shows how to <em>split</em> a leaderboard into divisions with the <strong>SAP Sailing Analytics</strong>.</td> |
|
| 172 | + </tr> |
|
| 173 | + <tr> |
|
| 174 | + <td><a href="sailinganalytics/course-areas.md">Working with course areas</a></td> |
|
| 175 | + <td>This tutorial shows how to work with <em>course areas</em> in <strong>SAP Sailing Analytics</strong>.</td> |
|
| 176 | + </tr> |
|
| 177 | + <tr> |
|
| 178 | + <td><a href="sailinganalytics/sailing-race-manager.md">Work with Race Manager app</a></td> |
|
| 179 | + <td>This tutorial explains how to work with <strong>the SAP Sailing Race Manager Application</strong>.</td> |
|
| 180 | + </tr> |
|
| 181 | + <tr> |
|
| 182 | + <td><a href="sailinganalytics/race-manager-connection.md">Connect Race Manager to event</a></td> |
|
| 183 | + <td>This tutorial shows how to connect <strong>the Race Manager Application</strong> to an <em>event</em>.</td> |
|
| 184 | + </tr> |
|
| 185 | + <tr> |
|
| 186 | + <td><a href="sailinganalytics/edit-results.md">Editing results</a></td> |
|
| 187 | + <td>This tutorial explains how to <em>edit</em> results with <strong>the SAP Sailing Race Manager Application</strong>.</td> |
|
| 188 | + </tr> |
|
| 189 | + <tr> |
|
| 190 | + <td><a href="sailinganalytics/set-matchrace-event.md">Set up matchrace event</a></td> |
|
| 191 | + <td>This episode shows you how to set up a <em>matchrace event</em> with <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 192 | + </tr> |
|
| 193 | + <tr> |
|
| 194 | + <td><a href="sailinganalytics/security-system.md">Security system intro</a></td> |
|
| 195 | + <td>This episode serves as an introduction to the <em>security system</em> of the <strong>SAP Sailing Analytics</strong>.</td> |
|
| 196 | + </tr> |
|
| 197 | + <tr> |
|
| 198 | + <td><a href="sailinganalytics/default-creation-group.md">Default creation group</a></td> |
|
| 199 | + <td>This episode shows how to set a default <em>creation group</em> in user details with <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 200 | + </tr> |
|
| 201 | + <tr> |
|
| 202 | + <td><a href="sailinganalytics/make-group-public.md">Make group public</a></td> |
|
| 203 | + <td>This episode shows how to make everything your group owns <em>readable</em> for everyone in <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 204 | + </tr> |
|
| 205 | + <tr> |
|
| 206 | + <td><a href="sailinganalytics/publish-event.md">Publish an event</a></td> |
|
| 207 | + <td>This episode shows how to <em>publish an event</em> through a dedicated user group in <strong>the SAP Sailing Analytics</strong>.</td> |
|
| 208 | + </tr> |
|
| 209 | + <tr> |
|
| 210 | + <td><a href="sailinganalytics/import-gpx-track.md">Import a GPX track</a></td> |
|
| 211 | + <td>This episode shows how to import a track from a <em>GPX file</em>.</td> |
|
| 212 | + </tr> |
|
| 213 | + <tr> |
|
| 214 | + <td><a href="sailinganalytics/race-fixes.md">Fix device assignment</a></td> |
|
| 215 | + <td>This episode shows how to <em>fix</em> races when a device is assigned to multiple competitors <strong>accidentally</strong>.</td> |
|
| 216 | + </tr> |
|
| 217 | + <tr> |
|
| 218 | + <td><a href="sailinganalytics/data-mining-tool.md">Data Mining Tool intro</a></td> |
|
| 219 | + <td>This tutorial gives an introduction to <strong>the SAP Sailing Analytics Data Mining Tool</strong>.</td> |
|
| 220 | + </tr> |
|
| 221 | + <tr> |
|
| 222 | + <td><a href="sailinganalytics/copy-course-to-race.md">Copy course to race</a></td> |
|
| 223 | + <td>This tutorial explains how to copy course to other race.</td> |
|
| 224 | + </tr> |
|
| 225 | + <tr> |
|
| 226 | + <td><a href="sailinganalytics/dset-up-course.md">Course set up</a></td> |
|
| 227 | + <td>This tutorial explains how to set up course in <strong>AdminConsole</strong>.</td> |
|
| 228 | + </tr> |
|
| 229 | + </tbody> |
|
| 140 | 230 | </table> |
| 141 | 231 | |
| 142 | 232 | <hr> |
wiki/howto/tutorials/sailinganalytics/adjust-mark-position.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows you how to adjust *the postion of a mark* with **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488483711" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010892770-68eafb9d9777a29688f3fa3dd35f2c82574a8072d69f1ae144ef4a56d7835562-d?f=webp®ion=us" alt="Adjust the position of a mark with the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488483711" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/AdjustingMarkPositionByCreatingNewFixCut.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/competitors-data-management.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | This tutorial shows how to *edit* scores/results in leaderboards and *change* competitor data in **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488484658" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010898993-1939bb3284504da7d6ce0c7ca44c68bac843370b6b01723d78ee672bf2568701-d?f=webp®ion=us" alt="How to edit scores/results in leaderboards and change competitor data in the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488484658" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | - <br><br><br> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/editingcompetitors.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/configure-wind-settings.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | This tutorial shows how to configure **wind settings** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488860317" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1011532573-a72305c16fcb48b67ff30dd60ffb19612a7dc407c852424da37cd49cdc238351-d?f=webp®ion=us" alt="Configuring wind settings" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em;"> |
|
| 8 | - <a href="https://vimeo.com/488860317" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| 3 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/ConfiguringWindSettings.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
wiki/howto/tutorials/sailinganalytics/copy-course-to-race.md
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +This tutorial explains how to copy course to other race with **the SAP Sailing Race Manager Application** : |
|
| 2 | + |
|
| 3 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/CopyingCourseToOtherRaces.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/course-areas.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to work with *course areas* in **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488486330" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010908534-67799518b2812965dddb3595fdf0452eae2e61346ae69213748313856a6f3b96-d?f=webp&region=us" alt="Working with course areas SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488486330" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/WorkingWithCourseAreas_Part1.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/create-regattas.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to create **more complex regattas** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/634988326" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1277334038-e9c29f5bf85d54076b7aacb78786efdc39b079d048a97f71a?f=webp®ion=us" alt="Creating more complex regattas (with multiple series)" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/634988326" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 3 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/CreateMoreComplexRegattas.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/data-mining-tool.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial gives an introduction to **the SAP Sailing Analytics Data Mining Tool** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/517202211" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1070935518-8b0ba12a0b853e140c36fd9dd89b1fe4929bdab5d4e464017628f508588e650d-d?f=webp®ion=us" alt="An Introduction to the SAP Sailing Analytics Data Mining Tool" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/517202211" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/DataMiningIntroduction.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/default-creation-group.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows how to set a default *creation group* in user details with **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488485747" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010869111-2290caea7e6f6cac1a64b58af3423fc31700f24c2839a92c5e3dc7dd85c1e669-d?f=webp®ion=us" alt="Setting a default creation group in user details with the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488485747" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/SettingDefaultCreationGroupInUserDetails.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/edit-results.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial explains how to *edit* results with **the SAP Sailing Race Manager Application** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488484797" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010874720-12e724c4940d378318204f96d75247de239f0728a9bd5beb5415aad70df1d2ca-d?f=webp®ion=us" alt="Editing results with the SAP Sailing Race Manager app" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488484797" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/editingresultswiththeracemanagerapp.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/import-gpx-track.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows how to import a track from a *GPX file* : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/517202462" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1070949814-e607721644fc5d9ed274d3fb23342634b75aefe69ef34b387095b86c8b4008ad-d?f=webp®ion=us" alt="Import a track from a GPX file" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/517202462" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/ImportGPXTrack.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/importing-results.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | The video explains how you can *import* results/scores from other regatta management systems for which **the SAP Sailing Analytics** have an integration in place, such as **YachtScoring** and **Manage2Sail**: |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/539278602" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1116473176-be00159b5f0aae985461bb734efb6adc52f27e5536d0b8f8960dde6192b6b66d-d?f=webp&region=us" alt="Importing Results" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/539278602" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/VideoEditLeaderBoard.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/into-to-backend.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | The guide **General Introduction to SAP Sailing Analytics Backend** will give you a look around the SAP Sailing Analytics *Admin Console* : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://vimeo.com/488483856" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010905634-646b489c3740b5d1c6d1e83fe88fbe50cd43be60a2073d209520983a4c4ce9c2-d?f=webp®ion=us" alt="General Introduction to SAP Sailing Analytics Backend" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488483856" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/ALookAroundAdminConsole.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/leaderboard-groups-explanation.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial explains how *Leaderboard Groups* work in **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/489321478" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1015856109-bb64efa3a2a4f304576789690646aaad0a7646c37c85c013ab43e016899e1aa1-d?f=webp®ion=us" alt="Leaderboard groups explained in the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/489321478" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/Advanced+Topics/Leaderboard+Group+explained.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/make-group-public.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows how to make everything your group owns *readable* for everyone in **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488485239" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010886232-bf31fae06774e2abed01713779a86d50704dbf34ce2341c9bfe3751468c64d20-d?f=webp&region=us" alt="Make everything your group owns readable for everyone in the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488485239" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/MakingEverythingYourGroupOwnsReadableForEveryone.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/publish-event.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows how to *publish an event* through a dedicated user group in **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488485433" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010883117-c1919ac1563d2d40fc5a6ff49f4ff66918ef1de005d0820a928d5478f0b98a64-d?f=webp®ion=us" alt="Publish an event through a dedicated user group in the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488485433" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/PublishingEventThroughDedicatedUserGroup.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/race-fixes.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows how to *fix* races when a device is assigned to multiple competitors **accidentally** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/560018855" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1157667852-8e3dae0f1479eee9c82e71f93a5b0a2b09b6065a406b11027d3fbff828fd03f5-d?f=webp®ion=us" alt="Fixing races when a device is assigned to multiple competitors accidentally" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/560018855" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/FixRaces.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/race-manager-connection.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to connect **the Race Manager Application** to an *event* : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/496362004" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1027112686-11119a6afa84a2196b43d0952cd504e43ab27ed67b256f138dac4481d9568cbf-d?f=webp®ion=us" alt="How to connect the Race Manager App to an event" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/496362004" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/ConnectToEvent.mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/sailing-race-manager.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial explains how to work with **the SAP Sailing Race Manager Application** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488484868" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010878230-c2c61789493de4080b910dbb08521af8ff8f7036aa66c9b1eabe51690a68e5a0-d?f=webp®ion=us" alt="How to work with the SAP Sailing Race Manager app" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488484868" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/howtogetgoingwiththeracemanagerapp.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/security-system.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode serves as an introduction to the *security system* of the **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488484953" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010855456-9066a0a6e5a3c8d8fb4a5804e04ece6688246ac7d34932ca3e68d4e51a70fffd-d?f=webp®ion=us" alt="Introduction to the security system of the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488484953" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/IntroductionSecuritySystem.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/set-mark-position.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows you how to set *the postion of a mark* with **SAP Sailing Analytics** in case the tracker failed or you were not able to attach a *tracker* to the mark : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/488486072" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010865518-60ba1a02a2fadb08c1f15bc00e8ad6fc5549c45abc521ca268ba291157e95480-d?f=webp®ion=us" alt="Set the position of a mark with the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488486072" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/SettingFirstPositionForAMarkOnTheMapCut.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/set-matchrace-event.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This episode shows you how to set up a *matchrace event* with **the SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/489321685" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1015853549-810174185dc750b52c9b061ff92f1579424fa5f89eeab0d12ef53ac6b4bc2cc5-d?f=webp®ion=us" alt="Setting up a matchrace event with the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/489321685" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/Advanced+Topics/MatchraceSetup.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/set-up-course.md
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +This tutorial explains how to set up course in *AdminConsole*: |
|
| 2 | + |
|
| 3 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/SettingUpCourseInAdminConsole.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/set-up-regattas.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to set up *multiple regattas* or *classes events* with the **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/489321331" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1015852481-18078496a5c92d5a7c894e3fa44678686e719e304baa3b0af31c976d1419e6d2-d?f=webp&region=us" alt="Setting up multiple regattas or classes events with the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/489321331" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/Advanced+Topics/Setting+up+Events+with+multiple+Regattas+or+Classes.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/sign-up.md
| ... | ... | @@ -1,20 +1,6 @@ |
| 1 | 1 | This tutorial will show you how to sign up for a user account at the **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | - |
|
| 4 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 5 | - <a href="https://vimeo.com/488486248" target="_blank"> |
|
| 6 | - <img src="https://i.vimeocdn.com/video/1010859699-4efcb1ff2d25befb03951c2ed835ca7e6dc07906902a6b80d832d1e8dcc6b74b-d?f=webp®ion=us" alt="Signing up for a user account at the SAP Sailing Analytics" style="display: inline-block;"> |
|
| 7 | - </a> |
|
| 8 | - <div style="line-height: normal; margin-top: -18em;margin-bottom: 16em "> |
|
| 9 | - <a href="https://vimeo.com/488486248" target="_blank" style=" |
|
| 10 | - display: inline-block; |
|
| 11 | - vertical-align: middle; |
|
| 12 | - background-color: #007BFF; |
|
| 13 | - color: white; |
|
| 14 | - padding: 10px 20px; |
|
| 15 | - border-radius: 4px; |
|
| 16 | - text-decoration: none; |
|
| 17 | - font-weight: bold; |
|
| 18 | - ">Watch the Video</a> |
|
| 19 | - </div> |
|
| 3 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/SigningUpForAUserAccount.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
|
| 20 | 6 |
wiki/howto/tutorials/sailinganalytics/simple-event-creation.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows you how to create an event with the SAP Analytics *Administration Console* on [my.sapsailing.com](https://my.sapsailing.com/gwt/Home.html) : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://vimeo.com/488484401" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010901946-6ef9df5238d5f517e69b301fc9eddb2069c59f1a41218f17539bfc5372fa685b-d?f=webp®ion=us" alt="create an event with the SAP Analytics Administration Console" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/488484401" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/CreatingYourFirstEvent.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/split-leaderboard.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to *split* a Leaderboard into divisions with the **SAP Sailing Analytics** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/555773483" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1148490178-0abd3500b3e9f273c259bf4e690dd3ff4fd59d24fc615c5f03db1659ee232a6a-d?f=webp®ion=us" alt="Splitting a Leaderboard into divisions" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/555773483" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/SplitLeaderBoard.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/tracking-race-player.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | The video below gives an excellent overview of the main functionality of the SAP Race Player which is accessible on a per-race basis via the "Races/Tracking" Tab of a regatta. |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 18em;"> |
|
| 4 | - <a href="https://player.vimeo.com/video/786236240?h=5837f0d8b3" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1580518450-d061bcba753035fe130227e4b1c3ac735440041fabb61c3a14e289b9cd102dd5-d?mw=640&q=85" alt="tracking race player" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -12em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://player.vimeo.com/video/786236240?h=5837f0d8b3" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| 3 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/RaceBoardDemo.mp4" type="video/mp4"> |
|
| 4 | + Your browser does not support the video tag. |
|
| 5 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinganalytics/video-tracking-management.md
| ... | ... | @@ -1,18 +1,5 @@ |
| 1 | 1 | This tutorial shows how to add and align *Youtube videos* to the **tracking timeline** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;"> |
|
| 4 | - <a href="https://vimeo.com/634989080" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1277334324-ee19c413fefcee3578a6b2e395e45cd678e31de2d3299670b?f=webp®ion=us" alt="Adding and aligning Youtube videos to the tracking timeline" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em"> |
|
| 8 | - <a href="https://vimeo.com/634989080" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/AddingYouTubeVideos.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailingracemanager/configure-wind-settings.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | -This episode shows you how to configure *wind settings* with **the SAP Sailing Race Manager App** : |
|
| 1 | +This tutorial shows how to configure **wind settings** : |
|
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="thttps://vimeo.com/488860317" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1011532573-a72305c16fcb48b67ff30dd60ffb19612a7dc407c852424da37cd49cdc238351-d?f=webp®ion=us" alt="How to configure wind settings" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em; margin-bottom: 16em;"> |
|
| 8 | - <a href="hhttps://vimeo.com/488860317" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/ConfiguringWindSettings.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailingracemanager/connect-to-event.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | This episode shows you how to connect **the Race Manager App** to an *Event* : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://vimeo.com/496362004" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1027112686-11119a6afa84a2196b43d0952cd504e43ab27ed67b256f138dac4481d9568cbf-d?f=webp®ion=us" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em;margin-bottom: 16em;"> |
|
| 8 | - <a href="https://vimeo.com/496362004" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/Advanced+Topics/Leaderboard+Group+explained.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailingracemanager/edit-results.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | This episode shows you how to edit results with **the SAP Sailing Race Manager App** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://vimeo.com/488484797" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010874720-12e724c4940d378318204f96d75247de239f0728a9bd5beb5415aad70df1d2ca-d?f=webp®ion=us" alt="Editing results with the SAP Sailing Race Manager App" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em;margin-bottom: 16em;"> |
|
| 8 | - <a href="https://vimeo.com/488484797" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| ... | ... | \ No newline at end of file |
| 0 | + <video controls="true" width="640" src="https://sapsailing-documentation.s3-eu-west-1.amazonaws.com/adminconsole/editingresultswiththeracemanagerapp.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailingracemanager/sailing-race-guide.md
| ... | ... | @@ -1,19 +1,5 @@ |
| 1 | 1 | This episode shows you how to work with **the SAP Sailing Race Manager App** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://vimeo.com/488484868" target="_blank"> |
|
| 5 | - <img src="https://i.vimeocdn.com/video/1010878230-c2c61789493de4080b910dbb08521af8ff8f7036aa66c9b1eabe51690a68e5a0-d?f=webp®ion=us" alt="How to work with the SAP Sailing Race Manager App" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -18em;margin-bottom: 16em;"> |
|
| 8 | - <a href="https://vimeo.com/488484868" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| ... | ... | \ No newline at end of file |
| 0 | +<video controls="true" width="640" src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/adminconsole/WorkWithSailingManagerApp.mp4" type="video/mp4"> |
|
| 1 | + Your browser does not support the video tag. |
|
| 2 | +</video> |
|
| ... | ... | \ No newline at end of file |
wiki/howto/tutorials/sailinsight/boat-team-creation.md
| ... | ... | @@ -2,23 +2,13 @@ This guide will show you how to create your *first boat* or manage multiple boat |
| 2 | 2 | |
| 3 | 3 | **Sail Insight** allows you to sail with multiple boats or teams. When you first set up **Sail Insight**, we ask you to set up your first boat, but you can edit it or create more at any time. |
| 4 | 4 | |
| 5 | -<div style="text-align: center; line-height: 0;margin-bottom: 13em;"> |
|
| 6 | - <a href="https://www.youtube.com/watch?v=rbdmoTf7w64$t=1s" target="_blank"> |
|
| 7 | - <img src="https://img.youtube.com/vi/rbdmoTf7w64/0.jpg" alt="Boat and team creation in Sail Insight" style="display: inline-block;"> |
|
| 8 | - </a> |
|
| 9 | - <div style="line-height: normal; margin-top: -14em;"> |
|
| 10 | - <a href="https://www.youtube.com/watch?v=rbdmoTf7w64$t=1s" target="_blank" style=" |
|
| 11 | - display: inline-block; |
|
| 12 | - vertical-align: middle; |
|
| 13 | - background-color: #007BFF; |
|
| 14 | - color: white; |
|
| 15 | - padding: 10px 20px; |
|
| 16 | - border-radius: 4px; |
|
| 17 | - text-decoration: none; |
|
| 18 | - font-weight: bold; |
|
| 19 | - ">Watch the Video</a> |
|
| 20 | - </div> |
|
| 21 | -</div> |
|
| 5 | +<video |
|
| 6 | + controls="true" |
|
| 7 | + src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/SailInsight/Boats+and+Team+Creation+%5BrbdmoTf7w64%5D.webm" |
|
| 8 | + type="video/webm" |
|
| 9 | + style="display:block;margin:1rem auto;max-width:100%;width:340px;height:auto;"> |
|
| 10 | + Your browser does not support the video tag. |
|
| 11 | +</video> |
|
| 22 | 12 | |
| 23 | 13 | ## Edit a boat |
| 24 | 14 | To edit a previsouly created boat, click the *"Account"* tab near the bottom right of the screen. Then click the *"My Boats"* menu item. Pick the boat that you previously created and edit the necesarry fields. Scroll down to press *"Save"*. |
wiki/howto/tutorials/sailinsight/create-events.md
| ... | ... | @@ -1,22 +1,12 @@ |
| 1 | 1 | This guide will show you how to create an event with **Sail Insight**. |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://www.youtube.com/watch?v=f_sguquMRsQ" target="_blank"> |
|
| 5 | - <img src="https://img.youtube.com/vi/f_sguquMRsQ/0.jpg" alt="How-to: Create an event with Sail Insight" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -14em;"> |
|
| 8 | - <a href="https://www.youtube.com/watch?v=f_sguquMRsQ" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| 3 | +<video |
|
| 4 | + controls="true" |
|
| 5 | + src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/SailInsight/Create+an+Event+%5Bf_sguquMRsQ%5D.mp4" |
|
| 6 | + type="video/mp4" |
|
| 7 | + style="display:block;margin:1rem auto;max-width:100%;width:340px;height:auto;"> |
|
| 8 | + Your browser does not support the video tag. |
|
| 9 | +</video> |
|
| 20 | 10 | |
| 21 | 11 | There are two ways of *placing marks*: |
| 22 | 12 | - Pair a mark with a real-world tracking device (another mobile phone) |
wiki/howto/tutorials/sailinsight/getting-started.md
| ... | ... | @@ -5,23 +5,13 @@ You can use **Sail Insight** without an account, if you have been invited to par |
| 5 | 5 | |
| 6 | 6 | If you want to track your own trips or set up an event to sail with or against other people you will need to sign up for an account. |
| 7 | 7 | |
| 8 | -<div style="text-align: center; line-height: 0;margin-bottom: 13em;"> |
|
| 9 | - <a href="https://www.youtube.com/watch?v=ZnRooWkRvas" target="_blank"> |
|
| 10 | - <img src="https://img.youtube.com/vi/ZnRooWkRvas/0.jpg" alt="Getting started with Sail Insight" style="display: inline-block;"> |
|
| 11 | - </a> |
|
| 12 | - <div style="line-height: normal; margin-top: -14em;"> |
|
| 13 | - <a href="https://www.youtube.com/watch?v=ZnRooWkRvas" target="_blank" style=" |
|
| 14 | - display: inline-block; |
|
| 15 | - vertical-align: middle; |
|
| 16 | - background-color: #007BFF; |
|
| 17 | - color: white; |
|
| 18 | - padding: 10px 20px; |
|
| 19 | - border-radius: 4px; |
|
| 20 | - text-decoration: none; |
|
| 21 | - font-weight: bold; |
|
| 22 | - ">Watch the Video</a> |
|
| 23 | - </div> |
|
| 24 | -</div> |
|
| 8 | +<video |
|
| 9 | + controls="true" |
|
| 10 | + src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/SailInsight/Getting+started+with+Sail+Insight.+%5BZnRooWkRvas%5D.webm" |
|
| 11 | + type="video/webm" |
|
| 12 | + style="display:block;margin:1rem auto;max-width:100%;width:340px;height:auto;"> |
|
| 13 | + Your browser does not support the video tag. |
|
| 14 | +</video> |
|
| 25 | 15 | |
| 26 | 16 | ## How-to create an account |
| 27 | 17 | After opening **Sail Insight** create a new account by clicking on *"Register"*. |
wiki/howto/tutorials/sailinsight/manage-marks.md
| ... | ... | @@ -1,22 +1,13 @@ |
| 1 | 1 | This episode shows you how to manage *marks* or pair them in **Sail Insight** : |
| 2 | 2 | |
| 3 | -<div style="text-align: center; line-height: 0;margin-bottom: 14em;"> |
|
| 4 | - <a href="https://www.youtube.com/watch?v=f3fjGuP-SbU" target="_blank"> |
|
| 5 | - <img src="https://img.youtube.com/vi/f3fjGuP-SbU/0.jpg" alt="manage marks or pair them" style="display: inline-block;"> |
|
| 6 | - </a> |
|
| 7 | - <div style="line-height: normal; margin-top: -14em;"> |
|
| 8 | - <a href="https://www.youtube.com/watch?v=f3fjGuP-SbU" target="_blank" style=" |
|
| 9 | - display: inline-block; |
|
| 10 | - vertical-align: middle; |
|
| 11 | - background-color: #007BFF; |
|
| 12 | - color: white; |
|
| 13 | - padding: 10px 20px; |
|
| 14 | - border-radius: 4px; |
|
| 15 | - text-decoration: none; |
|
| 16 | - font-weight: bold; |
|
| 17 | - ">Watch the Video</a> |
|
| 18 | - </div> |
|
| 19 | -</div> |
|
| 3 | +<video |
|
| 4 | + controls="true" |
|
| 5 | + src="https://sapsailing-documentation.s3.eu-west-1.amazonaws.com/SailInsight/Managing+Marks+%5Bf3fjGuP-SbU%5D.webm" |
|
| 6 | + type="video/webm" |
|
| 7 | + style="display:block;margin:1rem auto;max-width:100%;width:340px;height:auto;"> |
|
| 8 | + Your browser does not support the video tag. |
|
| 9 | +</video> |
|
| 10 | + |
|
| 20 | 11 | There are **two ways** of placing marks: |
| 21 | 12 | - Pair a mark with a real-world tracking device (another mobile phone) |
| 22 | 13 | - Give it a fixed location by providing latitude and longitude. |
wiki/info/landscape/amazon-ec2.md
| ... | ... | @@ -153,7 +153,7 @@ Any geo-blocking Web ACL that shall automatically be associated with ALBs that a |
| 153 | 153 | ``` |
| 154 | 154 | Note that in order to run this command you have to have valid credentials for the AWS region you're targeting with the request. Also consider using the ``--region`` argument if you're trying to tag a Web ACL in a region other than your AWS CLI's default region. Check your ``~/.aws/config`` file. Also see ``configuration/environments_scripts/repo/usr/local/bin/awsmfalogon.sh`` for logging on to the AWS CLI. |
| 155 | 155 | |
| 156 | -### MongoDB Replica Setsn |
|
| 156 | +### MongoDB Replica Sets |
|
| 157 | 157 | |
| 158 | 158 | There are currently three MongoDB replica sets: |
| 159 | 159 |
wiki/info/landscape/aws-automation.md
| ... | ... | @@ -328,7 +328,7 @@ The user can assign values to that variable that are then used as default propos |
| 328 | 328 | 7. Configure target group health check |
| 329 | 329 | 8. Register instance within target group |
| 330 | 330 | 9. Create new rule within https listener that points to the correct target group |
| 331 | -10. Append „Use Event-SSL [domain] [eventId] 127.0.0.1 8888“ or „Use Home-SSL [domain] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 331 | +10. Append „Use Event-SSL \[domain\] \[eventId\] 127.0.0.1 8888“ or „Use Home-SSL \[domain\] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 332 | 332 | |
| 333 | 333 | #### SAP instance on a shared EC2 instance |
| 334 | 334 | |
| ... | ... | @@ -349,7 +349,7 @@ The user can assign values to that variable that are then used as default propos |
| 349 | 349 | 15. Create target group with name „S-hared-instanceshortname“ |
| 350 | 350 | 16. Configuration of the target group health check with the server port of the sap instance |
| 351 | 351 | 17. Create new rule within https listener that points to the correct target group |
| 352 | -18. Append „Use Event-SSL [domain] [eventId] 127.0.0.1 8888“ or „Use Home-SSL [domain] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 352 | +18. Append „Use Event-SSL \[domain\] \[eventId\] 127.0.0.1 8888“ or „Use Home-SSL \[domain\] 127.0.0.1 8888“ to etc/httpd/conf.d/001-events.conf |
|
| 353 | 353 | 19. Check apache configuration with "apachectl configtest" and reload with "sudo service httpd reload“ |
| 354 | 354 | |
| 355 | 355 | #### SAP instance on a dedicated EC2 instance as a master |
wiki/info/landscape/operating-system-upgrade.md
| ... | ... | @@ -0,0 +1,131 @@ |
| 1 | +# Operating System Upgrade Across Landscape |
|
| 2 | + |
|
| 3 | +[[_TOC_]] |
|
| 4 | + |
|
| 5 | +Mainly for security reasons we strive to keep the operating systems on which our EC2 instances are running up to date. This includes running the latest Linux kernels and having all packages updated to their latest versions as per the Amazon Linux or other Linux versions used. While doing so, we aim to keep service interruptions to a minimum and in particular keep services available at least in read-only mode also during upgrades. |
|
| 6 | + |
|
| 7 | +We distinguish between in-place upgrades without the need to re-boot, in-place upgrade requiring a reboot (e.g., due to Linux kernel updates), and upgrades that replace EC2 instances by new EC2 instances. The latter case can be sub-divided into cases where an incremental image upgrade can be used to produce a new version of an Amazon Machine Image (AMI) used for that instance type, and cases where a new from-scratch AMI set-up will be required. Also, the procedures to use depend on the type of service run on the instance that requires an upgrade. |
|
| 8 | + |
|
| 9 | +## Approaches for Operating System Updates |
|
| 10 | + |
|
| 11 | +### Using AdminConsole Landscape Management Panel |
|
| 12 | + |
|
| 13 | +The AdminConsole offers the Landscape Management panel (see, e.g., [https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:](https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:)) with a table entitled "Amazon Machine Images (AMIs)." It shows the different AMIs in use, among them the ``sailing-analytics-server``, the ``mongodb-server`` and the ``disposable-reverse-proxy`` images. Each of them have an "Upgrade" action icon in the "Actions" column that can be used to launch an instance off the image and then apply the steps necessary to upgrade the image to the latest version of kernel, all packages, and Java VM (if installed), then creates a new version of the AMI. |
|
| 14 | + |
|
| 15 | +See below for how to proceed with the upgraded images for the different image types. |
|
| 16 | + |
|
| 17 | +### Log On with SSH and Use Package Manager for Live Upgrade |
|
| 18 | + |
|
| 19 | +Instead of or in addition to upgrading the AMIs to new package and kernel versions, you can also log in to a running instance using SSH, and as root (using, e.g., ``sudo``) upgrade packages and kernel in place. Should a reboot be required, however, it depends on the particular instance you have been applying this to. Some instances should not simply be rebooted as this may unnecessarily reduce availability of some services and may not always lead to a clean recovery of all services after the reboot. |
|
| 20 | + |
|
| 21 | +For example, when rebooting an instance that runs one or more primary application processes for which replica processes run on other instances, inconsistencies between primary and replicas may result by a brute-force restart of the primary in some cases. See below for cleaner ways to do this. |
|
| 22 | + |
|
| 23 | +#### Amazon Linux |
|
| 24 | + |
|
| 25 | +We use Amazon Linux as the default for most instance types and hence most AMIs, particularly those for running the Sailing Analytics application, the MongoDB instances, the reverse proxy instances, and MariaDB for our Bugzilla service. |
|
| 26 | + |
|
| 27 | +Amazon Linux 2023 uses ``dnf`` as its package manager. An operating system upgrade is performed by running, as ``root`` (e.g., by logging in as ``ec2-user`` and then using ``sudo``): |
|
| 28 | +``` |
|
| 29 | + dnf --releasever=latest upgrade |
|
| 30 | +``` |
|
| 31 | +This will upgrade all packages installed as well as the kernel. When run interactively, upgrade requiring a reboot will be displayed in the update list in red color. For scripted use, consider the ``needs-restarting -r`` command, delivering an exit status of ``1`` if a reboot is required. |
|
| 32 | + |
|
| 33 | +#### Debian |
|
| 34 | + |
|
| 35 | +Our use of Debian is currently restricted to running RabbitMQ which is a lot harder to install and configure on Amazon Linux. |
|
| 36 | + |
|
| 37 | +Debian uses ``apt`` as its package manager. Its default login user differs from Amazon Linux, where it is ``ec2-user`` and is called ``admin`` instead. Like the ``ec2-user`` on Amazon Linux, ``admin`` is eligible to use ``sudo`` to run commands with root privileges. |
|
| 38 | + |
|
| 39 | +Executing an update with apt works like this: |
|
| 40 | +``` |
|
| 41 | + apt-get update |
|
| 42 | + apt-get upgrade |
|
| 43 | +``` |
|
| 44 | +If this creates a file ``/var/run/reboot-required`` then the instance must be rebooted for all changes to take effect. |
|
| 45 | + |
|
| 46 | +## Upgrading the Different Instance Types |
|
| 47 | + |
|
| 48 | +### ``security-service.sapsailing.com`` Primary |
|
| 49 | + |
|
| 50 | +The corresponding ``security_service`` replica set usually has a single instance running only the primary application service. It offers a few ``Replicable``s that all other replica sets (except ``DEV``) replicate, such as the ``SecurityService`` and ``SharedSailingData`` services. It acts as a hub in particular for user, group, role and permission management. Other instances have their replicated versions of the service and can make decisions locally, sign in/up and authenticate users and manage their sessions locally. Replication through the ``security_service`` replica set serves the purpose of letting users roam about the landscape. Temporary outages of the ``security_service`` replica set will delay replication of these aspects across the landscape. However, transactions will not be lost but will be queued and applied when the service becomes available again. |
|
| 51 | + |
|
| 52 | +With this in mind, a restart of either the Java VM (in order to upgrade the application to a new version) or even a reboot of the EC2 instance, both typically done in less than 60s, will rarely cause effects noticeable to users. Therefore, we typically afford to upgrade the instance running the single primary process for the ``security_service`` replica set "in place:" |
|
| 53 | + |
|
| 54 | +- log on with ssh as ``ec2-user`` |
|
| 55 | +- run ``dnf --releasever=latest upgrade`` |
|
| 56 | +- if a reboot is required, reboot the instance |
|
| 57 | + |
|
| 58 | +It is useful to wait with the reboot until at least no known Sailing Analytics process start-up is happening which is in the middle of obtaining an initial load from the ``security_service`` replica set because this would be aborted and hence fail upon the reboot. Other than that, existing replicas will synchronize with the rebooted instance and the freshly started service once available again. |
|
| 59 | + |
|
| 60 | +Should you find good reasons against an in-place upgrade, make sure you have an upgraded ``sailing-analytics-server`` AMI, remove the running instance from the ``S-security-service`` and ``S-security-service-m`` target groups, launch a new instance off the upgraded AMI with the user data copied from the running instance, with only the ``INSTALL_FROM_RELEASE`` parameter upgrade to the latest release: |
|
| 61 | +``` |
|
| 62 | +INSTALL_FROM_RELEASE=main-202502181141 |
|
| 63 | +SERVER_NAME=security_service |
|
| 64 | +USE_ENVIRONMENT=security-service-master |
|
| 65 | +``` |
|
| 66 | + |
|
| 67 | +Then add the new instance to the ``S-security-service`` and ``S-security-service-m`` target groups and terminate the old instance. |
|
| 68 | + |
|
| 69 | +### ``DEV`` |
|
| 70 | + |
|
| 71 | +The ``DEV`` replica set is for testing only. Other than that, the instance runs our Hudson CI environment. Both are not expected to be highly available. Therefore, the same in-place update as for the ``security_service`` replica set is possible. For a clean Hudson shut-down, consider using [this link](https://hudson.sapsailing.com/quietDown). |
|
| 72 | + |
|
| 73 | +### ``ARCHIVE`` |
|
| 74 | + |
|
| 75 | +Make sure you have an up-to-date ``sailing-analytics-server`` AMI. Then, see [[Upgrading ARCHIVE server|wiki/info/landscape/archive-server-upgrade]] for how to launch a new ARCHIVE candidate with that new AMI and how to switch to it once the loading of all races has finished successfully. |
|
| 76 | + |
|
| 77 | +### ``my`` |
|
| 78 | + |
|
| 79 | +You can try an [in-place upgrade](#log-on-with-ssh-and-use-package-manager-for-live-upgrade) for these. Should this, however, require a reboot, you should then apply the following procedure: |
|
| 80 | + |
|
| 81 | +To start with, make sure you have an up-to-date ``sailing-analytics-server`` AMI (see above). Also make sure the auto-scaling group for the ``my`` replica set is set to use this latest AMI for any replicas launched by the auto-scaling group. |
|
| 82 | + |
|
| 83 | +The ``my`` replica set is special in comparison to most other replica sets. It runs its primary process on a dedicated instance and requires an instance type with at least 500GB of swap space. A good default is an ``i3.2xlarge`` instance type. The application settings, as of this writing, require 350GB of heap size, indicated by ``MEMORY="350000m"`` in the user data section for the instance. |
|
| 84 | + |
|
| 85 | +In order to move the ``my`` primary process to a new instance with a new operating system, use the AdminConsole's Landscape Management panel, and there the "Move master process to another instance" action. Make sure to select an appropricate ``i3....`` instance type with sufficient swap space, *not* the default ``C5_2_XLARGE`` suggestion. Explicitly enter the amount of memory you'd like to assign to the process, such as "350000" into the "Memory (MB)" field of the pop-up dialog, then confirm using the "OK" button. |
|
| 86 | + |
|
| 87 | +This will detach all running replicas (usually exactly one) from the primary process, remove the primary process from the ``S-my`` and ``S-my-m`` target groups, then stop and remove the primary process, which will also lead to the instance being terminated as this was the last (only) application process running on it. Then, a new instance off the latest AMI will be launched, deploying and starting a new primary process for the ``my`` replica set. Once this has loaded all contents from the DB and reports a healthy status, an explicit "Upgrade Replica" is launched which uses the explicit primary instance's IP address instead of the DNS host name to obtain an initial load. This works around the fact that so far the new primary hasn't been added to any target groups yet and hence isn't reachable under the ``my.sapsailing.com `` domain name. |
|
| 88 | + |
|
| 89 | +When the upgrade replica has reported a healthy status, the primary is added to the ``S-my`` and ``S-my-m`` target groups, and the upgrade replica is added to the ``S-my`` target group. Then, the old auto-replica which is expected to have been launched using the auto-scaling group will be terminated, causing the launching of a new instance to which a ``my`` replica is deployed and started. Once the auto-replica is healthy, the upgrade replica will be terminated which removes it from the ``S-my`` target group. |
|
| 90 | + |
|
| 91 | +### Sailing Analytics Multi-Servers |
|
| 92 | + |
|
| 93 | +You can try an [in-place upgrade](#log-on-with-ssh-and-use-package-manager-for-live-upgrade) for these. Should this, however, require a reboot, you should then apply the following procedure: |
|
| 94 | + |
|
| 95 | +To start with, make sure you have an up-to-date ``sailing-analytics-server`` AMI (see above). Also make sure that all auto-scaling groups for the application replica sets are set to use this latest AMI for any replicas launched by the auto-scaling group. (This can be achieved, e.g., using the AdminConsole's Landscape Management panel, and there the "Update machine image for auto-scaling replicas" button above the replica sets table.) |
|
| 96 | + |
|
| 97 | +Then, sort the replica sets table by the "Master Instance ID" column and identify the instances configured as "Multi-Server." Should this not be obvious, compare with the instances in the AWS EC2 console instance list named "SL Multi-Server". Click the "Move all application processes away from this replica set's master host to a new host" button. This will launch a new instance and move all application processes away from the old host, one by one. All replication aspects are handled automatically. The duration of migration varies depending on the content volumes hosted by the respective replica set. An empty replica sets migrates in a few minutes. Large replica sets may take an hour or more to migrate. The AdminConsole process may run into a timeout, but don't worry, the migration continues all the way to the end regardess of the web UI timeout. |
|
| 98 | + |
|
| 99 | +Still, should something take suspiciously long, maybe check the server logs of the server you used the Landscape Management panel on (usually ``security-service.sapsailing.com``). The ``logs/sailing.log.0`` file may show details of what went wrong or is taking long. Sometimes it may be the loading of one or more races that fails and doesn't let the instance report a healthy status. In such cases, a manual restart of that process may help, cd'ing into its folder and running ``./stop; ./start`` explicitly. |
|
| 100 | + |
|
| 101 | +### MariaDB |
|
| 102 | + |
|
| 103 | +This is a clear candidate for an in-place upgrade. Should a reboot be required, just reboot. It only takes about 10s, and for Bugzilla as system used mostly internally we can afford a 10s unavailability period. |
|
| 104 | + |
|
| 105 | +### RabbitMQ |
|
| 106 | + |
|
| 107 | +This is Debian-based. Try to go for an in-place upgrade. Should a reboot be required, ideally choose a time outside of major events and ongoing instance upgrades as those will require the RabbitMQ service to succeed. |
|
| 108 | + |
|
| 109 | +### MongoDB Replica Sets |
|
| 110 | + |
|
| 111 | +We currently have three MongoDB instances running in our EC2 landscape: ``[dbserver|mongo0|mongo1].internal.sapsailing.com``. The first hosts three ``mongod`` processes, for three separate replica sets: the single primary of the ``archive`` and the ``slow`` replica sets, and a hidden replica of the ``live`` replica set. The two other instance have primary/secondary ``mongod`` processes for the ``live`` replica set. Try an in-place upgrade first. If that doesn't require a reboot, you're done. |
|
| 112 | + |
|
| 113 | +If a reboot is required after an in-place upgrade, be gentle in how you carry out those reboots. The ``dbserver`` instance can be rebooted as long as no ARCHIVE server start-up is currently going on. During an ARCHIVE server start-up, failure to reach the database may lead to an incomplete state in the new ARCHIVE candidate which may require you to start over with the anyhow very time-consuming ARCHIVE start-up. The ``slow`` replica set and the hidden ``live`` replica, however, pose no obstacles regarding a reboot. |
|
| 114 | + |
|
| 115 | +For ``mongo0`` and ``mongo``, log on as ``ec2-user`` using SSH and use ``mongosh`` to see whether that instance is currently PRIMARY or SECONDARY. Reboot the SECONDARY first. When that has completed, SSH into the PRIMARY and in ``mongosh`` issue the command ``rs.stepDown()`` so that the PRIMARY becomes a SECONDARY, and the other instance that previously was a SECONDARY takes over as the new PRIMARY. With this courtesy, ongoing writing transactions will not even have to go through a re-try as you reboot the now SECONDAY instance. |
|
| 116 | + |
|
| 117 | +Should you choose to work with an upgraded ``mongodb-server`` AMI and the AdminConsole's Landscape Management panel, use the "Scale in/out" action on the respective MongoDB replica set to add new instances launched off the new AMI, then, once healthy, scale in to delete the old instances. This way you can handle the ``mongo0`` and ``mongo1`` instances. You should, however, have to adjust the DNS records in Route53 for ``mongo[01].internal.sapsailing.com`` to reflect the new instances because despite all tags-based resource discovery there are still some older configuration and environments files around that bootstrap new application instances by explicitly referring to ``mongo0.internal.sapsailing.com`` and ``mongo1.internal.sapsailing.com`` as their MongoDB instances to use. |
|
| 118 | + |
|
| 119 | +Should you need to upgrade the central ``dbserver.internal.sapsailing.com`` instance without an in-place upgrade, use the ``configuration/environments_scripts/central_mongo_setup/setup-central-mongo-instance.sh`` script to produce a new instance. When it has launched the new instance, it prints detailed instructions to its standard output for how to unmount the data volumes from the old and mount them to the new instance, as well as which DNS actions to take in Route53 and how to name and tag the new instance. |
|
| 120 | + |
|
| 121 | +### Central Reverse Proxy |
|
| 122 | + |
|
| 123 | +The central reverse proxy, currently running as ``sapsailing.com``, can typically be upgraded in-place. Should a re-boot be required, launch a disposable reverse proxy in the same availability zone as the central reverse proxy first, using the AdminConsole's Landscape Management panel with its "Reverse proxies" table and the corresponding "Add" button. Once that new disposable reverse proxy is shown as healthy in the corresponding ``CentralWebServerHTTP-Dyn`` target group you can reboot the central reverse proxy. It will lead to less than 30s of downtime regarding [https://bugzilla.sapsailing.com](https://bugzilla.sapsailing.com) and [https://wiki.sapsailing.com](https://wiki.sapsailing.com), as well as our self-hosted Git repository which is still used for back-up and cross-synchronization with the checked-out workspace for our Wiki. |
|
| 124 | + |
|
| 125 | +Should an in-place upgrade not be what you want, look into the ``configuration/environments_scripts/central_reverse_proxy`` folder with its setup scripts. They automate most parts of providing a new central reverse proxy instance that has been set up with the latest Amazon Linux from scratch. You will have to carry out a few steps manually, e.g., in the AWS Console, and the scripts will tell you in their standard output what these are. |
|
| 126 | + |
|
| 127 | +When done, terminate the additional disposable reverse proxy that you launched in the central reverse proxy's availability zone. |
|
| 128 | + |
|
| 129 | +### Disposable Reverse Proxy |
|
| 130 | + |
|
| 131 | +Make sure you have an upgraded ``disposable-reverse-proxy`` AMI, then use the AdminConsole Landscape Management panel and its "Reverse proxies" section to launch one or more disposable reverse proxies with the "Add" button. The default instance type suggested is usually a good fit. Make sure to launch one per availability zone in which you'd like to replace an old reverse proxy. When your new reverse proxy is healthy (this includes an error code 503 which only indicates that the reverse proxy is not in the same availability zone as the currently active production ARCHIVE server), terminate the corresponding old reverse proxy. |
|
| ... | ... | \ No newline at end of file |
wiki/info/landscape/typical-development-scenarios.md
| ... | ... | @@ -369,4 +369,4 @@ When TracTrac publishes a TracAPI upgrade, they usually notify us by e-mail and |
| 369 | 369 | |
| 370 | 370 | After downloading, extract the ``lib/TracAPI.jar`` and ``src/TracAPI-src.jar`` from the ``TracAPI-x.y.z.tar.gz`` to ``java/com.tractrac.clientmodule/lib``. Unpack the ``TracAPI-x.y.z-javadoc.tar.gz`` into ``java/com.tractrac.clientmodule/javadoc`` and adjust the versions in ``META-INF/MANIFEST.MF`` and ``pom.xml`` accordingly. Unpack the ``Readme.txt`` file from ``TracAPI-x.y.z.tar.gz`` to ``java/com.tractrac.clientmodule``. Optionally, unpack the sample sources from the ``src/com`` folder contained in ``TracAPI-x.y.z.tar.gz`` into ``java/com.tractrac.clientmodule/src``. |
| 371 | 371 | |
| 372 | -For non-trivial upgrades use of a git branch and dedicated build job is recommended. Otherwise, committing and pushing to master with the ``SAPSailingAnalytics-master`` build job picking up and testing the changes should suffice. |
|
| ... | ... | \ No newline at end of file |
| 0 | +For non-trivial upgrades use of a git branch and dedicated build job is recommended. Otherwise, committing and pushing to the ``main`` branch with the ``SAPSailingAnalytics-master`` build job picking up and testing the changes should suffice. |
|
| ... | ... | \ No newline at end of file |
wiki/info/mobile/sailinsight-privacy-policy.md
| ... | ... | @@ -0,0 +1,77 @@ |
| 1 | +# Sail Insight Privacy Policy |
|
| 2 | + |
|
| 3 | +Sail Insight (or the “App”) is a mobile application that allows its users to manage, participate, and watch regattas. The App is licensed for publication by SAP S.E. to d-labs (datenschutz@d-labs.com, Jörn Hartwig, D‑LABS GmbH, Marlene-Dietrich-Allee 15, D-14482 Potsdam). |
|
| 4 | + |
|
| 5 | +This policy aims to help you better understand the App privacy processes and your rights in connection with your personal data. To sign up and use the App, you have to carefully read this policy and provide your consent for us to use your personal data in line with this policy. |
|
| 6 | + |
|
| 7 | +The term “personal data” or “personal information” used in this policy means any information relating to any natural person, who can be identified, directly or indirectly, in particular by reference to such information. |
|
| 8 | + |
|
| 9 | +## WHAT INFORMATION DOES THE APP COLLECT? |
|
| 10 | +The App collects and stores different types of personal data: |
|
| 11 | + |
|
| 12 | +- Data that is provided directly by you |
|
| 13 | +- Data produced during your usage of the App |
|
| 14 | +- Data received from the partners of the App and the third-parties |
|
| 15 | + |
|
| 16 | +User profile: The App collects data when you create or update your account. This may include name, email, and password. |
|
| 17 | + |
|
| 18 | +Data about boats: You may provide the App with the data about your boat (type, registration number, boat rating, scoring, scoring group) or upload it from an external website. |
|
| 19 | + |
|
| 20 | +Location data: Once you grant the App access to your geolocation (and only in-between the timepoints when the user selects “start tracking” and “stop tracking), it may collect location data from your mobile device. The location data is collected when the app is in the foreground and when it’s running in the background. If you don’t grant the App access to the geolocation data, this may affect some functionality of the App. |
|
| 21 | + |
|
| 22 | +Usage data: The App collects data about how you and other users interact with the application. This includes data such as regattas that you participated in, classes, marks, courses, races you’ve created. It may also include specific technical data such as your access dates and times, app features, or pages viewed, app crashes. |
|
| 23 | + |
|
| 24 | +Device data: the App may collect data about the devices used to access the application, operating systems and versions, software, preferred languages, and other system settings of your device. |
|
| 25 | + |
|
| 26 | +## HOW DO WE USE THE INFORMATION? |
|
| 27 | +Initiate, participate, and watch regattas. The App collects and uses personal data to allow you to use the functionality of the application, personalize, maintain, and improve it. |
|
| 28 | + |
|
| 29 | +Marketing. The App may use your personal data for marketing purposes, which may include communication with the user about newsletters, upcoming events, new functionality, promotions. |
|
| 30 | + |
|
| 31 | +Other communications. The App may inform you about changes in the policies, EULA services, or send additional information about the application that is not intended for marketing purposes. |
|
| 32 | + |
|
| 33 | +Research and development. We may use the data we collect for development, testing, research, to improve the user experience. |
|
| 34 | + |
|
| 35 | +## GROUNDS FOR PROCESSING |
|
| 36 | +The App collects and uses personal data based on lawful grounds, that generally include processing personal data to provide you and other users with services and features or/and upon your direct consent. |
|
| 37 | + |
|
| 38 | +## HOW LONG DO WE KEEP YOUR DATA? |
|
| 39 | +We may store and use anonymized data for our Research and Development activities and public regatta statistics. Request for deletion of any personal data shall be sent to support@sapsailing.com. |
|
| 40 | + |
|
| 41 | +## THIRD PARTIES THAT HAVE ACCESS TO YOUR PERSONAL DATA |
|
| 42 | +The App may share your personal information with its partners and other third parties. |
|
| 43 | + |
|
| 44 | +The App has specific agreements with third parties who have access to your personal data. Such agreements include sufficient measures (1) to safeguard your personal data, your rights, freedoms, legitimate interest with regard to such information, (2) to prevent abuse or unlawful access or transfer of your personal data. |
|
| 45 | + |
|
| 46 | +## WHO MAY HAVE ACCESS TO YOUR DATA? |
|
| 47 | +Additionally, we may disclose specific types of personal data to third parties in the following cases: |
|
| 48 | + |
|
| 49 | +- To the purchaser or seller of our business; |
|
| 50 | +- To the extent that we are required to do so by law; |
|
| 51 | +- In the enforcement and/or court procedure and/or when it is required to disclose to a governmental agency in response to a valid court order or subpoena or similar requirement of the court or other regulatory bodies of other countries; |
|
| 52 | +- To establish, exercise, or defend the legal rights of our business, employees, and authorized legal representatives. |
|
| 53 | + |
|
| 54 | +## WHERE DO WE STORE YOUR PERSONAL INFORMATION? |
|
| 55 | +Your personal data is being stored on AWS servers. You can read more about AWS GDPR policies here https://aws.amazon.com/compliance/gdpr-center/. |
|
| 56 | + |
|
| 57 | +The App is exercising and implementing all the sufficient and suitable technical and organizational safeguards and measures needed to provide secure processing and storage of your data in accordance with GDPR and trying to ensure that the third parties that receive your personal data also adhere to the GDPR. |
|
| 58 | + |
|
| 59 | +## YOUR RIGHTS |
|
| 60 | +Given that this version of the App is a beta release that includes separate specific functionality for limited distribution among selected users functionality that allows exercising the rights of the data subjects established by GDPR is not realised in full. The subsequent versions of the application as soon as they are available will include the full functionality that will ensure GDPR compliance of the application as listed below. |
|
| 61 | + |
|
| 62 | +- To request to amend/renew the inaccurate and/or incomplete personal data concerning you; |
|
| 63 | +- To delete your personal data, partially or fully |
|
| 64 | +- To restrict the processing of your personal data; |
|
| 65 | +- To object to the processing of your personal data for profiling to the extent that it is related to direct marketing; |
|
| 66 | +- To request to familiarize and access the personal data that was collected. |
|
| 67 | +- You may submit to us a request to exercise any of the abovementioned rights. We will review your request within 30 calendar days and notify you of the results of its consideration. |
|
| 68 | +- For any requests connected with the rights listed above please contact the person that invited you to test the application. |
|
| 69 | + |
|
| 70 | +## LINKS TO THE SITES OF THE THIRD PARTIES |
|
| 71 | +The App may, from time to time, contain links to websites of third parties. Please note that we are not responsible for the content, terms of use and/or service as well as for the privacy policies of these websites. |
|
| 72 | + |
|
| 73 | +## MODIFICATIONS AND AMENDMENTS |
|
| 74 | +We may amend this privacy policy at any time by posting a new version and sending you a separate notification via email. It will become effective at the time it will be published. If you continue to use the App, it will be regarded as an acceptance of the amended policy. |
|
| 75 | + |
|
| 76 | +## CONTACT |
|
| 77 | +If you have any complaints about the policy and your personal data, please contact us at support@sapsailing.com |