Merge branch 'main' into bug6148

This commit is contained in:
Sohaib Baig committed 2025-10-29 16:35:32 +01:00
commit db76d70af5
261 files changed
+1998 -1326

No files matched your search

+14 -2
View File
@@ -17,6 +17,18 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Convert repo name to lowercase GHCR package
id: ghcr
env:
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
# $GITHUB_REPOSITORY is in format owner/repo
OWNER=${GITHUB_REPOSITORY%%/*} # extract owner
REPO=${GITHUB_REPOSITORY##*/} # extract repo
OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]') # lowercase owner
REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]') # lowercase repo
PACKAGE="$OWNER_LOWER/$REPO_LOWER"
echo "PACKAGE=${PACKAGE}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v4
with:
@@ -50,7 +62,7 @@ jobs:
- name: Download release
shell: bash
run: |
RELEASE_TAR_GZ_FILENAME=$( configuration/github-download-release-assets.sh ${{ secrets.GITHUB_TOKEN }} ${{ env.RELEASE_PREFIX }} )
RELEASE_TAR_GZ_FILENAME=$( configuration/github-download-release-assets.sh ${{ secrets.GITHUB_TOKEN }} ${{ env.RELEASE_PREFIX }} ${{ github.repository }} )
RELEASE=$( echo ${RELEASE_TAR_GZ_FILENAME} | sed -e 's/\.tar\.gz$//' )
if [ -n ${RELEASE_TAR_GZ_FILENAME} ]; then
mv ${RELEASE_TAR_GZ_FILENAME} docker/
@@ -60,7 +72,7 @@ jobs:
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
with:
build-args: RELEASE=${{ env.RELEASE }}
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' || '' }}
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) || '' }}
annotations: |
maintainer=axel.uhl@sap.com
index:org.opencontainers.image.title=Sailing Analytics
+40 -7
View File
@@ -5,6 +5,7 @@ on:
- 'wiki/**'
- '.github/workflows/*'
- '**/*.md'
- 'README.md'
workflow_dispatch:
inputs:
skip_tests:
@@ -14,8 +15,8 @@ on:
description: Build with a local target platform (set to "true" to use local target platform)
default: 'false'
runner_cpus:
description: Default is 16, but can be 4, 8, and 64 as well
default: '16'
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
default: ''
firefox_version:
description: Version to use for Selenium tests; default is 137.0, but could, e.g., be "141.0" or "latest"
default: '140.0'
@@ -25,16 +26,36 @@ jobs:
permissions:
contents: write
checks: write
runs-on: ubuntu-latest-${{ github.event.inputs.runner_cpus == '' && '16' || github.event.inputs.runner_cpus }}cpu
actions: read
runs-on: ${{ github.event.inputs.runner_cpus != '' && format('ubuntu-latest-{0}cpu', github.event.inputs.runner_cpus) || 'ubuntu-latest' }}
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@v1.3.1
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: false
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Allocate swap space
shell: bash
run: |
sudo dd if=/dev/zero of=/mnt/large-swapfile bs=1G count=10
sudo dd if=/dev/zero of=/mnt/large-swapfile bs=1G count=30
sudo chmod 600 /mnt/large-swapfile
sudo mkswap /mnt/large-swapfile
sudo chown root:root /mnt/large-swapfile
sudo swapon /mnt/large-swapfile
- name: Create TMP space under /mnt/tmp
shell: bash
run: |
sudo mkdir /mnt/tmp
sudo chmod 777 /mnt/tmp
- name: Collect Workflow Telemetry
uses: catchpoint/workflow-telemetry-action@94c3c3d9567a0205de6da68a76c428ce4e769af1 # v2.0.0
with:
@@ -79,10 +100,18 @@ jobs:
ports: '5672:5672'
- shell: bash
env: # Or as an environment variable
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 }}"
TMP: /mnt/tmp
AWS_S3_TEST_S3ACCESSID: ${{ secrets.AWS_S3_TEST_S3ACCESSID }}
AWS_S3_TEST_S3ACCESSKEY: ${{ secrets.AWS_S3_TEST_S3ACCESSKEY }}
GEONAMES_ORG_USERNAMES: ${{ secrets.GEONAMES_ORG_USERNAMES }}
GOOGLE_MAPS_AUTHENTICATION_PARAMS: ${{ secrets.GOOGLE_MAPS_AUTHENTICATION_PARAMS }}
APP_PARAMETERS: "-Daws.region=eu-west-1"
JAVA8_HOME: ${{env.JAVA_HOME_8_X64}}
run: |
./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
./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
- name: show disk stats
if: always()
run: df -h
- name: Upload build log
uses: actions/upload-artifact@v4
if: always()
@@ -156,7 +185,11 @@ jobs:
asset_name: release-notes.txt
asset_content_type: text/plain
- name: Trigger Hudson job
if: always()
env:
HUDSON_JOB_USERNAME: ${{ secrets.HUDSON_JOB_USERNAME }}
HUDSON_JOB_PASSWORD: ${{ secrets.HUDSON_JOB_PASSWORD }}
HUDSON_JOB_TOKEN: ${{ secrets.HUDSON_JOB_TOKEN }}
if: ${{ always() && env.HUDSON_JOB_USERNAME != '' && env.HUDSON_JOB_PASSWORD != '' && env.HUDSON_JOB_TOKEN != '' }}
shell: bash
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
+6 -6
View File
@@ -70,7 +70,7 @@ GEM
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.227.1)
fastlane (2.228.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -134,12 +134,12 @@ GEM
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.8.0)
google-cloud-core (1.7.1)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.5.0)
google-cloud-errors (1.4.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
@@ -176,14 +176,14 @@ GEM
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
public_suffix (6.0.1)
public_suffix (5.1.1)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.4.1)
rexml (3.4.2)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
@@ -231,4 +231,4 @@ DEPENDENCIES
json (> 0)
BUNDLED WITH
2.6.1
2.4.21
+28 -26
View File
@@ -14,25 +14,47 @@ SAP is at the center of today’s technology revolution, developing innovations
### General
* [[Onboarding|wiki/howto/onboarding]]
* [[Information about this Wiki and HowTo|wiki/info/general/wiki]]
* [[General Project Information|wiki/info/general/general-information]]
* [[Project History|wiki/info/general/project-history]]
* [[Development Environment|wiki/info/landscape/development-environment]]
### Landscape
### Development
* Architecture and Infrastructure
* [[Development Environment|wiki/info/landscape/development-environment]]
* [[Typical Development Scenarios|wiki/info/landscape/typical-development-scenarios]]
* [[Building and Deploying|wiki/info/landscape/building-and-deploying]]
* [[Continuous Integration with Hudson/Jenkins|wiki/howto/development/ci]]
* [[Dispatch|wiki/howto/development/dispatch]]
* [[Working with GWT UI Binder|wiki/howto/development/gwt-ui-binder]]
* [[Java De(Serialization) and Circular Dependencies|wiki/howto/development/java-de-serialization-and-circular-dependencies]]
* [[Create boat graphics for the 2D race viewer|wiki/howto/development/boatgraphicssvg]]
* [[JMX Support|wiki/howto/development/jmx]]
* [[Working with GWT Locally|wiki/howto/development/local-gwt]]
* [[UI Tests with Selenium|wiki/howto/development/selenium-ui-tests]]
* [[Profiling|wiki/howto/development/profiling]]
* [[Working with GWT Super Dev Mode|wiki/howto/development/super-dev-mode]]
* [[Training of internal Wind Estimation models|wiki/howto/windestimation]]
* [[Whitelabelling|wiki/howto/whitelabelling]]
* [[Secured Settings|wiki/howto/development/secured-settings]]
* [[Webdesign|wiki/info/landscape/webdesign]]
### Architecture and Infrastructure
* [[Workspace, Bundles, Projects|wiki/info/general/workspace-bundles-projects-structure]]
* [[Runtime Environment|wiki/info/landscape/runtime-environment]]
* [[Basic architectual principles|wiki/info/landscape/basic-architectural-principles]]
* [[User Management|wiki/info/landscape/usermanagement]]
* [[Igtimi Windbot Riot Connector|wiki/info/landscape/igtimi-riot]]
* [[Development Environment|wiki/info/landscape/development-environment]]
* [[Production Environment|wiki/info/landscape/production-environment]]
* [[Internationalization (i18n)|wiki/howto/development/i18n]]
* [[AI Agent|wiki/info/landscape/ai-agent]]
* [[Malware Scanning|wiki/info/landscape/malware-scanning]]
* [[RaceLog Tracking Server Architecture|wiki/info/landscape/server]]
* [[RaceLog Tracking Server Architecture|wiki/info/landscape/server]]
* Environment Overview [[PDF|wiki/info/mobile/event-tracking/architecture.pdf]] | [[SVG|wiki/info/mobile/event-tracking/architecture.svg]]
### Landscape
* Amazon
* [[Amazon EC2|wiki/info/landscape/amazon-ec2]]
* [[Upgrading ARCHIVE server|wiki/info/landscape/archive-server-upgrade]]
@@ -40,6 +62,7 @@ SAP is at the center of today’s technology revolution, developing innovations
* [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]]
* [[Upgrading an EC2 image|wiki/info/landscape/upgrading-ec2-image]]
* [[Creating a webserver EC2 image from scratch|wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch]]
* [[Upgrading Operating System Across Landscape|wiki/info/landscape/operating-system-upgrade]]
* [[EC2 mail relaying vs. Amazon Simple E-Mail Service (SES)|wiki/info/landscape/mail-relaying]]
* [[Establishing support@sapsailing.com with AWS SES, SNS, and Lambda|wiki/info/landscape/support-email]]
* [[Creating an EC2 image for a MongoDB Replica Set from scratch|wiki/info/landscape/creating-ec2-mongodb-image-from-scratch]]
@@ -50,28 +73,9 @@ SAP is at the center of today’s technology revolution, developing innovations
* [[Downloading and Archiving TracTrac Events|wiki/howto/downloading-and-archiving-tractrac-events]]
* [[Data Mining Architecture|wiki/info/landscape/data-mining-architecture]]
* [[Typical Data Mining Scenarios|wiki/info/landscape/typical-data-mining-scenarios]]
* [[Webdesign|wiki/info/landscape/webdesign]]
* [[sail-insight.com website|wiki/info/landscape/sail-insight.com-website]]
* [[Docker Registry|wiki/info/landscape/docker-registry]]
### Development
* [[Typical Development Scenarios|wiki/info/landscape/typical-development-scenarios]]
* [[Building and Deploying|wiki/info/landscape/building-and-deploying]]
* [[Create boat graphics for the 2D race viewer|wiki/howto/development/boatgraphicssvg]]
* [[Continuous Integration with Hudson/Jenkins|wiki/howto/development/ci]]
* [[Dispatch|wiki/howto/development/dispatch]]
* [[Working with GWT UI Binder|wiki/howto/development/gwt-ui-binder]]
* [[Java De(Serialization) and Circular Dependencies|wiki/howto/development/java-de-serialization-and-circular-dependencies]]
* [[JMX Support|wiki/howto/development/jmx]]
* [[Working with GWT Locally|wiki/howto/development/local-gwt]]
* [[UI Tests with Selenium|wiki/howto/development/selenium-ui-tests]]
* [[Profiling|wiki/howto/development/profiling]]
* [[Working with GWT Super Dev Mode|wiki/howto/development/super-dev-mode]]
* [[Training of internal Wind Estimation models|wiki/howto/windestimation]]
* [[Whitelabelling|wiki/howto/whitelabelling]]
* [[Secured Settings|wiki/howto/development/secured-settings]]
### Mobile
* [[Mobile Development|wiki/info/mobile/mobile-development]]
@@ -154,10 +158,8 @@ SAP is at the center of today’s technology revolution, developing innovations
* [[Monitoring Apache and RabbitMQ|wiki/misc/monitoring-apache-and-rabbitmq]]
## Projects
* [[Analytics on a stick|wiki/projects/analytics-on-a-stick]]
* [[Consolidating User Stores (bug 4006 / 4018)|wiki/projects/consolidating-user-stores]]
* [[Cloud Infrastructure Orchestration|wiki/projects/cloud-orchestrator]]
* [[Management Console for Easier Administration|wiki/howto/development/management-console]]
* [[Cloud Infrastructure Orchestration|wiki/projects/cloud-orchestrator]]
## Events and Planning
* [[Project Planning (bigger development)|wiki/events/planning]]
+118 -8
View File
@@ -2,7 +2,7 @@
## About this Project
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).
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).
## Description
@@ -32,11 +32,11 @@ Based on the ``docker/docker-compose.yml`` definition you should end up with thr
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.
To use Java 17, use the ``docker-compose-17.yml`` file instead:
To use Java 24, use the ``docker-compose-24.yml`` file instead:
```
cd docker
docker-compose -f docker-compose-17.yml up
docker-compose -f docker-compose-24.yml up
```
## Requirements
@@ -74,12 +74,123 @@ See [here](https://www.sapsailing.com/gwt/Home.html#/imprint/:) for a list of co
## Building and Running
This assumes you have completed the onboarding (see again [here](https://wiki.sapsailing.com/wiki/howto/onboarding)) successfully. To build, then invoke
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.
There are two options for building, detailed below; for both, you need to fulfill a few prerequisites.
### Prerequisites
If you have forked the repository and would like to run your own build, you will need the following prerequisites:
#### Google Maps API Key
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.
#### AWS S3 Bucket for Upload Tests
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:
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1422015883000",
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketPolicy",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetLifecycleConfiguration",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectTorrent",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTorrent",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListBucketVersions",
"s3:ListMultipartUploadParts",
"s3:PutBucketLogging",
"s3:PutBucketNotification",
"s3:PutBucketPolicy",
"s3:PutBucketTagging",
"s3:PutBucketVersioning",
"s3:PutBucketWebsite",
"s3:PutLifecycleConfiguration",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl",
"s3:RestoreObject"
],
"Resource": [
"arn:aws:s3:::sapsailing-automatic-upload-test/*",
"arn:aws:s3:::sapsailing-automatic-upload-test"
]
}
]
}
```
Create an access key for that user and note key ID and secret.
#### Account(s) for geonames.org
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.
### Get the GitHub Actions build to work in your forked repository
Assign the IDs and secrets from the prerequisites to repository secrets in your forked repository as follows:
```
AWS_S3_TEST_S3ACCESSID: {your-S3-test-bucket-upload-token-ID}
AWS_S3_TEST_S3ACCESSKEY: {key-for-your-S3-token}
GEONAMES_ORG_USERNAMES: {comma-separated-list-of-geonames.org-usernames}
GOOGLE_MAPS_AUTHENTICATION_PARAMS: key={your-Google-Maps-API-key}
```
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``.
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.
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:
```
REPO_TOKEN_FOR_MERGE_AND_PUSH: {a-GitHub-token-enabled-for-push}
```
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.
### Run a build locally
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:
```
export AWS_S3_TEST_S3ACCESSID={your-S3-test-bucket-upload-token-ID}
export AWS_S3_TEST_S3ACCESSKEY={key-for-your-S3-token}
export GEONAMES_ORG_USERNAMES={comma-separated-list-of-geonames.org-usernames}
export GOOGLE_MAPS_AUTHENTICATION_PARAMS=key={your-Google-Maps-API-key}
export JAVA8_HOME={location-of-your-JDK8}
export JAVA_HOME={location-of-your-JDK17-or-newer}
```
To build, then invoke
```
configuration/buildAndUpdateProduct.sh build
```
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
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
```
configuration/buildAndUpdateProduct.sh install [ -s <server-name> ]
@@ -99,9 +210,9 @@ Run the ``buildAndUpdateProduct.sh`` without any arguments to see the sub-comman
## Downloading, Installing and Running an Official Release
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.
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.
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:
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:
```
mkdir sailinganalytics
cd sailinganalytics
@@ -113,7 +224,6 @@ This will download and install the latest release and configure it such that it
In addition to the necessary ``MONGODB_URI`` variable you may need to inject a few secrets into your runtime environment:
- ``MANAGE2SAIL_ACCESS_TOKEN`` access token for result and regatta structure import from the Manage2Sail regatta management system
- ``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
- ``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).
- ``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).
+1
View File
@@ -2,6 +2,7 @@ version = 1
SPDX-PackageName = "sailing-analytics"
SPDX-PackageSupplier = "SAP Open Source Program Office ospo@sap.com"
SPDX-PackageDownloadLocation = "https://github.com/SAP/sailing-analytics"
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."
[[annotations]]
path = "java/com.google.gwt.servlet/**"
+1 -1
View File
@@ -222,7 +222,7 @@ echo TMP will be used for java.io.tmpdir and is $TMP
if [ "$TMP" = "" ]; then
export TMP=/tmp
fi
extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP"
extra="${extra} -Dgwt.workers=${GWT_WORKERS} -Djava.io.tmpdir=$TMP -Dgwt.workDir=$TMP"
extra="${extra} -Djdk.xml.maxGeneralEntitySizeLimit=0 -Djdk.xml.totalEntitySizeLimit=0"
shift $((OPTIND-1))
+2 -2
View File
@@ -27,9 +27,9 @@ curl -s -X GET $COPY_TEMPLATE_CONFIG_URL -u "$USERNAME:$PASSWORD" -o "$CONFIGFIL
# On macosx is gnu-sed needed
if [[ "$OSTYPE" == *"$OS_FOR_GSED"* ]]; then
echo "Using gsed"
gsed -i'' -e 's|<description>..*</description>|<description>This is the CI job for \&lt;a href=\&quot;'$BUGZILLA_BASE'/show_bug.cgi?id='$BUG_ID'\&quot;\&gt;Bug '$BUG_ID'\&lt;/a\&gt;</description>|' -e 's|<disabled>true</disabled>|<disabled>false</disabled>|' "$CONFIGFILE"
gsed -i'' -e 's|<description>..*</description>|<description>This is the CI job for \&lt;a href=\&quot;'$BUGZILLA_BASE'/show_bug.cgi?id='$BUG_ID'\&quot;\&gt;Bug '$BUG_ID'\&lt;/a\&gt;. See its latest \&lt;a href=\&quot;/userContent/measurements.html?job=bug'$BUG_ID'\&quot;\&gt;quality and performance measurements here.\&lt;/a\&gt;</description>|' -e 's|<disabled>true</disabled>|<disabled>false</disabled>|' "$CONFIGFILE"
else
sed -i -e 's|<description>..*</description>|<description>This is the CI job for \&lt;a href=\&quot;'$BUGZILLA_BASE'/show_bug.cgi?id='$BUG_ID'\&quot;\&gt;Bug '$BUG_ID'\&lt;/a\&gt;</description>|' -e 's|<disabled>true</disabled>|<disabled>false</disabled>|' "$CONFIGFILE"
sed -i -e 's|<description>..*</description>|<description>This is the CI job for \&lt;a href=\&quot;'$BUGZILLA_BASE'/show_bug.cgi?id='$BUG_ID'\&quot;\&gt;Bug '$BUG_ID'\&lt;/a\&gt;. See its latest \&lt;a href=\&quot;/userContent/measurements.html?job=bug'$BUG_ID'\&quot;\&gt;quality and performance measurements here.\&lt;/a\&gt;</description>|' -e 's|<disabled>true</disabled>|<disabled>false</disabled>|' "$CONFIGFILE"
fi
# On macosx is gnu-sed needed
@@ -35,7 +35,7 @@ HUDSON_USER="hudson"
#
# Options to pass to java when running Hudson.
#
HUDSON_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx2G -Dhudson.slaves.ChannelPinger.pingInterval=60 -Dhudson.slaves.ChannelPinger.pingIntervalSeconds=60 -Dhudson.slaves.ChannelPinger.pingTimeoutSeconds=60"
HUDSON_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx4G -Dhudson.slaves.ChannelPinger.pingInterval=60 -Dhudson.slaves.ChannelPinger.pingIntervalSeconds=60 -Dhudson.slaves.ChannelPinger.pingTimeoutSeconds=60 -XX:+UseG1GC -Xlog:gc+ergo*=trace:file=/var/log/hudson/gc_ergo.log:time:filecount=10,filesize=100000000 -Xlog:gc*:file=/var/log/hudson/gc.log:time:filecount=10,filesize=100000000 -XX:+GCHistory -XX:GCHistoryFilename=/var/log/hudson/sapjvm_gc@PID.prf"
## Type: integer(0:65535)
## Default: 8080
@@ -0,0 +1,295 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hudson CI – Measurement Graphs Overview</title>
<!-- Tailwind CSS (CDN) -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
@media print {
#toolbar, .card-actions, .footer { display: none !important; }
body { background: #fff !important; }
.card { break-inside: avoid; }
}
.spinner {
border: 4px solid rgba(0,0,0,0.1);
border-top: 4px solid #4f46e5;
border-radius: 50%;
width: 32px;
height: 32px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="min-h-screen bg-gray-50 text-gray-900">
<header id="toolbar" class="sticky top-0 z-50 backdrop-blur bg-white/80 border-b">
<div class="max-w-7xl mx-auto px-4 py-3 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-7 h-7 text-indigo-600"><path d="M12 2a1 1 0 0 1 .894.553l8 16A1 1 0 0 1 20 20H4a1 1 0 0 1-.894-1.447l8-16A1 1 0 0 1 12 2Zm0 3.618L6.618 18h10.764L12 5.618Z"/></svg>
<div>
<h1 id="pageTitle" class="text-xl font-semibold leading-tight">Hudson CI – Measurement Graphs</h1>
<p class="text-sm text-gray-600">At-a-glance trends from build/test history</p>
</div>
</div>
<div class="flex flex-wrap items-center gap-2">
<button id="refreshAll" class="px-3 py-2 rounded-xl bg-indigo-600 text-white text-sm font-medium shadow hover:bg-indigo-700 focus:outline-none focus:ring">Refresh all</button>
<label class="flex items-center gap-2 text-sm">
<input id="autoRefreshToggle" type="checkbox" class="rounded border-gray-300"> Auto-refresh
</label>
<select id="columns" class="px-2 py-2 rounded-xl border text-sm bg-white">
<option value="2">2 columns</option>
<option value="3" selected>3 columns</option>
<option value="4">4 columns</option>
</select>
<input id="search" type="search" placeholder="Filter by title…" class="px-3 py-2 rounded-xl border text-sm bg-white w-48" />
</div>
</div>
</header>
<main class="max-w-7xl mx-auto p-4">
<div id="grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 transition-all"></div>
</main>
<footer class="footer max-w-7xl mx-auto px-4 pb-8 text-sm text-gray-500">
<p>Tip: Click any chart to view full-screen. Right-click an image to open it in a new tab. This page only embeds existing Hudson graph PNGs; no data is stored.</p>
</footer>
<dialog id="viewer" class="backdrop:bg-black/60 p-0 rounded-2xl shadow-2xl w-[95vw] max-w-6xl">
<div class="flex items-center justify-between px-4 py-3 border-b">
<h3 id="viewerTitle" class="font-semibold"></h3>
<div class="flex items-center gap-2">
<a id="viewerOpenSource" href="#" target="_blank" rel="noopener" class="text-sm px-2 py-1 rounded-lg border">Open source</a>
<button id="viewerClose" class="text-sm px-2 py-1 rounded-lg bg-gray-900 text-white">Close</button>
</div>
</div>
<div class="p-2 sm:p-4">
<img id="viewerImg" alt="Measurement graph" class="w-full h-auto rounded-xl" />
</div>
</dialog>
<script>
// Determine job name from query parameter or default
const params = new URLSearchParams(window.location.search);
const jobName = params.get('job') || 'SAPSailingAnalytics-master';
// Update page title with job name
document.getElementById('pageTitle').textContent = `Hudson CI – Measurement Graphs (${jobName})`;
document.title = `Hudson CI Measurement Graphs Overview – ${jobName}`;
const testNames = [
"FlorianopolisMarkPassingTest",
"KW470MarkPassingTest",
"Kiel2013505Test",
"OBMR2012VRMarkPassingTest",
"StarIDM2013MarkPassingTest"
];
const measurementNames = ["Extra", "Skipped", "Different", "Accurate"];
const graphs = [];
// Build the graphs dynamically
testNames.forEach(test => {
measurementNames.forEach(measure => {
graphs.push({
title: `${test} ${measure}`,
url: `https://hudson.sapsailing.com/job/${jobName}/lastCompletedBuild/testReport/junit/com.sap.sailing.domain.test.markpassing/${test}/${test}/measurementPlots/${measure}/history/graph/png`,
source: `Hudson: ${jobName} → ${test} ${measure}`
});
});
});
// Also include your Branding client script size graph as the first entry:
graphs.unshift({
title: "Branding client script size in bytes",
url: `https://hudson.sapsailing.com/job/${jobName}/lastCompletedBuild/testReport/junit/com.sap.sse.branding.impl/TestAndMeasureSizeOfSAPBranding/TestAndMeasureSizeOfSAPBranding/measurementPlots/Branding%20client%20script%20size%20in%20bytes/history/graph/png`,
source: `Hudson: ${jobName} → Branding client script size in bytes`
});
// Also include the MarkPassingCalculatorPerformanceTest.ChooserPerformance Measurement
graphs.unshift({
title: "MarkPassingCalculator Chooser Performance",
url: `https://hudson.sapsailing.com/job/${jobName}/lastCompletedBuild/testReport/junit/com.sap.sailing.domain.test.markpassing/MarkPassingCalculatorPerformanceTest/ChooserPerformance/measurementPlots/ChooserPerformance/history/graph/png`,
source: `Hudson: ${jobName} → MarkPassingCalculator Chooser Performance`
});
// Also include the MarkPassingCalculatorPerformanceTest.FinderPerformance Measurement
graphs.unshift({
title: "MarkPassingCalculator Finder Performance",
url: `https://hudson.sapsailing.com/job/${jobName}/lastCompletedBuild/testReport/junit/com.sap.sailing.domain.test.markpassing/MarkPassingCalculatorPerformanceTest/FinderPerformance/measurementPlots/FinderPerformance/history/graph/png`,
source: `Hudson: ${jobName} → MarkPassingCalculator Finder Performance`
});
const PREFS_KEY = "hudson-graphs-prefs";
const defaultPrefs = { columns: 3, autoRefresh: false };
const prefs = Object.assign({}, defaultPrefs, JSON.parse(localStorage.getItem(PREFS_KEY) || "{}"));
const grid = document.getElementById('grid');
const search = document.getElementById('search');
const columns = document.getElementById('columns');
const refreshAll = document.getElementById('refreshAll');
const autoRefreshToggle = document.getElementById('autoRefreshToggle');
const viewer = document.getElementById('viewer');
const viewerImg = document.getElementById('viewerImg');
const viewerTitle = document.getElementById('viewerTitle');
const viewerOpenSource = document.getElementById('viewerOpenSource');
const viewerClose = document.getElementById('viewerClose');
columns.value = String(prefs.columns);
autoRefreshToggle.checked = !!prefs.autoRefresh;
applyColumns();
function applyColumns() {
const cols = Number(columns.value || 3);
grid.className = `grid gap-4 transition-all grid-cols-1 sm:grid-cols-2 ${cols>=3? 'lg:grid-cols-3':''} ${cols>=4? 'xl:grid-cols-4':''}`;
savePrefs();
}
columns.addEventListener('change', applyColumns);
function savePrefs() {
localStorage.setItem(PREFS_KEY, JSON.stringify({ columns: Number(columns.value), autoRefresh: autoRefreshToggle.checked }));
}
autoRefreshToggle.addEventListener('change', savePrefs);
function render() {
grid.innerHTML = '';
const q = (search.value || '').toLowerCase();
graphs
.filter(g => !q || g.title.toLowerCase().includes(q))
.forEach((g, idx) => grid.appendChild(cardForGraph(g, idx)));
}
function cardForGraph(g, idx) {
const card = document.createElement('article');
card.className = 'card rounded-2xl bg-white border shadow-sm overflow-hidden flex flex-col';
const header = document.createElement('div');
header.className = 'px-4 pt-4 flex items-start justify-between gap-3';
const title = document.createElement('h2');
title.className = 'font-semibold leading-snug';
title.textContent = g.title;
header.appendChild(title);
const actions = document.createElement('div');
actions.className = 'card-actions flex items-center gap-2';
actions.innerHTML = `
<a href="${g.url}" target="_blank" rel="noopener" title="Open image in new tab" class="text-sm px-2 py-1 rounded-lg border">Open</a>
<button data-idx="${idx}" class="refresh-btn text-sm px-2 py-1 rounded-lg bg-gray-900 text-white">Refresh</button>
`;
header.appendChild(actions);
card.appendChild(header);
const wrap = document.createElement('div');
wrap.className = 'relative m-3 rounded-xl overflow-hidden ring-1 ring-gray-100 flex items-center justify-center min-h-[200px]';
const spinner = document.createElement('div');
spinner.className = 'spinner';
wrap.appendChild(spinner);
const img = document.createElement('img');
img.alt = g.title;
img.loading = 'lazy';
img.decoding = 'async';
img.src = g.url;
img.className = 'w-full h-auto opacity-0 transition-opacity duration-300';
img.addEventListener('load', () => {
spinner.remove();
img.classList.remove('opacity-0');
});
img.addEventListener('error', () => {
spinner.className = 'absolute inset-0 flex items-center justify-center text-sm text-red-600 bg-red-50';
spinner.textContent = 'Failed to load image';
});
img.style.cursor = 'zoom-in';
img.addEventListener('click', () => openViewer(g));
wrap.appendChild(img);
card.appendChild(wrap);
const meta = document.createElement('div');
meta.className = 'px-4 pb-4 text-sm text-gray-600 flex items-center justify-between';
const left = document.createElement('div');
left.textContent = g.source || 'Hudson';
const right = document.createElement('div');
right.innerHTML = `<time class="last-updated" datetime="${new Date().toISOString()}">Updated just now</time>`;
meta.appendChild(left);
meta.appendChild(right);
card.appendChild(meta);
return card;
}
function openViewer(g) {
viewerImg.src = g.url;
viewerTitle.textContent = g.title;
viewerOpenSource.href = g.url;
if (typeof viewer.showModal === 'function') viewer.showModal();
else viewer.setAttribute('open', '');
}
viewerClose.addEventListener('click', () => viewer.close());
viewer.addEventListener('click', (e) => { if (e.target === viewer) viewer.close(); });
function refreshImages(scope=document) {
scope.querySelectorAll('img').forEach(img => {
const spinner = document.createElement('div');
spinner.className = 'spinner absolute inset-0 m-auto';
const parent = img.parentElement;
parent.appendChild(spinner);
img.classList.add('hidden');
const src = img.src.split('?')[0];
img.src = src + '?r=' + Date.now();
img.addEventListener('load', () => {
spinner.remove();
img.classList.remove('hidden');
}, { once: true });
});
scope.querySelectorAll('time.last-updated').forEach(t => {
t.textContent = 'Updated just now';
t.setAttribute('datetime', new Date().toISOString());
});
}
refreshAll.addEventListener('click', () => refreshImages(document));
grid.addEventListener('click', (e) => {
const btn = e.target.closest('button.refresh-btn');
if (!btn) return;
const card = btn.closest('.card');
const img = card.querySelector('img');
const time = card.querySelector('time.last-updated');
const spinner = document.createElement('div');
spinner.className = 'spinner absolute inset-0 m-auto';
img.parentElement.appendChild(spinner);
img.classList.add('hidden');
const src = img.src.split('?')[0];
img.src = src + '?r=' + Date.now();
img.addEventListener('load', () => {
spinner.remove();
img.classList.remove('hidden');
}, { once: true });
time.textContent = 'Updated just now';
time.setAttribute('datetime', new Date().toISOString());
});
search.addEventListener('input', render);
let autoTimer = null;
function updateAutoTimer() {
if (autoTimer) { clearInterval(autoTimer); autoTimer = null; }
if (autoRefreshToggle.checked) {
autoTimer = setInterval(() => refreshImages(document), 5 * 60 * 1000);
}
savePrefs();
}
autoRefreshToggle.addEventListener('change', updateAutoTimer);
updateAutoTimer();
render();
</script>
</body>
</html>
@@ -2,23 +2,21 @@
echo "
"
BODY=$( cat )
echo "${BODY}" >/tmp/github-hook-body
REF=$( echo "${BODY}" | jq -r '.ref' )
PUSHER=$( echo "${BODY}" | jq -r '.pusher.email' )
logger -t github-cgi "ref is $REF, pusher was $PUSHER"
# For testing:
#if [ "${PUSHER}" = "axel.uhl@sap.com" -a "${REF}" = "refs/heads/translation" ]; then
tmsatsls+github.com_service-tip-git@sap.com
# The filter in case this is to be used for github.tools.sap:
#if [ "${PUSHER}" = "your-email@example.com" -a "${REF}" = "refs/heads/translation" ]; then
# The filter in case this is to be used for alternative Github at github.tools.sap:
#if [ "${PUSHER}" = "tmsatsls+github.tools.sap_service-tip-git@sap.com" -a "${REF}" = "refs/heads/translation" ]; then
# The filter for github.com:
if [ "${PUSHER}" = "tmsatsls+github.com_service-tip-git@sap.com" -a "${REF}" = "refs/heads/translation" ]; then
echo "Identified a push to refs/heads/translation by ${PUSHER}."
echo "Fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git"
logger -t github-cgi "fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git"
echo "Fetching translation branch from github and pushing it to ssh://trac@sapsailing.com/home/trac/git"
logger -t github-cgi "fetching translation branch from github and pushing it to ssh://trac@sapsailing.com/home/trac/git"
cd /home/wiki/gitwiki
sudo -u wiki git fetch github translation:translation 2>&1
sudo -u wiki git push origin translation:translation 2>&1
else
echo "Either pusher was not tmsatsls+github.tools.sap_service-tip-git@sap.com or ref was not refs/heads/translation. Not triggering."
echo "Either pusher was not tmsatsls+github.com_service-tip-git@sap.com or ref was not refs/heads/translation. Not triggering."
fi
@@ -66,12 +66,14 @@ gem install gollum -v 5.3.2
gem update --system 3.5.7
cd /home
# copy bugzilla
scp -o StrictHostKeyChecking=no root@sapsailing.com:/var/www/static/bugzilla-5.0.4.tar.gz /usr/local/src
scp -o StrictHostKeyChecking=no root@sapsailing.com:/var/www/static/bugzilla-5.2.tar.gz /usr/local/src
cd /usr/local/src
tar -xzvf bugzilla-5.0.4.tar.gz
mv bugzilla-5.0.4 /usr/share/bugzilla
tar -xzvf bugzilla-5.2.tar.gz
mv bugzilla-5.2 /usr/share/bugzilla
cd /usr/share/bugzilla/
mkdir data
scp -o StrictHostKeyChecking=no root@sapsailing.com:/usr/share/bugzilla/localconfig .
scp -o StrictHostKeyChecking=no root@sapsailing.com:/usr/share/bugzilla/data/params.json ./data/
echo "Bugzilla has been copied. Now setting up bugzilla modules."
echo "This can take 5 minutes or so. The output is muted (but sent to log.txt) to prevent excessive warnings and clutter in the terminal."
SECONDEOF
@@ -79,6 +81,8 @@ terminationCheck "$?"
ssh -A "root@${IP}" "bash -s" << BUGZILLAEOF &>log.txt
cd /usr/share/bugzilla/
# essentials bugzilla
/usr/bin/perl -MCPAN -e 'install App::cpanminus'
cpanm --notest SOAP::Lite
/usr/bin/perl install-module.pl DateTime
/usr/bin/perl install-module.pl DateTime::TimeZone
/usr/bin/perl install-module.pl Email::Sender
@@ -91,7 +95,9 @@ cd /usr/share/bugzilla/
/usr/bin/perl install-module.pl Email::Address
/usr/bin/perl install-module.pl autodie
/usr/bin/perl install-module.pl Class::XSAccessor
/usr/bin/perl install-module.pl DBIx::Connector
# nice to have for buzilla
/usr/bin/perl install-module.pl Encode::Detect
/usr/bin/perl install-module.pl Date::Parse
/usr/bin/perl install-module.pl Email::Send
/usr/bin/perl install-module.pl DBI
@@ -109,6 +115,15 @@ cd /usr/share/bugzilla/
/usr/bin/perl install-module.pl File::Copy::Recursive
/usr/bin/perl install-module.pl MIME::Base64
/usr/bin/perl install-module.pl Authen::SASL
/usr/bin/perl install-module.pl XML::Twig
/usr/bin/perl install-module.pl Net::LDAP
/usr/bin/perl install-module.pl Net::SMTP::SSL
/usr/bin/perl install-module.pl XMLRPC::Lite
/usr/bin/perl install-module.pl Test::Taint
/usr/bin/perl install-module.pl HTML::Scrubber
/usr/bin/perl install-module.pl Email::Reply
/usr/bin/perl install-module.pl HTML::FormatText::WithLinks
/usr/bin/perl install-module.pl Cache::Memcached
BUGZILLAEOF
terminationCheck "$?"
read -n 1 -p "Bugzilla installation complete, when ready press a key to continue." key_pressed
@@ -28,7 +28,8 @@ if [ "${curl_exit_code}" = "0" ]; then
logger -t sailing "New SSH key changes for landscape managers (${last_change_millis} newer than ${PREVIOUS_CHANGE})"
if update_authorized_keys_for_landscape_managers "${BEARER_TOKEN}" "${BASE_URL}" "${LOGON_USER_HOME}" ; then
logger -t sailing "Updating SSH keys for landscape managers successful; updating ${LAST_CHANGE_FILE}"
echo ${last_change_millis} >${LAST_CHANGE_FILE}
# /var/run is writable only for root, so we need to be able to sudo:
sudo bash -c "echo ${last_change_millis} >${LAST_CHANGE_FILE}"
else
logger -t sailing "Updating SSH keys for landscape managers failed with exit code $?; not updating ${LAST_CHANGE_FILE}"
fi
@@ -1,6 +1,7 @@
#!/bin/bash
# Setup script for Amazon Linux 2023. May need to update macro definitions for the archive IP.
# Setup script for Amazon Linux 2023. Use 50g root partition.
# May need to update macro definitions for the archive IP.
# Parameter 1 is the IP and parameter 2 is the bearer token to be installed in the root home dir.
# Ensure that the security for requesting the metadata uses IMDSv1
if [[ "$#" -ne 2 ]]; then
@@ -17,7 +17,7 @@
# produce false matches for the "main-" prefix.
BEARER_TOKEN="${1}"
RELEASE_NAME_PREFIX="${2}"
RELEASE_TAR_GZ_FILE_NAME=$( `dirname "${0}"`/github-download-release-assets.sh "${BEARER_TOKEN}" "${RELEASE_NAME_PREFIX}" )
RELEASE_TAR_GZ_FILE_NAME=$( `dirname "${0}"`/github-download-release-assets.sh "${BEARER_TOKEN}" "${RELEASE_NAME_PREFIX}" SAP/sailing-analytics )
if [ "${RELEASE_TAR_GZ_FILE_NAME}" != "" ]; then
RELEASE_NAME=$( echo ${RELEASE_TAR_GZ_FILE_NAME} | sed -e 's/^\(.*\)-\([0-9]*\).tar.gz$/\1/' )
RELEASE_TIMESTAMP=$( echo ${RELEASE_TAR_GZ_FILE_NAME} | sed -e 's/^\(.*\)-\([0-9]*\).tar.gz$/\2/' )
@@ -5,9 +5,9 @@
# always be 0.
#
# Usage:
# ./github-download-release-assets.sh {BEARER_TOKEN} {release-name-prefix}
# ./github-download-release-assets.sh {BEARER_TOKEN} {release-name-prefix} {repository-name}
# For example:
# ./github-download-release-assets.sh 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 main-
# ./github-download-release-assets.sh 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 main- SAP/sailing-analytics
# which will download the latest release tar.gz and release-notes.txt of the main branch (main-xxxxxxxxxxx).
# Note the "-" at the end of the "main-" prefix specifier; this way we're making name
# clashes with releases whose name happens to start with "main" unlikely. This
@@ -15,7 +15,8 @@
# produce false matches for the "main-" prefix.
BEARER_TOKEN="${1}"
RELEASE_NAME_PREFIX="${2}"
RELEASES=$( curl -L -H 'Authorization: Bearer '${BEARER_TOKEN} https://api.github.com/repos/SAP/sailing-analytics/releases 2>/dev/null )
GITHUB_REPOSITORY="${3}"
RELEASES=$( curl -L -H 'Authorization: Bearer '${BEARER_TOKEN} https://api.github.com/repos/${GITHUB_REPOSITORY}/releases 2>/dev/null )
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)
if [ "$?" -ne "0" ]; then
echo "No release with prefix ${RELEASE_NAME_PREFIX} found. Not trying to download/upload anything." >&2
@@ -26,7 +27,7 @@ else
RELEASE_TIMESTAMP=$( echo ${RELEASE_FULL_NAME} | sed -e 's/^\(.*\)-\([0-9]*\)$/\2/' )
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
RELEASE_TAR_GZ_FILE_NAME="${RELEASE_FULL_NAME}.tar.gz"
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}
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}
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}
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}
echo "${RELEASE_TAR_GZ_FILE_NAME}"
fi
@@ -12,12 +12,13 @@
# an exit status of 2 is returned.
BRANCH="${1}"
BEARER_TOKEN="${2}"
GITHUB_REPOSITORY="${3}"
UNIX_TIME=$( date +%s )
UNIX_DATE=$( date --iso-8601=second )
UNIX_TIME_YESTERDAY=$(( UNIX_TIME - 10*24*3600 )) # look back ten days in time, trying to catch even re-runs of older jobs
DATE_YESTERDAY=$( date --iso-8601=second -d @${UNIX_TIME_YESTERDAY} )
HEADERS_FILE=$( mktemp headersXXXXX )
NEXT_PAGE="https://api.github.com/repos/SAP/sailing-analytics/actions/runs?created=${DATE_YESTERDAY/+/%2B}..${UNIX_DATE/+/%2B}&per_page=100"
NEXT_PAGE="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/runs?created=${DATE_YESTERDAY/+/%2B}..${UNIX_DATE/+/%2B}&per_page=100"
ARTIFACTS_JSON=""
LATEST_RUN_STARTED_AT="0000-00-00T00:00:00Z"
# 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
+9 -3
View File
@@ -1,5 +1,11 @@
#!/bin/bash
release_prefix=$1
release_prefix="$1"
GITHUB_REPOSITORY="$2"
OWNER=${GITHUB_REPOSITORY%%/*}
OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')
REPO=${GITHUB_REPOSITORY##*/}
REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]')
PACKAGE="$OWNER_LOWER/$REPO_LOWER"
GITROOT="`dirname $0`/.."
DOCKERDIR="${GITROOT}/docker"
DOCKERFILE="$DOCKERDIR/Dockerfile"
@@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then
release_prefix="main-"
fi
pushd "${DOCKERDIR}"
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" )
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" )
if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then
echo "No release with prefix ${release_prefix} found" >&2
else
@@ -20,7 +26,7 @@ else
cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR"
cd "$DOCKERDIR"
docker buildx create --name=container --driver=docker-container --use --bootstrap
docker buildx build --builder=container --platform=linux/amd64,linux/arm64 --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} --push .
docker buildx build --builder=container --platform=linux/amd64,linux/arm64 --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} --push .
echo "Cleaning up..."
rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt
fi
+12 -6
View File
@@ -1,5 +1,11 @@
#!/bin/bash
release_prefix=$1
release_prefix="$1"
GITHUB_REPOSITORY="$2"
OWNER=${GITHUB_REPOSITORY%%/*}
OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')
REPO=${GITHUB_REPOSITORY##*/}
REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]')
PACKAGE="$OWNER_LOWER/$REPO_LOWER"
GITROOT="`dirname $0`/.."
DOCKERDIR="${GITROOT}/docker"
DOCKERFILE="$DOCKERDIR/Dockerfile"
@@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then
release_prefix="main-"
fi
pushd "${DOCKERDIR}"
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" )
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" )
if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then
echo "No release with prefix ${release_prefix} found" >&2
else
@@ -19,13 +25,13 @@ else
cp "$GITROOT/java/target/start" "$DOCKERDIR"
cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR"
cd "$DOCKERDIR"
docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release}-arm .
docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release}-arm .
echo "Cleaning up..."
rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt
docker push ghcr.io/sap/sailing-analytics:${release}-arm
docker push ghcr.io/${PACKAGE}:${release}-arm
if [ "$SET_LATEST" = "1" ]; then
docker tag ghcr.io/sap/sailing-analytics:${release}-arm ghcr.io/sap/sailing-analytics:latest-arm
docker push ghcr.io/sap/sailing-analytics:latest-arm
docker tag ghcr.io/${PACKAGE}:${release}-arm ghcr.io/${PACKAGE}:latest-arm
docker push ghcr.io/${PACKAGE}:latest-arm
fi
fi
popd
@@ -1,5 +1,11 @@
#!/bin/bash
release_prefix=$1
release_prefix="$1"
GITHUB_REPOSITORY="$2"
OWNER=${GITHUB_REPOSITORY%%/*}
OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')
REPO=${GITHUB_REPOSITORY##*/}
REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]')
PACKAGE="$OWNER_LOWER/$REPO_LOWER"
GITROOT="`dirname $0`/.."
DOCKERDIR="${GITROOT}/docker"
DOCKERFILE="$DOCKERDIR/Dockerfile"
@@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then
release_prefix="docker-11-"
fi
pushd "${DOCKERDIR}"
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" )
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" )
if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then
echo "No release with prefix ${release_prefix} found" >&2
else
@@ -19,13 +25,13 @@ else
cp "$GITROOT/java/target/start" "$DOCKERDIR"
cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR"
cd "$DOCKERDIR"
docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} -f Dockerfile_sapsailing_on_sapmachine11 .
docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} -f Dockerfile_sapsailing_on_sapmachine11 .
echo "Cleaning up..."
rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt
docker push ghcr.io/sap/sailing-analytics:${release}
docker push ghcr.io/${PACKAGE}:${release}
if [ "$SET_LATEST" = "1" ]; then
docker tag ghcr.io/sap/sailing-analytics:${release} ghcr.io/sap/sailing-analytics:latest-11
docker push ghcr.io/sap/sailing-analytics:latest-11
docker tag ghcr.io/${PACKAGE}:${release} ghcr.io/${PACKAGE}:latest-11
docker push ghcr.io/${PACKAGE}:latest-11
fi
fi
popd
@@ -1,5 +1,11 @@
#!/bin/bash
release_prefix=$1
release_prefix="$1"
GITHUB_REPOSITORY="$2"
OWNER=${GITHUB_REPOSITORY%%/*}
OWNER_LOWER=$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')
REPO=${GITHUB_REPOSITORY##*/}
REPO_LOWER=$(echo "$REPO" | tr '[:upper:]' '[:lower:]')
PACKAGE="$OWNER_LOWER/$REPO_LOWER"
GITROOT="`dirname $0`/.."
DOCKERDIR="${GITROOT}/docker"
DOCKERFILE="$DOCKERDIR/Dockerfile"
@@ -8,7 +14,7 @@ if [ "${release_prefix}" = "" ]; then
release_prefix="docker-17-"
fi
pushd "${DOCKERDIR}"
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" )
RELEASE_TAR_GZ_FILENAME=$( ${GITROOT}/configuration/github-download-release-assets.sh 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 "${release_prefix}" "${GITHUB_REPOSITORY}" )
if [ "${RELEASE_TAR_GZ_FILENAME}" = "" ]; then
echo "No release with prefix ${release_prefix} found" >&2
else
@@ -19,13 +25,13 @@ else
cp "$GITROOT/java/target/start" "$DOCKERDIR"
cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR"
cd "$DOCKERDIR"
docker build --build-arg RELEASE=${release} -t ghcr.io/sap/sailing-analytics:${release} -f Dockerfile_sapsailing_on_sapmachine17 .
docker build --build-arg RELEASE=${release} -t ghcr.io/${PACKAGE}:${release} -f Dockerfile_sapsailing_on_sapmachine17 .
echo "Cleaning up..."
rm start env.sh JavaSE-11.profile ${RELEASE_TAR_GZ_FILENAME} release-notes.txt
docker push ghcr.io/sap/sailing-analytics:${release}
docker push ghcr.io/${PACKAGE}:${release}
if [ "$SET_LATEST" = "1" ]; then
docker tag ghcr.io/sap/sailing-analytics:${release} ghcr.io/sap/sailing-analytics:latest-17
docker push ghcr.io/sap/sailing-analytics:latest-17
docker tag ghcr.io/${PACKAGE}:${release} ghcr.io/${PACKAGE}:latest-17
docker push ghcr.io/${PACKAGE}:latest-17
fi
fi
popd
@@ -3,3 +3,4 @@
/build.properties
/.classpath
/META-INF/
/target/
@@ -10,9 +10,7 @@ Automatic-Module-Name: com.sap.sailing.aiagent
Import-Package: com.sap.sailing.aiagent.interfaces,
com.sap.sailing.domain.abstractlog.race,
com.sap.sailing.domain.abstractlog.regatta,
com.sap.sailing.domain.leaderboard,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl,
com.sap.sailing.domain.shared.tracking,
com.sap.sse.common,
com.sap.sse.concurrent,
com.sap.sse.security.shared,
@@ -46,7 +46,7 @@ import com.sap.sse.shared.util.WeakValueCache;
public class AIAgentImpl implements AIAgent {
private static final Logger logger = Logger.getLogger(AIAgentImpl.class.getName());
private static final String DEFAULT_MODEL_NAME = "gpt-4o-mini";
private static final String DEFAULT_MODEL_NAME = "o4-mini";
private static final String SAP_AI_CORE_TAG = "SAP AI Core on %s";
@@ -182,7 +182,7 @@ public class AIAgentImpl implements AIAgent {
chatSession
.addSystemPrompt(systemPrompt)
.addPrompt(prompt)
.setTemperature(0.2)
.setTemperature(1.0)
.submit(response->{
try {
getRacingEventService().getTaggingService().addTag(leaderboardName, raceColumnName, fleetName,
@@ -27,7 +27,7 @@ import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.AddResult;
import com.sap.sailing.domain.shared.tracking.AddResult;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceChangeListener;
@@ -22,7 +22,7 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -15,8 +15,8 @@ import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
@@ -29,15 +29,15 @@ import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
@@ -34,8 +34,8 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.geocoding.ReverseGeocoder;
@@ -1,9 +1,11 @@
package com.sap.sailing.declination.test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import com.sap.sailing.declination.impl.NOAAImporterForTesting;
@Disabled("US Government Shutdown around 2025-10-01")
public class NOAADeclinationImportTest extends DeclinationImportTest<NOAAImporterForTesting> {
@BeforeEach
public void setUp() {
@@ -1,9 +1,11 @@
package com.sap.sailing.declination.test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import com.sap.sailing.declination.impl.NOAAImporter;
@Disabled("US Government Shutdown around 2025-10-01")
public class NOAADeclinationServiceTest extends DeclinationServiceTest<NOAAImporter> {
@Override
@BeforeEach
@@ -31,6 +31,7 @@ public enum BoatClassMasterdata {
BAVARIA_CRUISER_46 ("Bavaria Cruiser 46", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B46", "B 46", "BAVARIACRUISER46"),
BB10M ("BB 10m", true, 10.00, 2.30, BoatHullType.MONOHULL, true, "Dansk BB10M klub"),
BENETEAU_FIRST_35 ("Benetau First 35", true, 10.66, 3.636, BoatHullType.MONOHULL, true, "First 35"),
BENETEAU_FIRST_36 ("Benetau First 36", true, 11.98, 3.80, BoatHullType.MONOHULL, true, "First 36"),
BENETEAU_FIRST_45 ("Benetau First 45", true, 13.68, 4.202, BoatHullType.MONOHULL, true, "First 45"),
BRASSFAHRT_I ("Brassfahrt I", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 1"),
BRASSFAHRT_II ("Brassfahrt II", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 2"),
@@ -49,6 +50,7 @@ public enum BoatClassMasterdata {
DELPHIA_24 ("Delphia 24", true, 7.70, 2.50, BoatHullType.MONOHULL, true, "Delphia 24 One Design", "Delphia 24 OD"),
DYAS("Dyas", true, 7.15, 1.95, BoatHullType.MONOHULL, true),
ELAN350("Elan 350", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan 350 Performance"),
ELAN_E4("Elan E4", true, 10.6, 3.5, BoatHullType.MONOHULL, true, "Elan E4"),
EXTREME_40 ("Extreme 40", false, 12.2, 6.60, BoatHullType.CATAMARAN, true, "Extreme-40", "Extreme40", "ESS40", "ess"),
D_35 ("D35", false, 10.81, 6.89, BoatHullType.CATAMARAN, false),
ELLIOTT_6M ("Elliott 6m", true, 6.0, 2.35, BoatHullType.MONOHULL, true, "Elliott6m"),
@@ -96,8 +96,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final Document query = new Document();
appendMultiTimeRangeQuery(query, timeRanges);
query.append(FieldNames.IGTIMI_MESSAGES_DEVICE_SERIAL_NUMBER.name(), deviceSerialNumber);
final Iterable<Document> queryResult = clientSessionOrNull == null ? messagesCollection.find(query)
: messagesCollection.find(clientSessionOrNull, query);
final Iterable<Document> queryResult = (clientSessionOrNull == null ? messagesCollection.find(query)
: messagesCollection.find(clientSessionOrNull, query)).sort(Sorts.ascending(FieldNames.IGTIMI_MESSAGES_TIMESTAMP.name()));
result = Util.filter(Util.map(queryResult,
doc->{
try {
@@ -53,9 +53,9 @@ import com.sap.sse.replication.Replicable;
* can be registered with this server using the {@link #addListener(BulkFixReceiver)} method.
* <p>
*
* When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver},
* When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver}),
* {@link IgtimiWindListener}s can be registered on the wind receiver, just as they can
* for a websocket connection.
* for a websocket connection.<p>
*
* The server uses <tt>java.nio</tt> and {@link ServerSocketChannel}s, avoiding the creation
* of a thread per connection.<p>
@@ -337,24 +337,26 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
// obtain the securityService only if connections exist; otherwise, we may still be in start-up mode
final SecurityService securityService = liveWebSocketConnections.isEmpty() ? null : getSecurityService();
for (final RiotWebsocketHandler webSocketClient : liveWebSocketConnections) {
final User user = webSocketClient.getAuthenticatedUser();
final OwnershipAnnotation deviceOwnership = securityService.getOwnership(device.getIdentifier());
final AccessControlListAnnotation deviceAccessControlList = securityService.getAccessControlList(device.getIdentifier());
if (!Util.isEmpty(Util.filter(daws, daw->{
final OwnershipAnnotation dawOownership = securityService.getOwnership(daw.getIdentifier());
final AccessControlListAnnotation dawAccessControlList = securityService.getAccessControlList(daw.getIdentifier());
return PermissionChecker.isPermitted(
daw.getIdentifier().getPermission(DefaultActions.READ),
if (webSocketClient.getDeviceSerialNumbers().contains(deviceSerialNumber)) {
final User user = webSocketClient.getAuthenticatedUser();
final OwnershipAnnotation deviceOwnership = securityService.getOwnership(device.getIdentifier());
final AccessControlListAnnotation deviceAccessControlList = securityService.getAccessControlList(device.getIdentifier());
if (!Util.isEmpty(Util.filter(daws, daw->{
final OwnershipAnnotation dawOownership = securityService.getOwnership(daw.getIdentifier());
final AccessControlListAnnotation dawAccessControlList = securityService.getAccessControlList(daw.getIdentifier());
return PermissionChecker.isPermitted(
daw.getIdentifier().getPermission(DefaultActions.READ),
user, securityService.getAllUser(),
dawOownership==null?null:dawOownership.getAnnotation(),
dawAccessControlList==null?null:dawAccessControlList.getAnnotation());
}))
&& PermissionChecker.isPermitted(
device.getIdentifier().getPermission(DefaultActions.READ),
user, securityService.getAllUser(),
dawOownership==null?null:dawOownership.getAnnotation(),
dawAccessControlList==null?null:dawAccessControlList.getAnnotation());
}))
&& PermissionChecker.isPermitted(
device.getIdentifier().getPermission(DefaultActions.READ),
user, securityService.getAllUser(),
deviceOwnership==null?null:deviceOwnership.getAnnotation(),
deviceAccessControlList==null?null:deviceAccessControlList.getAnnotation()) ) {
webSocketClient.sendBytesByFuture(ByteBuffer.wrap(messageAsBytes));
deviceOwnership==null?null:deviceOwnership.getAnnotation(),
deviceAccessControlList==null?null:deviceAccessControlList.getAnnotation()) ) {
webSocketClient.sendBytesByFuture(ByteBuffer.wrap(messageAsBytes));
}
}
}
}
@@ -23,8 +23,8 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.DegreeBearingImpl;
@@ -13,9 +13,9 @@ import org.json.simple.parser.ParseException;
import com.igtimi.IgtimiStream.Msg;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.security.shared.HasPermissions;
@@ -188,8 +188,9 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
}
/**
* 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}
* fix, if any was found, although this did technically not contribute to the production of the {@link Wind} object.
* Returns a wind fix produced out of an AWS/AWA and other fixes, as well as the fixes used for this (in case of
* interpolation between two fixes, the older one of the two is returned), including a {@link BatteryLevel} fix, if
* any was found, although this did technically not contribute to the production of the {@link Wind} object.
*/
private Pair<Wind, Set<Fix>> getWind(final TimePoint timePoint, String deviceSerialNumber) throws ClassNotFoundException, IOException, ParseException {
final Wind result;
@@ -213,7 +214,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
final SpeedWithBearing apparentWindSpeedWithDirection = new KnotSpeedWithBearingImpl(aws.getKnots(), apparentWindDirection);
/*
* Hint from Brent Russell from Igtimi, at 2013-12-05 on the question whether to use GpsLatLong to
* improve precision of boat speed / coarse over SOG/COG measurements:
* improve precision of boat speed / course over SOG/COG measurements:
*
* "Personally I would use COG/SOG exclusively, and if unhappy with the result add a small amount of
* smoothing and consider dropping samples as outliers if they cause a SOG discontinuity. The latter
@@ -260,7 +261,11 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
/**
* Searches for the last fix at or before {@code timePoint} in the given {@link trackToCleanUp}. If such a fix is
* found, adds the given fix to {@code fixesUsed} and clears all fixes from the {@code trackToCleanUp} that are
* earlier than or at the {@code timePoint} given.
* earlier than the {@code timePoint} given. This in particular preserves at least one live fix which will "hide"
* older, e.g., buffered fixes that may be received when a WindBot device sends them out-of-order. For old, buffered
* fixes this may still mean that could be dropped prior to being converted into a {@link Wind} object with an older
* time stamp, but usually---and particularly with buffered data from previous days---this shouldn't be a problem.
* Also, old data can still be imported at a later point in time.
* <p>
*
* 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 {
final FixType fix = trackToCleanUp.getLastFixAtOrBefore(timePoint);
if (fix != null) {
fixesUsed.add(fix);
trackToCleanUp.removeAllUpToAndIncluding(fix);
trackToCleanUp.removeAllUpToExcluding(fix);
}
}
@@ -45,8 +45,8 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServi
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
@@ -68,10 +68,10 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
@@ -88,13 +88,13 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -38,7 +38,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneImeiIdentifierImpl;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sailing.domain.tracking.AddResult;
import com.sap.sailing.domain.shared.tracking.AddResult;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSTrackListener;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -64,6 +64,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.AbstractRaceTrackerBaseImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -73,7 +74,6 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -44,19 +44,19 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.OutlierFilter;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.MultiTimeRange;
@@ -55,9 +55,9 @@ Export-Package: com.sap.sailing.domain.abstractlog,
com.sap.sailing.domain.racelogtracking,
com.sap.sailing.domain.racelogtracking.impl,
com.sap.sailing.domain.resultimport,
com.sap.sailing.domain.shared.tracking,
com.sap.sailing.domain.shared.tracking.impl,
com.sap.sailing.domain.statistics,
com.sap.sailing.domain.statistics.impl,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl
com.sap.sailing.domain.statistics.impl
Automatic-Module-Name: com.sap.sailing.domain.shared.android
Import-Package: com.sap.sse.security.shared
@@ -7,7 +7,7 @@ import java.util.NavigableSet;
import java.util.UUID;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
@@ -24,9 +24,9 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventComparator;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView;
import com.sap.sailing.domain.tracking.impl.TrackImpl;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView;
import com.sap.sailing.domain.shared.tracking.impl.TrackImpl;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
import com.sap.sse.shared.util.impl.ArrayListNavigableSet;
@@ -6,7 +6,7 @@ import java.util.Comparator;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
@@ -7,7 +7,7 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.shared.tracking.Track;
public abstract class RaceLogAnalyzer<ResultType> extends BaseLogAnalyzer
<RaceLog, RaceLogEvent, RaceLogEventVisitor, ResultType> {
@@ -16,7 +16,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.scalablevalue.ScalableValue;
@@ -273,8 +273,8 @@ public class NoAddingRaceLogWrapper implements RaceLog {
@Override
public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement,
com.sap.sailing.domain.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache,
com.sap.sailing.domain.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) {
com.sap.sailing.domain.shared.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache,
com.sap.sailing.domain.shared.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) {
return innerRaceLog.getValueSum(from, to, nullElement, adder, cache, valueCalculator);
}
}
@@ -8,8 +8,8 @@ import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.impl.TrackImpl;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sailing.domain.shared.tracking.impl.TrackImpl;
/**
* {@link Track} implementation for {@link RaceLogEvent}s.
@@ -5,7 +5,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sse.common.NamedWithID;
import com.sap.sse.common.Renamable;
import com.sap.sse.common.TimePoint;
@@ -6,7 +6,7 @@ import java.util.UUID;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sse.common.TimePoint;
/**
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
import javax.swing.plaf.basic.BasicSliderUI.TrackListener;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
/**
* A predicate for a fix, for use in
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
import com.sap.sse.common.Timed;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
import java.io.Serializable;
import java.util.Iterator;
@@ -6,7 +6,7 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.function.Function;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
@@ -1,5 +1,5 @@
package com.sap.sailing.domain.tracking;
package com.sap.sailing.domain.shared.tracking;
import java.io.Serializable;
@@ -1,10 +1,10 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.TimePoint;
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import com.sap.sailing.domain.tracking.MappedTrack;
import com.sap.sailing.domain.shared.tracking.MappedTrack;
import com.sap.sse.common.Timed;
import com.sap.sse.shared.util.impl.ArrayListNavigableSet;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import java.util.ArrayList;
import java.util.Collection;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import java.util.Comparator;
import java.util.Iterator;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import java.io.Serializable;
import java.util.Comparator;
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import java.io.IOException;
import java.io.ObjectOutputStream;
@@ -7,9 +7,9 @@ import java.util.Iterator;
import java.util.NavigableSet;
import java.util.function.Function;
import com.sap.sailing.domain.tracking.AddResult;
import com.sap.sailing.domain.tracking.FixAcceptancePredicate;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.shared.tracking.AddResult;
import com.sap.sailing.domain.shared.tracking.FixAcceptancePredicate;
import com.sap.sailing.domain.shared.tracking.Track;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
@@ -292,6 +292,11 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
return result;
}
/**
* Calculates a linear interpolation of values based on their time points and a target time point that is expected
* to be in between (inclusive) the two time points for the two values. If the two time points for the two values
* are equal, the average of the two values is returned.
*/
private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) {
final T acc;
if (timePoint1.equals(timePoint2)) {
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.tracking.impl;
package com.sap.sailing.domain.shared.tracking.impl;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
public class TrackingConnectorInfoImpl implements TrackingConnectorInfo {
private static final long serialVersionUID = 7970268841592389145L;
@@ -56,9 +56,7 @@ public class EditCAM extends javax.swing.JDialog {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@SuppressWarnings("serial")
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jRaceID = new javax.swing.JTextField();
jSeparator1 = new javax.swing.JSeparator();
@@ -73,47 +71,35 @@ public class EditCAM extends javax.swing.JDialog {
jButton3 = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
jMarkList = new javax.swing.JList<ClockAtMarkElement>();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jLabel1.setText("Race ID:");
jRaceID.setText("jTextField1");
jLabel2.setText("Mark index:");
jMarkIndex.setText("0");
jLabel3.setText("Mark time:");
jMarkTime.setText("12:34:17");
jLabel4.setText("Sail number:");
jSailNumber.setText("w123");
jButton1.setText("Ok");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("Remove");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText("Add");
jButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
jMarkList.setModel(new javax.swing.AbstractListModel<ClockAtMarkElement>() {
private static final long serialVersionUID = 1L;
ClockAtMarkElement[] clockAtMarkElements = { new ClockAtMarkElement(1, new Date(), "Item 1"),
new ClockAtMarkElement(1, new Date(), "Item 2"), new ClockAtMarkElement(1, new Date(), "Item 3"),
new ClockAtMarkElement(1, new Date(), "Item 4"), new ClockAtMarkElement(1, new Date(), "Item 5") };
@@ -51,9 +51,7 @@ public class EditCCG extends javax.swing.JDialog {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@SuppressWarnings("serial")
private void initComponents() {
jBuoyType = new javax.swing.ButtonGroup();
jLabel1 = new javax.swing.JLabel();
jRaceID = new javax.swing.JTextField();
@@ -130,6 +128,7 @@ public class EditCCG extends javax.swing.JDialog {
});
jMarkList.setModel(new javax.swing.AbstractListModel<CCGMessage>() {
private static final long serialVersionUID = 1L;
CCGMessage[] ccgMessages = { new CCGMessage("Item 1", new ArrayList<Mark>()),
new CCGMessage("Item 2", new ArrayList<Mark>()),
new CCGMessage("Item 3", new ArrayList<Mark>()),
@@ -49,9 +49,7 @@ public class EditSTL extends javax.swing.JDialog {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@SuppressWarnings("serial")
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jRaceID = new javax.swing.JTextField();
jSeparator1 = new javax.swing.JSeparator();
@@ -66,48 +64,36 @@ public class EditSTL extends javax.swing.JDialog {
jButton2 = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
jCompetitorList = new javax.swing.JList<Competitor>();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jLabel1.setText("Race ID:");
jRaceID.setText("jTextField1");
jLabel2.setText("Sail Number:");
jSailNumber.setText("jTextField1");
jLabel3.setText("NOC:");
jNOC.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "AUS", "FRA", "GBR", "GER", "ITA", "USA" }));
jNOC.setSelectedIndex(2);
jLabel4.setText("Name:");
jName.setText("jTextField1");
jButton1.setText("Add");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jRemove.setText("Remove");
jRemove.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jRemoveActionPerformed(evt);
}
});
jButton2.setText("Ok");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jCompetitorList.setModel(new javax.swing.AbstractListModel<Competitor>() {
private static final long serialVersionUID = 1L;
Competitor[] competitors = { new CompetitorWithoutID("Item 1", "DEU", "Item 1"),
new CompetitorWithoutID("Item 2", "DEU", "Item 2"),
new CompetitorWithoutID("Item 3", "DEU", "Item 3"),
@@ -117,7 +103,6 @@ public class EditSTL extends javax.swing.JDialog {
public Competitor getElementAt(int i) { return competitors[i]; }
});
jScrollPane1.setViewportView(jCompetitorList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@@ -54,9 +54,7 @@ public class EditTMD extends javax.swing.JDialog {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
@SuppressWarnings("serial")
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jRaceId = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
@@ -73,51 +71,37 @@ public class EditTMD extends javax.swing.JDialog {
jButton3 = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
jTmdData = new javax.swing.JList<TimingDataElement>();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jLabel1.setText("Race ID:");
jRaceId.setText("jTextField1");
jLabel2.setText("Sail number:");
jSailNumber.setText("jTextField2");
jLabel3.setText("Mark index:");
jMarkIndex.setText("1");
jLabel4.setText("Rank:");
jRank.setText("1");
jLabel5.setText("Time since start:");
jTimeSinceStart.setText("00:12:14");
jButton1.setText("Remove");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("Add");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText("Ok");
jButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
jTmdData.setModel(new javax.swing.AbstractListModel<TimingDataElement>() {
private static final long serialVersionUID = 1L;
TimingDataElement[] strings = { new TimingDataElement(1, 1, new Date()),
new TimingDataElement(2, 2, new Date()),
new TimingDataElement(3, 3, new Date()),
@@ -127,7 +111,6 @@ public class EditTMD extends javax.swing.JDialog {
public TimingDataElement getElementAt(int i) { return strings[i]; }
});
jScrollPane1.setViewportView(jTmdData);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@@ -70,10 +70,8 @@ public class SwissTimingRaceEditor extends javax.swing.JFrame {
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings({ "serial" })
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jCommandList = new javax.swing.JList<Object>();
jAdd = new javax.swing.JButton();
@@ -86,54 +84,46 @@ public class SwissTimingRaceEditor extends javax.swing.JFrame {
jImport = new javax.swing.JMenuItem();
jExport = new javax.swing.JMenuItem();
jMenuItem3 = new javax.swing.JMenuItem();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("SwissTiming-Sample-Race Editor");
jCommandList.setModel(new javax.swing.AbstractListModel<Object>() {
private static final long serialVersionUID = 1L;
String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
jScrollPane1.setViewportView(jCommandList);
jAdd.setText("Add");
jAdd.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jAddActionPerformed(evt);
}
});
jRemove.setText("Remove");
jRemove.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jRemoveActionPerformed(evt);
}
});
jEdit.setText("Edit");
jEdit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jEditActionPerformed(evt);
}
});
jMoveUp.setText("Up");
jMoveUp.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMoveUpActionPerformed(evt);
}
});
jMoveDown.setText("Down");
jMoveDown.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMoveDownActionPerformed(evt);
}
});
jMenu1.setText("File");
jImport.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_I, java.awt.event.InputEvent.CTRL_DOWN_MASK));
jImport.setText("Import");
jImport.addActionListener(new java.awt.event.ActionListener() {
@@ -142,7 +132,6 @@ public class SwissTimingRaceEditor extends javax.swing.JFrame {
}
});
jMenu1.add(jImport);
jExport.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_DOWN_MASK));
jExport.setText("Export");
jExport.addActionListener(new java.awt.event.ActionListener() {
@@ -151,7 +140,6 @@ public class SwissTimingRaceEditor extends javax.swing.JFrame {
}
});
jMenu1.add(jExport);
jMenuItem3.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ESCAPE, 0));
jMenuItem3.setText("Exit");
jMenuItem3.addActionListener(new java.awt.event.ActionListener() {
@@ -160,11 +148,8 @@ public class SwissTimingRaceEditor extends javax.swing.JFrame {
}
});
jMenu1.add(jMenuItem3);
jMenuBar1.add(jMenu1);
setJMenuBar(jMenuBar1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.swisstimingadapter.Course;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.Fix;
@@ -70,7 +71,6 @@ import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.impl.UpdateHandler;
import com.sap.sse.common.Distance;
import com.sap.sse.common.TimePoint;
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus;
@@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test"/>
</listAttribute>
@@ -10,7 +11,7 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src=/test=/true=/&lt;com.sap.sailing.domain.test"/>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src&lt;com.sap.sailing.domain.test"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.markpassingcalculation.Candidate;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -25,11 +25,11 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Distance;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.DegreeBearingImpl;
@@ -15,7 +15,7 @@ import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.AddResult;
import com.sap.sailing.domain.shared.tracking.AddResult;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -1,8 +1,9 @@
package com.sap.sailing.domain.test;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
import com.sap.sailing.domain.test.LeaderboardCourseChangeTest;
public class LeaderboardCourseChangeWithEliminationTest extends LeaderboardCourseChangeTest {
@Override
@@ -74,12 +74,12 @@ import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -2918,16 +2918,10 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
assertTrue(Util.indexOf(rankedCompetitors, lastRaceGoldParticipant) < Util.indexOf(rankedCompetitors, silverParticipant));
}
}
// assert that theUntrackedCompetitorInLastRace ended up between the last race's silver and gold fleet participants
// based on the "extreme fleet" rule:
// assert that theUntrackedCompetitorInLastRace ended up ranked worse than all participants of ranked fleets:
for (final Competitor c : competitors) {
if (c != theUntrackedCompetitorInLastRace) {
if (lastRaceGold.contains(c) || medal.contains(c)) {
assertTrue(Util.indexOf(rankedCompetitors, c) < Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace));
} else {
assertTrue(silver.contains(c));
assertTrue(Util.indexOf(rankedCompetitors, c) > Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace));
}
assertTrue(Util.indexOf(rankedCompetitors, c) < Util.indexOf(rankedCompetitors, theUntrackedCompetitorInLastRace));
}
}
}
@@ -32,12 +32,12 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -14,9 +14,9 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.TimePoint;
@@ -149,12 +149,9 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest i
case Begin:
logger.info("Stored data begin");
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0);
new Thread(()->{
final RaceDefinition raceDefinition = domainFactory.getAndWaitForRaceDefinition(getTracTracRace().getId(), /* timeout in millis */ 10000);
if (trackedRegatta != null && raceDefinition != null && trackedRegatta.getTrackedRace(raceDefinition) != null) {
trackedRegatta.getTrackedRace(raceDefinition).onStatusChanged(OnlineTracTracBasedTest.this, lastStatus);
}
}, "waiting for race definition for race "+getTracTracRace().getId()).start();
if (getTrackedRace() != null) {
getTrackedRace().onStatusChanged(OnlineTracTracBasedTest.this, lastStatus);
}
break;
case End:
logger.info("Stored data end. Delaying status update on tracked race "+getTrackedRace()+" until all events queued in receivers so far have been processed");
@@ -12,7 +12,7 @@ import java.util.TreeSet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView;
import com.sap.sailing.domain.shared.tracking.impl.PartialNavigableSetView;
public class PartialNavigableSetViewTest {
private PartialNavigableSetView<Integer> fullSet;
@@ -47,12 +47,11 @@ public class PassingInstructionParserTest extends OnlineTracTracBasedTest {
@Test
public void test() {
int i = 0;
for(Waypoint w : getRace().getCourse().getWaypoints()){
if(w.getPassingInstructions()==PassingInstruction.Starboard){
for (Waypoint w : getRace().getCourse().getWaypoints()) {
if (w.getPassingInstructions() == PassingInstruction.Starboard) {
i++;
}
}
assertEquals(2,i);
assertEquals(2, i);
}
}
@@ -20,7 +20,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.AddResult;
import com.sap.sailing.domain.shared.tracking.AddResult;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -0,0 +1,112 @@
package com.sap.sailing.domain.test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
/**
* See bug6168; tests fleet-specific check for score corrections in race columns that suggest that this
* fleet has finished the race.
*
* @author Axel Uhl (d043530)
*
*/
public class ScoreCorrectionForUntrackedRacesTest extends StoredTrackBasedTest {
private RaceColumnInSeries f1;
private RaceColumnInSeries f2;
private Series series;
private Fleet gold;
private Fleet silver;
private RegattaLog regattaLog;
private RaceLog goldRaceLogF1;
private RaceLog goldRaceLogF2;
private RaceLog silverRaceLogF1;
private RaceLog silverRaceLogF2;
private SettableScoreCorrection scoreCorrection;
private CompetitorWithBoat c1;
private CompetitorWithBoat c2;
private AbstractLogEventAuthor author;
private Leaderboard leaderboard;
@BeforeEach
public void setUp() {
leaderboard = mock(Leaderboard.class);
c1 = createCompetitorWithBoat("C1");
c2 = createCompetitorWithBoat("C2");
author = new LogEventAuthorImpl("Test", 1);
gold = new FleetImpl("Gold", 1);
silver = new FleetImpl("Silver", 2);
series = new SeriesImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Arrays.asList(gold, silver), Arrays.asList("F1", "F2"), /* trackedRegattaRegistry */ null);
f1 = mock(RaceColumnInSeries.class);
when(f1.getName()).thenReturn("F1");
when(f1.getSeries()).thenReturn(series);
when(f1.getKey(c1)).thenReturn(new Pair<>(c1, f1));
when(f1.getKey(c2)).thenReturn(new Pair<>(c2, f1));
goldRaceLogF1 = new RaceLogImpl(UUID.randomUUID());
silverRaceLogF1 = new RaceLogImpl(UUID.randomUUID());
when(f1.getRaceLog(gold)).thenReturn(goldRaceLogF1);
when(f1.getRaceLog(silver)).thenReturn(silverRaceLogF1);
f2 = mock(RaceColumnInSeries.class);
when(f2.getName()).thenReturn("F2");
when(f2.getSeries()).thenReturn(series);
when(f2.getKey(c1)).thenReturn(new Pair<>(c1, f2));
when(f2.getKey(c2)).thenReturn(new Pair<>(c2, f2));
goldRaceLogF2 = new RaceLogImpl(UUID.randomUUID());
silverRaceLogF2 = new RaceLogImpl(UUID.randomUUID());
when(f2.getRaceLog(gold)).thenReturn(goldRaceLogF2);
when(f2.getRaceLog(silver)).thenReturn(silverRaceLogF2);
goldRaceLogF1.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0));
goldRaceLogF1.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c1));
goldRaceLogF2.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0));
goldRaceLogF2.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c1));
silverRaceLogF1.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0));
silverRaceLogF1.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c2));
silverRaceLogF2.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(TimePoint.now(), author, TimePoint.now(), UUID.randomUUID(), 0));
silverRaceLogF2.add(new RaceLogRegisterCompetitorEventImpl(TimePoint.now(), author, /* passId */ 0, c2));
regattaLog = new RegattaLogImpl(UUID.randomUUID());
when(f1.getAllCompetitors(gold)).thenReturn(new RegisteredCompetitorsAnalyzer(goldRaceLogF1, regattaLog).analyze());
when(f1.getAllCompetitors(silver)).thenReturn(new RegisteredCompetitorsAnalyzer(silverRaceLogF1, regattaLog).analyze());
when(f2.getAllCompetitors(gold)).thenReturn(new RegisteredCompetitorsAnalyzer(goldRaceLogF2, regattaLog).analyze());
when(f2.getAllCompetitors(silver)).thenReturn(new RegisteredCompetitorsAnalyzer(silverRaceLogF2, regattaLog).analyze());
scoreCorrection = new ScoreCorrectionImpl(leaderboard);
scoreCorrection.correctScore(c1, f1, 1.0);
scoreCorrection.correctScore(c1, f2, 1.0);
scoreCorrection.correctScore(c2, f1, 1.0);
}
@Test
public void testNumberOfRacesWithCorrections() {
assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f1, gold));
assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f1, silver));
assertTrue(scoreCorrection.hasCorrectionForNonTrackedFleet(f2, gold));
assertFalse(scoreCorrection.hasCorrectionForNonTrackedFleet(f2, silver));
}
}
@@ -26,8 +26,8 @@ import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -44,14 +44,14 @@ import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleExceptio
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.shared.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.shared.tracking.impl.TrackImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.MaxSpeedCache;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.tracking.impl.TrackImpl;
import com.sap.sse.common.AbstractBearing;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
@@ -156,7 +156,6 @@ public class TrackTest {
* {@link Timed} objects in ascending order, this method compares those results to the ordinary explicit calls
* to {@link GPSFixTrack#getEstimatedPosition(TimePoint, boolean)}.
*/
@SuppressWarnings("serial")
@Test
public void testGetEstimatedPositionSingleVsIteratedWithSmallerSteps() {
TimePoint start = gpsFix1.getTimePoint().minus((gpsFix5.getTimePoint().asMillis()-gpsFix1.getTimePoint().asMillis())/2);
@@ -164,13 +163,15 @@ public class TrackTest {
List<Timed> timeds = new ArrayList<>();
for (TimePoint t = start; !t.after(end); t = t.plus((gpsFix5.getTimePoint().asMillis()-gpsFix1.getTimePoint().asMillis())/10)) {
final TimePoint finalT = t;
timeds.add(new Timed() {public TimePoint getTimePoint() { return finalT; }});
timeds.add(new Timed() {
private static final long serialVersionUID = 7038806820707652754L;
public TimePoint getTimePoint() { return finalT; }
});
}
assertEqualEstimatedPositionsSingleVsIterated(timeds, /* extrapolate */ true);
assertEqualEstimatedPositionsSingleVsIterated(timeds, /* extrapolate */ false);
}
@SuppressWarnings("serial")
@Test
public void testGetEstimatedPositionSingleVsIteratedWithLargerSteps() {
TimePoint start = gpsFix1.getTimePoint().minus((gpsFix5.getTimePoint().asMillis()-gpsFix1.getTimePoint().asMillis())/2);
@@ -178,7 +179,10 @@ public class TrackTest {
List<Timed> timeds = new ArrayList<>();
for (TimePoint t = start; !t.after(end); t = t.plus(gpsFix5.getTimePoint().asMillis()-gpsFix1.getTimePoint().asMillis())) {
final TimePoint finalT = t;
timeds.add(new Timed() {public TimePoint getTimePoint() { return finalT; }});
timeds.add(new Timed() {
private static final long serialVersionUID = -6329517520161330872L;
public TimePoint getTimePoint() { return finalT; }
});
}
assertEqualEstimatedPositionsSingleVsIterated(timeds, /* extrapolate */ true);
assertEqualEstimatedPositionsSingleVsIterated(timeds, /* extrapolate */ false);
@@ -64,6 +64,8 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeListener;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
@@ -71,7 +73,6 @@ import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
@@ -86,7 +87,6 @@ import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
@@ -53,9 +53,10 @@ import com.sap.sailing.domain.leaderboard.impl.RankAndRankComparable;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.shared.tracking.LineDetails;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
@@ -69,7 +70,6 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sailing.domain.tracking.WindStore;
@@ -69,6 +69,8 @@ import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -78,13 +80,11 @@ import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingHandler;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.FinishTimeUpdateHandler;
import com.sap.sailing.domain.tracking.impl.RaceAbortedHandler;
import com.sap.sailing.domain.tracking.impl.StartTimeUpdateHandler;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.JSONService;
import com.sap.sailing.domain.tractracadapter.MetadataParser;
@@ -23,13 +23,13 @@ import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintRegistry;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTrackingHandler;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.TracTracAdapter;
import com.sap.sse.common.TimePoint;
@@ -7,7 +7,8 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sailing.domain.windfinderadapter.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sse.security,
Import-Package: com.sap.sailing.domain.shared.tracking,
com.sap.sse.security,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.5.1"
Bundle-ActivationPolicy: lazy
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.shared.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -60,7 +61,6 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRace;
import com.sap.sailing.domain.yellowbrickadapter.YellowBrickRaceTrackingConnectivityParams;
import com.sap.sailing.domain.yellowbrickadapter.YellowBrickTrackingAdapter;
@@ -24,8 +24,8 @@ import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.shared.tracking.TrackingConnectorInfo;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
import com.sap.sailing.geocoding.ReverseGeocoder;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.concurrent.Callable;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
@@ -121,8 +122,9 @@ public interface ScoreCorrection extends Serializable {
* for any competitor who in <code>raceInLeaderboard</code> is not in a tracked race and hence the fleet assignment
* cannot be determined. This is helpful, e.g., for progress detection. If score corrections are present for such
* untracked competitors then all untracked fleets need to be assumed as finished.
* @param fleet TODO
*/
boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard);
boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard, Fleet fleet);
/**
* @return all race columns for which this score corrections object has at least one correction; note that this
@@ -290,7 +290,6 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
}
}
}
// 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
int result = scoringScheme.compareByMedalRaceParticipation(zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, zeroBasedIndexOfLastMedalSeriesInWhichO2Scored);
if (result == 0) {
result = defaultFleetBasedComparisonResult;
@@ -482,29 +481,26 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
}
/**
* If the race column only has one fleet, no decision is made and 0 is returned. Otherwise, if <code>fleet</code> is the
* best fleet with others in the column being worse, return "better" (lesser; -1). If <code>fleet</code> is the worst fleet
* with others in the column being better, return "worse" (greater; 1). Otherwise, return 0.
* If the race column only has one fleet, no decision is made and 0 is returned. Otherwise, if there are other fleets with
* a {@link Fleet#getOrdering() rank} different from that of <code>fleet</code>, we want to sort competitors with no fleet
* assignment to the "worse" end of the leaderboard, therefore returning 1. Otherwise, 0 is returned.
*/
private int extremeFleetComparison(RaceColumn raceColumn, Fleet fleet) {
boolean allOthersAreGreater = true;
boolean allOthersAreLess = true;
boolean greaterFleetExists = false;
boolean lesserFleetExists = false;
boolean othersExist = false;
for (Fleet f : raceColumn.getFleets()) {
if (f != fleet) {
othersExist = true;
allOthersAreGreater = allOthersAreGreater && f.compareTo(fleet) > 0;
allOthersAreLess = allOthersAreLess && f.compareTo(fleet) < 0;
greaterFleetExists = greaterFleetExists || f.compareTo(fleet) > 0;
lesserFleetExists = lesserFleetExists || f.compareTo(fleet) < 0;
}
}
int result = 0;
if (othersExist) {
assert !(allOthersAreGreater && allOthersAreLess);
if (allOthersAreGreater) {
result = -1;
} else if (allOthersAreLess) {
result = 1;
}
final int result;
if (othersExist && (greaterFleetExists || lesserFleetExists)) {
result = -1; // the competitor with no fleet is considered worse than a competitor in one of the ranked fleets
} else {
result = 0;
}
return result;
}
@@ -14,6 +14,7 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.common.MaxPointsReason;
@@ -610,19 +611,17 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
@Override
public boolean hasCorrectionFor(RaceColumn raceInLeaderboard) {
return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ false);
return internalHasScoreCorrectionFor(raceInLeaderboard);
}
private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard, boolean considerOnlyUntrackedRaces) {
for (Pair<Competitor, RaceColumn> correctedScoresKey : correctedScores.keySet()) {
if (correctedScoresKey.getB() == raceInLeaderboard &&
(!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(correctedScoresKey.getA()) == null)) {
private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard) {
for (final Pair<Competitor, RaceColumn> correctedScoresKey : correctedScores.keySet()) {
if (correctedScoresKey.getB() == raceInLeaderboard) {
return true;
}
}
for (Pair<Competitor, RaceColumn> maxPointsReasonsKey : maxPointsReasons.keySet()) {
if (maxPointsReasonsKey.getB() == raceInLeaderboard &&
(!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(maxPointsReasonsKey.getA()) == null)) {
if (maxPointsReasonsKey.getB() == raceInLeaderboard) {
return true;
}
}
@@ -630,8 +629,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
@Override
public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard) {
return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ true);
public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard, Fleet fleet) {
boolean result;
if (raceInLeaderboard.getTrackedRace(fleet) == null) {
result = false;
for (final Competitor competitor : raceInLeaderboard.getAllCompetitors(fleet)) {
final Pair<Competitor, RaceColumn> key = raceInLeaderboard.getKey(competitor);
if (correctedScores.containsKey(key) || maxPointsReasons.containsKey(key)) {
result = true;
break;
}
}
} else {
result = false;
}
return result;
}
@Override
@@ -10,10 +10,10 @@ import java.util.NavigableSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.maneuverdetection.IncrementalApproximatedFixesCalculator;
import com.sap.sailing.domain.shared.tracking.impl.TimedComparator;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.TimedComparator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
Loaded 100 of 261 files, more files were not shown because too many files have changed in this diff. Show more