mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-22 21:55:39 +00:00
Merge branch 'master' into bug5510
Conflicts: java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
This commit is contained in:
@@ -15,3 +15,21 @@ Title sponsor of the SAP 5O5 World Championships
|
||||
Official Technology Partner of WORLD SAILING
|
||||
|
||||
Start contributing: [https://wiki.sapsailing.com/wiki/howto/onboarding](https://wiki.sapsailing.com/wiki/howto/onboarding)
|
||||
|
||||
To build, invoke
|
||||
```
|
||||
configuration/buildAndUpdateProduct.sh build
|
||||
```
|
||||
To build a docker image, try ``docker/makeImageForLatestRelease``. To run that docker image, try something like
|
||||
```
|
||||
docker run -d -e "MEMORY=4g" -e "MONGODB_URI=mongodb://my.mongohost.org?replicaSet=rs0&retryWrites=true" -P <yourimage>
|
||||
```
|
||||
Do a "docker ps" to figure out the port exposing the web application:
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 79f6faf19b6a donaldduck70/sapsailing:latest "/home/sailing/serve…" 33 seconds ago Up 32 seconds 0.0.0.0:32782->6666/tcp, 0.0.0.0:32781->7091/tcp, 0.0.0.0:32780->8000/tcp, 0.0.0.0:32779->8888/tcp, 0.0.0.0:32778->14888/tcp modest_dhawan
|
||||
|
||||
In this example, find your web application at http://localhost:32779 which is where the port 8888 exposed by the application is exposed at on your host. In the example with telnet port 14888 mapped to localhost:32788 do a
|
||||
```
|
||||
telnet localhost 32778
|
||||
```
|
||||
to connect to the server's OSGi console.
|
||||
|
||||
+5
-6
@@ -45,10 +45,10 @@ allprojects {
|
||||
// dependency versions for all sub modules
|
||||
ext {
|
||||
// regular
|
||||
buildTools = "29.0.3"
|
||||
compileSdk = 29
|
||||
buildTools = "30.0.3"
|
||||
compileSdk = 30
|
||||
minSdk = 16
|
||||
targetSdk = 29
|
||||
targetSdk = 30
|
||||
|
||||
sourceCompatibilityVersion = JavaVersion.VERSION_1_8
|
||||
targetCompatibilityVersion = JavaVersion.VERSION_1_8
|
||||
@@ -68,11 +68,11 @@ ext {
|
||||
isXmakeBuild = project.hasProperty("xmake")
|
||||
isReleaseBuild = Boolean.parseBoolean(System.env.XMAKE_RELEASE_BUILD)
|
||||
|
||||
destinationFolder = new File("").absolutePath + "/java/com.sap.sailing.www/apps/"
|
||||
destinationFolder = project.rootDir.absolutePath + "/java/com.sap.sailing.www/apps/"
|
||||
}
|
||||
|
||||
apply plugin: "org.ajoberstar.grgit"
|
||||
if (project.hasProperty("xmake")) {
|
||||
apply plugin: "org.ajoberstar.grgit"
|
||||
task wrapper(type: Wrapper) {
|
||||
distributionUrl "${sapGradleDistBaseUrl}/${sapGradleVersion}/gradle-${sapGradleVersion}-all.zip"
|
||||
System.setProperty("http.proxyHost", sapProxyHost)
|
||||
@@ -83,4 +83,3 @@ if (project.hasProperty("xmake")) {
|
||||
System.setProperty("https.nonProxyHosts", sapNonProxyHosts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -20,7 +20,7 @@ assert mavenclient.exists()
|
||||
println "mavenclient check passed"
|
||||
|
||||
def setup_android_tool(String toolname) {
|
||||
def tool = new File(CODESIGN_TOOL_DIR, "android-8.1.0/" + toolname )
|
||||
def tool = new File(CODESIGN_TOOL_DIR, "android-11/" + toolname )
|
||||
//check zipalign in the exploded build-tools dir
|
||||
tool.setExecutable(true)
|
||||
assert tool.canExecute()
|
||||
@@ -53,15 +53,15 @@ if (new File(SIGN_DOCKER_FILE_PY).exists()){
|
||||
def apkUnsigned = apkFile.getAbsolutePath()
|
||||
def name = apkFile.getName()
|
||||
def index = name.indexOf("-")
|
||||
def apkToDeploy = "$repodir/${name.substring(0 , index)}-release-centralsigned.apk"
|
||||
def apkToDeploy = apkUnsigned
|
||||
|
||||
//align the already signed apks
|
||||
assert execute(zipalign.absolutePath, "-v", "4", apkUnsigned, apkToDeploy) == 0
|
||||
//don't align the already signed apks anymore
|
||||
//assert execute(zipalign.absolutePath, "-v", "4", apkUnsigned, apkToDeploy) == 0
|
||||
assert execute(apksigner.absolutePath, "verify", "--print-certs", apkToDeploy) == 0
|
||||
|
||||
def deployable = new File(apkToDeploy)
|
||||
def expectedLocation = new File("$gendir/$name")
|
||||
deployable.renameTo(expectedLocation)
|
||||
expectedLocation << deployable.bytes
|
||||
}
|
||||
} else {
|
||||
// snapshot and milestone builds are not eligible for central signing
|
||||
@@ -71,7 +71,6 @@ if (new File(SIGN_DOCKER_FILE_PY).exists()){
|
||||
|
||||
println "Execute APK local signing ..."
|
||||
assert execute(apksigner.absolutePath, "sign", "--ks", "${CODESIGN_TOOL_DIR}/localSigningKeystore-1.0.0.jks", "--ks-pass", "pass:localSigningPassword", "-in", "${apkToSign}", "-out", "${apkToDeploy}" ) == 0
|
||||
|
||||
assert execute(apksigner.absolutePath, "verify", "--print-certs", apkToDeploy) == 0
|
||||
}
|
||||
}
|
||||
@@ -91,4 +90,4 @@ artifacts builderVersion: "1.1", {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -4,10 +4,12 @@ imports dslVersion:"1.3", {
|
||||
// required dependencies
|
||||
artifactFile "com.oracle.download.java:jdk:tar.gz:linux-x64:1.8.0_66-sap-01"
|
||||
|
||||
// see http://nexus.wdf.sap.corp:8081/nexus/content/groups/build.releases/com/google/download/android/
|
||||
// for the versions available in an xmake environment
|
||||
artifactFile "com.google.download.android:tools:zip:linux:26.1.1"
|
||||
artifactFile "com.google.download.android:platform-tools:zip:linux:30.0.5"
|
||||
artifactFile "com.google.download.android:build-tools:zip:linux:29.0.3"
|
||||
artifactFile "com.google.download.android:platform:zip:29.5"
|
||||
artifactFile "com.google.download.android:build-tools:zip:linux:30.0.3"
|
||||
artifactFile "com.google.download.android:platform:zip:30.3"
|
||||
|
||||
// optional dependencies for Android SDK - use when required (e.g. appcompat-v7)
|
||||
artifactFile "com.google.download.android:google-repository:zip:58"
|
||||
@@ -15,7 +17,7 @@ imports dslVersion:"1.3", {
|
||||
// tools required to sign + zipalign APKs
|
||||
unzip {
|
||||
artifactFile "com.sap.prd.codesign.mavenclient:com.sap.prd.codesign.mavenclient.dist.cli:zip:dist:1.4.0"
|
||||
artifactFile "com.google.download.android:build-tools:zip:linux:27.0.3"
|
||||
artifactFile "com.google.download.android:build-tools:zip:linux:30.0.3"
|
||||
}
|
||||
artifactFile "com.sap.ldi.signing:localSigningKeystore:jks:1.0.0"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Uses mongodump / mongorestore to copy a database from the live replica set to
|
||||
# the archive database. Afterwards, a comparison is made using the mongohash
|
||||
# command
|
||||
# Usage: archiveDb.sh <dbname>
|
||||
# Usage: archiveDb.sh [ -y ] <dbname>
|
||||
|
||||
if [ "$1" = "-y" ]; then
|
||||
CONFIRMATION="y"
|
||||
@@ -12,7 +12,7 @@ else
|
||||
fi
|
||||
|
||||
DB=$1
|
||||
DUMPBASEDIR=/var/lib/mongodb
|
||||
DUMPBASEDIR=/mnt/mongodb-slow
|
||||
|
||||
if [ "$CONFIRMATION" = "" ]; then
|
||||
echo -n "This will drop the database $DB in the archive DB before importing. Are you sure (y/n)? "
|
||||
@@ -22,16 +22,17 @@ if [ "$CONFIRMATION" = "" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$DUMPBASEDIR"
|
||||
pushd "$DUMPBASEDIR"
|
||||
mongodump --port 10203 --db $DB
|
||||
# Drop the entire DB in the archive DB to ensure there are no left-over collections
|
||||
echo "use $DB
|
||||
db.dropDatabase()" | mongo --port 10202
|
||||
mongorestore --noIndexRestore --drop --port 10202 dump
|
||||
rm -rf dump/$DB
|
||||
ORIGINAL_HASH=$(./mongohash -p 10203 -d $DB)
|
||||
ARCHIVE_HASH=$(./mongohash -p 10202 -d $DB)
|
||||
if [ $ORIGINAL_HASH = $ARCHIVE_HASH ]; then
|
||||
popd
|
||||
ORIGINAL_HASH=$(`dirname $0`/mongohash -p 10203 -d $DB)
|
||||
ARCHIVE_HASH=$(`dirname $0`/mongohash -p 10202 -d $DB)
|
||||
if [ -n "${ORIGINAL_HASH}" -a -n "${ARCHIVE_HASH}" -a $ORIGINAL_HASH = $ARCHIVE_HASH ]; then
|
||||
echo Hashes of old and new are equal: $ORIGINAL_HASH and $ARCHIVE_HASH. Dropping $DB and ${DB}-replica in live DB
|
||||
echo "use $DB
|
||||
db.dropDatabase()
|
||||
@@ -40,6 +41,7 @@ db.dropDatabase()" | mongo "mongodb://localhost:10203/$DB?replicaSet=live"
|
||||
echo "Don't forget to switch your server's env.sh so it has MONGODB_URI=\"mongodb://dbserver.internal.sapsailing.com:10202/$DB\""
|
||||
exit 0
|
||||
else
|
||||
echo Hashes of old and new differ: $ORIGINAL_HASH and $ARCHIVE_HASH
|
||||
echo "Hashes of old and new are empty of differ: ${ORIGINAL_HASH} and ${ARCHIVE_HASH}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
obj_name="$1"
|
||||
shift
|
||||
git log "$@" --pretty=format:'%T %h %s' \
|
||||
| while read tree commit subject ; do
|
||||
if git ls-tree -r $tree | grep -q "$obj_name" ; then
|
||||
echo $commit "$subject"
|
||||
fi
|
||||
done
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
#set -x
|
||||
|
||||
# Shows you the largest objects in your repo's pack file.
|
||||
# Written for osx.
|
||||
#
|
||||
# @see https://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
|
||||
# @author Antony Stubbs
|
||||
|
||||
# set the internal field separator to line break, so that we can iterate easily over the verify-pack output
|
||||
IFS=$'\n';
|
||||
|
||||
# list all objects including their size, sort by size, take top 10
|
||||
objects=`git verify-pack -v .git/objects/pack/pack-*.idx | grep -v chain | sort -k3nr | head`
|
||||
|
||||
echo "All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file."
|
||||
|
||||
output="size,pack,SHA,location"
|
||||
allObjects=`git rev-list --all --objects`
|
||||
for y in $objects
|
||||
do
|
||||
# extract the size in bytes
|
||||
size=$((`echo $y | cut -f 5 -d ' '`/1024))
|
||||
# extract the compressed size in bytes
|
||||
compressedSize=$((`echo $y | cut -f 6 -d ' '`/1024))
|
||||
# extract the SHA
|
||||
sha=`echo $y | cut -f 1 -d ' '`
|
||||
# find the objects location in the repository tree
|
||||
other=`echo "${allObjects}" | grep $sha`
|
||||
#lineBreak=`echo -e "\n"`
|
||||
output="${output}\n${size},${compressedSize},${other}"
|
||||
done
|
||||
|
||||
echo -e $output | column -t -s ', '
|
||||
@@ -5,7 +5,7 @@ APP_DIRS="mobile/com.sap.sailing.android.tracking.app/ mobile/com.sap.sailing.an
|
||||
APP_GRADLE_PROPERTIES="gradle.properties"
|
||||
FILES2SIGN=cfg/files2sign.json
|
||||
VERSION_FILE=cfg/VERSION
|
||||
GIT_REMOTE=origin
|
||||
GIT_REMOTE=sap
|
||||
|
||||
OPTION_UPDATE_ANDROID_VERSIONS=1
|
||||
OPTION_PERFORM_GIT_OPERATIONS=1
|
||||
|
||||
@@ -40,7 +40,7 @@ start_tmux() {
|
||||
}
|
||||
|
||||
start_servers() {
|
||||
/usr/local/bin/update_authorized_keys_for_landscape_managers $( cat ${SSH_KEY_READER_BEARER_TOKEN} ) https://security-service.sapsailing.com 2>&1 >>/var/log/sailing.err
|
||||
/usr/local/bin/update_authorized_keys_for_landscape_managers $( cat ${SSH_KEY_READER_BEARER_TOKEN} ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err
|
||||
cp /home/sailing/code/configuration/cp_root_mail_properties /usr/local/bin
|
||||
chown root /usr/local/bin/cp_root_mail_properties
|
||||
chgrp root /usr/local/bin/cp_root_mail_properties
|
||||
@@ -84,7 +84,10 @@ configure_and_start_server() {
|
||||
chgrp sailing "${SERVERS_DIR}/${conf}"
|
||||
# If there is a secret /root/mail.properties, copy it into the default server's configuration directory:
|
||||
/usr/local/bin/cp_root_mail_properties "${conf}"
|
||||
su - sailing -c "cd ${SERVERS_DIR}/${conf} && ${GIT_REPOSITORY}/java/target/refreshInstance.sh auto-install; ./start; ./defineReverseProxyMappings.sh" 2>>/var/log/sailing.err >>/var/log/sailing.err
|
||||
su - sailing -c "cd ${SERVERS_DIR}/${conf} && ${GIT_REPOSITORY}/java/target/refreshInstance.sh auto-install; ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err
|
||||
pushd ${SERVERS_DIR}/${conf}
|
||||
./defineReverseProxyMappings.sh 2>>/var/log/sailing.err >>/var/log/sailing.err
|
||||
popd
|
||||
RETVAL=$?
|
||||
[ $RETVAL -eq 0 ] && success || failure
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Application Load Balancer (ALB) Ingress Controller Deployment Manifest.
|
||||
# This manifest details sensible defaults for deploying an ALB Ingress Controller.
|
||||
# GitHub: https://github.com/kubernetes-sigs/aws-alb-ingress-controller
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: alb-ingress-controller
|
||||
name: alb-ingress-controller
|
||||
# Namespace the ALB Ingress Controller should run in. Does not impact which
|
||||
# namespaces it's able to resolve ingress resource for. For limiting ingress
|
||||
# namespace scope, see --watch-namespace.
|
||||
namespace: kube-system
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: alb-ingress-controller
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 1
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: alb-ingress-controller
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
# Limit the namespace where this ALB Ingress Controller deployment will
|
||||
# resolve ingress resources. If left commented, all namespaces are used.
|
||||
# - --watch-namespace=your-k8s-namespace
|
||||
|
||||
# Setting the ingress-class flag below ensures that only ingress resources with the
|
||||
# annotation kubernetes.io/ingress.class: "alb" are respected by the controller. You may
|
||||
# choose any class you'd like for this controller to respect.
|
||||
- --ingress-class=alb
|
||||
|
||||
# Name of your cluster. Used when naming resources created
|
||||
# by the ALB Ingress Controller, providing distinction between
|
||||
# clusters.
|
||||
- --cluster-name=sailingaws
|
||||
|
||||
# AWS VPC ID this ingress controller will use to create AWS resources.
|
||||
# If unspecified, it will be discovered from ec2metadata.
|
||||
- --aws-vpc-id=vpc-03fb317cc6d32840e
|
||||
|
||||
# AWS region this ingress controller will operate in.
|
||||
# If unspecified, it will be discovered from ec2metadata.
|
||||
# List of regions: http://docs.aws.amazon.com/general/latest/gr/rande.html#vpc_region
|
||||
- --aws-region=eu-central-1
|
||||
|
||||
# Enables logging on all outbound requests sent to the AWS API.
|
||||
# If logging is desired, set to true.
|
||||
# - ---aws-api-debug
|
||||
# Maximum number of times to retry the aws calls.
|
||||
# defaults to 10.
|
||||
# - --aws-max-retries=10
|
||||
env:
|
||||
# AWS key id for authenticating with the AWS API.
|
||||
# This is only here for examples. It's recommended you instead use
|
||||
# a project like kube2iam for granting access.
|
||||
#- name: AWS_ACCESS_KEY_ID
|
||||
# value: KEYVALUE
|
||||
|
||||
# AWS key secret for authenticating with the AWS API.
|
||||
# This is only here for examples. It's recommended you instead use
|
||||
# a project like kube2iam for granting access.
|
||||
#- name: AWS_SECRET_ACCESS_KEY
|
||||
# value: SECRETVALUE
|
||||
# Repository location of the ALB Ingress Controller.
|
||||
image: docker.io/amazon/aws-alb-ingress-controller:v1.1.0
|
||||
imagePullPolicy: Always
|
||||
name: server
|
||||
resources: {}
|
||||
terminationMessagePath: /dev/termination-log
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
serviceAccountName: alb-ingress
|
||||
serviceAccount: alb-ingress
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: sailing-kw2019-master-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: alb
|
||||
alb.ingress.kubernetes.io/subnets: subnet-08481a7845eb5607d,subnet-0c989099f5391f368
|
||||
spec:
|
||||
rules:
|
||||
- host: kw2019-master.k8s.sapsailing.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: sailing-kw2019-master-svc
|
||||
servicePort: 80
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: sailing-kw2019-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: alb
|
||||
alb.ingress.kubernetes.io/subnets: subnet-08481a7845eb5607d,subnet-0c989099f5391f368
|
||||
spec:
|
||||
rules:
|
||||
- host: kw2019.k8s.sapsailing.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: sailing-kw2019-svc
|
||||
servicePort: 80
|
||||
@@ -0,0 +1,19 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
sailing-analytics:
|
||||
image: "donaldduck70/sapsailing:latest"
|
||||
ports:
|
||||
- "8888:8888"
|
||||
- "6666:6666"
|
||||
- "14888:14888"
|
||||
environment:
|
||||
MONGODB_URI: mongodb://mongo/test
|
||||
SERVER_NAME: test
|
||||
REPLICATION_HOST: rabbitmq
|
||||
REPLICATION_CHANNEL: test
|
||||
REPLICATION_PORT: 5672
|
||||
mongo:
|
||||
image: "mongo:4.4-rc"
|
||||
rabbitmq:
|
||||
image: "rabbitmq:3.9"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
image: registry.opensource.zalan.do/teapot/external-dns:v0.5.9
|
||||
args:
|
||||
- --source=service
|
||||
- --source=ingress
|
||||
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
|
||||
- --provider=aws
|
||||
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
|
||||
- --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
|
||||
helm install incubator/aws-alb-ingress-controller --set autoDiscoverAwsRegion=true --set autoDiscoverAwsVpcID=true --set clusterName=sailingaws --set image.tag=v1.2.0-alpha.1 --name alb-ingress-controller --namespace kube-system
|
||||
@@ -48,8 +48,9 @@
|
||||
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-logLevel INFO -noserver -incremental -war "${project_loc:com.sap.sailing.dashboards.gwt}" -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -codeServerPort 9875 -startupUrl /dashboards/RibDashboard.html com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:+UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:-UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.declination.impl.DeclinationStore"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2021 2021 1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.declination"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.declination.impl.DeclinationStore"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2022 2022 1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.declination"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.declination.impl.DeclinationStore"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2018 2018 1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.declination.impl.DeclinationStore"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2022 2022 1"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.declination"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080"/>
|
||||
</launchConfiguration>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+6
@@ -70,6 +70,12 @@ public enum DataImportSubProgress {
|
||||
return messages.importTrackedRaces();
|
||||
}
|
||||
},
|
||||
WAITING_FOR_TRACKED_RACES_TO_FINISH_LOADING {
|
||||
@Override
|
||||
public String getMessage(CommonStringMessages messages) {
|
||||
return messages.waitingForTrackedRacesToFinishLoading();
|
||||
}
|
||||
},
|
||||
UPDATE_EVENT_LEADERBOARD_GROUP_LINKS {
|
||||
@Override
|
||||
public String getMessage(CommonStringMessages messages) {
|
||||
|
||||
+10
-2
@@ -1,7 +1,15 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public interface LegIdentifier extends RegattaAndRaceIdentifier {
|
||||
/**
|
||||
* @return only the {@link RegattaAndRaceIdentifier} without a leg index; although this interface extends
|
||||
* RegattaAndRaceIdentifier, {@code this} != {@code this.getRaceIdentifier()}, and not even
|
||||
* {@code this.equals(this.getRaceIdentifier())} because a class cast exception would result if the
|
||||
* {@link RegattaAndRaceIdentifier} used to construct this {@link LegIdentifier} was itself not a
|
||||
* {@link LegIdentifier}. This is also important when trying to look up a race by its race identifier, as,
|
||||
* e.g., in a map.
|
||||
*/
|
||||
RegattaAndRaceIdentifier getRaceIdentifier();
|
||||
String getLegName();
|
||||
int getLegNumber();
|
||||
|
||||
int getOneBasedLegIndex();
|
||||
}
|
||||
|
||||
+15
-17
@@ -3,14 +3,15 @@ package com.sap.sailing.domain.common;
|
||||
public class LegIdentifierImpl extends RegattaNameAndRaceName implements LegIdentifier {
|
||||
private static final long serialVersionUID = 3599904513673776450L;
|
||||
private RegattaAndRaceIdentifier raceIdentifier = null;
|
||||
private String legName;
|
||||
private int oneBasedLegIndex;
|
||||
|
||||
LegIdentifierImpl() {}
|
||||
@Deprecated
|
||||
LegIdentifierImpl() {} // for GWT RPC serialization only
|
||||
|
||||
public LegIdentifierImpl(RegattaAndRaceIdentifier raceIdentifier, String legName) {
|
||||
public LegIdentifierImpl(RegattaAndRaceIdentifier raceIdentifier, int oneBasedLegIndex) {
|
||||
super(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
|
||||
this.raceIdentifier = raceIdentifier;
|
||||
this.legName = legName;
|
||||
this.oneBasedLegIndex = oneBasedLegIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,25 +20,21 @@ public class LegIdentifierImpl extends RegattaNameAndRaceName implements LegIden
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLegName() {
|
||||
return legName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLegNumber() {
|
||||
return Integer.parseInt(legName)-1;
|
||||
public int getOneBasedLegIndex() {
|
||||
return oneBasedLegIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getRegattaName() + "/" + getRaceName() + "/" + getLegName();
|
||||
return getRegattaName() + "/" + getRaceName() + "/ leg " + getOneBasedLegIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((legName == null) ? 0 : legName.hashCode());
|
||||
result = prime * result + ((raceIdentifier == null) ? 0 : raceIdentifier.hashCode());
|
||||
result = prime * result + oneBasedLegIndex;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -50,12 +47,13 @@ public class LegIdentifierImpl extends RegattaNameAndRaceName implements LegIden
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
LegIdentifierImpl other = (LegIdentifierImpl) obj;
|
||||
if (legName == null) {
|
||||
if (other.legName != null)
|
||||
if (raceIdentifier == null) {
|
||||
if (other.raceIdentifier != null)
|
||||
return false;
|
||||
} else if (!legName.equals(other.legName))
|
||||
} else if (!raceIdentifier.equals(other.raceIdentifier))
|
||||
return false;
|
||||
if (oneBasedLegIndex != other.oneBasedLegIndex)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ public interface Mile {
|
||||
static double METERS_PER_NAUTICAL_MILE = 1852;
|
||||
static double METERS_PER_GEOGRAPHICAL_MILE = 400080. / 216. /* = 40008000/360/60 */;
|
||||
static double METERS_PER_STATUTE_MILE = 1609.344;
|
||||
static double METERS_PER_SECOND_PER_KNOT = METERS_PER_NAUTICAL_MILE / 3600.;
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,6 +17,6 @@ public interface CommonStringMessages {
|
||||
String competitorRegistrationTypeClosed();
|
||||
String competitorRegistrationTypeOpenModerated();
|
||||
String competitorRegistrationTypeOpenUnmoderated();
|
||||
String importTrackedRaces();
|
||||
|
||||
String importTrackedRaces();
|
||||
String waitingForTrackedRacesToFinishLoading();
|
||||
}
|
||||
|
||||
-1
@@ -15,7 +15,6 @@ public class RaceTrackerListenersTest {
|
||||
final RaceTrackerListeners listeners = new RaceTrackerListeners();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
listeners.addListener(new RaceTracker.Listener() {
|
||||
|
||||
@Override
|
||||
public void onTrackerWillStop(boolean preemptive, boolean willBeRemoved) {
|
||||
listeners.removeListener(this);
|
||||
|
||||
+15
-8
@@ -7,6 +7,7 @@ import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
@@ -40,6 +41,8 @@ import com.tractrac.util.lib.api.exceptions.TimeOutException;
|
||||
*
|
||||
*/
|
||||
public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler {
|
||||
private static final Logger logger = Logger.getLogger(TracTracConnectivityParamsHandler.class.getName());
|
||||
|
||||
private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm";
|
||||
private static final String USE_OFFICIAL_EVENTS_TO_UPDATE_RACE_LOG = "useOfficialEventsToUpdateRaceLog";
|
||||
private static final String TRAC_TRAC_USERNAME = "tracTracUsername";
|
||||
@@ -146,13 +149,17 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne
|
||||
throws MalformedURLException, IOException, ParseException, CreateModelException, URISyntaxException, TimeOutException {
|
||||
final IRace tractracRace = params.getTractracRace();
|
||||
final String jsonURL = tractracRace.getParameterSet().getParameter("eventJSON");
|
||||
final String creatorName = SessionUtils.getPrincipal().toString();
|
||||
final TracTracConfigurationImpl tracTracConfiguration = new TracTracConfigurationImpl(creatorName, tractracRace.getEvent().getName(), jsonURL,
|
||||
(params.getLiveURI() == null ? null : params.getLiveURI().toString()),
|
||||
/* stored URI */ params.isReplayRace(tractracRace) ? null // we mainly want to enable the user to list the event's races again in case they are removed;
|
||||
: (params.getStoredURI() == null ? null : params.getStoredURI().toString()), // live/stored stuff comes from the tracking params
|
||||
params.getCourseDesignUpdateURI()==null?null:params.getCourseDesignUpdateURI().toString(), params.getTracTracUsername(), params.getTracTracPassword());
|
||||
tractracMongoObjectFactory.updateTracTracConfiguration(tracTracConfiguration);
|
||||
securityService.setDefaultOwnershipIfNotSet(tracTracConfiguration.getIdentifier());
|
||||
if (jsonURL == null) {
|
||||
logger.warning("No eventJSON field set in TracTrac race "+tractracRace.getName()+". Cannot add configuration to list.");
|
||||
} else {
|
||||
final String creatorName = SessionUtils.getPrincipal().toString();
|
||||
final TracTracConfigurationImpl tracTracConfiguration = new TracTracConfigurationImpl(creatorName, tractracRace.getEvent().getName(), jsonURL,
|
||||
(params.getLiveURI() == null ? null : params.getLiveURI().toString()),
|
||||
/* stored URI */ params.isReplayRace(tractracRace) ? null // we mainly want to enable the user to list the event's races again in case they are removed;
|
||||
: (params.getStoredURI() == null ? null : params.getStoredURI().toString()), // live/stored stuff comes from the tracking params
|
||||
params.getCourseDesignUpdateURI()==null?null:params.getCourseDesignUpdateURI().toString(), params.getTracTracUsername(), params.getTracTracPassword());
|
||||
tractracMongoObjectFactory.updateTracTracConfiguration(tracTracConfiguration);
|
||||
securityService.setDefaultOwnershipIfNotSet(tracTracConfiguration.getIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.base;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
|
||||
|
||||
public interface RegattaRegistry {
|
||||
/**
|
||||
@@ -14,4 +15,8 @@ public interface RegattaRegistry {
|
||||
Regatta getRegattaByName(String name);
|
||||
|
||||
Regatta getRegatta(RegattaIdentifier regattaIdentifier);
|
||||
|
||||
void removeTrackedRegattaListener(TrackedRegattaListener listener);
|
||||
|
||||
void addTrackedRegattaListener(TrackedRegattaListener listener);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import com.sap.sse.common.Speed;
|
||||
|
||||
/**
|
||||
* For a {@link Competitor} and the {@link ORCPerformanceCurveCourse} which the competitor sailed until the creation of
|
||||
* an instance of an implementing class, this instance represents a so called "Perfromance Curve". This Curve is
|
||||
* specified by the so called "Implied Wind" a {@link Speed} on the x-Axis and the allowance in s/nm respectively a
|
||||
* an instance of an implementing class, this instance represents a so called "Performance Curve". This Curve is
|
||||
* specified by the so called "Implied Wind" a {@link Speed} on the x-Axis and the allowance in s/NM respectively a
|
||||
* {@link Duration} on the y-Axis. It represents a simplified polar curve for the given boat and the given part of the
|
||||
* course. For a given windspeed the performance curve returns the allowance for the boat or in simpler words: how long
|
||||
* should the boat need for a nautical mile when sailing 100% performance.
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ public class NotEnoughDataHasBeenAddedException extends Exception {
|
||||
private static final long serialVersionUID = 4134116458606258315L;
|
||||
|
||||
public NotEnoughDataHasBeenAddedException() {
|
||||
super("No data has been added to the Processor yet. At least to value-pairs have to be added.");
|
||||
super("No data has been added to the Processor yet. At least two value-pairs have to be added.");
|
||||
}
|
||||
|
||||
public NotEnoughDataHasBeenAddedException(String string) {
|
||||
|
||||
@@ -29,7 +29,10 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
* <p>
|
||||
*
|
||||
* A {@link RaceTracker} controls the lifecycle of a {@link TrackedRace} in contrast to a {@link TrackingDataLoader} which just
|
||||
* contributes to the composite status but does not control the lifecycle.
|
||||
* contributes to the composite status but does not control the lifecycle.<p>
|
||||
*
|
||||
* Implementing classes must not override {@link Object#equals(Object)} or {@link Object#hashCode()}; collections have to rely
|
||||
* on objects whose class implement this interface to compare by object identity.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
@@ -43,7 +46,7 @@ public interface RaceTracker {
|
||||
static long TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS = 60000;
|
||||
|
||||
/**
|
||||
* Stops tracking the race.
|
||||
* Stops tracking the race. Same as calling {@link #stop(boolean, boolean) stop(preemptive, false)}.
|
||||
*
|
||||
* @param preemptive
|
||||
* if <code>false</code>, the tracker will continue to process data already received but will stop
|
||||
|
||||
+1
-6
@@ -1127,12 +1127,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public long getMillisecondsOverWhichToAverageWind() {
|
||||
long result = 0; // default in case there is no competitor
|
||||
for (WindSource windSource : getWindSources()) {
|
||||
WindTrack someTrack = getOrCreateWindTrack(windSource);
|
||||
result = someTrack.getMillisecondsOverWhichToAverageWind();
|
||||
}
|
||||
return result;
|
||||
return this.millisecondsOverWhichToAverageWind;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
<plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="7" />
|
||||
<plugin id="com.sap.sailing.news" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.polars" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sailing.landscape.gateway" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sailing.polars.datamining" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="4" />
|
||||
|
||||
@@ -522,6 +522,13 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.sap.sailing.landscape.gateway"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.sap.sailing.landscape.common"
|
||||
download-size="0"
|
||||
|
||||
@@ -277,8 +277,6 @@
|
||||
"acknowledgements": [
|
||||
"Hyperic SIGAR includes some third-party open source components in its distribution. The list below identifies the community or organization and links to their appropriate license terms. The Hyperic team would like to thank all the communities of the projects listed below for their contributions.",
|
||||
"---------------------------------------------------------------------",
|
||||
"The following components are included without modification: ",
|
||||
" - log4j - Information: http://logging.apache.org/ License: http://www.apache.org/licenses/LICENSE-2.0",
|
||||
"The following components are included with modification:",
|
||||
" - cpptasks - Information: http://ant-contrib.sourceforge.net/ License: http://ant-contrib.sourceforge.net/LICENSE.txt",
|
||||
" - (portions of) APR - Information: http://apr.apache.org/ License: http://www.apache.org/licenses/LICENSE-2.0",
|
||||
|
||||
+6
@@ -4,6 +4,12 @@
|
||||
<div id="mainContent">
|
||||
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
|
||||
<div class="innerContent">
|
||||
<h5 class="articleSubheadline">December 2021</h5>
|
||||
<ul class="bulletList">
|
||||
<li>The strategy simulator (see <a href="https://sapsailing.com/gwt/Simulator.html">https://sapsailing.com/gwt/Simulator.html</a>)
|
||||
now offers all the boat polars trained during more than 25,000 races tracked.
|
||||
</li>
|
||||
</ul>
|
||||
<h5 class="articleSubheadline">November 2021</h5>
|
||||
<ul class="bulletList">
|
||||
<li>When signing up for a new user account, the user name is now trimmed, meaning that
|
||||
|
||||
+103
-1
@@ -6,6 +6,7 @@ import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.cell.client.FieldUpdater;
|
||||
@@ -64,6 +65,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WaypointDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.util.NaturalComparator;
|
||||
import com.sap.sse.gwt.client.DateTimeUtil;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
@@ -112,6 +114,8 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
private Column<RegattaOverviewEntryDTO, SafeHtml> raceCourseColumn;
|
||||
private TextColumn<RegattaOverviewEntryDTO> boatClass;
|
||||
private TextColumn<RegattaOverviewEntryDTO> startTimeColumn;
|
||||
private TextColumn<RegattaOverviewEntryDTO> finishingTimeColumn;
|
||||
private TextColumn<RegattaOverviewEntryDTO> finishedTimeColumn;
|
||||
private SimplePanel tableHolder = new SimplePanel();
|
||||
private final long _1_HOUR = 60 /* seconds */* 60 /* minutes */* 1 /* hour */;
|
||||
private final long HIDE_COL_TIME_THRESHOLD = _1_HOUR;
|
||||
@@ -182,12 +186,22 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
boolean canRemoveBoatClass = true;
|
||||
boolean canRemoveLastUpdate = true;
|
||||
boolean canRemoveProtestTime = true;
|
||||
boolean canRemoveFinishingTime = true;
|
||||
boolean canRemoveFinishedTime = true;
|
||||
boolean first = true;
|
||||
for (RegattaOverviewEntryDTO loopEntryDTO : allEntries) {
|
||||
final RaceInfoDTO loopRaceInfo = loopEntryDTO.raceInfo;
|
||||
if (canRemoveLastUpdate && timePassedInSeconds(loopRaceInfo.lastUpdateTime) <= HIDE_COL_TIME_THRESHOLD) {
|
||||
canRemoveLastUpdate = false;
|
||||
}
|
||||
if (canRemoveFinishingTime && loopRaceInfo.finishingTime != null
|
||||
&& timePassedInSeconds(loopRaceInfo.finishingTime) <= HIDE_COL_TIME_THRESHOLD) {
|
||||
canRemoveFinishingTime = false;
|
||||
canRemoveFinishedTime = false;
|
||||
}
|
||||
if (canRemoveFinishedTime && loopRaceInfo.finishedTime != null) {
|
||||
canRemoveFinishedTime = false;
|
||||
}
|
||||
if (canRemoveProtestTime && loopRaceInfo.protestFinishTime != null
|
||||
&& timePassedInSeconds(loopRaceInfo.protestFinishTime) <= HIDE_COL_TIME_THRESHOLD) {
|
||||
canRemoveProtestTime = false;
|
||||
@@ -292,6 +306,10 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
table.addColumn(lastUpdateColumn, stringMessages.lastUpdate());
|
||||
}
|
||||
table.addColumn(startTimeColumn, stringMessages.startTime());
|
||||
if (!canRemoveFinishingTime) {
|
||||
table.addColumn(finishingTimeColumn, stringMessages.finishingTime());
|
||||
}
|
||||
table.addColumn(finishedTimeColumn, stringMessages.finishTimeString());
|
||||
if (!canRemoveProtestTime) {
|
||||
table.addColumn(endOfProtestTime, stringMessages.protestTime());
|
||||
}
|
||||
@@ -509,6 +527,52 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
return result;
|
||||
}
|
||||
});
|
||||
finishingTimeColumn = new TextColumn<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public String getValue(RegattaOverviewEntryDTO entryDTO) {
|
||||
return formatTime(entryDTO.raceInfo.finishingTime);
|
||||
}
|
||||
};
|
||||
finishingTimeColumn.setSortable(true);
|
||||
regattaOverviewListHandler.setComparator(finishingTimeColumn, new Comparator<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public int compare(RegattaOverviewEntryDTO left, RegattaOverviewEntryDTO right) {
|
||||
int result = 0;
|
||||
if (left.raceInfo.finishingTime != null && right.raceInfo.finishingTime != null) {
|
||||
result = left.raceInfo.finishingTime.compareTo(right.raceInfo.finishingTime);
|
||||
} else if (left.raceInfo.finishingTime == null && right.raceInfo.finishingTime == null) {
|
||||
result = 0;
|
||||
} else if (left.raceInfo.finishingTime == null) {
|
||||
result = 1;
|
||||
} else if (right.raceInfo.finishingTime == null) {
|
||||
result = -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
finishedTimeColumn = new TextColumn<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public String getValue(RegattaOverviewEntryDTO entryDTO) {
|
||||
return formatTime(entryDTO.raceInfo.finishedTime);
|
||||
}
|
||||
};
|
||||
finishedTimeColumn.setSortable(true);
|
||||
regattaOverviewListHandler.setComparator(finishedTimeColumn, new Comparator<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public int compare(RegattaOverviewEntryDTO left, RegattaOverviewEntryDTO right) {
|
||||
int result = 0;
|
||||
if (left.raceInfo.finishedTime != null && right.raceInfo.finishedTime != null) {
|
||||
result = left.raceInfo.finishedTime.compareTo(right.raceInfo.finishedTime);
|
||||
} else if (left.raceInfo.finishedTime == null && right.raceInfo.finishedTime == null) {
|
||||
result = 0;
|
||||
} else if (left.raceInfo.finishedTime == null) {
|
||||
result = 1;
|
||||
} else if (right.raceInfo.finishedTime == null) {
|
||||
result = -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
raceStatusColumn = new Column<RegattaOverviewEntryDTO, SafeHtml>(new ClickableSafeHtmlCell()) {
|
||||
@Override
|
||||
public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) {
|
||||
@@ -686,7 +750,15 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
if (isInfoBefore) {
|
||||
additionalInformation.append(" / ");
|
||||
}
|
||||
additionalInformation.append(stringMessages.finishTime((timeFormatter.format(entryDTO.raceInfo.finishedTime))));
|
||||
final Duration finishDuration;
|
||||
if (entryDTO.raceInfo.finishingTime != null) {
|
||||
finishDuration = new MillisecondsDurationImpl(
|
||||
entryDTO.raceInfo.finishedTime.getTime()
|
||||
- entryDTO.raceInfo.finishingTime.getTime());
|
||||
} else {
|
||||
finishDuration = new MillisecondsDurationImpl(0);
|
||||
}
|
||||
additionalInformation.append(stringMessages.finishDuration(formatDuration(finishDuration)));
|
||||
isInfoBefore = true;
|
||||
}
|
||||
return additionalInformation.toString();
|
||||
@@ -874,6 +946,36 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
|
||||
return (new Date().getTime() - dateTime.getTime()) / 1000;
|
||||
}
|
||||
|
||||
private String formatDuration(Duration duration) {
|
||||
long seconds = (long) Math.round(duration.asSeconds());
|
||||
final long days = seconds / (long) Duration.ONE_DAY.asSeconds();
|
||||
seconds %= 24 * 3600;
|
||||
final long hours = seconds / (long) Duration.ONE_HOUR.asSeconds();
|
||||
seconds %= 3600;
|
||||
final long minutes = seconds / (long) Duration.ONE_MINUTE.asSeconds();
|
||||
seconds %= 60;
|
||||
final StringJoiner sj = new StringJoiner(":");
|
||||
if (days > 0) {
|
||||
sj.add(String.valueOf(days));
|
||||
}
|
||||
if (days > 0 && hours < 10) {
|
||||
sj.add("0" + String.valueOf(hours));
|
||||
} else {
|
||||
sj.add(String.valueOf(hours));
|
||||
}
|
||||
if (minutes < 10) {
|
||||
sj.add("0" + String.valueOf(minutes));
|
||||
} else {
|
||||
sj.add(String.valueOf(minutes));
|
||||
}
|
||||
if (seconds < 10) {
|
||||
sj.add("0" + String.valueOf(seconds));
|
||||
} else {
|
||||
sj.add(String.valueOf(seconds));
|
||||
}
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependentCssClassName() {
|
||||
return "regattaRaceStates";
|
||||
|
||||
+11
@@ -36,8 +36,19 @@ public interface SimulatorService extends RemoteService {
|
||||
SimulatorResultsDTO getSimulatorResults(char mode, char rcDirection, WindFieldGenParamsDTO params, WindPatternDisplay pattern, boolean withWindField,
|
||||
SimulatorUISelectionDTO selection) throws WindPatternNotFoundException, ConfigurationException;
|
||||
|
||||
/**
|
||||
* The ordering of boat classes in {@link BoatClassDTOsAndNotificationMessage#getBoatClassDTOs()} can be used to
|
||||
* infer a zero-based boat class "index" which can then be used to identify such a boat class in a call to
|
||||
* {@link #getPolarDiagram(Double, int)}.
|
||||
*/
|
||||
BoatClassDTOsAndNotificationMessage getBoatClasses() throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* @param boatClassIndex
|
||||
* refers to the order of boat classes as found in
|
||||
* {@link #getBoatClasses()}.{@link BoatClassDTOsAndNotificationMessage#getBoatClassDTOs()
|
||||
* getBoatClassDTOs()} result.
|
||||
*/
|
||||
PolarDiagramDTOAndNotificationMessage getPolarDiagram(Double bearingStep, int boatClassIndex) throws ConfigurationException;
|
||||
|
||||
ResponseTotalTimeDTO getTotalTime(RequestTotalTimeDTO requestData) throws ConfigurationException;
|
||||
|
||||
+2
@@ -1594,6 +1594,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String loadingRegattasAndRaces();
|
||||
String selectFromRacesWithOverlappingTimeRange();
|
||||
String finishTime(String timeAsString);
|
||||
String finishDuration(String durationAsString);
|
||||
String showCompetitorCharts();
|
||||
String showWindChart();
|
||||
String watchLive();
|
||||
@@ -2387,4 +2388,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String yellowBrickEvents();
|
||||
String timePointOfLastFix();
|
||||
String copyMarkDeviceMappings();
|
||||
String errorLoadingPolarDataForBoatClass(String boatClass, String message);
|
||||
}
|
||||
|
||||
+5
-2
@@ -1619,6 +1619,7 @@ noRacesAvailable=No races available
|
||||
loadingRegattasAndRaces=Loading Regattas and Races
|
||||
selectFromRacesWithOverlappingTimeRange=Select from the races whose time range overlaps with media track those to which the track shall be linked
|
||||
finishTime=Finish at: {0}
|
||||
finishDuration=Finish duration: {0}
|
||||
showCompetitorCharts=Show competitor chart
|
||||
showWindChart=Show wind chart
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Due to privacy reasons, mail notifications require that you enter your mail address on the user profile page and verify this mail address.
|
||||
@@ -1680,7 +1681,7 @@ seriesHeaderUpcoming=Upcoming
|
||||
eventRegattaLeaderboardLastScoreUpdate=Last Score Update
|
||||
eventRegattaRaceNotTracked=Not Tracked
|
||||
footerJobs=Jobs
|
||||
footerCopyright=\u00A9 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=\u00A9 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing in
|
||||
footerLegal=Legal Disclosure
|
||||
footerPrivacy=Privacy
|
||||
@@ -2343,6 +2344,7 @@ runAngles=Run angles
|
||||
exportTrackedRacesAndStartTracking=Import Tracked Races and Start Tracking
|
||||
exportTrackedRacesAndStartTrackingTooltip=Import the tracked Races, start the tracking and add the connectivity parameters to the connectors-tab
|
||||
importTrackedRaces=Importing Tracked Races
|
||||
waitingForTrackedRacesToFinishLoading=Waiting for tracked races to finish loading
|
||||
unsetPosition=Unset position
|
||||
errorTryingToUpdateMarkProperties=Error trying to update mark properties: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Really remove positioning information for mark properties {0}?runAngles=Run angles
|
||||
@@ -2424,4 +2426,5 @@ addYellowBrickConfiguration=Add YellowBrick Configuration
|
||||
filterYellowBrickConfigurations=Filter YellowBrick Configuration
|
||||
yellowBrickEvents=YellowBrick Events
|
||||
timePointOfLastFix=Time point of last fix
|
||||
copyMarkDeviceMappings=Copy mark device mappings?
|
||||
copyMarkDeviceMappings=Copy mark device mappings?
|
||||
errorLoadingPolarDataForBoatClass=Error loading polar data for boat class {0}: {1}
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Ingen kapsejladser tilgængelige
|
||||
loadingRegattasAndRaces=Indlæser regattaer og kapsejladser
|
||||
selectFromRacesWithOverlappingTimeRange=Vælg fra kapsejladserne med tidsintervaller, der overlapper med mediespor, som sporet skal knyttes til
|
||||
finishTime=Slut kl.: {0}
|
||||
finishDuration=Slut varighed: {0}
|
||||
showCompetitorCharts=Vis deltagerdiagram
|
||||
showWindChart=Vis vinddiagram
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Af hensyn til beskyttelsen af personlige oplysninger kræver e-mail-meddelelser, at du indtaster din e-mail-adresse på brugerprofilsiden og bekræfter denne e-mail-adresse.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Kommende
|
||||
eventRegattaLeaderboardLastScoreUpdate=Seneste opdatering af point
|
||||
eventRegattaRaceNotTracked=Ikke sporet
|
||||
footerJobs=Job
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing på
|
||||
footerLegal=Ansvarsbegrænsning
|
||||
footerPrivacy=Databeskyttelse
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Plat læns vinkler
|
||||
exportTrackedRacesAndStartTracking=Importer sporede kapsejladser og start sporing
|
||||
exportTrackedRacesAndStartTrackingTooltip=Import de sporede kapsejladser, start sporingen, og tilføj konnektivitetsparametrene til konnektorfanen.
|
||||
importTrackedRaces=Importerer sporede kapsejladser
|
||||
waitingForTrackedRacesToFinishLoading=Venter på, at sporede kapsejladser er indlæst
|
||||
unsetPosition=Resæt position
|
||||
errorTryingToUpdateMarkProperties=Fejl ved forsøg på at opdatere mærkeegenskaber: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Er du sikker på, at du vil fjerne positionsoplysninger for mærkeegenskaber {0}?runAngles=Run angles
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtrer YellowBrick-konfiguration
|
||||
yellowBrickEvents=YellowBrick-begivenheder
|
||||
timePointOfLastFix=Tidspunkt for seneste registrering
|
||||
copyMarkDeviceMappings=Kopier mærkeenhedsallokeringer?
|
||||
errorLoadingPolarDataForBoatClass=Fejl ved indlæsning af polardata for bådklasse {0}: {1}
|
||||
|
||||
+5
-2
@@ -1603,6 +1603,7 @@ noRacesAvailable=Keine Rennen verfügbar
|
||||
loadingRegattasAndRaces=Regatten und Rennen werden geladen
|
||||
selectFromRacesWithOverlappingTimeRange=Das oder die Rennen aus der Liste derer zum Verknüpfen auswählen, die zeitlich mit dem Medien-Track überlappen
|
||||
finishTime=Ende um: {0}
|
||||
finishDuration=Zieldauer: {0}
|
||||
showCompetitorCharts=Teilnehmer-Diagramme zeigen
|
||||
showWindChart=Windiagramm zeigen
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Aus Gründen des Datenschutzes können Benachrichtigungen nur verwendet werden, wenn im Benutzerprofil eine gültige Mail-Adresse angegeben und diese verfifiziert wurde.
|
||||
@@ -1664,7 +1665,7 @@ seriesHeaderUpcoming=Demnächst
|
||||
eventRegattaLeaderboardLastScoreUpdate=Letztes Ergebnis-Update
|
||||
eventRegattaRaceNotTracked=Nicht getrackt
|
||||
footerJobs=Jobs
|
||||
footerCopyright=\u00A9 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=\u00A9 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing in
|
||||
footerLegal=Impressum
|
||||
footerPrivacy=Datenschutz
|
||||
@@ -2354,6 +2355,7 @@ unsetPosition=Positionsangabe entfernen
|
||||
exportTrackedRacesAndStartTracking=Getrackte Rennen importieren und Tracking starten
|
||||
exportTrackedRacesAndStartTrackingTooltip=Getrackte Rennen importieren, Tracking starten und Konnektor-spezifische Informationen zum Konnektoren-Tab hinzufügen
|
||||
importTrackedRaces=Getrackte Rennen werden importiert
|
||||
waitingForTrackedRacesToFinishLoading=Warten auf erfolgreiches Laden der getrackten Rennen
|
||||
errorTryingToUpdateMarkProperties=Fehler beim Ändern der Bahnmarkeneigenschaften: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Positionierungsinformation für Bahnmarkeneigenschaften {0} wirklich entfernen?
|
||||
useOfficialResultsForAutomaticUpdates=Automatisch von "offiziellen" von TracTrac veröffentlichten Ergebnissen aktualisieren
|
||||
@@ -2434,4 +2436,5 @@ addYellowBrickConfiguration=YellowBrick-Konfiguration hinzufügen
|
||||
filterYellowBrickConfigurations=YellowBrick-Konfigurationen filtern
|
||||
yellowBrickEvents=YellowBrick Events
|
||||
timePointOfLastFix=Zeitpunkt der letzten Position
|
||||
copyMarkDeviceMappings=Gerätezuordnungen für Bahnmarken übernehmen?
|
||||
copyMarkDeviceMappings=Gerätezuordnungen für Bahnmarken übernehmen?
|
||||
errorLoadingPolarDataForBoatClass=Fehler beim Laden der Polardaten zu Bootsklasse {0}: {1}
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=No hay carreras disponibles
|
||||
loadingRegattasAndRaces=Cargando regatas y carreras
|
||||
selectFromRacesWithOverlappingTimeRange=Seleccione las carreras cuyo intervalo se solapa con el seguimiento medio de aquellas a las que se debe vincular el seguimiento
|
||||
finishTime=Llegada a las: {0}
|
||||
finishDuration=Duración finalización: {0}
|
||||
showCompetitorCharts=Mostrar gráfico de competidores
|
||||
showWindChart=Mostrar gráfico de vientos
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Por motivos de privacidad, las notificaciones de correo electrónico requieren que introduzca su dirección de correo electrónico en su página de perfil de usuario y verifique esta dirección de correo electrónico.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Próximo
|
||||
eventRegattaLeaderboardLastScoreUpdate=Última actualización de puntuación
|
||||
eventRegattaRaceNotTracked=No rastreado
|
||||
footerJobs=Jobs
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing en
|
||||
footerLegal=Publicidad legal
|
||||
footerPrivacy=Privacidad
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Ángulos de popa
|
||||
exportTrackedRacesAndStartTracking=Importar carreras rastreadas e iniciar rastreo
|
||||
exportTrackedRacesAndStartTrackingTooltip=Importar carreras rastreadas e iniciar rastreo y añadir los parámetros de conectividad a la pestaña Conectores
|
||||
importTrackedRaces=Importando carreras rastreadas
|
||||
waitingForTrackedRacesToFinishLoading=Esperando a que las carreras rastreadas terminen de cargarse
|
||||
unsetPosition=Posición no fijada
|
||||
errorTryingToUpdateMarkProperties=Error al intentar actualizar las propiedades de la marca: {0}
|
||||
confirmUnsettingPositionForMarkProperties=¿Desea realmente eliminar la información de las propiedades de las marcas {0}?runAngles=Ángulos de popa
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtrar configuración YellowBrick
|
||||
yellowBrickEvents=Eventos YellowBrick
|
||||
timePointOfLastFix=Momento de última corrección
|
||||
copyMarkDeviceMappings=¿Copiar las asignaciones de los dispositivos de marca?
|
||||
errorLoadingPolarDataForBoatClass=Error al cargar los datos polares para la clase de embarcación {0}: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Aucune course disponible
|
||||
loadingRegattasAndRaces=Chargement de régates et de courses
|
||||
selectFromRacesWithOverlappingTimeRange=Sélectionnez parmi les courses dont l''intervalle de temps de la piste média se chevauche avec l''intervalle des courses auxquelles la piste doit être liée.
|
||||
finishTime=Arrivée à : {0}
|
||||
finishDuration=Durée d''arrivée : {0}
|
||||
showCompetitorCharts=Afficher graphique de concurrents
|
||||
showWindChart=Afficher graphique du vent
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Pour des raisons de confidentialité, les notifications par mail requièrent la saisie et vérification de votre adresse e-mail sur la page du profil utilisateur.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=à venir
|
||||
eventRegattaLeaderboardLastScoreUpdate=Dernière mise à jour des scores
|
||||
eventRegattaRaceNotTracked=Non suivi(e)
|
||||
footerJobs=Carrières
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing in
|
||||
footerLegal=Mentions légales
|
||||
footerPrivacy=Confidentialité des données
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Angles de course
|
||||
exportTrackedRacesAndStartTracking=Importer les courses suivies et lancer le suivi
|
||||
exportTrackedRacesAndStartTrackingTooltip=Importer les courses suivies, lancer le suivi et ajouter les paramètres de connectivité dans le volet Connecteurs
|
||||
importTrackedRaces=Importation des courses suivies en cours
|
||||
waitingForTrackedRacesToFinishLoading=En attente de la fin du chargement des courses suivies
|
||||
unsetPosition=Réinitialiser la position
|
||||
errorTryingToUpdateMarkProperties=Erreur lors de la tentative de mise à jour des propriétés de marque : {0}
|
||||
confirmUnsettingPositionForMarkProperties=Voulez-vous vraiment effacer les informations de localisation pour les propriétés de marque {0} ?runAngles=Run angles
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtrer par configuration YellowBrick
|
||||
yellowBrickEvents=Événéments YellowBrick
|
||||
timePointOfLastFix=Moment du dernier point
|
||||
copyMarkDeviceMappings=Copier les mappages d''appareil de marques ?
|
||||
errorLoadingPolarDataForBoatClass=Erreur lors du chargement des données polaires pour la catégorie de bateau {0} : {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Nessuna gara disponibile
|
||||
loadingRegattasAndRaces=Caricamento regate e gare
|
||||
selectFromRacesWithOverlappingTimeRange=Seleziona dalle gare il cui intervallo di tempo si sovrappone al tracciamento multimediale quelle a cui il tracciamento verrà collegato
|
||||
finishTime=Ora di fine: {0}
|
||||
finishDuration=Tempo di completamento: {0}
|
||||
showCompetitorCharts=Mostra grafico concorrenti
|
||||
showWindChart=Mostra grafico venti
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Per motivi di privacy, le notifiche e-mail richiedono l''inserimento dell''indirizzo e-mail nella pagina del profilo utente e la verifica di tale indirizzo.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Prossimamente
|
||||
eventRegattaLeaderboardLastScoreUpdate=Ultimo aggiornamento punteggio
|
||||
eventRegattaRaceNotTracked=Tracciamento non eseguito
|
||||
footerJobs=Jobs
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing in
|
||||
footerLegal=Informativa legale
|
||||
footerPrivacy=Privacy
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Angoli di lasco
|
||||
exportTrackedRacesAndStartTracking=Importa gare tracciate e avvia tracciamento
|
||||
exportTrackedRacesAndStartTrackingTooltip=Importa le gare tracciate, avvia il tracciamento e aggiungi i parametri di connettività alla scheda dei connettori
|
||||
importTrackedRaces=Importazione gare tracciate
|
||||
waitingForTrackedRacesToFinishLoading=In attesa di completamento del caricamento delle gare tracciate
|
||||
unsetPosition=Annulla parametrizzazione posizione
|
||||
errorTryingToUpdateMarkProperties=Errore durante il tentativo di aggiornare le proprietà segnale: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Confermare l''eliminazione delle informazioni di posizione per le proprietà del segnale {0}?angolidilasco= Angoli di lasco
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtra configurazione YellowBrick
|
||||
yellowBrickEvents=Eventi YellowBrick
|
||||
timePointOfLastFix=Momento dell''ultimo punto nave
|
||||
copyMarkDeviceMappings=Copiare il mappaggio del dispositivo segnali?
|
||||
errorLoadingPolarDataForBoatClass=Errore nel caricamento dei dati polari per la classe barche {0}: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=利用可能なレースはありません
|
||||
loadingRegattasAndRaces=レガッタとレースをロード中
|
||||
selectFromRacesWithOverlappingTimeRange=トラックがリンクされるメディアトラックと時間範囲が重複しているレースから選択してください
|
||||
finishTime=フィニッシュ時刻: {0}
|
||||
finishDuration=フィニッシュ期間: {0}
|
||||
showCompetitorCharts=競技者チャート表示
|
||||
showWindChart=風チャート表示
|
||||
userNotificationsOnlyIfMailAddressIsVerified=個人情報保護上の理由から、メールの通知には、ユーザプロファイルページでメールアドレスを入力し、このメールアドレスを確認する必要があります。
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=予定
|
||||
eventRegattaLeaderboardLastScoreUpdate=前回スコア更新
|
||||
eventRegattaRaceNotTracked=未追跡
|
||||
footerJobs=採用
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing -
|
||||
footerLegal=利用規約
|
||||
footerPrivacy=個人情報保護
|
||||
@@ -2337,6 +2338,7 @@ runAngles=ランニングアングル
|
||||
exportTrackedRacesAndStartTracking=追跡レースをインポートし、追跡を開始
|
||||
exportTrackedRacesAndStartTrackingTooltip=追跡レースをインポートし、追跡を開始し、コネクタタブに接続パラメータを追加
|
||||
importTrackedRaces=追跡レースのインポート
|
||||
waitingForTrackedRacesToFinishLoading=ロード終了のために追跡レースを待機
|
||||
unsetPosition=位置の設定解除
|
||||
errorTryingToUpdateMarkProperties=マークプロパティを更新しようとしてエラーが発生しました: {0}
|
||||
confirmUnsettingPositionForMarkProperties=マークプロパティ {0} の位置決め情報を削除しますか。runAngles=Run angles
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=YellowBrick 設定のフィルタリング
|
||||
yellowBrickEvents=YellowBrick イベント
|
||||
timePointOfLastFix=最終フィックスの時点
|
||||
copyMarkDeviceMappings=マークデバイスマッピングをコピーしますか?
|
||||
errorLoadingPolarDataForBoatClass=艇種 {0} のポーラーデータのロードでエラーが発生: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Nenhuma corrida disponível
|
||||
loadingRegattasAndRaces=Carregando regatas e corridas
|
||||
selectFromRacesWithOverlappingTimeRange=Das corridas cujo intervalo de tempo se sobrepõe com a faixa de mídia, selecione aquelas às quais deve ser ligada a pista
|
||||
finishTime=Chegada às: {0}
|
||||
finishDuration=Duração de chegada: {0}
|
||||
showCompetitorCharts=Visualizar diagrama do competidor
|
||||
showWindChart=Visualizar carta de ventos
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Por motivos de privacidade, as notificações por e-mail requerem que você insira seu endereço de e-mail na página de perfil do usuário e que verifique esse endereço de e-mail.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Próximo
|
||||
eventRegattaLeaderboardLastScoreUpdate=Atualização da última pontuação
|
||||
eventRegattaRaceNotTracked=Não rastreado
|
||||
footerJobs=Tarefas
|
||||
footerCopyright=© 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing em
|
||||
footerLegal=Divulgação de informação legal
|
||||
footerPrivacy=Privacidade
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Ângulos de curso
|
||||
exportTrackedRacesAndStartTracking=Importar corridas rastreadas e iniciar rastreamento
|
||||
exportTrackedRacesAndStartTrackingTooltip=Importar as corridas rastreadas, iniciar o rastreamento e adicionar os parâmetros de conectividade à ficha Conectores
|
||||
importTrackedRaces=Importando corridas rastreadas
|
||||
waitingForTrackedRacesToFinishLoading=Aguardando a conclusão do carregamento das corridas rastreadas
|
||||
unsetPosition=Remover definição de posição
|
||||
errorTryingToUpdateMarkProperties=Erro ao tentar atualizar propriedades da marca: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Remover informações de posicionamento para propriedades da marca {0}?
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtrar configuração YellowBrick
|
||||
yellowBrickEvents=Eventos YellowBrick
|
||||
timePointOfLastFix=Data/hora do último ponto fixo
|
||||
copyMarkDeviceMappings=Copiar mapeamentos de dispositivo de marcas?
|
||||
errorLoadingPolarDataForBoatClass=Erro ao carregar dados da carta polar para a classe de barcos {0}: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Нет доступных гонок
|
||||
loadingRegattasAndRaces=Загрузка регат и гонок
|
||||
selectFromRacesWithOverlappingTimeRange=Выберите из гонок, временной диапазон которых перекрывается с объектом мультимедиа, те, с которыми должен быть связан данный объект
|
||||
finishTime=Финиш в: {0}
|
||||
finishDuration=Длительность финиша: {0}
|
||||
showCompetitorCharts=Показать диаграмму участника
|
||||
showWindChart=Показать диаграмму ветра
|
||||
userNotificationsOnlyIfMailAddressIsVerified=В целях безопасности для уведомления по почте необходимо ввести почтовый адрес на странице профиля пользователя и подтвердить этот адрес.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Скоро
|
||||
eventRegattaLeaderboardLastScoreUpdate=Последнее обновление оценок
|
||||
eventRegattaRaceNotTracked=Не отслеживается
|
||||
footerJobs=Работа
|
||||
footerCopyright=© SAP Sailing Analytics, 2011-2021.
|
||||
footerCopyright=© SAP Sailing Analytics, 2011-2022.
|
||||
footerLanguage=Язык SAP Sailing
|
||||
footerLegal=Раскрытие юридической информации
|
||||
footerPrivacy=Конфиденциальность
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Углы по ветру
|
||||
exportTrackedRacesAndStartTracking=Импортировать отслеживаемые гонки и начать отслеживание
|
||||
exportTrackedRacesAndStartTrackingTooltip=Импортируйте отслеживаемые гонки, начните отслеживание и добавьте параметры соединения на вкладке "Соединители"
|
||||
importTrackedRaces=Импорт отслеживаемых гонок
|
||||
waitingForTrackedRacesToFinishLoading=Ожидание окончания загрузки отслеживаемых гонок
|
||||
unsetPosition=Неустановленная позиция
|
||||
errorTryingToUpdateMarkProperties=Ошибка при попытке обновить свойства знака: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Действительно удалить информацию о положении для свойств знака {0}?runAngles=Углы по ветру
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Фильтровать конфигурацию
|
||||
yellowBrickEvents=События YellowBrick
|
||||
timePointOfLastFix=Момент времени последней фиксации
|
||||
copyMarkDeviceMappings=Скопировать сопоставления устройства отметки
|
||||
errorLoadingPolarDataForBoatClass=Ошибка при загрузке полярных данных для класса лодки {0}: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=Plovi niso na voljo
|
||||
loadingRegattasAndRaces=Nalaganje regat in plovov
|
||||
selectFromRacesWithOverlappingTimeRange=Izberite od plovov, čigar interval se prekriva s posnetkom medija, do plovov, do katerih bo posnetek povezan
|
||||
finishTime=Konec: {0}
|
||||
finishDuration=Trajanje zaključka: {0}
|
||||
showCompetitorCharts=Pokaži karto tekmovalca
|
||||
showWindChart=Pokaži vetrovno karto
|
||||
userNotificationsOnlyIfMailAddressIsVerified=Zaradi zasebnosti je treba za e-poštna obvestila vnesti e-poštni naslov na strani uporabniškega profila in preveriti ta e-poštni naslov.
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=Kmalu
|
||||
eventRegattaLeaderboardLastScoreUpdate=Posodobitev zadnjega rezultata
|
||||
eventRegattaRaceNotTracked=Brez sledenja
|
||||
footerJobs=Opravila
|
||||
footerCopyright=© 2011–2021 SAP Sailing Analytics
|
||||
footerCopyright=© 2011–2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing v
|
||||
footerLegal=Pravno razkritje
|
||||
footerPrivacy=Zasebnost
|
||||
@@ -2337,6 +2338,7 @@ runAngles=Koti jadranja z vetrom
|
||||
exportTrackedRacesAndStartTracking=Uvozi sledene plove in začni sledenje
|
||||
exportTrackedRacesAndStartTrackingTooltip=Uvozite sledene plove, začnite sledenje in dodajte parametre povezljivosti v jeziček Povezovalniki
|
||||
importTrackedRaces=Uvažanje sledenih plovov
|
||||
waitingForTrackedRacesToFinishLoading=Čakanje na končanje prenosa sledenih plovov
|
||||
unsetPosition=Prekliči nastavitev položaja
|
||||
errorTryingToUpdateMarkProperties=Napaka pri poskusu posodobitve lastnosti oznake: {0}
|
||||
confirmUnsettingPositionForMarkProperties=Ali res želite odstraniti informacije o določitvi položaja za lastnosti oznake {0}?runAngles=koti jadranja z vetrom
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=Filtriranje konfiguracije YellowBricka
|
||||
yellowBrickEvents=Dogodki YellowBrick
|
||||
timePointOfLastFix=Časovna točka zadnjega položaja
|
||||
copyMarkDeviceMappings=Želite kopirati preslikave označevalne naprave?
|
||||
errorLoadingPolarDataForBoatClass=Napaka pri nalaganju podatkov za polarni diagram za kategorijo jadrnice {0}: {1}
|
||||
|
||||
+4
-1
@@ -1613,6 +1613,7 @@ noRacesAvailable=比赛轮次不可用
|
||||
loadingRegattasAndRaces=正在加载比赛和轮次
|
||||
selectFromRacesWithOverlappingTimeRange=从时间范围与应链接轨道的媒体轨道重叠的比赛轮次中选择
|
||||
finishTime=结束时间:{0}
|
||||
finishDuration=结束持续时间:{0}
|
||||
showCompetitorCharts=显示参赛队记录
|
||||
showWindChart=显示风力记录
|
||||
userNotificationsOnlyIfMailAddressIsVerified=由于隐私原因,邮件通知需要您在用户参数文件页面输入邮件地址,并验证此邮件地址。
|
||||
@@ -1674,7 +1675,7 @@ seriesHeaderUpcoming=即将到来
|
||||
eventRegattaLeaderboardLastScoreUpdate=上次得分更新
|
||||
eventRegattaRaceNotTracked=未跟踪
|
||||
footerJobs=招贤纳士
|
||||
footerCopyright=版权所有 © 2011-2021 SAP Sailing Analytics
|
||||
footerCopyright=版权所有 © 2011-2022 SAP Sailing Analytics
|
||||
footerLanguage=SAP Sailing 语言
|
||||
footerLegal=法律声明
|
||||
footerPrivacy=隐私
|
||||
@@ -2337,6 +2338,7 @@ runAngles=顺风航行角度
|
||||
exportTrackedRacesAndStartTracking=导入跟踪的比赛轮次并开始跟踪
|
||||
exportTrackedRacesAndStartTrackingTooltip=导入跟踪的比赛,开始跟踪并将连接参数添加到连接器选项卡
|
||||
importTrackedRaces=导入跟踪的比赛轮次
|
||||
waitingForTrackedRacesToFinishLoading=正在等待跟踪的比赛轮次完成加载
|
||||
unsetPosition=取消设置位置
|
||||
errorTryingToUpdateMarkProperties=尝试更新标志属性时发生错误:{0}
|
||||
confirmUnsettingPositionForMarkProperties=是否确定移除标志属性 {0} 的位置信息?runAngles=顺风航行角度
|
||||
@@ -2415,3 +2417,4 @@ filterYellowBrickConfigurations=筛选 YellowBrick 配置
|
||||
yellowBrickEvents=YellowBrick 活动
|
||||
timePointOfLastFix=上次修复的时间点
|
||||
copyMarkDeviceMappings=复制标志设备映射?
|
||||
errorLoadingPolarDataForBoatClass=为船只级别{0}加载极坐标数据出错:{1}
|
||||
|
||||
+2
-3
@@ -1521,7 +1521,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
|
||||
quickFlagDataProvider.quickRanksReceivedFromServer(raceMapDataDTO.quickRanks);
|
||||
if (isSimulationEnabled && settings.isShowSimulationOverlay()) {
|
||||
lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber;
|
||||
simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion);
|
||||
simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion);
|
||||
}
|
||||
Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> boatData = raceMapDataDTO.boatPositions;
|
||||
Map<CompetitorDTO, Double> quickSpeedsFromServerInKnots = getCompetitorsSpeedInKnotsMap(boatData);
|
||||
@@ -3192,7 +3192,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
|
||||
private void showSimulationOverlay(boolean visible) {
|
||||
simulationOverlay.setVisible(visible);
|
||||
if (visible) {
|
||||
simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */);
|
||||
simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), true, -1 /* ensure ui-update */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3225,7 +3225,6 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
|
||||
}
|
||||
return newBounds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected {
|
||||
|
||||
+11
-8
@@ -1020,6 +1020,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
if (raceLog.getLastRawFix() != null) {
|
||||
raceInfoDTO.lastUpdateTime = raceLog.getLastRawFix().getCreatedAt().asDate();
|
||||
}
|
||||
TimePoint finishingTime = state.getFinishingTime();
|
||||
if (finishingTime != null) {
|
||||
raceInfoDTO.finishingTime = finishingTime.asDate();
|
||||
} else {
|
||||
raceInfoDTO.finishingTime = null;
|
||||
}
|
||||
TimePoint finishedTime = state.getFinishedTime();
|
||||
if (finishedTime != null) {
|
||||
raceInfoDTO.finishedTime = finishedTime.asDate();
|
||||
@@ -1598,18 +1604,18 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
rcDTO.coursePositions.waypointPositions = new ArrayList<Position>();
|
||||
rcDTO.coursePositions.waypointPositions.add(simulationResults.getStartPosition());
|
||||
rcDTO.coursePositions.waypointPositions.add(simulationResults.getEndPosition());
|
||||
result = new SimulatorResultsDTO(simulationResults.getVersion().asMillis(), legIdentifier.getLegNumber()+1, simulationResults.getStartTime(), simulationResults.getTimeStep(),
|
||||
simulationResults.getLegDuration(), rcDTO, pathDTOs, null, null);
|
||||
result = new SimulatorResultsDTO(simulationResults.getVersion().asMillis(),
|
||||
legIdentifier.getOneBasedLegIndex(), simulationResults.getStartTime(),
|
||||
simulationResults.getTimeStep(), simulationResults.getLegDuration(), rcDTO, pathDTOs, null,
|
||||
null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SimulatorWindDTO createSimulatorWindDTO(TimedPositionWithSpeed timedPositionWithSpeed) {
|
||||
|
||||
Position position = timedPositionWithSpeed.getPosition();
|
||||
SpeedWithBearing speedWithBearing = timedPositionWithSpeed.getSpeed();
|
||||
TimePoint timePoint = timedPositionWithSpeed.getTimePoint();
|
||||
|
||||
SimulatorWindDTO result = new SimulatorWindDTO();
|
||||
if (speedWithBearing == null) {
|
||||
result.trueWindBearingDeg = 0.0;
|
||||
@@ -1618,15 +1624,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
result.trueWindBearingDeg = speedWithBearing.getBearing().getDegrees();
|
||||
result.trueWindSpeedInKnots = speedWithBearing.getKnots();
|
||||
}
|
||||
|
||||
if (position != null) {
|
||||
result.position = position;
|
||||
}
|
||||
|
||||
if (timePoint != null) {
|
||||
result.timepoint = timePoint.asMillis();
|
||||
result.timepoint = timePoint;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+199
-223
@@ -9,11 +9,17 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.common.PathType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
@@ -22,6 +28,7 @@ import com.sap.sailing.domain.common.dto.BoatClassDTO;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.gwt.ui.client.SimulatorService;
|
||||
import com.sap.sailing.gwt.ui.shared.BoatClassDTOsAndNotificationMessage;
|
||||
import com.sap.sailing.gwt.ui.shared.ConfigurationException;
|
||||
@@ -50,6 +57,7 @@ import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplay;
|
||||
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplayManager;
|
||||
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternNotFoundException;
|
||||
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternSetting;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.server.interfaces.SimulationService;
|
||||
import com.sap.sailing.server.simulation.SimulationServiceFactory;
|
||||
import com.sap.sailing.simulator.BoatClassProperties;
|
||||
@@ -83,12 +91,13 @@ import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
import com.sap.sse.util.ThreadPoolUtil;
|
||||
|
||||
public class SimulatorServiceImpl extends RemoteServiceServlet implements SimulatorService {
|
||||
|
||||
private static final long serialVersionUID = 4445427185387524086L;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SimulatorServiceImpl.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger("com.sap.sailing");
|
||||
private static final WindFieldGeneratorFactory wfGenFactory = WindFieldGeneratorFactory.INSTANCE;
|
||||
private static final WindPatternDisplayManager wpDisplayManager = WindPatternDisplayManager.INSTANCE;
|
||||
@@ -103,12 +112,108 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
private WindControlParameters controlParameters = new WindControlParameters(0, 0);
|
||||
private SpeedWithBearing averageWind = null;
|
||||
|
||||
public SimulatorServiceImpl() {
|
||||
final ScheduledExecutorService simulatorExecutor = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor();
|
||||
// TODO: initialize smart-future-cache for simulation-results and add to simulation-service
|
||||
simulationService = SimulationServiceFactory.INSTANCE.getService(simulatorExecutor, null);
|
||||
}
|
||||
private final PolarDiagramCache polarDiagramCache;
|
||||
|
||||
private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
|
||||
|
||||
/**
|
||||
* The Simulator service references boat classes and their polar data by numeric indices. In order for this to work
|
||||
* reliably, also in the face of possibly changing orders and numbers of boat classes as returned by
|
||||
* {@link PolarDataService#getAllBoatClassesWithPolarSheetsAvailable()} and an initial set of polar diagrams
|
||||
* as obtained from the {@link ConfigurationManager#getBoatClassesInfo()} method, a stable indexing mechanism
|
||||
* is required. This cache implements such a mechanism by first installing all polar diagrams from
|
||||
* {@link ConfigurationManager#getBoatClassesInfo()}, and then dynamically adding boat classes from
|
||||
* {@link PolarDataService#getAllBoatClassesWithPolarSheetsAvailable()}
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
private static class PolarDiagramCache {
|
||||
/**
|
||||
* Result of {@link ConfigurationManager#getErrorMessage()} in case there was an error reading the pre-defined
|
||||
* polar diagram data; {@code null} otherwise.
|
||||
*/
|
||||
private final String notificationMessage;
|
||||
|
||||
private final List<BoatClassDTO> boatClasses;
|
||||
|
||||
private final ConcurrentMap<String, Integer> boatClassesByNameForQuickLookup;
|
||||
|
||||
private final ConcurrentMap<String, PolarDiagram> predefinedPolarsByBoatClassName;
|
||||
|
||||
private final SimulationService simulationService;
|
||||
|
||||
public PolarDiagramCache(SimulationService simulationService) {
|
||||
this.simulationService = simulationService;
|
||||
boatClasses = new ArrayList<>();
|
||||
boatClassesByNameForQuickLookup = new ConcurrentHashMap<>();
|
||||
predefinedPolarsByBoatClassName = new ConcurrentHashMap<>();
|
||||
String messageToUse = null;
|
||||
ConfigurationManager config = ConfigurationManager.INSTANCE;
|
||||
if (config.getStatus() == ReadingConfigurationFileStatus.IO_ERROR) {
|
||||
messageToUse = config.getErrorMessage();
|
||||
} else if (config.getStatus() == ReadingConfigurationFileStatus.ERROR_FINDING_CONFIG_FILE
|
||||
|| config.getStatus() == ReadingConfigurationFileStatus.ERROR_READING_ENV_VAR_VALUE) {
|
||||
messageToUse = config.getErrorMessage();
|
||||
}
|
||||
for (BoatClassProperties tuple : ConfigurationManager.INSTANCE.getBoatClassesInfo()) {
|
||||
final BoatClassDTO boatClass = new BoatClassDTO(tuple.getName(), tuple.getLength(), null);
|
||||
addBoatClass(boatClass);
|
||||
try {
|
||||
predefinedPolarsByBoatClassName.put(boatClass.getName(), new PolarDiagramCSV(tuple.getPolar()));
|
||||
} catch (IOException exception) {
|
||||
messageToUse = "An IO error occured when parsing the CSV file! The original error message is " + exception.getMessage();
|
||||
}
|
||||
}
|
||||
notificationMessage = messageToUse;
|
||||
}
|
||||
|
||||
private synchronized void addBoatClass(final BoatClassDTO boatClass) {
|
||||
if (!boatClassesByNameForQuickLookup.containsKey(boatClass.getName())) {
|
||||
final int index = boatClasses.size();
|
||||
boatClasses.add(boatClass);
|
||||
boatClassesByNameForQuickLookup.put(boatClass.getName(), index);
|
||||
}
|
||||
}
|
||||
|
||||
String getNotificationMessage() {
|
||||
return notificationMessage;
|
||||
}
|
||||
|
||||
BoatClassDTO getBoatClass(int boatClassIndex) {
|
||||
return boatClassIndex < 0 || boatClassIndex >= boatClasses.size() ? null : boatClasses.get(boatClassIndex);
|
||||
}
|
||||
|
||||
PolarDiagram getPolarDiagram(BoatClassDTO boatClass) {
|
||||
final PolarDiagram predefinedPolar = predefinedPolarsByBoatClassName.get(boatClass.getName());
|
||||
final PolarDiagram result;
|
||||
if (predefinedPolar != null) {
|
||||
result = predefinedPolar;
|
||||
} else {
|
||||
result = simulationService.getPolarDiagram(simulationService.getBoatClass(boatClass.getName()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public BoatClassDTO[] getBoatClasses() {
|
||||
updateBoatClassesFromPolarDataService();
|
||||
return boatClasses.toArray(new BoatClassDTO[boatClasses.size()]);
|
||||
}
|
||||
|
||||
private void updateBoatClassesFromPolarDataService() {
|
||||
for (final BoatClass boatClassWithPolarData : simulationService.getBoatClassesWithPolarData()) {
|
||||
addBoatClass(new BoatClassDTO(boatClassWithPolarData.getName(), boatClassWithPolarData.getHullLength(), boatClassWithPolarData.getHullBeam()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SimulatorServiceImpl() throws InterruptedException {
|
||||
final ScheduledExecutorService simulatorExecutor = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor();
|
||||
racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(Activator.getDefault(), RacingEventService.class);
|
||||
// TODO: initialize smart-future-cache for simulation-results and add to simulation-service
|
||||
simulationService = SimulationServiceFactory.INSTANCE.getService(simulatorExecutor, racingEventServiceTracker.waitForService(0));
|
||||
polarDiagramCache = new PolarDiagramCache(simulationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position[] getRaceLocations() {
|
||||
@@ -190,7 +295,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
Grid bd = new CurvedGrid(start, end);
|
||||
|
||||
controlParameters.resetBlastRandomStream = params.isKeepState();
|
||||
retreiveWindControlParameters(pattern);
|
||||
retrieveWindControlParameters(pattern);
|
||||
LOGGER.info("Boundary south direction " + bd.getSouth());
|
||||
controlParameters.baseWindBearing += bd.getSouth().getDegrees();
|
||||
|
||||
@@ -250,7 +355,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
Duration timeStep = params.getTimeStep();
|
||||
|
||||
this.controlParameters.resetBlastRandomStream = params.isKeepState();
|
||||
this.retreiveWindControlParameters(pattern);
|
||||
this.retrieveWindControlParameters(pattern);
|
||||
if (rcDirection == SailingSimulatorConstants.LegTypeDownwind) {
|
||||
this.controlParameters.baseWindBearing += 180.0;
|
||||
}
|
||||
@@ -260,7 +365,6 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
if (wf == null) {
|
||||
throw new WindPatternNotFoundException("Please select a valid wind pattern.");
|
||||
}
|
||||
|
||||
if (mode != SailingSimulatorConstants.ModeMeasured) {
|
||||
Position start = params.getRaceCourseStart();
|
||||
Position end = params.getRaceCourseEnd();
|
||||
@@ -271,124 +375,95 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
gridAreaGps = course.toArray(gridAreaGps);
|
||||
wf.setGridAreaGps(gridAreaGps);
|
||||
}
|
||||
|
||||
int[] gridRes = new int[4];
|
||||
gridRes[0] = params.getxRes();
|
||||
gridRes[1] = params.getyRes();
|
||||
gridRes[2] = params.getBorderY();
|
||||
gridRes[3] = params.getBorderX();
|
||||
wf.setGridResolution(gridRes);
|
||||
|
||||
wf.generate(startTime, null, timeStep);
|
||||
Long longestPathTime = 0L;
|
||||
|
||||
Duration longestPathTime = Duration.NULL;
|
||||
SimulatedPathsEvenTimedResultDTO simulatedPaths = this.getSimulatedPathsEvenTimed(course, wf, mode, selection,
|
||||
params.showOmniscient, params.showOpportunist);
|
||||
PathDTO[] pathDTOs = simulatedPaths.pathDTOs;
|
||||
RaceMapDataDTO rcDTO = simulatedPaths.raceMapDataDTO;
|
||||
|
||||
for (PathDTO path : pathDTOs) {
|
||||
if (path.getName().equals(POLYLINE_PATH_NAME)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<SimulatorWindDTO> points = path.getPoints();
|
||||
long pathTime = points.get(points.size() - 1).timepoint - points.get(0).timepoint;
|
||||
longestPathTime = Math.max(longestPathTime, pathTime);
|
||||
final Duration pathTime = points.get(0).timepoint.until(points.get(points.size() - 1).timepoint);
|
||||
if (pathTime.compareTo(longestPathTime) > 0) {
|
||||
longestPathTime = pathTime;
|
||||
}
|
||||
}
|
||||
|
||||
TimePoint endTime = new MillisecondsTimePoint(startTime.asMillis() + longestPathTime);
|
||||
|
||||
TimePoint endTime = startTime.plus(longestPathTime);
|
||||
WindFieldDTO windFieldDTO = null;
|
||||
if (pattern != null) {
|
||||
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(),
|
||||
// params.isShowLines(),
|
||||
// params.getSeedLines());
|
||||
}
|
||||
|
||||
return new SimulatorResultsDTO(0, 0, null, timeStep.asMillis(), 0, rcDTO, pathDTOs, windFieldDTO,
|
||||
return new SimulatorResultsDTO(0, 0, null, timeStep, Duration.NULL, rcDTO, pathDTOs, windFieldDTO,
|
||||
simulatedPaths.notificationMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoatClassDTOsAndNotificationMessage getBoatClasses() throws ConfigurationException {
|
||||
|
||||
List<BoatClassDTO> boatClassesDTOs = new ArrayList<BoatClassDTO>();
|
||||
|
||||
BoatClassDTOsAndNotificationMessage result = new BoatClassDTOsAndNotificationMessage();
|
||||
|
||||
ConfigurationManager config = ConfigurationManager.INSTANCE;
|
||||
if (config.getStatus() == ReadingConfigurationFileStatus.IO_ERROR) {
|
||||
throw new ConfigurationException(config.getErrorMessage());
|
||||
} else if (config.getStatus() == ReadingConfigurationFileStatus.ERROR_FINDING_CONFIG_FILE
|
||||
|| config.getStatus() == ReadingConfigurationFileStatus.ERROR_READING_ENV_VAR_VALUE) {
|
||||
result.setNotificationMessage(config.getErrorMessage());
|
||||
}
|
||||
|
||||
for (BoatClassProperties tuple : ConfigurationManager.INSTANCE.getBoatClassesInfo()) {
|
||||
boatClassesDTOs.add(new BoatClassDTO(tuple.getName(), null, tuple.getLength()));
|
||||
}
|
||||
|
||||
result.setBoatClassDTOs(boatClassesDTOs.toArray(new BoatClassDTO[boatClassesDTOs.size()]));
|
||||
|
||||
final BoatClassDTOsAndNotificationMessage result = new BoatClassDTOsAndNotificationMessage();
|
||||
result.setBoatClassDTOs(polarDiagramCache.getBoatClasses());
|
||||
result.setNotificationMessage(polarDiagramCache.getNotificationMessage());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolarDiagramDTOAndNotificationMessage getPolarDiagram(Double bearingStep, int boatClassIndex)
|
||||
throws ConfigurationException {
|
||||
|
||||
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(boatClassIndex);
|
||||
|
||||
NavigableMap<Speed, NavigableMap<Bearing, Speed>> navMap = polarDiagramAndNotificationMessage.getA()
|
||||
.polarDiagramPlot(bearingStep);
|
||||
|
||||
Set<Speed> validSpeeds = navMap.keySet();
|
||||
validSpeeds.remove(Speed.NULL);
|
||||
|
||||
Number[][] series = new Number[validSpeeds.size()][];
|
||||
int i = 0;
|
||||
for (Speed s : validSpeeds) {
|
||||
Collection<Speed> boatSpeeds = navMap.get(s).values();
|
||||
series[i] = new Number[boatSpeeds.size()];
|
||||
int j = 0;
|
||||
for (Speed boatSpeed : boatSpeeds) {
|
||||
series[i][j++] = new Double(boatSpeed.getKnots());
|
||||
final PolarDiagramDTOAndNotificationMessage result;
|
||||
if (polarDiagramAndNotificationMessage != null && polarDiagramAndNotificationMessage.getA() != null) {
|
||||
NavigableMap<Speed, NavigableMap<Bearing, Speed>> navMap = polarDiagramAndNotificationMessage.getA()
|
||||
.polarDiagramPlot(bearingStep);
|
||||
Set<Speed> validSpeeds = navMap.keySet();
|
||||
validSpeeds.remove(Speed.NULL);
|
||||
Number[][] series = new Number[validSpeeds.size()][];
|
||||
int i = 0;
|
||||
for (Speed s : validSpeeds) {
|
||||
Collection<Speed> boatSpeeds = navMap.get(s).values();
|
||||
series[i] = new Number[boatSpeeds.size()];
|
||||
int j = 0;
|
||||
for (Speed boatSpeed : boatSpeeds) {
|
||||
series[i][j++] = new Double(boatSpeed.getKnots());
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
PolarDiagramDTO dto = new PolarDiagramDTO();
|
||||
dto.setNumberSeries(series);
|
||||
result = new PolarDiagramDTOAndNotificationMessage();
|
||||
result.setPolarDiagramDTO(dto);
|
||||
result.setNotificationMessage(polarDiagramAndNotificationMessage.getB());
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
PolarDiagramDTO dto = new PolarDiagramDTO();
|
||||
dto.setNumberSeries(series);
|
||||
|
||||
PolarDiagramDTOAndNotificationMessage result = new PolarDiagramDTOAndNotificationMessage();
|
||||
result.setPolarDiagramDTO(dto);
|
||||
result.setNotificationMessage(polarDiagramAndNotificationMessage.getB());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseTotalTimeDTO getTotalTime(RequestTotalTimeDTO requestData) throws ConfigurationException {
|
||||
|
||||
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils
|
||||
.getAverage(requestData.allPoints) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
|
||||
|
||||
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots())
|
||||
* (requestData.stepDurationMilliseconds / 1000.);
|
||||
|
||||
// List<Position> points = SimulatorServiceUtils.getIntermediatePoints2(requestData.turnPoints,
|
||||
// this.stepSizeMeters);
|
||||
// int noOfPointsMinus1 = points.size() - 1;
|
||||
|
||||
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
|
||||
.getPolarDiagram(requestData.selection.boatClassIndex);
|
||||
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
|
||||
String notificationMessage = polarDiagramAndNotificationMessage.getB();
|
||||
|
||||
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null,
|
||||
null, SailingSimulatorConstants.ModeMeasured, true, true));
|
||||
Path gpsTrack = simulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection));
|
||||
|
||||
// Position startPoint = null;
|
||||
// Position endPoint = null;
|
||||
// double boatBearingDeg = 0.;
|
||||
@@ -431,82 +506,59 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
//
|
||||
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1)
|
||||
.getTimePoint().asMillis() - gpsTrack.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
|
||||
|
||||
// while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
|
||||
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
|
||||
// }
|
||||
|
||||
long startTimePoint = requestData.allPoints.get(0).timepoint;
|
||||
long segmentTime = 0;
|
||||
final TimePoint startTimePoint = requestData.allPoints.get(0).timepoint;
|
||||
long segmentDurationMillis = 0;
|
||||
int segmentIndex = 0;
|
||||
long totalTime = 0;
|
||||
|
||||
long totalDurationMillis = 0;
|
||||
for (int index = 0; index < requestData.turnPoints.size() - 1; index++) {
|
||||
|
||||
segmentIndex++;
|
||||
|
||||
totalTime += segmentTime;
|
||||
|
||||
segmentTime = getTimeMillisecondsBetween(requestData.turnPoints.get(index),
|
||||
totalDurationMillis += segmentDurationMillis;
|
||||
segmentDurationMillis = getTimeMillisecondsBetween(requestData.turnPoints.get(index),
|
||||
requestData.turnPoints.get(index + 1), this.stepSizeMeters, requestData.useRealAverageWindSpeed,
|
||||
gpsTrack, polarDiagram, startTimePoint + totalTime);
|
||||
|
||||
System.out.println("Total time of segment " + segmentIndex + " = " + segmentTime + " milliseconds");
|
||||
gpsTrack, polarDiagram, startTimePoint.plus(totalDurationMillis).asMillis());
|
||||
logger.fine("Total time of segment " + segmentIndex + " = " + segmentDurationMillis + " milliseconds");
|
||||
}
|
||||
|
||||
totalTime += segmentTime;
|
||||
|
||||
long totalTimeSeconds = totalTime / 1000;
|
||||
|
||||
System.out.println("TotalTimeGPS: " + totalTimeGPSTrackSeconds + " TotalTimePoly: " + totalTimeSeconds);
|
||||
|
||||
totalDurationMillis += segmentDurationMillis;
|
||||
long totalTimeSeconds = totalDurationMillis / 1000;
|
||||
logger.fine("TotalTimeGPS: " + totalTimeGPSTrackSeconds + " TotalTimePoly: " + totalTimeSeconds);
|
||||
return new ResponseTotalTimeDTO(totalTimeSeconds, totalTimeSeconds / totalTimeGPSTrackSeconds,
|
||||
notificationMessage);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public ResponseTotalTimeDTO getTotalTimeOld(RequestTotalTimeDTO requestData) throws ConfigurationException {
|
||||
|
||||
this.averageWind = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils
|
||||
.getAverage(requestData.allPoints) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
|
||||
|
||||
this.stepSizeMeters = SimulatorServiceUtils.knotsToMetersPerSecond(this.averageWind.getKnots())
|
||||
* (requestData.stepDurationMilliseconds / 1000.);
|
||||
|
||||
List<Position> points = SimulatorServiceUtils.getIntermediatePoints2(requestData.turnPoints,
|
||||
this.stepSizeMeters);
|
||||
int noOfPointsMinus1 = points.size() - 1;
|
||||
|
||||
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
|
||||
.getPolarDiagram(requestData.selection.boatClassIndex);
|
||||
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
|
||||
String notificationMessage = polarDiagramAndNotificationMessage.getB();
|
||||
|
||||
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, polarDiagram, null,
|
||||
null, SailingSimulatorConstants.ModeMeasured, true, true));
|
||||
Path gpsTrack = simulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection));
|
||||
|
||||
Position startPoint = null;
|
||||
Position endPoint = null;
|
||||
double boatBearingDeg = 0.;
|
||||
double boatSpeedMetersPerSecond = 0.;
|
||||
double distanceMeters = 0.;
|
||||
|
||||
SimulatorWindDTO courseStartPoint = requestData.allPoints.get(0);
|
||||
long timepointAsMillis = courseStartPoint.timepoint;
|
||||
long timepointAsMillis = courseStartPoint.timepoint.asMillis();
|
||||
SpeedWithBearing windAtTimePoint = null;
|
||||
|
||||
long stepTimeMilliseconds = 0L;
|
||||
|
||||
for (int index = 0; index < noOfPointsMinus1; index++) {
|
||||
|
||||
startPoint = points.get(index);
|
||||
endPoint = points.get(index + 1);
|
||||
distanceMeters = startPoint.getDistance(endPoint).getMeters();
|
||||
|
||||
windAtTimePoint = requestData.useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(
|
||||
timepointAsMillis, gpsTrack) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
|
||||
|
||||
boatBearingDeg = SimulatorServiceUtils.getInitialBearing(startPoint, endPoint);
|
||||
polarDiagram.setWind(windAtTimePoint);
|
||||
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg))
|
||||
@@ -518,42 +570,32 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
if (boatSpeedMetersPerSecond == 0.0) {
|
||||
stepTimeMilliseconds = 1000;
|
||||
}
|
||||
|
||||
timepointAsMillis += stepTimeMilliseconds;
|
||||
|
||||
}
|
||||
|
||||
double totalTimeSeconds = (timepointAsMillis - requestData.allPoints.get(0).timepoint) / 1000;
|
||||
|
||||
double totalTimeSeconds = (timepointAsMillis - requestData.allPoints.get(0).timepoint.asMillis()) / 1000;
|
||||
double totalTimeGPSTrackSeconds = (gpsTrack.getPathPoints().get(gpsTrack.getPathPoints().size() - 1)
|
||||
.getTimePoint().asMillis() - gpsTrack.getPathPoints().get(0).getTimePoint().asMillis()) / 1000;
|
||||
|
||||
// while (totalTimeSeconds > totalTimeGPSTrackSeconds) {
|
||||
// totalTimeSeconds *= TOTAL_TIME_SCALE_FACTOR;
|
||||
// }
|
||||
System.out.println("TotalTimeGPS: " + totalTimeGPSTrackSeconds + " TotalTimePoly: " + totalTimeSeconds);
|
||||
|
||||
return new ResponseTotalTimeDTO((long) totalTimeSeconds, (double) totalTimeSeconds
|
||||
/ (double) totalTimeGPSTrackSeconds, notificationMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response1TurnerDTO get1Turner(Request1TurnerDTO requestData) throws ConfigurationException {
|
||||
|
||||
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this
|
||||
.getPolarDiagram(requestData.selection.boatClassIndex);
|
||||
PolarDiagram polarDiagram = polarDiagramAndNotificationMessage.getA();
|
||||
String notificationMessage = polarDiagramAndNotificationMessage.getB();
|
||||
|
||||
Position edgeStart = requestData.edgeStart;
|
||||
Position edgeEnd = requestData.edgeEnd;
|
||||
|
||||
Position oldMovedPosition = requestData.oldMovedPoint;
|
||||
Position newMovedPosition = requestData.newMovedPoint;
|
||||
Bearing oldMovedToNewMovedBearing = oldMovedPosition.getBearingGreatCircle(newMovedPosition);
|
||||
boolean areTowardsSameDirection = SimulatorServiceUtils.areTowardsSameDirection(oldMovedToNewMovedBearing,
|
||||
new DegreeBearingImpl(requestData.startToEndBearingDegrees));
|
||||
|
||||
System.out.println("oldMovedToNewMovedBearing = " + oldMovedToNewMovedBearing.getDegrees() + " degrees");
|
||||
System.out.println("requestData.startToEndBearingDegrees = " + requestData.startToEndBearingDegrees
|
||||
+ " degrees");
|
||||
@@ -632,53 +674,40 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
|
||||
@Override
|
||||
public List<String> getLegsNames(int selectedRaceIndex) {
|
||||
|
||||
if (selectedRaceIndex < 0) {
|
||||
selectedRaceIndex = 0;
|
||||
}
|
||||
|
||||
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
|
||||
SailingSimulatorConstants.ModeMeasured, true, true));
|
||||
|
||||
return simulator.getLegsNames(selectedRaceIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRacesNames() {
|
||||
|
||||
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
|
||||
SailingSimulatorConstants.ModeMeasured, true, true));
|
||||
|
||||
return simulator.getRacesNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getCompetitorsNames(int selectedRaceIndex) {
|
||||
|
||||
if (selectedRaceIndex < 0) {
|
||||
selectedRaceIndex = 0;
|
||||
}
|
||||
|
||||
Simulator simulator = new SimulatorImpl(new SimulationParametersImpl(null, null, null, null,
|
||||
SailingSimulatorConstants.ModeMeasured, true, true));
|
||||
|
||||
return simulator.getCompetitorsNames(selectedRaceIndex);
|
||||
}
|
||||
|
||||
private void retreiveWindControlParameters(WindPatternDisplay pattern) {
|
||||
|
||||
private void retrieveWindControlParameters(WindPatternDisplay pattern) {
|
||||
controlParameters.setDefaults();
|
||||
|
||||
for (WindPatternSetting<?> s : pattern.getSettings()) {
|
||||
Field f;
|
||||
try {
|
||||
f = controlParameters.getClass().getField(s.getName());
|
||||
try {
|
||||
|
||||
LOGGER.info("Setting " + f.getName() + " to " + s.getName() + " value : " + s.getValue());
|
||||
LOGGER.fine("Setting " + f.getName() + " to " + s.getName() + " value : " + s.getValue());
|
||||
f.set(controlParameters, s.getValue());
|
||||
// f.setDouble(controlParameters, (Double) s.getValue());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.warning("SimulatorServiceImpl => IllegalArgumentException with message " + e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
@@ -689,35 +718,28 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
} catch (NoSuchFieldException e) {
|
||||
LOGGER.warning("SimulatorServiceImpl => NoSuchFieldException with message " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private SimulatorWindDTO createSimulatorWindDTO(Wind wind) {
|
||||
|
||||
Position position = wind.getPosition();
|
||||
TimePoint timePoint = wind.getTimePoint();
|
||||
|
||||
SimulatorWindDTO result = new SimulatorWindDTO();
|
||||
result.trueWindBearingDeg = wind.getBearing().getDegrees();
|
||||
result.trueWindSpeedInKnots = wind.getKnots();
|
||||
|
||||
if (position != null) {
|
||||
result.position = position;
|
||||
}
|
||||
if (timePoint != null) {
|
||||
result.timepoint = timePoint.asMillis();
|
||||
result.timepoint = timePoint;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SimulatorWindDTO createSimulatorWindDTO(TimedPositionWithSpeed timedPositionWithSpeed) {
|
||||
|
||||
Position position = timedPositionWithSpeed.getPosition();
|
||||
SpeedWithBearing speedWithBearing = timedPositionWithSpeed.getSpeed();
|
||||
TimePoint timePoint = timedPositionWithSpeed.getTimePoint();
|
||||
|
||||
SimulatorWindDTO result = new SimulatorWindDTO();
|
||||
if (speedWithBearing == null) {
|
||||
result.trueWindBearingDeg = 0.0;
|
||||
@@ -726,15 +748,12 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
result.trueWindBearingDeg = speedWithBearing.getBearing().getDegrees();
|
||||
result.trueWindSpeedInKnots = speedWithBearing.getKnots();
|
||||
}
|
||||
|
||||
if (position != null) {
|
||||
result.position = position;
|
||||
}
|
||||
|
||||
if (timePoint != null) {
|
||||
result.timepoint = timePoint.asMillis();
|
||||
result.timepoint = timePoint;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -886,29 +905,31 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
private SimulatedPathsEvenTimedResultDTO getSimulatedPathsEvenTimed(List<Position> course, WindFieldGenerator wf,
|
||||
char mode, SimulatorUISelectionDTO selection, boolean showOmniscient, boolean showOpportunist)
|
||||
throws ConfigurationException {
|
||||
LOGGER.info("Retrieving simulated paths");
|
||||
LOGGER.fine("Retrieving simulated paths");
|
||||
Util.Pair<PolarDiagram, String> polarDiagramAndNotificationMessage = this.getPolarDiagram(selection.boatClassIndex);
|
||||
PolarDiagram pd = polarDiagramAndNotificationMessage.getA();
|
||||
int[] gridRes = wf.getGridResolution();
|
||||
Position[] gridArea = wf.getGridAreaGps();
|
||||
LOGGER.info("showOmniscient : "+showOmniscient);
|
||||
LOGGER.info("showOpportunist: "+showOpportunist);
|
||||
LOGGER.fine("showOmniscient : "+showOmniscient);
|
||||
LOGGER.fine("showOpportunist: "+showOpportunist);
|
||||
if (gridArea != null) {
|
||||
// initialize grid of supporting location for windfield
|
||||
Grid bd = new CurvedGrid(gridArea[0], gridArea[1]);
|
||||
// set base wind bearing
|
||||
wf.getWindParameters().baseWindBearing += bd.getSouth().getDegrees();
|
||||
LOGGER.info("base wind: " + pd.getWind().getKnots() + " kn, "
|
||||
LOGGER.fine("base wind: " + (pd==null?"null":pd.getWind().getKnots() + " kn, ")
|
||||
+ ((wf.getWindParameters().baseWindBearing) % 360.0) + "\u00B0");
|
||||
// set water current
|
||||
SpeedWithBearing current = new KnotSpeedWithBearingImpl(wf.getWindParameters().curSpeed, new DegreeBearingImpl(wf.getWindParameters().curBearing));
|
||||
if (wf.getWindParameters().curSpeed > 0) {
|
||||
pd.initializeSOGwithCurrent(); // polar-diagram is extended with data to support water currents
|
||||
}
|
||||
pd.setCurrent(current);
|
||||
if (pd.getCurrent() != null) {
|
||||
LOGGER.info("water current: " + pd.getCurrent().getKnots() + " kn, "
|
||||
+ pd.getCurrent().getBearing().getDegrees() + "\u00B0");
|
||||
if (pd != null) {
|
||||
if (wf.getWindParameters().curSpeed > 0) {
|
||||
pd.initializeSOGwithCurrent(); // polar-diagram is extended with data to support water currents
|
||||
}
|
||||
pd.setCurrent(current);
|
||||
if (pd.getCurrent() != null) {
|
||||
LOGGER.fine("water current: " + pd.getCurrent().getKnots() + " kn, "
|
||||
+ pd.getCurrent().getBearing().getDegrees() + "\u00B0");
|
||||
}
|
||||
}
|
||||
wf.setBoundary(bd);
|
||||
Position[][] positionGrid = bd.generatePositions(gridRes[0], gridRes[1], gridRes[2], gridRes[3]);
|
||||
@@ -920,11 +941,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
try {
|
||||
pathsAndNames = simulationService.getAllPathsEvenTimed(sp, wf.getTimeStep().asMillis());
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
logger.log(Level.WARNING, "Interrupted while calculating paths", e);
|
||||
} catch (ExecutionException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
logger.log(Level.SEVERE, "Problem while calculating paths", e);
|
||||
}
|
||||
int noOfPaths = pathsAndNames.size();
|
||||
if (mode == SailingSimulatorConstants.ModeMeasured) {
|
||||
@@ -932,21 +951,17 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
}
|
||||
PathDTO[] pathDTOs = new PathDTO[noOfPaths];
|
||||
int index = noOfPaths - 1;
|
||||
|
||||
if (mode == SailingSimulatorConstants.ModeMeasured) {
|
||||
// Adding the polyline
|
||||
// TODO bug4427: Eclipse Oxygen warnings have pointed at the strange get(String) invocations below; it turns out the whole mode=m set-up in the standalone simulator seems broken; Christopher to clarify
|
||||
// pathDTOs[0] = this.getPolylinePathDTO(pathsAndNames.get("6#GPS Poly"), pathsAndNames.get("7#GPS Track"));
|
||||
pathDTOs[0] = this.getPolylinePathDTO(pathsAndNames.get(null), pathsAndNames.get(null)); // TODO bug4427: the above expressions evaluate to null anyway, provoking an NPE; however, mode=m fails much earlier because the SimulatorMap.regattaAreaCanvasOverlay field is null, causing an NPE even earlier
|
||||
}
|
||||
|
||||
for (Entry<PathType, Path> entry : pathsAndNames.entrySet()) {
|
||||
LOGGER.info("Path " + entry.getKey().getTxtId());
|
||||
|
||||
LOGGER.fine("Path " + entry.getKey().getTxtId());
|
||||
// NOTE: pathName convention is: sort-digit + "#" + path-name
|
||||
// pathsAndNames is TreeMap which ensures sorting
|
||||
pathDTOs[index] = new PathDTO(entry.getKey());
|
||||
|
||||
// fill pathDTO with path points where speed is true wind speed
|
||||
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
|
||||
for (TimedPositionWithSpeed p : entry.getValue().getPathPoints()) {
|
||||
@@ -955,10 +970,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
pathDTOs[index].setPoints(wList);
|
||||
pathDTOs[index].setAlgorithmTimedOut(entry.getValue().getAlgorithmTimedOut());
|
||||
pathDTOs[index].setMixedLeg(entry.getValue().getMixedLeg());
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
RaceMapDataDTO rcDTO;
|
||||
if (mode == SailingSimulatorConstants.ModeMeasured) {
|
||||
rcDTO = new RaceMapDataDTO();
|
||||
@@ -969,75 +982,55 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
} else {
|
||||
rcDTO = null;
|
||||
}
|
||||
|
||||
return new SimulatedPathsEvenTimedResultDTO(pathDTOs, rcDTO, null, polarDiagramAndNotificationMessage.getB());
|
||||
}
|
||||
|
||||
private Util.Pair<PolarDiagram, String> getPolarDiagram(int boatClassIndex) throws ConfigurationException {
|
||||
|
||||
ConfigurationManager config = ConfigurationManager.INSTANCE;
|
||||
PolarDiagram polarDiagram = null;
|
||||
String notificationMessage = "";
|
||||
|
||||
if (config.getStatus() == ReadingConfigurationFileStatus.IO_ERROR) {
|
||||
throw new ConfigurationException(config.getErrorMessage());
|
||||
} else if (config.getStatus() == ReadingConfigurationFileStatus.ERROR_FINDING_CONFIG_FILE
|
||||
|| config.getStatus() == ReadingConfigurationFileStatus.ERROR_READING_ENV_VAR_VALUE) {
|
||||
notificationMessage = config.getErrorMessage();
|
||||
final BoatClassDTO boatClass = polarDiagramCache.getBoatClass(boatClassIndex);
|
||||
final String notificationMessage;
|
||||
final PolarDiagram polarDiagram;
|
||||
if (boatClass == null) {
|
||||
notificationMessage = Util.hasLength(polarDiagramCache.getNotificationMessage())
|
||||
? polarDiagramCache.getNotificationMessage()
|
||||
: "Boat class with index " + boatClassIndex + " not found";
|
||||
polarDiagram = null;
|
||||
} else {
|
||||
polarDiagram = polarDiagramCache.getPolarDiagram(boatClass);
|
||||
if (polarDiagram == null) {
|
||||
notificationMessage = Util.hasLength(polarDiagramCache.getNotificationMessage())
|
||||
? polarDiagramCache.getNotificationMessage()
|
||||
: "Couldn't find polar diagram for boat class "+boatClass.getName();
|
||||
} else {
|
||||
notificationMessage = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (boatClassIndex < 0) {
|
||||
boatClassIndex = 0;
|
||||
}
|
||||
String csvFilePath = config.getPolarDiagramFileLocation(boatClassIndex);
|
||||
|
||||
try {
|
||||
polarDiagram = new PolarDiagramCSV(csvFilePath);
|
||||
} catch (IOException exception) {
|
||||
throw new ConfigurationException(
|
||||
"An IO error occured when parsing the CSV file! The original error message is "
|
||||
+ exception.getMessage());
|
||||
}
|
||||
|
||||
return new Util.Pair<PolarDiagram, String>(polarDiagram, notificationMessage);
|
||||
}
|
||||
|
||||
private PathDTO getPolylinePathDTO(Path gpsPoly, Path gpsTrack) {
|
||||
|
||||
List<TimedPositionWithSpeed> gpsTrackPoints = gpsTrack.getPathPoints();
|
||||
List<TimedPositionWithSpeed> gpsPolyPoints = gpsPoly.getPathPoints();
|
||||
|
||||
int noOfGpsTrackPoints = gpsTrackPoints.size();
|
||||
int noOfGpsPolyPoints = gpsPolyPoints.size();
|
||||
|
||||
if (noOfGpsTrackPoints == 0 || noOfGpsTrackPoints == 1 || noOfGpsPolyPoints == 0 || noOfGpsPolyPoints == 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TimedPositionWithSpeed startPoint = gpsPolyPoints.get(0);
|
||||
TimedPositionWithSpeed endPoint = gpsPolyPoints.get(noOfGpsPolyPoints - 1);
|
||||
|
||||
// System.out.println("gpsTrackPoints.size() = " + gpsTrackPoints.size());
|
||||
int startPointIndex = SimulatorServiceUtils.getIndexOfClosest(gpsTrackPoints, startPoint);
|
||||
// System.out.println("startPointIndex = " + startPointIndex);
|
||||
int endPointIndex = SimulatorServiceUtils.getIndexOfClosest(gpsTrackPoints, endPoint);
|
||||
// System.out.println("endPointIndex = " + endPointIndex);
|
||||
|
||||
List<TimedPositionWithSpeed> polylinePoints = gpsTrackPoints.subList(startPointIndex, endPointIndex + 1);
|
||||
|
||||
List<TimedPositionWithSpeed> turns = (new PathImpl(polylinePoints, null, false /* algorithmTimedOut */, false /* mixedLeg */)).getTurns();
|
||||
// PathImpl.saveToGpxFile(new PathImpl(turns, null), "C:\\gps_path_turns_20deg_not_even_timed.gpx");
|
||||
|
||||
List<SimulatorWindDTO> points = new ArrayList<SimulatorWindDTO>();
|
||||
|
||||
boolean isTurn = false;
|
||||
SpeedWithBearing speedWithBearing = null;
|
||||
Position position = null;
|
||||
|
||||
for (TimedPositionWithSpeed point : polylinePoints) {
|
||||
|
||||
isTurn = false;
|
||||
|
||||
for (TimedPositionWithSpeed turn : turns) {
|
||||
if (turn.getPosition().getLatDeg() == point.getPosition().getLatDeg()
|
||||
&& turn.getPosition().getLngDeg() == point.getPosition().getLngDeg()
|
||||
@@ -1048,18 +1041,14 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
speedWithBearing = point.getSpeed();
|
||||
position = point.getPosition();
|
||||
|
||||
points.add(new SimulatorWindDTO(position.getLatDeg(), position.getLngDeg(), speedWithBearing.getKnots(),
|
||||
speedWithBearing.getBearing().getDegrees(), point.getTimePoint().asMillis(), isTurn));
|
||||
speedWithBearing.getBearing().getDegrees(), point.getTimePoint(), isTurn));
|
||||
}
|
||||
|
||||
PathDTO result = new PathDTO(POLYLINE_PATH_NAME);
|
||||
result.setPoints(points);
|
||||
result.setAlgorithmTimedOut(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1120,51 +1109,38 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
|
||||
}
|
||||
|
||||
public long getTimeMillisecondsBetween(Position turn1, Position turn2, double stepSizeMeters, boolean useRealAverageWindSpeed, Path gpsTrack,
|
||||
PolarDiagram polarDiagram, long startTimePoint2) {
|
||||
|
||||
PolarDiagram polarDiagram, long startTimePoint2asMillis) {
|
||||
Position p1 = turn1;
|
||||
Position p2 = turn2;
|
||||
|
||||
List<Position> points = getIntermediatePoints(p1, p2, stepSizeMeters);
|
||||
int noOfPointsMinus1 = points.size() - 1;
|
||||
|
||||
Position startPoint = null;
|
||||
Position endPoint = null;
|
||||
double boatBearingDeg = 0.;
|
||||
double boatSpeedMetersPerSecond = 0.;
|
||||
double distanceMeters = 0.;
|
||||
|
||||
long timepointAsMillis = startTimePoint2;
|
||||
long timepointAsMillis = startTimePoint2asMillis;
|
||||
SpeedWithBearing windAtTimePoint = null;
|
||||
|
||||
long stepTimeMilliseconds = 0L;
|
||||
|
||||
for (int index = 0; index < noOfPointsMinus1; index++) {
|
||||
|
||||
startPoint = points.get(index);
|
||||
endPoint = points.get(index + 1);
|
||||
distanceMeters = startPoint.getDistance(endPoint).getMeters();
|
||||
|
||||
windAtTimePoint = useRealAverageWindSpeed ? SimulatorServiceUtils.getWindAtTimepoint(timepointAsMillis,
|
||||
gpsTrack) : SimulatorServiceUtils.DEFAULT_AVERAGE_WIND;
|
||||
|
||||
boatBearingDeg = SimulatorServiceUtils.getInitialBearing(startPoint, endPoint);
|
||||
polarDiagram.setWind(windAtTimePoint);
|
||||
boatSpeedMetersPerSecond = polarDiagram.getSpeedAtBearing(new DegreeBearingImpl(boatBearingDeg))
|
||||
.getMetersPerSecond();
|
||||
stepTimeMilliseconds = (long) ((distanceMeters / boatSpeedMetersPerSecond) * 1000);
|
||||
|
||||
// problem right here: boatSpeed might be 0 for very small distances
|
||||
// this is a rough fix
|
||||
if (boatSpeedMetersPerSecond == 0.0) {
|
||||
stepTimeMilliseconds = 1000;
|
||||
}
|
||||
|
||||
timepointAsMillis += stepTimeMilliseconds;
|
||||
}
|
||||
|
||||
return (timepointAsMillis - startTimePoint2);
|
||||
|
||||
return timepointAsMillis - startTimePoint2asMillis;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-5
@@ -394,13 +394,10 @@ public class SimulatorServiceUtils {
|
||||
List<TimedPositionWithSpeed> pathPoints = gpsTrack.getPathPoints();
|
||||
int noOfPathPoints = pathPoints.size();
|
||||
List<Double> diffs = new ArrayList<Double>();
|
||||
|
||||
for (int index = 0; index < noOfPathPoints; index++) {
|
||||
diffs.add(new Double(Math.abs(pathPoints.get(index).getTimePoint().asMillis() - timepointAsMillis)));
|
||||
}
|
||||
|
||||
int indexOfMinDiff = diffs.indexOf(Collections.min(diffs));
|
||||
|
||||
return pathPoints.get(indexOfMinDiff).getSpeed();
|
||||
}
|
||||
|
||||
@@ -423,8 +420,7 @@ public class SimulatorServiceUtils {
|
||||
double lngDeg = position.getLngDeg();
|
||||
double windSpeedKn = windSpeedWithBearing.getKnots();
|
||||
double windBearingDeg = windSpeedWithBearing.getBearing().getDegrees();
|
||||
long timepointMsec = timePoint.asMillis();
|
||||
return new SimulatorWindDTO(latDeg, lngDeg, windSpeedKn, windBearingDeg, timepointMsec);
|
||||
return new SimulatorWindDTO(latDeg, lngDeg, windSpeedKn, windBearingDeg, timePoint);
|
||||
}
|
||||
|
||||
public static SimulatorUISelection toSimulatorUISelection(SimulatorUISelectionDTO selection) {
|
||||
|
||||
+22
-23
@@ -4,27 +4,26 @@ import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.dto.BoatClassDTO;
|
||||
|
||||
public class BoatClassDTOsAndNotificationMessage implements IsSerializable {
|
||||
|
||||
private BoatClassDTO[] boatClassDTOs;
|
||||
private String notificationMessage;
|
||||
|
||||
public BoatClassDTOsAndNotificationMessage() {
|
||||
this.notificationMessage = "";
|
||||
}
|
||||
|
||||
public BoatClassDTO[] getBoatClassDTOs() {
|
||||
return this.boatClassDTOs;
|
||||
}
|
||||
|
||||
public void setBoatClassDTOs(BoatClassDTO[] boatClassDTOs) {
|
||||
this.boatClassDTOs = boatClassDTOs;
|
||||
}
|
||||
|
||||
public String getNotificationMessage() {
|
||||
return this.notificationMessage;
|
||||
}
|
||||
|
||||
public void setNotificationMessage(String notificationMessage) {
|
||||
this.notificationMessage = notificationMessage;
|
||||
}
|
||||
private BoatClassDTO[] boatClassDTOs;
|
||||
private String notificationMessage;
|
||||
|
||||
public BoatClassDTOsAndNotificationMessage() {
|
||||
this.notificationMessage = "";
|
||||
}
|
||||
|
||||
public BoatClassDTO[] getBoatClassDTOs() {
|
||||
return this.boatClassDTOs;
|
||||
}
|
||||
|
||||
public void setBoatClassDTOs(BoatClassDTO[] boatClassDTOs) {
|
||||
this.boatClassDTOs = boatClassDTOs;
|
||||
}
|
||||
|
||||
public String getNotificationMessage() {
|
||||
return this.notificationMessage;
|
||||
}
|
||||
|
||||
public void setNotificationMessage(String notificationMessage) {
|
||||
this.notificationMessage = notificationMessage;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -41,8 +41,7 @@ public class PathDTO extends NamedDTO {
|
||||
if (points == null || points.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return points.get(points.size() - 1).timepoint - points.get(0).timepoint;
|
||||
return points.get(0).timepoint.until(points.get(points.size() - 1).timepoint).asMillis();
|
||||
}
|
||||
|
||||
public boolean getAlgorithmTimedOut() {
|
||||
@@ -60,5 +59,4 @@ public class PathDTO extends NamedDTO {
|
||||
public void setMixedLeg(boolean mixedLeg) {
|
||||
this.mixedLeg = mixedLeg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ public class RaceInfoDTO implements IsSerializable {
|
||||
public String seriesName;
|
||||
public RaceLogRaceStatus lastStatus;
|
||||
public Date startTime;
|
||||
public Date finishingTime;
|
||||
public Date finishedTime;
|
||||
public Date protestStartTime;
|
||||
public Date protestFinishTime;
|
||||
|
||||
+13
-44
@@ -1,34 +1,35 @@
|
||||
package com.sap.sailing.gwt.ui.shared;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class SimulatorResultsDTO implements IsSerializable {
|
||||
|
||||
private long version;
|
||||
private int leg;
|
||||
private Date startTime;
|
||||
private long timeStep;
|
||||
private long legDuration;
|
||||
private TimePoint startTime;
|
||||
private Duration timeStep;
|
||||
private Duration legDuration;
|
||||
private RaceMapDataDTO raceCourse;
|
||||
private WindFieldDTO windField;
|
||||
private PathDTO[] paths;
|
||||
private String notificationMessage;
|
||||
|
||||
public SimulatorResultsDTO(){
|
||||
@Deprecated
|
||||
SimulatorResultsDTO() { // for GWT RPC serialization only
|
||||
this.version = 0;
|
||||
this.leg = 0;
|
||||
this.startTime = null;
|
||||
this.timeStep = 0;
|
||||
this.legDuration = 0;
|
||||
this.timeStep = null;
|
||||
this.legDuration = null;
|
||||
this.raceCourse = null;
|
||||
this.windField = null;
|
||||
this.paths = null;
|
||||
this.notificationMessage = "";
|
||||
}
|
||||
|
||||
public SimulatorResultsDTO(final long version, final int leg, final Date startTime, final long timeStep, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
|
||||
public SimulatorResultsDTO(final long version, final int leg, final TimePoint startTime, final Duration timeStep, final Duration legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
|
||||
this.version = version;
|
||||
this.leg = leg;
|
||||
this.startTime = startTime;
|
||||
@@ -48,63 +49,31 @@ public class SimulatorResultsDTO implements IsSerializable {
|
||||
return this.leg;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
public TimePoint getStartTime() {
|
||||
return this.startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public long getTimeStep() {
|
||||
public Duration getTimeStep() {
|
||||
return this.timeStep;
|
||||
}
|
||||
|
||||
public void setTimeStep(long timeStep) {
|
||||
this.timeStep = timeStep;
|
||||
}
|
||||
|
||||
public long getLegDuration() {
|
||||
public Duration getLegDuration() {
|
||||
return this.legDuration;
|
||||
}
|
||||
|
||||
public void setLegDuration(long legDuration) {
|
||||
this.legDuration = legDuration;
|
||||
}
|
||||
|
||||
public RaceMapDataDTO getRaceCourse() {
|
||||
return this.raceCourse;
|
||||
}
|
||||
|
||||
public void setRaceCourse(final RaceMapDataDTO raceCourse) {
|
||||
this.raceCourse = raceCourse;
|
||||
}
|
||||
|
||||
public WindFieldDTO getWindField() {
|
||||
return this.windField;
|
||||
}
|
||||
|
||||
public void setWindField(final WindFieldDTO windField) {
|
||||
this.windField = windField;
|
||||
}
|
||||
|
||||
public PathDTO[] getPaths() {
|
||||
return this.paths;
|
||||
}
|
||||
|
||||
public void setPaths(final PathDTO[] paths) {
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
public String getNotificationMessage() {
|
||||
return this.notificationMessage;
|
||||
}
|
||||
|
||||
public void setNotificationMessage(final String notificationMessage) {
|
||||
this.notificationMessage = notificationMessage;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,9 +1,14 @@
|
||||
package com.sap.sailing.gwt.ui.shared;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.gwt.ui.client.SimulatorService;
|
||||
|
||||
public class SimulatorUISelectionDTO implements IsSerializable {
|
||||
|
||||
/**
|
||||
* The index refers to the order in which {@link SimulatorService#getBoatClasses()} returns the boat classes inside
|
||||
* the {@link BoatClassDTOsAndNotificationMessage#getBoatClassDTOs()} result. Starts with 0. Can be used, e.g., for
|
||||
* the {@link SimulatorService#getPolarDiagram(Double, int)} method's {@code boatClassIndex} parameter.
|
||||
*/
|
||||
public Integer boatClassIndex;
|
||||
public Integer raceIndex;
|
||||
public Integer competitorIndex;
|
||||
|
||||
+11
-11
@@ -3,43 +3,44 @@ package com.sap.sailing.gwt.ui.shared;
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class SimulatorWindDTO implements IsSerializable {
|
||||
public Boolean isTurn;
|
||||
public Double trueWindSpeedInKnots;
|
||||
public Double trueWindBearingDeg;
|
||||
public Position position;
|
||||
public Long timepoint;
|
||||
public TimePoint timepoint;
|
||||
|
||||
public SimulatorWindDTO() {
|
||||
this.isTurn = false;
|
||||
this.trueWindBearingDeg = 0.0;
|
||||
this.trueWindSpeedInKnots = 0.0;
|
||||
this.position = null;
|
||||
this.timepoint = 0L;
|
||||
this.timepoint = null;
|
||||
}
|
||||
|
||||
public SimulatorWindDTO(Position position, double windSpeedKn, double windBearingDeg, long timepointMsec) {
|
||||
public SimulatorWindDTO(Position position, double windSpeedKn, double windBearingDeg, TimePoint timepoint) {
|
||||
this.position = position;
|
||||
this.trueWindBearingDeg = windBearingDeg;
|
||||
this.trueWindSpeedInKnots = windSpeedKn;
|
||||
this.timepoint = timepointMsec;
|
||||
this.timepoint = timepoint;
|
||||
this.isTurn = false;
|
||||
}
|
||||
|
||||
public SimulatorWindDTO(double latDeg, double lngDeg, double windSpeedKn, double windBearingDeg, long timepointMsec) {
|
||||
public SimulatorWindDTO(double latDeg, double lngDeg, double windSpeedKn, double windBearingDeg, TimePoint timepoint) {
|
||||
this.position = new DegreePosition(latDeg, lngDeg);
|
||||
this.trueWindBearingDeg = windBearingDeg;
|
||||
this.trueWindSpeedInKnots = windSpeedKn;
|
||||
this.timepoint = timepointMsec;
|
||||
this.timepoint = timepoint;
|
||||
this.isTurn = false;
|
||||
}
|
||||
|
||||
public SimulatorWindDTO(double latDeg, double lngDeg, double windSpeedKn, double windBearingDeg, long timepointMsec, boolean isTurn) {
|
||||
public SimulatorWindDTO(double latDeg, double lngDeg, double windSpeedKn, double windBearingDeg, TimePoint timepoint, boolean isTurn) {
|
||||
this.position = new DegreePosition(latDeg, lngDeg);
|
||||
this.trueWindBearingDeg = windBearingDeg;
|
||||
this.trueWindSpeedInKnots = windSpeedKn;
|
||||
this.timepoint = timepointMsec;
|
||||
this.timepoint = timepoint;
|
||||
this.isTurn = isTurn;
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ public class SimulatorWindDTO implements IsSerializable {
|
||||
temp = this.trueWindBearingDeg.intValue();
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
|
||||
temp = this.timepoint.intValue();
|
||||
temp = this.timepoint.asMillis();
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
|
||||
temp = this.position.hashCode();
|
||||
@@ -74,8 +75,7 @@ public class SimulatorWindDTO implements IsSerializable {
|
||||
} else {
|
||||
if (o instanceof SimulatorWindDTO) {
|
||||
SimulatorWindDTO other = (SimulatorWindDTO) o;
|
||||
|
||||
return this.position.equals(other.position) && (Math.abs(this.timepoint - other.timepoint) < 9999);
|
||||
return this.position.equals(other.position) && (Math.abs(this.timepoint.until(other.timepoint).asMillis()) < 9999);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+9
-8
@@ -32,6 +32,7 @@ import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO;
|
||||
import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay;
|
||||
import com.sap.sailing.gwt.ui.simulator.util.ColorPalette;
|
||||
import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
import com.sap.sse.gwt.client.Notification.NotificationType;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
@@ -81,7 +82,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
|
||||
}
|
||||
|
||||
public void updateLeg(int newLeg, boolean clearCanvas, long newVersion) {
|
||||
if (((newLeg != raceLeg)||((newLeg == raceLeg)&&(newVersion > this.getVersion()))) && (this.isVisible())) {
|
||||
if ((newLeg != raceLeg || (newLeg == raceLeg && newVersion > this.getVersion())) && this.isVisible()) {
|
||||
if (newLeg != raceLeg) {
|
||||
raceLeg = newLeg;
|
||||
requestedSimulationVersion = 0;
|
||||
@@ -96,7 +97,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
|
||||
}
|
||||
|
||||
public LegIdentifier getLegIdentifier() {
|
||||
return new LegIdentifierImpl(raceIdentifier, String.valueOf(raceLeg));
|
||||
return new LegIdentifierImpl(raceIdentifier, raceLeg);
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
@@ -204,10 +205,10 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
|
||||
}
|
||||
}
|
||||
ctxt.stroke();
|
||||
SimulatorWindDTO start = points.get(0);
|
||||
long timeStep = simulationResult.getTimeStep();
|
||||
final SimulatorWindDTO start = points.get(0);
|
||||
final Duration timeStep = simulationResult.getTimeStep();
|
||||
for (SimulatorWindDTO point : points) {
|
||||
if ((point.timepoint - start.timepoint) % (timeStep) != 0) {
|
||||
if (start.timepoint.until(point.timepoint).asMillis() % timeStep.asMillis() != 0) {
|
||||
continue;
|
||||
}
|
||||
Point px = mapProjection.fromLatLngToContainerPixel(coordinateSystem.toLatLng(point.position));
|
||||
@@ -337,7 +338,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
|
||||
}
|
||||
|
||||
public void simulate(int leg) {
|
||||
LegIdentifier legIdentifier = new LegIdentifierImpl(raceIdentifier, String.valueOf(leg));
|
||||
LegIdentifier legIdentifier = new LegIdentifierImpl(raceIdentifier, leg);
|
||||
GetSimulationAction getSimulation = new GetSimulationAction(sailingService, legIdentifier);
|
||||
asyncActionsExecutor.execute(getSimulation, GET_SIMULATION_CATEGORY,
|
||||
new MarkedAsyncCallback<>(new AsyncCallback<SimulatorResultsDTO>() {
|
||||
@@ -357,11 +358,11 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
|
||||
if ((result.getPaths() != null) && (result.getVersion() >= requestedSimulationVersion) && (result.getLeg() == raceLeg)) {
|
||||
simulationResult = result;
|
||||
PathDTO[] paths = result.getPaths();
|
||||
if (result.getLegDuration() > 0) {
|
||||
if (result.getLegDuration().compareTo(Duration.NULL) > 0) {
|
||||
racePath = new PathDTO("0#Race Leader");
|
||||
List<SimulatorWindDTO> racePathPoints = new ArrayList<SimulatorWindDTO>();
|
||||
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint));
|
||||
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint + result.getLegDuration()));
|
||||
racePathPoints.add(new SimulatorWindDTO(null, 0, 0, paths[0].getPoints().get(0).timepoint.plus(result.getLegDuration())));
|
||||
racePath.setPoints(racePathPoints);
|
||||
} else {
|
||||
racePath = null;
|
||||
|
||||
+9
-8
@@ -18,7 +18,9 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.CoordinateSystem;
|
||||
import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO;
|
||||
import com.sap.sse.common.AbstractBearing;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
|
||||
@@ -112,12 +114,11 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
|
||||
|
||||
@Override
|
||||
protected void drawWindField(List<SimulatorWindDTO> windDTOList) {
|
||||
|
||||
int numPoints = windDTOList.size();
|
||||
if (numPoints < 1) {
|
||||
return;
|
||||
}
|
||||
long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint;
|
||||
Duration totalDuration = windDTOList.get(0).timepoint.until(windDTOList.get(numPoints - 1).timepoint);
|
||||
double distance = SphericalUtils.computeDistanceBetween(startPoint, endPoint) / Mile.METERS_PER_NAUTICAL_MILE;
|
||||
if (windDTOList != null && windDTOList.size() > 0) {
|
||||
Context2d context2d = canvas.getContext2d();
|
||||
@@ -133,7 +134,7 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
|
||||
}
|
||||
windDTOIter = windDTOList.iterator();
|
||||
int index = 0;
|
||||
long startTime = windDTOList.get(0).timepoint;
|
||||
final TimePoint startTime = windDTOList.get(0).timepoint;
|
||||
prevWindDTO = null; // For the last time arrow was displayed
|
||||
while (windDTOIter.hasNext()) {
|
||||
SimulatorWindDTO windDTO = windDTOIter.next();
|
||||
@@ -146,12 +147,12 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
|
||||
}
|
||||
index++;
|
||||
long timeStep = windParams.getTimeStep().asMillis();
|
||||
if ((windDTO.timepoint - startTime) % (timeStep) == 0) {
|
||||
if (startTime.until(windDTO.timepoint).asMillis() % timeStep == 0) {
|
||||
drawPoint(windDTO);
|
||||
}
|
||||
}
|
||||
context2d.setGlobalAlpha(1.0);
|
||||
Date timeDiffDate = new Date(totalTime);
|
||||
final Date timeDiffDate = new Date(totalDuration.asMillis());
|
||||
TimeZone gmt = TimeZone.createTimeZone(0);
|
||||
getCanvas().setTitle(StringMessages.INSTANCE.pathCanvasOverlayTitle(numPoints,
|
||||
NumberFormat.getFormat("0.00").format(distance),
|
||||
@@ -223,14 +224,14 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named {
|
||||
return mixedLeg;
|
||||
}
|
||||
|
||||
public long getPathTime() {
|
||||
public long getPathDurationMillis() {
|
||||
if (totalTimeIsGiven) {
|
||||
return totalTimeMilliseconds;
|
||||
} else {
|
||||
List<SimulatorWindDTO> windDTOList = windFieldDTO.getMatrix();
|
||||
int numPoints = windDTOList.size();
|
||||
long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint;
|
||||
return totalTime;
|
||||
final Duration totalTime = windDTOList.get(0).timepoint.until(windDTOList.get(numPoints - 1).timepoint);
|
||||
return totalTime.asMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay {
|
||||
timewidth = newwidth;
|
||||
}
|
||||
for (PathCanvasOverlay path : pathOverlays) {
|
||||
String timeText = (path.getMixedLeg() ? mixedLegText : (path.getAlgorithmTimedOut() ? algorithmTimedOutText : getFormattedTime(path.getPathTime())));
|
||||
String timeText = (path.getMixedLeg() ? mixedLegText : (path.getAlgorithmTimedOut() ? algorithmTimedOutText : getFormattedTime(path.getPathDurationMillis())));
|
||||
drawRectangleWithText(xOffset, yOffset + (pathOverlays.size()-1-index) * rectHeight, path.getPathColor(),
|
||||
path.getName(), timeText, txtmaxwidth, timewidth, (path.getMixedLeg()?deltaMixedLeg:(path.getAlgorithmTimedOut()?deltaTimeOut:deltaTime)));
|
||||
index++;
|
||||
|
||||
+2
-6
@@ -18,6 +18,7 @@ import com.google.gwt.maps.client.overlays.PolylineOptions;
|
||||
import com.google.gwt.maps.client.overlays.overlayhandlers.OverlayViewMethods;
|
||||
import com.google.gwt.maps.client.overlays.overlayhandlers.OverlayViewOnDrawHandler;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.Mile;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.RadianPosition;
|
||||
@@ -1099,15 +1100,12 @@ public class PathPolyline {
|
||||
|
||||
private void getTotalTime() {
|
||||
List<Position> turnPointsAsPositionDTO = new ArrayList<>();
|
||||
|
||||
for (LatLng point : this.turnPoints) {
|
||||
turnPointsAsPositionDTO.add(toPositionDTO(point));
|
||||
}
|
||||
|
||||
RequestTotalTimeDTO requestData = new RequestTotalTimeDTO(new SimulatorUISelectionDTO(this.selectedBoatClassIndex, this.selectedRaceIndex,
|
||||
this.selectedCompetitorIndex, this.selectedLegIndex), STEP_DURATION_MILLISECONDS, this.allPoints, turnPointsAsPositionDTO,
|
||||
USE_REAL_AVERAGE_WIND);
|
||||
|
||||
this.simulatorService.getTotalTime(requestData, new AsyncCallback<ResponseTotalTimeDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
@@ -1138,13 +1136,11 @@ public class PathPolyline {
|
||||
this.selectedBoatClassIndex = boatClassIndex;
|
||||
}
|
||||
|
||||
private static final double FACTOR_KN2MPS = 0.514444;
|
||||
|
||||
/**
|
||||
* Converts knots to meters per second
|
||||
*/
|
||||
public static double knotsToMetersPerSecond(double knots) {
|
||||
return knots * FACTOR_KN2MPS;
|
||||
return knots * Mile.METERS_PER_SECOND_PER_KNOT;
|
||||
}
|
||||
|
||||
private class Pair {
|
||||
|
||||
+2
-5
@@ -9,6 +9,7 @@ import com.google.gwt.maps.client.MapWidget;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.CoordinateSystem;
|
||||
import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
|
||||
public class ReplayPathCanvasOverlay extends PathCanvasOverlay {
|
||||
@@ -33,22 +34,18 @@ public class ReplayPathCanvasOverlay extends PathCanvasOverlay {
|
||||
*/
|
||||
@Override
|
||||
public void timeChanged(final Date newTime, Date oldTime) {
|
||||
|
||||
if (windFieldDTO == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.getContext2d().clearRect(0/* canvas.getAbsoluteLeft() */, 0/* canvas.getAbsoluteTop() */,
|
||||
canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
|
||||
|
||||
windDTOToDraw = new ArrayList<SimulatorWindDTO>();
|
||||
for (final SimulatorWindDTO windDTO : windFieldDTO.getMatrix()) {
|
||||
if (windDTO.timepoint <= newTime.getTime()) {
|
||||
if (!windDTO.timepoint.after(TimePoint.of(newTime))) {
|
||||
windDTOToDraw.add(windDTO);
|
||||
}
|
||||
}
|
||||
logger.info("In ReplayPathCanvasOverlay.drawWindField drawing " + windDTOToDraw.size() + " points");
|
||||
|
||||
drawWindField(windDTOToDraw);
|
||||
}
|
||||
|
||||
|
||||
+83
-175
@@ -58,6 +58,7 @@ import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternSetting;
|
||||
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternSetting.SettingName;
|
||||
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
|
||||
@@ -75,7 +76,9 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
private class ResizableFlowPanel extends FlowPanel implements RequiresResize {
|
||||
@Override
|
||||
public void onResize() {
|
||||
simulatorMap.getMap().triggerResize();
|
||||
if (simulatorMap != null && simulatorMap.getMap() != null) {
|
||||
simulatorMap.getMap().triggerResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,19 +350,14 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mainPanel = new DockLayoutPanel(Unit.PX);
|
||||
|
||||
mainPanel.setSize("100%", "100%");
|
||||
mainPanel.addWest(leftPanel, 470);
|
||||
mainPanel.addSouth(fullTimePanel, 67);
|
||||
mainPanel.setWidgetHidden(fullTimePanel, true);
|
||||
|
||||
createMapOptionsPanel(); // add map-options to mainPanel-North
|
||||
|
||||
mainPanel.add(rightPanel);
|
||||
this.setWidget(mainPanel);
|
||||
|
||||
this.polarDiagramDialogBox = this.createPolarDiagramDialogBox();
|
||||
}
|
||||
|
||||
@@ -401,7 +399,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
private void createOptionsPanelTop() {
|
||||
HorizontalPanel optionsPanel = new HorizontalPanel();
|
||||
optionsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
|
||||
|
||||
optionsPanel.setTitle(stringMessages.optionsBar());
|
||||
optionsPanel.getElement().setClassName("optionsPanel");
|
||||
Label options = new Label(stringMessages.optionsBar());
|
||||
@@ -409,16 +406,11 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
optionsPanel.setSize("100%", "45px");
|
||||
optionsPanel.add(options);
|
||||
optionsPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
|
||||
|
||||
HorizontalPanel buttonPanel = new HorizontalPanel();
|
||||
buttonPanel.setWidth("0px");
|
||||
|
||||
initPolarDiagramButton();
|
||||
buttonPanel.add(polarDiagramButton);
|
||||
|
||||
initUpdateButton();
|
||||
buttonPanel.add(updateButton);
|
||||
|
||||
optionsPanel.add(buttonPanel);
|
||||
leftPanel.add(optionsPanel);
|
||||
}
|
||||
@@ -572,28 +564,21 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
mapOptions.setSize("100%", "45px");
|
||||
mapOptions.setTitle(stringMessages.maps());
|
||||
mapOptions.getElement().setClassName("mapOptions");
|
||||
|
||||
Label mapsLabel = new Label(stringMessages.maps());
|
||||
mapsLabel.getElement().setClassName("sectorHeadline");
|
||||
mapOptions.add(mapsLabel);
|
||||
|
||||
mapOptions.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
|
||||
|
||||
if ((mode != SailingSimulatorConstants.ModeMeasured)&&(mode != SailingSimulatorConstants.ModeEvent)) {
|
||||
initCourseInputButton();
|
||||
if ((mode != SailingSimulatorConstants.ModeMeasured) && (mode != SailingSimulatorConstants.ModeEvent)) {
|
||||
initCourseInputButton();
|
||||
mapOptions.add(courseInputButton);
|
||||
}
|
||||
|
||||
//rightPanel.add(mapOptions);
|
||||
mainPanel.addNorth(mapOptions, 45);
|
||||
|
||||
initDisplayOptions(mapOptions);
|
||||
if (mode == SailingSimulatorConstants.ModeEvent) {
|
||||
summaryButton.setValue(true);
|
||||
replayButton.setValue(false);
|
||||
windDisplayButton.setValue(false);
|
||||
summaryButton.setValue(true);
|
||||
replayButton.setValue(false);
|
||||
windDisplayButton.setValue(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// initialize timer with a default time span based on windParams
|
||||
@@ -606,7 +591,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
|
||||
private void initCourseInputButton() {
|
||||
courseInputButton = new Button(stringMessages.startEnd());
|
||||
|
||||
courseInputButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent arg0) {
|
||||
@@ -619,7 +603,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
windDisplayButton.setValue(false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private Panel createRaceDirectionSelector() {
|
||||
@@ -769,7 +752,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void loadPolarDiagramData(final int selectedBoatClass) {
|
||||
|
||||
this.simulatorSvc.getBoatClasses(new AsyncCallback<BoatClassDTOsAndNotificationMessage>() {
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
@@ -778,17 +760,14 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
@Override
|
||||
public void onSuccess(BoatClassDTOsAndNotificationMessage boatClassesAndMsg) {
|
||||
String notificationMessage = boatClassesAndMsg.getNotificationMessage();
|
||||
if(notificationMessage != "" && notificationMessage.length() != 0 && warningAlreadyShown == false) {
|
||||
if (Util.hasLength(notificationMessage) && !warningAlreadyShown) {
|
||||
errorReporter.reportError(boatClassesAndMsg.getNotificationMessage(), true);
|
||||
warningAlreadyShown = true;
|
||||
}
|
||||
|
||||
boatClasses = boatClassesAndMsg.getBoatClassDTOs();
|
||||
chart.setChartTitleText(boatClasses[selectedBoatClass].getName());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (this.chart != null) {
|
||||
this.polarDiv.remove(this.chart);
|
||||
}
|
||||
@@ -808,53 +787,52 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
this.simulatorSvc.getPolarDiagram(5.0, selectedBoatClass, new AsyncCallback<PolarDiagramDTOAndNotificationMessage>() {
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
|
||||
errorReporter.reportError(stringMessages.errorLoadingBoatClasses(error.getMessage()));
|
||||
errorReporter.reportError(stringMessages.errorLoadingPolarDataForBoatClass(boatClasses[selectedBoatClass].getName(), error.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(PolarDiagramDTOAndNotificationMessage polar) {
|
||||
String notificationMessage = polar.getNotificationMessage();
|
||||
if(notificationMessage != "" && notificationMessage.length() != 0 && warningAlreadyShown == false) {
|
||||
errorReporter.reportError(polar.getNotificationMessage(), true);
|
||||
warningAlreadyShown = true;
|
||||
}
|
||||
|
||||
Number[][] Nseries = polar.getPolarDiagramDTO().getNumberSeries();
|
||||
int[] windSpeedCatalog = new int[] { 6, 8, 10, 12, 14, 16, 20 };
|
||||
PolarChartColorRange cc = new PolarChartColorRange(Nseries.length + 1);
|
||||
ArrayList<String> windSpeedColor = cc.GetColors();
|
||||
Series ser = chart.createSeries();
|
||||
|
||||
for (int i = 0; i < Nseries.length; i++) {
|
||||
ser = chart.createSeries();
|
||||
ser.setName("" + windSpeedCatalog[i] + " kn");
|
||||
ser.setPoints(Nseries[i]);
|
||||
ser.setOption("color", windSpeedColor.get(i));
|
||||
chart.addSeries(ser);
|
||||
}
|
||||
|
||||
chart.getXAxis().setTickInterval(10);
|
||||
chart.getYAxis().setMin(0);
|
||||
chart.setOption("plotOptions/series/pointInterval", 360.0 / (Nseries[0].length));
|
||||
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
|
||||
@Override
|
||||
public String format(AxisLabelsData axisLabelsData) {
|
||||
String labelD = "";
|
||||
if (axisLabelsData.getValueAsLong() % 30 == 0) {
|
||||
labelD = axisLabelsData.getValueAsLong() + "\u00B0";
|
||||
}
|
||||
return labelD;
|
||||
if (polar == null) {
|
||||
errorReporter.reportError(stringMessages.errorLoadingPolarDataForBoatClass(boatClasses[selectedBoatClass].getName(), ""));
|
||||
} else {
|
||||
String notificationMessage = polar.getNotificationMessage();
|
||||
if (Util.hasLength(notificationMessage) && warningAlreadyShown == false) {
|
||||
errorReporter.reportError(polar.getNotificationMessage(), true);
|
||||
warningAlreadyShown = true;
|
||||
}
|
||||
}));
|
||||
|
||||
polarDiv.add(chart);
|
||||
polarDiv.add(polarDiagramDialogCloseButton);
|
||||
Number[][] Nseries = polar.getPolarDiagramDTO().getNumberSeries();
|
||||
int[] windSpeedCatalog = new int[] { 6, 8, 10, 12, 14, 16, 20 };
|
||||
PolarChartColorRange cc = new PolarChartColorRange(Nseries.length + 1);
|
||||
ArrayList<String> windSpeedColor = cc.GetColors();
|
||||
Series ser = chart.createSeries();
|
||||
for (int i = 0; i < Nseries.length; i++) {
|
||||
ser = chart.createSeries();
|
||||
ser.setName("" + windSpeedCatalog[i] + " kn");
|
||||
ser.setPoints(Nseries[i]);
|
||||
ser.setOption("color", windSpeedColor.get(i));
|
||||
chart.addSeries(ser);
|
||||
}
|
||||
chart.getXAxis().setTickInterval(10);
|
||||
chart.getYAxis().setMin(0);
|
||||
chart.setOption("plotOptions/series/pointInterval", 360.0 / (Nseries[0].length));
|
||||
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
|
||||
@Override
|
||||
public String format(AxisLabelsData axisLabelsData) {
|
||||
String labelD = "";
|
||||
if (axisLabelsData.getValueAsLong() % 30 == 0) {
|
||||
labelD = axisLabelsData.getValueAsLong() + "\u00B0";
|
||||
}
|
||||
return labelD;
|
||||
}
|
||||
}));
|
||||
polarDiv.add(chart);
|
||||
polarDiv.add(polarDiagramDialogCloseButton);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initUpdateButton() {
|
||||
|
||||
this.updateButton = new Button(stringMessages.simulateButton());
|
||||
this.updateButton.getElement().getStyle().setProperty("marginLeft", "6px");
|
||||
if (mode == SailingSimulatorConstants.ModeEvent) {
|
||||
@@ -873,25 +851,13 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void update() {
|
||||
|
||||
if (this.windParams.isShowStreamlets()) {
|
||||
|
||||
this.setMapInstance(this.simulatorMap.getMap().getJso());
|
||||
this.setCanvasProjectionInstance(this.simulatorMap.getRegattaAreaCanvasOverlay().getMapProjection());
|
||||
|
||||
//this.setDefaultTimeSettings();
|
||||
//this.simulatorMap.generateWindField(this.getWindPatternDisplay(), true);
|
||||
|
||||
}
|
||||
|
||||
// int selectedBoatClassIndex = boatClassSelector.getSelectedIndex();
|
||||
// int selectedRaceIndex = raceSelector.getSelectedIndex();
|
||||
// int selectedCompetitorIndex = competitorSelector.getSelectedIndex();
|
||||
int selectedLegIndex = legSelector.getSelectedIndex();
|
||||
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(),
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(getSelectedBoatClassIndex(),
|
||||
raceSelector.getSelectedIndex(), competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
|
||||
|
||||
if (windDisplayButton.getValue()) {
|
||||
showTimePanel(true);
|
||||
simulatorMap.refreshView(SimulatorMap.ViewName.WINDDISPLAY, currentWPDisplay, selection, true);
|
||||
@@ -904,7 +870,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
simulatorMap.refreshView(SimulatorMap.ViewName.REPLAY, currentWPDisplay, selection, true);
|
||||
} else {
|
||||
if (mode == SailingSimulatorConstants.ModeMeasured) {
|
||||
|
||||
if (selectedLegIndex % 2 != 0) {
|
||||
errorReporter.reportError(stringMessages.downwindLegsNotSupported());
|
||||
} else {
|
||||
@@ -917,77 +882,61 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void initPolarDiagramButton() {
|
||||
|
||||
this.polarDiagramButton = new Button(stringMessages.polarDiagramButton());
|
||||
this.polarDiagramButton = new Button(stringMessages.polarDiagramButton());
|
||||
this.polarDiagramButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent arg0) {
|
||||
|
||||
boolean checked = polarDiv.isVisible(); //((CheckBox) event.getSource()).getValue();
|
||||
|
||||
if (!checked) {
|
||||
|
||||
polarDiv.setVisible(true);
|
||||
|
||||
//TODO: change the hardcoded values bellow...
|
||||
|
||||
polarDiagramDialogBox.setPopupPositionAndShow(new PositionCallback() {
|
||||
@Override
|
||||
public void setPosition(int offsetWidth, int offsetHeight) {
|
||||
|
||||
int width = (Window.getClientWidth() - 492)/2;
|
||||
int height = (Window.getClientHeight() - 608)/2;
|
||||
|
||||
polarDiagramDialogBox.setPopupPosition(width, height);
|
||||
}
|
||||
});
|
||||
|
||||
polarDiagramDialogCloseButton.setFocus(true);
|
||||
//cb.setValue(false);
|
||||
|
||||
}
|
||||
boolean checked = polarDiv.isVisible();
|
||||
if (!checked) {
|
||||
polarDiv.setVisible(true);
|
||||
//TODO: change the hardcoded values below...
|
||||
polarDiagramDialogBox.setPopupPositionAndShow(new PositionCallback() {
|
||||
@Override
|
||||
public void setPosition(int offsetWidth, int offsetHeight) {
|
||||
int width = (Window.getClientWidth() - 492)/2;
|
||||
int height = (Window.getClientHeight() - 608)/2;
|
||||
polarDiagramDialogBox.setPopupPosition(width, height);
|
||||
}
|
||||
});
|
||||
polarDiagramDialogCloseButton.setFocus(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void initDisplayOptions(Panel mapOptions) {
|
||||
|
||||
this.summaryButton = new RadioButton(RADIOBOX_GROUP_MAP_DISPLAY_OPTIONS, stringMessages.summary());
|
||||
this.summaryButton.getElement().setClassName("MapDisplayOptions");
|
||||
this.summaryButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent arg0) {
|
||||
showTimePanel(false);
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(), raceSelector.getSelectedIndex(),
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(getSelectedBoatClassIndex(), raceSelector.getSelectedIndex(),
|
||||
competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
|
||||
simulatorMap.refreshView(SimulatorMap.ViewName.SUMMARY, currentWPDisplay, selection, false);
|
||||
}
|
||||
});
|
||||
|
||||
this.replayButton = new RadioButton(RADIOBOX_GROUP_MAP_DISPLAY_OPTIONS, stringMessages.replay());
|
||||
this.replayButton.getElement().setClassName("MapDisplayOptions");
|
||||
this.replayButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent arg0) {
|
||||
showTimePanel(true);
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(), raceSelector.getSelectedIndex(),
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(getSelectedBoatClassIndex(), raceSelector.getSelectedIndex(),
|
||||
competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
|
||||
simulatorMap.refreshView(SimulatorMap.ViewName.REPLAY, currentWPDisplay, selection, false);
|
||||
}
|
||||
});
|
||||
|
||||
this.windDisplayButton = new RadioButton(RADIOBOX_GROUP_MAP_DISPLAY_OPTIONS, stringMessages.windDisplay());
|
||||
this.windDisplayButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent arg0) {
|
||||
showTimePanel(true);
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(), raceSelector.getSelectedIndex(),
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(getSelectedBoatClassIndex(), raceSelector.getSelectedIndex(),
|
||||
competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
|
||||
simulatorMap.refreshView(SimulatorMap.ViewName.WINDDISPLAY, currentWPDisplay, selection, false);
|
||||
}
|
||||
});
|
||||
|
||||
HorizontalPanel p = new HorizontalPanel();
|
||||
p.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
|
||||
p.add(busyIndicator);
|
||||
@@ -999,17 +948,13 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private DialogBox createPolarDiagramDialogBox() {
|
||||
|
||||
final DialogBox dialogBox = new DialogBox();
|
||||
dialogBox.getElement().getStyle().setZIndex(10); // put polardiagram on-top of sapsailing header and parameter sliders
|
||||
dialogBox.setText("Polar Diagram");
|
||||
dialogBox.setAnimationEnabled(true);
|
||||
dialogBox.setAutoHideEnabled(false);
|
||||
dialogBox.setModal(false);
|
||||
|
||||
dialogBox.setWidget(this.polarDiv);
|
||||
|
||||
|
||||
this.polarDiagramDialogCloseButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
@@ -1017,7 +962,6 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
dialogBox.hide();
|
||||
}
|
||||
});
|
||||
|
||||
return dialogBox;
|
||||
}
|
||||
|
||||
@@ -1025,9 +969,8 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
return this.event;
|
||||
}
|
||||
|
||||
|
||||
public int getSelectedBoatClassIndex() {
|
||||
return this.boatClassSelector.getSelectedIndex();
|
||||
return Integer.valueOf(boatClassSelector.getSelectedValue());
|
||||
}
|
||||
|
||||
public int getSelectedRaceIndex() {
|
||||
@@ -1043,35 +986,27 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void loadRaceData(int selectedRaceIndex) {
|
||||
|
||||
if (selectedRaceIndex < 0) {
|
||||
selectedRaceIndex = 0;
|
||||
}
|
||||
|
||||
this.loadCompetitors(selectedRaceIndex);
|
||||
|
||||
this.loadLegs(selectedRaceIndex);
|
||||
}
|
||||
|
||||
private void loadLegData(int selectedLegIndex) {
|
||||
|
||||
}
|
||||
|
||||
private void loadCompetitorData(int selectedCompetitorIndex) {
|
||||
|
||||
}
|
||||
|
||||
private Panel getRacesSelector() {
|
||||
Label raceLabel = new Label(this.stringMessages.raceLabel());
|
||||
raceLabel.getElement().setClassName("boatClassLabel");
|
||||
|
||||
HorizontalPanel panel = new HorizontalPanel();
|
||||
panel.getElement().setClassName("boatClassPanel");
|
||||
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
|
||||
panel.add(raceLabel);
|
||||
|
||||
this.simulatorSvc.getRacesNames(new AsyncCallback<List<String>>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
errorReporter.reportError(stringMessages.errorLoadingRaceNames(error.getMessage()));
|
||||
@@ -1079,90 +1014,66 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> response) {
|
||||
|
||||
for (String raceName : response) {
|
||||
raceSelector.addItem(raceName);
|
||||
}
|
||||
|
||||
raceSelector.setItemSelected(0, true); // first race
|
||||
loadRaceData(0);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
this.raceSelector.addChangeHandler(new ChangeHandler() {
|
||||
|
||||
@Override
|
||||
public void onChange(ChangeEvent arg0) {
|
||||
loadRaceData(raceSelector.getSelectedIndex());
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(this.raceSelector);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Panel getLegsSelector() {
|
||||
|
||||
Label legLabel = new Label(this.stringMessages.legLabel());
|
||||
legLabel.getElement().setClassName("boatClassLabel");
|
||||
|
||||
HorizontalPanel panel = new HorizontalPanel();
|
||||
panel.getElement().setClassName("boatClassPanel");
|
||||
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
|
||||
panel.add(legLabel);
|
||||
|
||||
this.loadLegs(this.raceSelector.getSelectedIndex());
|
||||
|
||||
this.legSelector.addChangeHandler(new ChangeHandler() {
|
||||
|
||||
@Override
|
||||
public void onChange(ChangeEvent arg0) {
|
||||
loadLegData(legSelector.getSelectedIndex());
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(this.legSelector);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Panel getCompetitorsSelector() {
|
||||
|
||||
Label competitorLabel = new Label(this.stringMessages.competitorLabel());
|
||||
competitorLabel.getElement().setClassName("boatClassLabel");
|
||||
|
||||
HorizontalPanel panel = new HorizontalPanel();
|
||||
panel.getElement().setClassName("boatClassPanel");
|
||||
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
|
||||
panel.add(competitorLabel);
|
||||
|
||||
this.loadCompetitors(this.raceSelector.getSelectedIndex());
|
||||
|
||||
this.competitorSelector.addChangeHandler(new ChangeHandler() {
|
||||
|
||||
@Override
|
||||
public void onChange(ChangeEvent arg0) {
|
||||
loadCompetitorData(competitorSelector.getSelectedIndex());
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(this.competitorSelector);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Panel getBoatClassesSelector() {
|
||||
|
||||
Label boatClassLabel = new Label(this.stringMessages.boatClass());
|
||||
boatClassLabel.getElement().setClassName("boatClassLabel");
|
||||
|
||||
HorizontalPanel panel = new HorizontalPanel();
|
||||
panel.getElement().setClassName("boatClassPanel");
|
||||
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
|
||||
panel.add(boatClassLabel);
|
||||
|
||||
this.simulatorSvc.getBoatClasses(new AsyncCallback<BoatClassDTOsAndNotificationMessage>() {
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
@@ -1172,37 +1083,42 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
@Override
|
||||
public void onSuccess(BoatClassDTOsAndNotificationMessage response) {
|
||||
String notificationMessage = response.getNotificationMessage();
|
||||
if (notificationMessage != "" && notificationMessage.length() != 0 && warningAlreadyShown == false) {
|
||||
if (Util.hasLength(notificationMessage) && !warningAlreadyShown) {
|
||||
errorReporter.reportError(response.getNotificationMessage(), true);
|
||||
warningAlreadyShown = true;
|
||||
}
|
||||
|
||||
boatClasses = response.getBoatClassDTOs();
|
||||
final Map<BoatClassDTO, Integer> boatClassIndices = new HashMap<>();
|
||||
for (int i = 0; i < boatClasses.length; ++i) {
|
||||
boatClassSelector.addItem(boatClasses[i].getName());
|
||||
final BoatClassDTO boatClass = boatClasses[i];
|
||||
boatClassIndices.put(boatClass, i);
|
||||
|
||||
}
|
||||
boatClassSelector.setItemSelected(3, true); // polar diagram 49er STG
|
||||
loadPolarDiagramData(3);
|
||||
int[] index = new int[1];
|
||||
boatClassIndices.keySet().stream().sorted().forEach(bc->{
|
||||
final Integer boatClassIndex = boatClassIndices.get(bc);
|
||||
boatClassSelector.addItem(bc.getName(), ""+boatClassIndex);
|
||||
if (bc.getName().equals("49er STG")) {
|
||||
boatClassSelector.setSelectedIndex(index[0]);
|
||||
loadPolarDiagramData(boatClassIndex);
|
||||
}
|
||||
index[0]++;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.boatClassSelector.addChangeHandler(new ChangeHandler() {
|
||||
@Override
|
||||
public void onChange(ChangeEvent evnet) {
|
||||
int selectedIndex = boatClassSelector.getSelectedIndex();
|
||||
int selectedIndex = getSelectedBoatClassIndex();
|
||||
loadPolarDiagramData(selectedIndex);
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(boatClassSelector);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void loadLegs(int selectedRaceIndex) {
|
||||
|
||||
this.simulatorSvc.getLegsNames(selectedRaceIndex, new AsyncCallback<List<String>>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
errorReporter.reportError(stringMessages.errorLoadingLegInformation(error.getMessage()));
|
||||
@@ -1210,16 +1126,13 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> response) {
|
||||
|
||||
legSelector.clear();
|
||||
|
||||
int index = 0;
|
||||
for (String legName : response) {
|
||||
legSelector.addItem(legName + ((index % 2 == 0) ? " (" + stringMessages.upWind() + ")"
|
||||
: " (" + stringMessages.downWind() + ")"));
|
||||
index++;
|
||||
}
|
||||
|
||||
legSelector.setItemSelected(0, true); // first leg
|
||||
loadLegData(0);
|
||||
}
|
||||
@@ -1227,9 +1140,7 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
}
|
||||
|
||||
private void loadCompetitors(int selectedRaceIndex) {
|
||||
|
||||
this.simulatorSvc.getCompetitorsNames(selectedRaceIndex, new AsyncCallback<List<String>>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
errorReporter.reportError(stringMessages.errorLoadingCompetitors(error.getMessage()));
|
||||
@@ -1237,13 +1148,10 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> response) {
|
||||
|
||||
competitorSelector.clear();
|
||||
|
||||
for (String competitorName : response) {
|
||||
competitorSelector.addItem(competitorName);
|
||||
}
|
||||
|
||||
competitorSelector.setItemSelected(0, true); // first competitor
|
||||
loadCompetitorData(0);
|
||||
}
|
||||
|
||||
+11
-28
@@ -54,6 +54,8 @@ import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator;
|
||||
import com.sap.sailing.gwt.ui.simulator.util.SimulatorResources;
|
||||
import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplay;
|
||||
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.RGBColor;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
@@ -134,70 +136,57 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
|
||||
public void onSuccess(SimulatorResultsDTO result) {
|
||||
|
||||
String notificationMessage = result.getNotificationMessage();
|
||||
if (notificationMessage != "" && notificationMessage.length() != 0 && warningAlreadyShown == false) {
|
||||
if (Util.hasLength(notificationMessage) && warningAlreadyShown == false) {
|
||||
errorReporter.reportError(notificationMessage, true);
|
||||
warningAlreadyShown = true;
|
||||
}
|
||||
|
||||
PathDTO[] paths = result.getPaths();
|
||||
|
||||
LOGGER.info("Number of Paths : " + paths.length);
|
||||
long startTime = paths[0].getPoints().get(0).timepoint;
|
||||
long maxDurationTime = 0;
|
||||
|
||||
final TimePoint startTime = paths[0].getPoints().get(0).timepoint;
|
||||
long maxDurationMillis = 0;
|
||||
if (mode == SailingSimulatorConstants.ModeMeasured) {
|
||||
Position pos1 = result.getRaceCourse().coursePositions.waypointPositions.get(0);
|
||||
Position pos2 = result.getRaceCourse().coursePositions.waypointPositions.get(1);
|
||||
raceCourseCanvasOverlay.setStartEndPoint(coordinateSystem.toLatLng(pos1), coordinateSystem.toLatLng(pos2));
|
||||
}
|
||||
|
||||
raceCourseCanvasOverlay.draw();
|
||||
removeOverlays();
|
||||
// pathCanvasOverlays.clear();
|
||||
replayPathCanvasOverlays.clear();
|
||||
colorPalette.reset();
|
||||
|
||||
PathDTO currentPath = null;
|
||||
//String color = null;
|
||||
String pathName = null;
|
||||
boolean algorithmTimedOut = false;
|
||||
int noOfPaths = paths.length;
|
||||
|
||||
for (int index = 0; index < noOfPaths; ++index) {
|
||||
|
||||
currentPath = paths[index];
|
||||
pathName = currentPath.getName();
|
||||
algorithmTimedOut = currentPath.getAlgorithmTimedOut();
|
||||
//color = colorPalette.getColor(noOfPaths - 1 - index);
|
||||
|
||||
if (pathName.equals("Polyline")) {
|
||||
pathPolyline = createPathPolyline(currentPath);
|
||||
} else if (pathName.equals("GPS Poly")) {
|
||||
continue;
|
||||
} else {
|
||||
|
||||
/* TODO Revisit for now creating a WindFieldDTO from the path */
|
||||
WindFieldDTO pathWindDTO = null;
|
||||
if (!currentPath.getMixedLeg()) {
|
||||
pathWindDTO = new WindFieldDTO();
|
||||
pathWindDTO.setMatrix(currentPath.getPoints());
|
||||
}
|
||||
|
||||
ReplayPathCanvasOverlay replayPathCanvasOverlay = new ReplayPathCanvasOverlay(map, SimulatorMapOverlaysZIndexes.PATH_ZINDEX,
|
||||
pathNameFormatter.format(currentPath), timer, windParams, algorithmTimedOut, currentPath.getMixedLeg(), coordinateSystem);
|
||||
replayPathCanvasOverlays.add(replayPathCanvasOverlay);
|
||||
replayPathCanvasOverlay.setPathColor(colorPalette.getColor(Integer.parseInt(pathName.split("#")[0])-1));
|
||||
|
||||
if (summaryView) {
|
||||
replayPathCanvasOverlay.displayWindAlongPath = true;
|
||||
timer.removeTimeListener(replayPathCanvasOverlay);
|
||||
replayPathCanvasOverlay.setTimer(null);
|
||||
}
|
||||
|
||||
if (SHOW_ONLY_PATH_POLYLINE == false) {
|
||||
replayPathCanvasOverlay.addToMap();
|
||||
}
|
||||
|
||||
replayPathCanvasOverlay.setWindField(pathWindDTO);
|
||||
replayPathCanvasOverlay.setRaceCourse(raceCourseCanvasOverlay.getStartPoint(), raceCourseCanvasOverlay.getEndPoint());
|
||||
/*if (index == 0) {
|
||||
@@ -209,32 +198,27 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
|
||||
replayPathCanvasOverlay.draw();
|
||||
}
|
||||
legendCanvasOverlay.setPathOverlays(replayPathCanvasOverlays);
|
||||
|
||||
long tmpDurationTime = currentPath.getPathTime();
|
||||
if ((!currentPath.getMixedLeg())&&(!currentPath.getAlgorithmTimedOut())&&(tmpDurationTime > maxDurationTime)) {
|
||||
maxDurationTime = tmpDurationTime;
|
||||
if ((!currentPath.getMixedLeg())&&(!currentPath.getAlgorithmTimedOut())&&(tmpDurationTime > maxDurationMillis)) {
|
||||
maxDurationMillis = tmpDurationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legendCanvasOverlay.setCurrent(result.getWindField().curSpeed,result.getWindField().curBearing);
|
||||
|
||||
legendCanvasOverlay.setCurrent(result.getWindField().curSpeed,result.getWindField().curBearing);
|
||||
if (timePanel != null) {
|
||||
Date endDate = new Date(startTime + maxDurationTime);
|
||||
timePanel.setMinMax(new Date(startTime), endDate, true);
|
||||
final TimePoint endDate = startTime.plus(maxDurationMillis);
|
||||
timePanel.setMinMax(startTime.asDate(), endDate.asDate(), true);
|
||||
timePanel.resetTimeSlider();
|
||||
timePanel.timeChanged(windParams.getStartTime(), null);
|
||||
if (windParams.isShowStreamlets()) {
|
||||
windStreamletsCanvasOverlay.setEndDate(endDate);
|
||||
windStreamletsCanvasOverlay.setEndDate(endDate.asDate());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Now we always get the wind field
|
||||
*/
|
||||
WindFieldDTO windFieldDTO = result.getWindField();
|
||||
//LOGGER.info("Number of windDTO : " + windFieldDTO.getMatrix().size());
|
||||
|
||||
if (windParams.isShowGrid()) {
|
||||
windGridCanvasOverlay.addToMap();
|
||||
}
|
||||
@@ -1024,7 +1008,6 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
|
||||
private PathPolyline createPathPolyline(final PathDTO pathDTO) {
|
||||
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(parent.getSelectedBoatClassIndex(), parent.getSelectedRaceIndex(),
|
||||
parent.getSelectedCompetitorIndex(), parent.getSelectedLegIndex());
|
||||
|
||||
return PathPolyline.createPathPolyline(pathDTO.getPoints(), errorReporter, simulatorService, map, this, parent, selection, coordinateSystem);
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -72,14 +72,13 @@ public class WindFieldCanvasOverlay extends FullCanvasOverlay implements TimeLis
|
||||
|
||||
public void setWindField(final WindFieldDTO windFieldDTO) {
|
||||
this.windFieldDTO = windFieldDTO;
|
||||
|
||||
timePointWindDTOMap.clear();
|
||||
if (windFieldDTO != null) {
|
||||
for(final SimulatorWindDTO w : windFieldDTO.getMatrix()) {
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint)) {
|
||||
timePointWindDTOMap.put(w.timepoint, new LinkedList<SimulatorWindDTO>());
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint.asMillis())) {
|
||||
timePointWindDTOMap.put(w.timepoint.asMillis(), new LinkedList<SimulatorWindDTO>());
|
||||
}
|
||||
timePointWindDTOMap.get(w.timepoint).add(w);
|
||||
timePointWindDTOMap.get(w.timepoint.asMillis()).add(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +91,6 @@ public class WindFieldCanvasOverlay extends FullCanvasOverlay implements TimeLis
|
||||
@Override
|
||||
public void addToMap() {
|
||||
super.addToMap();
|
||||
|
||||
if (timer != null) {
|
||||
timer.addTimeListener(this);
|
||||
}
|
||||
|
||||
+3
-3
@@ -113,10 +113,10 @@ public class WindGridCanvasOverlay extends FullCanvasOverlay implements TimeList
|
||||
timePointWindDTOMap.clear();
|
||||
if (wl != null) {
|
||||
for (final SimulatorWindDTO w : wl.getMatrix()) {
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint)) {
|
||||
timePointWindDTOMap.put(w.timepoint, new LinkedList<SimulatorWindDTO>());
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint.asMillis())) {
|
||||
timePointWindDTOMap.put(w.timepoint.asMillis(), new LinkedList<SimulatorWindDTO>());
|
||||
}
|
||||
timePointWindDTOMap.get(w.timepoint).add(w);
|
||||
timePointWindDTOMap.get(w.timepoint.asMillis()).add(w);
|
||||
}
|
||||
|
||||
final SortByWindSpeed windSpeedSorter = new SortByWindSpeed();
|
||||
|
||||
+3
-5
@@ -80,7 +80,6 @@ public class WindLineGuidesCanvasOverlay extends FullCanvasOverlay implements Ti
|
||||
@Override
|
||||
public void removeFromMap() {
|
||||
super.removeFromMap();
|
||||
|
||||
if (timer != null) {
|
||||
timer.removeTimeListener(this);
|
||||
}
|
||||
@@ -88,14 +87,13 @@ public class WindLineGuidesCanvasOverlay extends FullCanvasOverlay implements Ti
|
||||
|
||||
public void setWindField(final WindFieldDTO windField) {
|
||||
this.windFieldDTO = windField;
|
||||
|
||||
timePointWindDTOMap.clear();
|
||||
if (windField != null) {
|
||||
for(final SimulatorWindDTO w : windField.getMatrix()) {
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint)) {
|
||||
timePointWindDTOMap.put(w.timepoint, new LinkedList<SimulatorWindDTO>());
|
||||
if (!timePointWindDTOMap.containsKey(w.timepoint.asMillis())) {
|
||||
timePointWindDTOMap.put(w.timepoint.asMillis(), new LinkedList<SimulatorWindDTO>());
|
||||
}
|
||||
timePointWindDTOMap.get(w.timepoint).add(w);
|
||||
timePointWindDTOMap.get(w.timepoint.asMillis()).add(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="resources"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.landscape.gateway</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,8 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.release=disabled
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
@@ -0,0 +1,35 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Gateway
|
||||
Bundle-SymbolicName: com.sap.sailing.landscape.gateway
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Automatic-Module-Name: com.sap.sailing.landscape.gateway
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Web-ContextPath: /sailinglandscape
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Import-Package: com.sun.jersey.spi.container.servlet;version="1.17.0",
|
||||
javax.servlet;version="3.1.0",
|
||||
javax.ws.rs;version="1.1.1",
|
||||
javax.ws.rs.core;version="1.1.1",
|
||||
org.osgi.framework,
|
||||
org.osgi.util.tracker;version="1.5.2"
|
||||
Bundle-Activator: com.sap.sailing.landscape.gateway.impl.Activator
|
||||
Require-Bundle: com.sap.sailing.landscape,
|
||||
com.sap.sse,
|
||||
com.sap.sse.common,
|
||||
com.sap.sse.security,
|
||||
com.sap.sse.landscape,
|
||||
com.sap.sse.landscape.aws,
|
||||
com.sap.sailing.landscape,
|
||||
org.apache.shiro.core;bundle-version="1.7.1",
|
||||
org.apache.shiro.ehcache;bundle-version="1.7.1",
|
||||
org.apache.shiro.web;bundle-version="1.7.1",
|
||||
org.json.simple;bundle-version="1.1.0",
|
||||
com.sap.sailing.domain,
|
||||
com.sap.sailing.shared.server,
|
||||
com.sap.sailing.shared.server.gateway,
|
||||
com.sap.sailing.server.gateway.interfaces,
|
||||
com.sap.sailing.server.gateway.serialization,
|
||||
com.sap.sse.shared.android,
|
||||
com.sap.sse.landscape.common
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<!-- Disable directory listing: -->
|
||||
<servlet>
|
||||
<servlet-name>default</servlet-name>
|
||||
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>dirAllowed</param-name>
|
||||
<param-value>false</param-value>
|
||||
</init-param>
|
||||
</servlet>
|
||||
|
||||
<!-- Apache Shiro -->
|
||||
<context-param>
|
||||
<param-name>shiroEnvironmentClass</param-name>
|
||||
<param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
|
||||
</context-param>
|
||||
<listener>
|
||||
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
|
||||
</listener>
|
||||
<filter>
|
||||
<filter-name>ShiroFilter</filter-name>
|
||||
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
|
||||
</filter>
|
||||
<!-- Make sure any request you want accessible to Shiro is filtered. "/*"
|
||||
catches all requests. Usually this filter mapping is defined first (before
|
||||
all others) to ensure that Shiro works in subsequent filters in the filter
|
||||
chain: -->
|
||||
<filter-mapping>
|
||||
<filter-name>ShiroFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>FORWARD</dispatcher>
|
||||
<dispatcher>INCLUDE</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
</filter-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Jersey REST API</servlet-name>
|
||||
<servlet-class>com.sap.sailing.landscape.gateway.jaxrs.RestServletContainer</servlet-class>
|
||||
<init-param>
|
||||
<param-name>javax.ws.rs.Application</param-name>
|
||||
<param-value>com.sap.sailing.landscape.gateway.jaxrs.api.RestApiApplication</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
|
||||
<param-value>com.sap.sse.rest.AccessControlAndEncodingResponseFilter</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>com.sun.jersey.config.property.packages</param-name>
|
||||
<param-value>com.sap.sailing.landscape.gateway.jaxrs</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>com.sun.jersey.config.feature.DisableWADL</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Jersey REST API</servlet-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
source.. = src/,\
|
||||
resources/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
webservices/,\
|
||||
.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>com.sap.sailing.landscape.gateway</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
@@ -0,0 +1,70 @@
|
||||
[main]
|
||||
invalidRequest = org.apache.shiro.web.filter.InvalidRequestFilter
|
||||
invalidRequest.blockBackslash = false
|
||||
invalidRequest.blockSemicolon = false
|
||||
invalidRequest.blockNonAscii = false
|
||||
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
|
||||
credentialsMatcher.storedCredentialsHexEncoded = false
|
||||
credentialsMatcher.hashIterations = 1024
|
||||
|
||||
# Realm configuration:
|
||||
# --------------------
|
||||
|
||||
upRealm = com.sap.sse.security.UsernamePasswordRealm
|
||||
upRealm.credentialsMatcher = $credentialsMatcher
|
||||
oauthRealm = com.sap.sse.security.OAuthRealm
|
||||
bearerTokenRealm = com.sap.sse.security.BearerTokenRealm
|
||||
|
||||
# Putting it all together:
|
||||
sessionManager = com.sap.sse.security.SecurityWebSessionManager
|
||||
securityManager.sessionManager = $sessionManager
|
||||
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
|
||||
securityManager.sessionManager.sessionDAO = $sessionDAO
|
||||
subjectDAO = com.sap.sse.security.NoSessionStorageSessionDAO
|
||||
securityManager.subjectDAO = $subjectDAO
|
||||
# 1y timeout
|
||||
securityManager.sessionManager.globalSessionTimeout = 31536000000
|
||||
cacheManager = com.sap.sse.security.SessionCacheManager
|
||||
securityManager.cacheManager = $cacheManager
|
||||
|
||||
# Support for anonymous user permissions
|
||||
webSubjectFactoryWithAnonymousPrincipalSupport=com.sap.sse.security.WebSubjectFactoryWithAnonymousPrincipalSupport
|
||||
securityManager.subjectFactory=$webSubjectFactoryWithAnonymousPrincipalSupport
|
||||
|
||||
# Authentication Filter Configurations
|
||||
# ------------------------------------
|
||||
|
||||
# A filter that requires the user to have at least one of the roles specified as
|
||||
# filter parameter. This makes it different from the default roles[...] filter
|
||||
# which requires the user to have ALL of the roles specified as parameters.
|
||||
anyofroles = com.sap.sse.security.AnyOfRolesFilter
|
||||
|
||||
# A custom filter for GWT pages that require an authenticated user.
|
||||
# Forwards unauthenticated users to the default login page. When
|
||||
# addressed directly (and not based on a redirect), users who logged
|
||||
# on successfully will be redirected to the UserManagement.html page
|
||||
# where they can adjust their profile.
|
||||
customGwt = com.sap.sse.security.CustomFilter
|
||||
customGwt.loginUrl = /security/ui/Login.html
|
||||
customGwt.successUrl = /UserManagement.html
|
||||
|
||||
# Configuration for the default shiro HTTP form authentication filter.
|
||||
# It assumes that form-based login parameters are sent to the
|
||||
# /api/restsecurity/login RESTlet and forwards successful login attempts
|
||||
# to the /api/restsecurity/hello service which is expected to respond
|
||||
# with a JSON document containing the authenticated subject's properties.
|
||||
authc.loginUrl = /api/restsecurity/login
|
||||
authc.successUrl = /api/restsecurity/hello
|
||||
|
||||
# This authentication filter accepts a bearer access token in the HTTP
|
||||
# Authorization header field, as in
|
||||
# Authorization: Bearer 1029741026501365024376093245
|
||||
# If no such bearer token is provided, the filter falls back to basic HTTP
|
||||
# authentication ("Authentication: Basic ...") and then regular form-based
|
||||
# authentication with POST parameters "username" and "password".
|
||||
bearerTokenOrAnonymous = com.sap.sailing.shared.server.security.BearerTokenOrBasicOrFormOrAnonymousAuthenticationFilterSAPSailing
|
||||
|
||||
# Specifying filter chains for URL patterns
|
||||
|
||||
[urls]
|
||||
/api/** = bearerTokenOrAnonymous
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.landscape.gateway.impl;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
public class Activator implements BundleActivator {
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.sap.sailing.landscape.gateway.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsProcess;
|
||||
import com.sap.sse.landscape.aws.AwsApplicationReplicaSet;
|
||||
import com.sap.sse.shared.json.JsonSerializer;
|
||||
|
||||
public class AwsApplicationReplicaSetJsonSerializer implements JsonSerializer<AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>>> {
|
||||
public static String NAME = "name";
|
||||
public static String VERSION = "version";
|
||||
private final String releaseName;
|
||||
|
||||
public AwsApplicationReplicaSetJsonSerializer(String releaseName) {
|
||||
this.releaseName = releaseName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject serialize(
|
||||
AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> replicaSet) {
|
||||
final JSONObject result = new JSONObject();
|
||||
result.put(NAME, replicaSet.getName());
|
||||
result.put(VERSION, releaseName);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.sap.sailing.landscape.gateway.jaxrs;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.landscape.LandscapeService;
|
||||
import com.sap.sailing.shared.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sse.landscape.common.shared.SecuredLandscapeTypes;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
|
||||
public abstract class AbstractLandscapeResource extends AbstractSailingServerResource {
|
||||
@Context
|
||||
ServletContext servletContext;
|
||||
|
||||
// Working with concrete implementation not to create new dependencies between domain and datamining
|
||||
// (PolarDataMiner is unavailable in com.sap.sailing.domain)
|
||||
public LandscapeService getLandscapeService() {
|
||||
@SuppressWarnings("unchecked")
|
||||
ServiceTracker<LandscapeService, LandscapeService> tracker = (ServiceTracker<LandscapeService, LandscapeService>) servletContext
|
||||
.getAttribute(RestServletContainer.LANDSCAPE_SERVICE_TRACKER_NAME);
|
||||
return tracker.getService();
|
||||
}
|
||||
|
||||
protected void checkLandscapeManageAwsPermission() {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.sap.sailing.landscape.gateway.jaxrs;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.landscape.LandscapeService;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.util.ServiceTrackerFactory;
|
||||
import com.sun.jersey.spi.container.servlet.ServletContainer;
|
||||
|
||||
public class RestServletContainer extends ServletContainer {
|
||||
private static final long serialVersionUID = 3129261471808425410L;
|
||||
|
||||
private static final String OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME = "osgi-bundlecontext";
|
||||
|
||||
public static final String LANDSCAPE_SERVICE_TRACKER_NAME = "landscapeServiceTracker";
|
||||
|
||||
public static final String SECURITY_SERVICE_TRACKER_NAME = "securityServiceTracker";
|
||||
|
||||
private ServiceTracker<LandscapeService, LandscapeService> landscapeServiceTracker;
|
||||
|
||||
private ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
|
||||
|
||||
public RestServletContainer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RestServletContainer(Application app) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
public RestServletContainer(Class<? extends Application> appClass) {
|
||||
super(appClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
super.init(config);
|
||||
BundleContext context = (BundleContext) config.getServletContext().getAttribute(OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME);
|
||||
landscapeServiceTracker = ServiceTrackerFactory.createAndOpen(context, LandscapeService.class);
|
||||
securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
|
||||
config.getServletContext().setAttribute(LANDSCAPE_SERVICE_TRACKER_NAME, landscapeServiceTracker);
|
||||
config.getServletContext().setAttribute(SECURITY_SERVICE_TRACKER_NAME, securityServiceTracker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if (landscapeServiceTracker != null) {
|
||||
landscapeServiceTracker.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.landscape.gateway.jaxrs.api;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
import com.sap.sse.security.jaxrs.ShiroAuthorizationExceptionTo401ResponseMapper;
|
||||
|
||||
public class RestApiApplication extends Application {
|
||||
|
||||
public Set<Class<?>> getClasses() {
|
||||
HashSet<Class<?>> classes = new HashSet<>();
|
||||
// RESTlets
|
||||
classes.add(SailingLandscapeResource.class);
|
||||
|
||||
// Exception Mapper
|
||||
classes.add(ShiroAuthorizationExceptionTo401ResponseMapper.class);
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
package com.sap.sailing.landscape.gateway.jaxrs.api;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.landscape.AwsSessionCredentialsWithExpiry;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsProcess;
|
||||
import com.sap.sailing.landscape.SharedLandscapeConstants;
|
||||
import com.sap.sailing.landscape.gateway.impl.AwsApplicationReplicaSetJsonSerializer;
|
||||
import com.sap.sailing.landscape.gateway.jaxrs.AbstractLandscapeResource;
|
||||
import com.sap.sailing.landscape.procedures.SailingAnalyticsHostSupplier;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.landscape.Release;
|
||||
import com.sap.sse.landscape.aws.AwsApplicationReplicaSet;
|
||||
import com.sap.sse.landscape.aws.AwsLandscape;
|
||||
import com.sap.sse.landscape.aws.MongoUriParser;
|
||||
import com.sap.sse.landscape.aws.impl.AwsRegion;
|
||||
import com.sap.sse.landscape.mongodb.MongoEndpoint;
|
||||
|
||||
@Path("/landscape")
|
||||
public class SailingLandscapeResource extends AbstractLandscapeResource {
|
||||
private static final Logger logger = Logger.getLogger(SailingLandscapeResource.class.getName());
|
||||
|
||||
private static final String REGION_FORM_PARAM = "regionId";
|
||||
private static final String REPLICA_SET_NAME_FORM_PARAM = "replicaSetName";
|
||||
private static final String MASTER_INSTANCE_TYPE_FORM_PARAM = "masterInstanceType";
|
||||
private static final String DYNAMIC_LOAD_BALANCER_MAPPING_FORM_PARAM = "dynamicLoadBalancerMapping";
|
||||
private static final String RELEASE_NAME_FORM_PARAM = "releaseName";
|
||||
private static final String KEY_NAME_FORM_PARAM = "keyName";
|
||||
private static final String PRIVATE_KEY_ENCRYPTION_PASSPHRASE_FORM_PARAM = "privateKeyEncryptionPassphrase";
|
||||
private static final String MASTER_REPLICATION_BEARER_TOKEN_FORM_PARAM = "masterReplicationBearerToken";
|
||||
private static final String REPLICA_REPLICATION_BEARER_TOKEN_FORM_PARAM = "replicaReplicationBearerToken";
|
||||
private static final String DOMAIN_NAME_FORM_PARAM = "domainName";
|
||||
private static final String MEMORY_IN_MEGABYTES_FORM_PARAM = "memoryInMegabytes";
|
||||
private static final String MEMORY_TOTAL_SIZE_FACTOR_FORM_PARAM = "memoryTotalSizeFactor";
|
||||
private static final String TIMEOUT_IN_MILLISECONDS_FORM_PARAM = "timeoutInMilliseconds";
|
||||
private static final String REMOVE_APPLICATION_REPLICA_SET_FORM_PARAM = "removeApplicationReplicaSet";
|
||||
private static final String MAX_NUMBER_OF_COMPARE_SERVER_ATTEMPTS_FORM_PARAM = "maxNumberOfCompareserverAttempts";
|
||||
private static final String DURATION_TO_WAIT_BEFORE_COMPARE_SERVERS_IN_MILLISECONDS_FORM_PARAM = "durationToWaitBeforeCompareServersInMilliseconds";
|
||||
private static final String UUID_FOR_MDI_PROGRESS = "uuidForMdiProgress";
|
||||
private static final String BEARER_TOKEN_FOR_REPLICA_SET_TO_ARCHIVE_FORM_PARAM = "bearerTokenForReplicaSetToArchive";
|
||||
private static final String BEARER_TOKEN_FOR_ARCHIVE_FORM_PARAM = "bearerTokenForArchive";
|
||||
private static final String MONGO_URI_TO_ARCHIVE_DB_TO_FORM_PARAM = "mongoUriToArchiveDbTo";
|
||||
private static final String AWS_KEY_ID_FORM_PARAM = "awsKeyId";
|
||||
private static final String AWS_KEY_SECRET_FORM_PARAM = "awsKeySecret";
|
||||
private static final String AWS_MFA_TOKEN_FORM_PARAM = "awsMfaToken";
|
||||
private static final String SESSION_TOKEN_EXPIRY_UNIX_TIME_MILLIS = "sessionTokenExpiryMillis";
|
||||
private static final String SESSION_TOKEN_EXPIRY_ISO = "sessionTokenExpiryISO";
|
||||
|
||||
@Context
|
||||
UriInfo uriInfo;
|
||||
|
||||
@Path("/createsessioncredentials")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response createSessionCredentials(
|
||||
@FormParam(AWS_KEY_ID_FORM_PARAM) String awsKeyId,
|
||||
@FormParam(AWS_KEY_SECRET_FORM_PARAM) String awsKeySecret,
|
||||
@FormParam(AWS_MFA_TOKEN_FORM_PARAM) String mfaToken
|
||||
) {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
try {
|
||||
getLandscapeService().createMfaSessionCredentials(awsKeyId, awsKeySecret, mfaToken);
|
||||
response = Response.ok().build();
|
||||
} catch (Exception e) {
|
||||
response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Path("/getsessioncredentials")
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response getSessionCredentials() {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
try {
|
||||
final AwsSessionCredentialsWithExpiry sessionCredentials = getLandscapeService().getSessionCredentials();
|
||||
if (sessionCredentials == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("No session credentials found for user "+getSecurityService().getCurrentUser().getName()).build();
|
||||
} else {
|
||||
final JSONObject result = new JSONObject();
|
||||
result.put(SESSION_TOKEN_EXPIRY_UNIX_TIME_MILLIS, sessionCredentials.getExpiration().asMillis());
|
||||
result.put(SESSION_TOKEN_EXPIRY_ISO, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(sessionCredentials.getExpiration().asDate()));
|
||||
response = Response.ok().entity(streamingOutput(result)).build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Path("/clearsessioncredentials")
|
||||
@POST
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response clearSessionCredentials() {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
try {
|
||||
getLandscapeService().clearSessionCredentials();
|
||||
response = Response.ok().build();
|
||||
} catch (Exception e) {
|
||||
response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Path("/createapplicationreplicaset")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response createApplicationReplicaSet(
|
||||
@FormParam(REGION_FORM_PARAM) String regionId,
|
||||
@FormParam(REPLICA_SET_NAME_FORM_PARAM) String replicaSetName,
|
||||
@FormParam(MASTER_INSTANCE_TYPE_FORM_PARAM) String masterInstanceType,
|
||||
@FormParam(DYNAMIC_LOAD_BALANCER_MAPPING_FORM_PARAM) @DefaultValue("false") boolean dynamicLoadBalancerMapping,
|
||||
@FormParam(RELEASE_NAME_FORM_PARAM) String releaseNameOrNullForLatestMaster,
|
||||
@FormParam(KEY_NAME_FORM_PARAM) String optionalKeyName,
|
||||
@FormParam(PRIVATE_KEY_ENCRYPTION_PASSPHRASE_FORM_PARAM) String privateKeyEncryptionPassphrase,
|
||||
@FormParam(MASTER_REPLICATION_BEARER_TOKEN_FORM_PARAM) String masterReplicationBearerToken,
|
||||
@FormParam(REPLICA_REPLICATION_BEARER_TOKEN_FORM_PARAM) String replicaReplicationBearerToken,
|
||||
@FormParam(DOMAIN_NAME_FORM_PARAM) String domainName,
|
||||
@FormParam(MEMORY_IN_MEGABYTES_FORM_PARAM) Integer optionalMemoryInMegabytesOrNull,
|
||||
@FormParam(MEMORY_TOTAL_SIZE_FACTOR_FORM_PARAM) Integer optionalMemoryTotalSizeFactorOrNull) {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
try {
|
||||
final Release release = getLandscapeService().getRelease(releaseNameOrNullForLatestMaster);
|
||||
final AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> replicaSet = getLandscapeService()
|
||||
.createApplicationReplicaSet(regionId, replicaSetName, masterInstanceType, dynamicLoadBalancerMapping,
|
||||
release.getName(), optionalKeyName,
|
||||
privateKeyEncryptionPassphrase == null ? null : privateKeyEncryptionPassphrase.getBytes(),
|
||||
masterReplicationBearerToken, replicaReplicationBearerToken, domainName,
|
||||
optionalMemoryInMegabytesOrNull, optionalMemoryTotalSizeFactorOrNull);
|
||||
final JSONObject result = new AwsApplicationReplicaSetJsonSerializer(release.getName()).serialize(replicaSet);
|
||||
response = Response.ok(streamingOutput(result)).build();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Exception trying to create application replica set "+replicaSetName+" on behalf of user "+getSecurityService().getCurrentUser(), e);
|
||||
response = badRequest(e.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Path("/movetoarchiveserver")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response moveToArchiveServer(
|
||||
@FormParam(REGION_FORM_PARAM) String regionId,
|
||||
@FormParam(REPLICA_SET_NAME_FORM_PARAM) String replicaSetName,
|
||||
@FormParam(KEY_NAME_FORM_PARAM) String optionalKeyName,
|
||||
@FormParam(PRIVATE_KEY_ENCRYPTION_PASSPHRASE_FORM_PARAM) String privateKeyEncryptionPassphrase,
|
||||
@FormParam(BEARER_TOKEN_FOR_REPLICA_SET_TO_ARCHIVE_FORM_PARAM) String bearerTokenOrNullForApplicationReplicaSetToArchive,
|
||||
@FormParam(BEARER_TOKEN_FOR_ARCHIVE_FORM_PARAM) String bearerTokenOrNullForArchive,
|
||||
@FormParam(TIMEOUT_IN_MILLISECONDS_FORM_PARAM) Long optionalTimeoutInMilliseconds,
|
||||
@FormParam(DURATION_TO_WAIT_BEFORE_COMPARE_SERVERS_IN_MILLISECONDS_FORM_PARAM) @DefaultValue("300000") long durationToWaitBeforeCompareServersInMillis,
|
||||
@FormParam(MAX_NUMBER_OF_COMPARE_SERVER_ATTEMPTS_FORM_PARAM) @DefaultValue("5") int maxNumberOfCompareServerAttempts,
|
||||
@FormParam(REMOVE_APPLICATION_REPLICA_SET_FORM_PARAM) @DefaultValue("true") boolean removeApplicationReplicaSet,
|
||||
@FormParam(MONGO_URI_TO_ARCHIVE_DB_TO_FORM_PARAM) String mongoUriToArchiveDbTo) {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
final AwsRegion region = new AwsRegion(regionId);
|
||||
byte[] passphraseForPrivateKeyDecryption = privateKeyEncryptionPassphrase==null?null:privateKeyEncryptionPassphrase.getBytes();
|
||||
final AwsLandscape<String> landscape = getLandscapeService().getLandscape();
|
||||
final MongoUriParser<String> mongoUriParser = new MongoUriParser<>(landscape, region);
|
||||
try {
|
||||
final MongoEndpoint moveDatabaseHere = mongoUriToArchiveDbTo == null ? null : mongoUriParser.parseMongoUri(mongoUriToArchiveDbTo).getEndpoint();
|
||||
final AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> applicationReplicaSetToArchive = Util.first(Util.filter(
|
||||
landscape.getApplicationReplicaSetsByTag(region,
|
||||
SharedLandscapeConstants.SAILING_ANALYTICS_APPLICATION_HOST_TAG, new SailingAnalyticsHostSupplier<String>(),
|
||||
Optional.ofNullable(optionalTimeoutInMilliseconds).map(Duration::ofMillis), Optional.ofNullable(optionalKeyName),
|
||||
passphraseForPrivateKeyDecryption),
|
||||
rs->rs.getName().equals(replicaSetName)));
|
||||
if (applicationReplicaSetToArchive == null) {
|
||||
response = badRequest("Application replica set with name "+replicaSetName+" not found in region "+regionId);
|
||||
} else {
|
||||
final UUID uuidForMdiProgress = getLandscapeService().archiveReplicaSet(regionId, applicationReplicaSetToArchive,
|
||||
bearerTokenOrNullForApplicationReplicaSetToArchive, bearerTokenOrNullForArchive,
|
||||
Duration.ofMillis(durationToWaitBeforeCompareServersInMillis), maxNumberOfCompareServerAttempts, removeApplicationReplicaSet,
|
||||
moveDatabaseHere, optionalKeyName, passphraseForPrivateKeyDecryption);
|
||||
final JSONObject result = new JSONObject();
|
||||
result.put(UUID_FOR_MDI_PROGRESS, uuidForMdiProgress);
|
||||
response = Response.ok(streamingOutput(result)).build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error trying to archive replica set "+replicaSetName+" in region "+regionId+": "+e.getMessage(), e);
|
||||
response = badRequest("Error trying to archive replica set "+replicaSetName+" in region "+regionId+": "+e.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Path("/removeapplicationreplicaset")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response removeApplicationServerReplicaSet(
|
||||
@FormParam(REGION_FORM_PARAM) String regionId,
|
||||
@FormParam(REPLICA_SET_NAME_FORM_PARAM) String replicaSetName,
|
||||
@FormParam(TIMEOUT_IN_MILLISECONDS_FORM_PARAM) Long optionalTimeoutInMilliseconds,
|
||||
@FormParam(KEY_NAME_FORM_PARAM) String optionalKeyName,
|
||||
@FormParam(PRIVATE_KEY_ENCRYPTION_PASSPHRASE_FORM_PARAM) String privateKeyEncryptionPassphrase) {
|
||||
checkLandscapeManageAwsPermission();
|
||||
Response response;
|
||||
final AwsRegion region = new AwsRegion(regionId);
|
||||
byte[] passphraseForPrivateKeyDecryption = privateKeyEncryptionPassphrase==null?null:privateKeyEncryptionPassphrase.getBytes();
|
||||
final AwsLandscape<String> landscape = getLandscapeService().getLandscape();
|
||||
try {
|
||||
final AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> applicationReplicaSetToRemove = Util.first(Util.filter(
|
||||
landscape.getApplicationReplicaSetsByTag(region,
|
||||
SharedLandscapeConstants.SAILING_ANALYTICS_APPLICATION_HOST_TAG, new SailingAnalyticsHostSupplier<String>(),
|
||||
Optional.ofNullable(optionalTimeoutInMilliseconds).map(Duration::ofMillis), Optional.ofNullable(optionalKeyName),
|
||||
passphraseForPrivateKeyDecryption),
|
||||
rs->rs.getName().equals(replicaSetName)));
|
||||
if (applicationReplicaSetToRemove == null) {
|
||||
response = badRequest("Application replica set with name "+replicaSetName+" not found in region "+regionId);
|
||||
} else {
|
||||
getLandscapeService().removeApplicationReplicaSet(regionId, applicationReplicaSetToRemove, optionalKeyName, passphraseForPrivateKeyDecryption);
|
||||
response = Response.ok().build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error trying to archive replica set "+replicaSetName+" in region "+regionId+": "+e.getMessage(), e);
|
||||
response = badRequest("Error trying to archive replica set "+replicaSetName+" in region "+regionId+": "+e.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API</h1>
|
||||
<h2>URL: /api/clearsessioncredentials</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Clears all information about active hyperscaler (e.g., AWS) session credentials. Subsequent
|
||||
requests by the same authenticated subject to manipulate the landscape will fail, and the
|
||||
/getsessioncredentials method will return a 404 (not found) status until new credentials
|
||||
are generated using the <a href="createsessioncredentials.html">/createsessioncredentials</a> method.
|
||||
</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
None
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' "https://sapsailing.com/sailinglandscape/api/landscape/clearsessioncredentials"<br>
|
||||
produces an empty output with a 200 status code.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/createapplicationreplicaset</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Creates an application replica set with an auto-scaling group and corresponding launch configuration, load
|
||||
balancer rules and corresponding target groups, hooking up to the region's default MongoDB replica set and
|
||||
RabbitMQ and replicating the security service and shared sailing data from the security-service.sapsailing.com
|
||||
server by default.</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
<div><tt>regionId</tt>, region where to set up the replica set, e.g., eu-west-1</div>
|
||||
<div><tt>replicaSetName</tt>, name of the replica set; used as the hostname to which the domain name is appended, as well as
|
||||
for the database and RabbitMQ exchange name</div>
|
||||
<div><tt>masterInstanceType</tt>, instance type to use in the hyperscaler for master and replica nodes, e.g., C5_4_XLARGE;
|
||||
see <a href="https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/InstanceType.html">here</a></div>
|
||||
<div><tt>keyName</tt>, the name of the SSH key to use; must identify an existing SSH key by that name in the region specified
|
||||
and will be used to log on to existing and new instances using a root ssh connection.</div>
|
||||
<div><tt>privateKeyEncryptionPassphrase</tt>, the pass phrase to decrypt the private key for the key specified by the keyName parameter</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional form parameters:</td>
|
||||
<td>
|
||||
<div><tt>dynamicLoadBalancerMappingionId</tt>, boolean value (false or true; defaulting to false); if true, the rules will be entered
|
||||
into the region's default dynamic load balancer without a specific DNS mapping. In this
|
||||
case the domain must not be specified or must be "sapsailing.com", and the region must
|
||||
be the default region (eu-west-1), or an error status will result.</div>
|
||||
<div><tt>releaseName</tt>, the release name, such as build-202108091125; if not provided, the latest master build is used by default.</div>
|
||||
<div><tt>masterReplicationBearerToken</tt>, the bearer token the new replica set's master will use to replicate the security service and
|
||||
shared sailing data from the central security-service.sapsailing.com server; defaults to the
|
||||
bearer token of the user authenticated for this request. Note that this requires the
|
||||
server processing this request to share the security information from security-service.sapsailing.com, too.</div>
|
||||
<div><tt>replicaReplicationBearerToken</tt>, the bearer token the new replica set's replicas will use to replicate their master server; defaults to the
|
||||
bearer token of the user authenticated for this request. Note that this requires the
|
||||
server processing this request to share the security information from security-service.sapsailing.com, too.</div>
|
||||
<div><tt>domainName</tt>, the domain name to use for the DNS entry that points to the load balancer holding the rules for the new replica set.
|
||||
Defaults to sapsailing.com. If dynamic load balancing is used, this parameter must either not be provided or must
|
||||
use the default domain sapsailing.com.</div>
|
||||
<div><tt>memoryInMegabytes</tt>, can be used to specify the absolute amount of heap size used for the Java VM running the applications, both, on master
|
||||
and replicas; if not specified, memoryTotalSizeFactor may be used (see below); if that isn't used either, heap size
|
||||
is computed based on the amount of memory available on the instance, not considering any swap space available.</div>
|
||||
<div><tt>memoryTotalSizeFactor</tt>, can be used if memoryInMegabytes is not used; it roughly specifies how many processes are intended to be run on the
|
||||
host; e.g., if 4 is specified, approximately 1/4 of the physical memory available (not considering swap space) is
|
||||
assigned as the process's heap space. If neither memoryInMegabytes nor memoryTotalSizeFactor are specified then
|
||||
the amount of memory is calculated automatically based on the amount of physical memory available on the host.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST -d 'regionId=eu-west-2' -d 'replicaSetName=mynewevent' -d 'masterInstanceType=C5_4_XLARGE' -d 'keyName=Horst' --data-urlencode 'privateKeyEncryptionPassphrase=HorstsSuperSecretPassword' --data-urlencode 'masterReplicationBearerToken=DE7x+W/dwFKRAxFbJkC/IMafEnJ8kTQF+MUHNVhEwsD4=' --data-urlencode 'replicaReplicationBearerToken=CHxGpF0nr78Z4m/f28/MgiYhdRB3xoDCYd+rLc37rTt=' -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' "https://sapsailing.com/sailinglandscape/api/landscape/createapplicationreplicaset"
|
||||
<br>
|
||||
produces output:<pre>
|
||||
{
|
||||
"name": "mynewevent",
|
||||
"version": "build-202112151552"
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/createsessioncredentials</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Creates and stores session credentials for the user authenticated for this request.
|
||||
The user requires LANDSCAPE:MANAGE:AWS permission and has to provide a key ID, a corresponding
|
||||
secret as well as an MFA token. If successful, subsequent requests, e.g., to /createapplicationreplicaset
|
||||
will use these credentials to authenticate towards the hyperscaler landscape and to manage its resources.</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>no output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
<div><tt>awsKeyId</tt>, the AWS Key ID</div>
|
||||
<div><tt>awsKeySecret</tt>, the secret for the key</div>
|
||||
<div><tt>awsMfaToken</tt>, an MFA token from the MFA device associated with the key</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST --data-urlencode 'awsKeyId=AKIAND3FQAC49IMUHP8D' --data-urlencode 'awsKeySecret=r44x4VAkacgQw7NMvtpjDuq1Ks+Y2Q943ERlMYTD' -d 'awsMfaToken=306961' -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' "https://sapsailing.com/sailinglandscape/api/landscape/createsessioncredentials"<br>
|
||||
produces no output and a 200 status code
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API</h1>
|
||||
<h2>URL: /api/getsessioncredentials</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Obtains information about active hyperscaler (e.g., AWS) session credentials, in particular
|
||||
their expiry time point.
|
||||
</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
None
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X GET -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' "https://sapsailing.com/sailinglandscape/api/landscape/getsessioncredentials"<br>
|
||||
produces output:<pre>
|
||||
{
|
||||
"sessionTokenExpiryMillis": 1639712346000,
|
||||
"sessionTokenExpiryISO": "2021-12-17T04:39:06.000Z"
|
||||
}
|
||||
</pre>
|
||||
|
||||
If the subject is not properly authenticated or does not have any active credentials stored, a 404 (not found) status will result.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API for Landscape Management - Overview</h1>
|
||||
|
||||
<p>
|
||||
The services listed herein can be used to automatically manage aspects of a system landscape
|
||||
running the SAP Sailing Analytics. This includes creating a new application server replica set
|
||||
for a server name, archive and remove such a replica set's contents, as well as managing the
|
||||
AWS credentials including MFA token handling that allow accessing the hyperscaler infrastructure
|
||||
in the first place.
|
||||
</p>
|
||||
<p>
|
||||
<b>ATTRIBUTION / BRANDING:</b> If you make use of this API, SAP requires you to include SAP branding in any
|
||||
end-user facing interface or document that is based on the data, information and insights you obtain through
|
||||
the use of this API. A good place to get started regarding SAP branding tips is
|
||||
<a href="https://www.sapbrandtools.com/">www.sapbrandtools.com</a>. Please also note that those APIs granting
|
||||
access to the original position and weather data requires the accessing user account to have special permissions.
|
||||
</p>
|
||||
<p>
|
||||
For questions and support please get in touch through <a href="mailto:support@sapsailing.com">support@sapsailing.com</a>.
|
||||
</p>
|
||||
<div style="height: 1em;"></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Webservice URL</td>
|
||||
<td>Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="createsessioncredentials.html">/sailinglandscape/api/createsessioncredentials</a></td>
|
||||
<td>Creates AWS session credentials, including MFA support</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="getsessioncredentials.html">/sailinglandscape/api/getsessioncredentials</a></td>
|
||||
<td>Gets information about active AWS session credentials, telling their expiry time.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="clearsessioncredentials.html">/sailinglandscape/api/clearsessioncredentials</a></td>
|
||||
<td>Clears any active AWS session credentials</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="createapplicationreplicaset.html">/sailinglandscape/api/createapplicationreplicaset</a></td>
|
||||
<td>Creates a new application server replica set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="movetoarchiveserver.html">/sailinglandscape/api/movetoarchiveserver</a></td>
|
||||
<td>Archives an existing application server replica set's contents</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="removeapplicationreplicaset.html">/sailinglandscape/api/removeapplicationreplicaset</a></td>
|
||||
<td>Removes an application server replica set without archiving it</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="/sailingserver/webservices/index.html">Back to Web Services Home</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/movetoarchiveserver</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Archives the contents of an application replica set, optionally also its underlying database, and if the result compares equal, removes
|
||||
the entire replica set. Optionally, the replica set's database can be moved to a different (e.g., less expensive "cold-storage") MongoDB environment.
|
||||
See also <a href="removeapplicationreplicaset.html">here</a> for the steps taken during replica set removal.</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
<div><tt>regionId</tt>, region where to set up the replica set, e.g., eu-west-1</div>
|
||||
<div><tt>replicaSetName</tt>, name of the replica set; used as the hostname to which the domain name is appended, as well as
|
||||
for the database and RabbitMQ exchange name</div>
|
||||
<div><tt>keyName</tt>, the name of the SSH key to use; must identify an existing SSH key by that name in the region specified
|
||||
and will be used to log on to existing and new instances using a root ssh connection.</div>
|
||||
<div><tt>privateKeyEncryptionPassphrase</tt>, the pass phrase to decrypt the private key for the key specified by the keyName parameter</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional form parameters:</td>
|
||||
<td>
|
||||
<div><tt>bearerTokenForReplicaSetToArchive</tt>, the bearer token to use to authenticate with the replica set that is to be archived.
|
||||
If not provided, the bearer token of the subject used to authenticate this request
|
||||
is used by default.</div>
|
||||
<div><tt>bearerTokenForArchive</tt>, the bearer token to use to authenticate with the archive server to which to import the content.
|
||||
If not provided, the bearer token of the subject used to authenticate this request
|
||||
is used by default.</div>
|
||||
<div><tt>timeoutInMilliseconds</tt>, timeout in milliseconds to wait for responses from nodes when analyzing the replica sets
|
||||
existing in the region; if not specified, waiting will not time out.</div>
|
||||
<div><tt>durationToWaitBeforeCompareServersInMilliseconds</tt>, tells how long to wait before starting to compare the content in the archive
|
||||
with the original content after importing. The reason such a wait time
|
||||
can be appropriate is, among other things, that after starting to load all tracked
|
||||
races in the archive some time will pass until all data hase been loaded and
|
||||
all calculations based on the data have completed, such as maneuver calculations
|
||||
and wind estimation. A comparison may fail if wind estimations haven't completed
|
||||
yet for a race in the archive. If not provided, this defaults to five minutes.</div>
|
||||
<div><tt>maxNumberOfCompareserverAttempts</tt>, if the comparison started after durationToWaitBeforeCompareServersInMilliseconds fails, this many
|
||||
attempts will be made, each after waiting another durationToWaitBeforeCompareServersInMilliseconds.
|
||||
Only if these many comparisons all fail will the process be called a failure, and further processing
|
||||
such as removing the replica set or archiving its database will not be executed.</div>
|
||||
<div><tt>removeApplicationReplicaSet</tt>, boolean value defaulting to true; if true, the original replica set will be
|
||||
<a href="removeapplicationreplicaset.html">removed</a> if importing and comparing the content worked
|
||||
successfully.</div>
|
||||
<div><tt>mongoUriToArchiveDbTo</tt>, if provided, this must identify a full-fledged database-specific MongoDB URI, such as
|
||||
<tt>mongodb://172.16.0.123/mynewevent?replicaSet=archive&retryWrites=true&readPreference=nearest</tt>.
|
||||
If provided, it specified the database end point to which to move the replica set's original DB content.
|
||||
After moving, the DB content will be compared by hashing original and copy, and only if this comparison
|
||||
was successful will the original be dropped. Note: although you must specify a DB name ("mynewevent" in
|
||||
the example above), the DB name in the URI will be ignored, and the original DB name will be used under
|
||||
all circumstances for the copy.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST -d 'regionId=eu-west-2' -d 'replicaSetName=axelstest' -d 'keyName=Horst' --data-urlencode 'privateKeyEncryptionPassphrase=HorstsSuperSecretPassword' -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' --data-urlencode 'bearerTokenForReplicaSetToArchive=DE7x+W/dwFKRAxFbJkC/IMafEnJ8kTQF+MUHNVhEwsD4=' --data-urlencode 'bearerTokenForArchive=CHxGpF0nr78Z4m/f28/MgiYhdRB3xoDCYd+rLc37rTt=' --data-urlencode 'mongoUriToArchiveDbTo=mongodb://172.31.36.221/mynewevent?replicaSet=archive&retryWrites=true&readPreference=nearest' "https://sapsailing.com/sailinglandscape/api/landscape/movetoarchiveserver"<br>
|
||||
produces output:<pre>
|
||||
{
|
||||
"uuidForMdiProgress": "1f07d1cf-0652-4fc3-83bc-a79612a5aa68"
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/removoeapplicationreplicaset</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Removes an application replica set without archiving its contents and without touching its
|
||||
database. The master and replica processes are stopped, and if they were the last on their nodes then
|
||||
the node is terminated. The load balancing rules and the corresponding target groups are removed.
|
||||
If this turns a load balancer empty (other than its default rule), the load balancer is removed too,
|
||||
unless it was the dynamic load balancer. Any DNS record for the replica set (in case of a DNS-mapped
|
||||
load balancer being used) is removed, too, and so are the launch configuration and auto-scaling group
|
||||
in case they exist.</p>
|
||||
<br />
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory query parameters:</td>
|
||||
<td>
|
||||
<div><tt>regionId</tt>, region where to set up the replica set, e.g., eu-west-1</div>
|
||||
<div><tt>replicaSetName</tt>, name of the replica set; used as the hostname to which the domain name is appended, as well as
|
||||
for the database and RabbitMQ exchange name</div>
|
||||
<div><tt>keyName</tt>, the name of the SSH key to use; must identify an existing SSH key by that name in the region specified
|
||||
and will be used to log on to existing and new instances using a root ssh connection.</div>
|
||||
<div><tt>privateKeyEncryptionPassphrase</tt>, the pass phrase to decrypt the private key for the key specified by the keyName parameter</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional query parameters:</td>
|
||||
<td>
|
||||
<div><tt>timeoutInMilliseconds</tt>, timeout in milliseconds to wait for responses from nodes when analyzing the replica sets
|
||||
existing in the region; if not specified, waiting will not time out.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST -d 'regionId=eu-west-2' -d 'replicaSetName=mynewevent' -d 'keyName=Horst' --data-urlencode 'privateKeyEncryptionPassphrase=HorstsSuperSecretPassword' -H 'Authorization: Bearer RwGK+DV38LKjeuDu+Bx87/3blLJHD7Ff+NCLeud9nRQ=' "https://sapsailing.com/sailinglandscape/api/landscape/removeapplicationreplicaset"<br>
|
||||
produces no output and a 200 status code if things worked.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
+3
-2
@@ -27,6 +27,7 @@ import com.sap.sailing.landscape.SailingAnalyticsHost;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsProcess;
|
||||
import com.sap.sailing.landscape.SailingReleaseRepository;
|
||||
import com.sap.sailing.landscape.SharedLandscapeConstants;
|
||||
import com.sap.sailing.landscape.impl.BearerTokenReplicationCredentials;
|
||||
import com.sap.sailing.landscape.impl.SailingAnalyticsHostImpl;
|
||||
import com.sap.sailing.landscape.impl.SailingAnalyticsProcessImpl;
|
||||
@@ -102,7 +103,7 @@ public class TestProcedures {
|
||||
@Test
|
||||
public void testGetImageTypes() {
|
||||
final Iterable<String> imageTypes = landscape.getMachineImageTypes(region);
|
||||
assertTrue(Util.contains(imageTypes, "sailing-analytics-server"));
|
||||
assertTrue(Util.contains(imageTypes, SharedLandscapeConstants.IMAGE_TYPE_TAG_VALUE_SAILING));
|
||||
assertTrue(Util.contains(imageTypes, "mongodb-server"));
|
||||
}
|
||||
|
||||
@@ -127,7 +128,7 @@ public class TestProcedures {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
landscape.createKeyPair(region, keyName, privateKeyEncryptionPassphrase);
|
||||
final StartMultiServer.Builder<?, String> builder = StartMultiServer.builder();
|
||||
final String sailingAnalyticsServerTag = "sailing-analytics-server";
|
||||
final String sailingAnalyticsServerTag = SharedLandscapeConstants.SAILING_ANALYTICS_APPLICATION_HOST_TAG;
|
||||
final StartMultiServer<String> startEmptyMultiServer = builder
|
||||
.setLandscape(landscape)
|
||||
.setKeyName(keyName)
|
||||
|
||||
Executable → Regular
+7
-1
@@ -14,6 +14,8 @@ public class ApplicationReplicaSetsImagesBarCell extends ImagesBarCell {
|
||||
static final String ACTION_ARCHIVE = "ARCHIVE";
|
||||
static final String ACTION_DEFINE_LANDING_PAGE = "DEFINE_LANDING_PAGE";
|
||||
static final String ACTION_CREATE_LOAD_BALANCER_MAPPING = "CREATE_LOAD_BALANGER_MAPPING";
|
||||
public static final String ACTION_LAUNCH_ANOTHER_REPLICA_SET_ON_THIS_MASTER = "LAUNCH_ANOTHER_REPLICA_SET_ON_THIS_MASTER";
|
||||
public static final String ACTION_ENSURE_ONE_REPLICA_THEN_STOP_REPLICATING_AND_REMOVE_MASTER_FROM_TARGET_GROUPS = "ACTION_ENSURE_ONE_REPLICA_THEN_STOP_REPLICATING_AND_REMOVE_MASTER_FROM_TARGET_GROUPS";
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
@@ -38,8 +40,12 @@ public class ApplicationReplicaSetsImagesBarCell extends ImagesBarCell {
|
||||
IconResources.INSTANCE.editIcon()),
|
||||
new ImageSpec(ACTION_CREATE_LOAD_BALANCER_MAPPING, stringMessages.createLoadBalancerMapping(),
|
||||
IconResources.INSTANCE.loadBalancerIcon()),
|
||||
new ImageSpec(ACTION_LAUNCH_ANOTHER_REPLICA_SET_ON_THIS_MASTER, stringMessages.launchAnotherReplicaSetOnThisMaster(),
|
||||
IconResources.INSTANCE.launchAnotherReplicaSetOnThisMasterIcon()),
|
||||
new ImageSpec(ACTION_UPGRADE, stringMessages.upgrade(),
|
||||
IconResources.INSTANCE.refreshIcon())
|
||||
IconResources.INSTANCE.refreshIcon()),
|
||||
new ImageSpec(ACTION_ENSURE_ONE_REPLICA_THEN_STOP_REPLICATING_AND_REMOVE_MASTER_FROM_TARGET_GROUPS, stringMessages.ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(),
|
||||
IconResources.INSTANCE.unlinkIcon())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-4
@@ -7,10 +7,11 @@ import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.landscape.SharedLandscapeConstants;
|
||||
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
|
||||
import com.sap.sailing.landscape.ui.shared.SharedLandscapeConstants;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.controls.IntegerBox;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplicaSetDialog<CreateApplicationReplicaSetDialog.CreateApplicationReplicaSetInstructions> {
|
||||
@@ -21,15 +22,20 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
private final String instanceType;
|
||||
private final boolean dynamicLoadBalancerMapping;
|
||||
private final String optionalDomainName;
|
||||
private final Integer optionalMemoryInMegabytesOrNull;
|
||||
private final Integer optionalMemoryTotalSizeFactorOrNull;
|
||||
|
||||
public CreateApplicationReplicaSetInstructions(String name, String instanceType,
|
||||
String releaseNameOrNullForLatestMaster, boolean dynamicLoadBalancerMapping,
|
||||
String masterReplicationBearerToken, String replicaReplicationBearerToken, String optionalDomainName) {
|
||||
String masterReplicationBearerToken, String replicaReplicationBearerToken, String optionalDomainName,
|
||||
Integer optionalMemoryInMegabytesOrNull, Integer optionalMemoryTotalSizeFactorOrNull) {
|
||||
super(releaseNameOrNullForLatestMaster, masterReplicationBearerToken, replicaReplicationBearerToken);
|
||||
this.name = name;
|
||||
this.dynamicLoadBalancerMapping = dynamicLoadBalancerMapping;
|
||||
this.optionalDomainName = Util.hasLength(optionalDomainName) ? optionalDomainName : null;
|
||||
this.instanceType = instanceType;
|
||||
this.optionalMemoryInMegabytesOrNull = optionalMemoryInMegabytesOrNull;
|
||||
this.optionalMemoryTotalSizeFactorOrNull = optionalMemoryTotalSizeFactorOrNull;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -43,6 +49,12 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
public String getInstanceType() {
|
||||
return instanceType;
|
||||
}
|
||||
public Integer getOptionalMemoryInMegabytesOrNull() {
|
||||
return optionalMemoryInMegabytesOrNull;
|
||||
}
|
||||
public Integer getOptionalMemoryTotalSizeFactorOrNull() {
|
||||
return optionalMemoryTotalSizeFactorOrNull;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Validator implements DataEntryDialog.Validator<CreateApplicationReplicaSetInstructions> {
|
||||
@@ -73,6 +85,8 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
private final CheckBox dynamicLoadBalancerCheckBox;
|
||||
private final TextBox domainNameBox;
|
||||
private final ListBox instanceTypeListBox;
|
||||
private final IntegerBox memoryInMegabytesBox;
|
||||
private final IntegerBox memoryTotalSizeFactorBox;
|
||||
|
||||
public CreateApplicationReplicaSetDialog(LandscapeManagementWriteServiceAsync landscapeManagementService, Iterable<String> releaseNames,
|
||||
StringMessages stringMessages, ErrorReporter errorReporter, DialogCallback<CreateApplicationReplicaSetInstructions> callback) {
|
||||
@@ -82,6 +96,9 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
dynamicLoadBalancerCheckBox = createCheckbox(stringMessages.useDynamicLoadBalancer());
|
||||
domainNameBox = createTextBox(SharedLandscapeConstants.DEFAULT_DOMAIN_NAME, 40);
|
||||
instanceTypeListBox = LandscapeDialogUtil.createInstanceTypeListBox(this, landscapeManagementService, stringMessages, DEFAULT_INSTANCE_TYPE, errorReporter);
|
||||
memoryInMegabytesBox = createIntegerBox(null, 7);
|
||||
memoryTotalSizeFactorBox = createIntegerBox(null, 2);
|
||||
memoryInMegabytesBox.addValueChangeHandler(e->memoryTotalSizeFactorBox.setEnabled(e.getValue() == null));
|
||||
}
|
||||
|
||||
protected ListBox getInstanceTypeListBox() {
|
||||
@@ -90,7 +107,7 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
final Grid result = new Grid(7, 2);
|
||||
final Grid result = new Grid(9, 2);
|
||||
int row=0;
|
||||
result.setWidget(row, 0, new Label(stringMessages.name()));
|
||||
result.setWidget(row++, 1, nameBox);
|
||||
@@ -106,6 +123,10 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
result.setWidget(row++, 1, getReplicaReplicationBearerTokenBox());
|
||||
result.setWidget(row, 0, new Label(stringMessages.domainName()));
|
||||
result.setWidget(row++, 1, domainNameBox);
|
||||
result.setWidget(row, 0, new Label(stringMessages.memoryInMegabytes()));
|
||||
result.setWidget(row++, 1, memoryInMegabytesBox);
|
||||
result.setWidget(row, 0, new Label(stringMessages.memoryTotalSizeFactor()));
|
||||
result.setWidget(row++, 1, memoryTotalSizeFactorBox);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -119,6 +140,7 @@ public class CreateApplicationReplicaSetDialog extends AbstractApplicationReplic
|
||||
return new CreateApplicationReplicaSetInstructions(nameBox.getValue(),
|
||||
getInstanceTypeListBox().getSelectedValue(), getReleaseNameBoxValue(),
|
||||
dynamicLoadBalancerCheckBox.getValue(), getMasterReplicationBearerTokenBox().getValue(),
|
||||
getReplicaReplicationBearerTokenBox().getValue(), domainNameBox.getValue());
|
||||
getReplicaReplicationBearerTokenBox().getValue(), domainNameBox.getValue(),
|
||||
memoryInMegabytesBox.getValue(), memoryTotalSizeFactorBox.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -8,15 +8,15 @@ import com.google.gwt.user.client.ui.RadioButton;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
|
||||
import com.sap.sailing.landscape.ui.shared.EventRedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.EventSeriesRedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.HomeRedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.PlainRedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.RedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.RedirectWithIdDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SailingApplicationReplicaSetDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.landscape.aws.common.shared.EventRedirectDTO;
|
||||
import com.sap.sse.landscape.aws.common.shared.EventSeriesRedirectDTO;
|
||||
import com.sap.sse.landscape.aws.common.shared.HomeRedirectDTO;
|
||||
import com.sap.sse.landscape.aws.common.shared.PlainRedirectDTO;
|
||||
import com.sap.sse.landscape.aws.common.shared.RedirectDTO;
|
||||
import com.sap.sse.landscape.aws.common.shared.RedirectWithIdDTO;
|
||||
|
||||
public class DefineRedirectDialog extends DataEntryDialog<RedirectDTO> {
|
||||
private final String RADIO_BUTTON_GROUP_NAME = "redirect-choice";
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.sap.sailing.landscape.ui.client;
|
||||
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class EnsureReplicaStopReplicatingRemoveMasterFromTargetGroupsDialog extends DataEntryDialog<String> {
|
||||
private final StringMessages stringMessages;
|
||||
private final TextBox replicaReplicationBearerTokenBox;
|
||||
|
||||
public EnsureReplicaStopReplicatingRemoveMasterFromTargetGroupsDialog(
|
||||
StringMessages stringMessages, ErrorReporter errorReporter, DialogCallback<String> callback) {
|
||||
super(stringMessages.ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(), /* message */ null,
|
||||
stringMessages.ok(), stringMessages.cancel(), /* validator */ null, callback);
|
||||
this.stringMessages = stringMessages;
|
||||
replicaReplicationBearerTokenBox = createTextBox("", 40);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
final Grid result = new Grid(1, 2);
|
||||
int row=0;
|
||||
result.setWidget(row, 0, new Label(stringMessages.replicaReplicationBearerToken()));
|
||||
result.setWidget(row++, 1, replicaReplicationBearerTokenBox);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FocusWidget getInitialFocusWidget() {
|
||||
return replicaReplicationBearerTokenBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResult() {
|
||||
return replicaReplicationBearerTokenBox.getValue();
|
||||
}
|
||||
}
|
||||
+211
-21
@@ -11,7 +11,11 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.cell.client.SafeHtmlCell;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.safehtml.shared.SafeHtml;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.Window;
|
||||
@@ -28,19 +32,19 @@ import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.landscape.SharedLandscapeConstants;
|
||||
import com.sap.sailing.landscape.ui.client.CreateApplicationReplicaSetDialog.CreateApplicationReplicaSetInstructions;
|
||||
import com.sap.sailing.landscape.ui.client.UpgradeApplicationReplicaSetDialog.UpgradeApplicationReplicaSetInstructions;
|
||||
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
|
||||
import com.sap.sailing.landscape.ui.shared.AmazonMachineImageDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.AwsInstanceDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoScalingInstructionsDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ProcessDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.RedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ReleaseDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SailingAnalyticsProcessDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SailingApplicationReplicaSetDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SharedLandscapeConstants;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -60,6 +64,7 @@ import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
import com.sap.sse.landscape.aws.common.shared.RedirectDTO;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.component.SelectedElementsCountingButton;
|
||||
@@ -112,8 +117,8 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
* archived. This waiting period is owed to the process of loading the race content which is an asynchronous
|
||||
* process running mainly in the "background" and after the master data import has reported "completion."
|
||||
*/
|
||||
private static final Duration DEFAULT_DURATION_TO_WAIT_BEFORE_COMPARE_SERVERS = Duration.ONE_MINUTE.times(1);
|
||||
private static final int DEFAULT_NUMBER_OF_COMPARE_SERVERS_ATTEMPTS = 3;
|
||||
private static final Duration DEFAULT_DURATION_TO_WAIT_BEFORE_COMPARE_SERVERS = Duration.ONE_MINUTE.times(5);
|
||||
private static final int DEFAULT_NUMBER_OF_COMPARE_SERVERS_ATTEMPTS = 5;
|
||||
|
||||
public LandscapeManagementPanel(StringMessages stringMessages, UserService userService,
|
||||
AdminConsoleTableResources tableResources, ErrorReporter errorReporter) {
|
||||
@@ -231,16 +236,82 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getReplicaSetName(), stringMessages.name());
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getVersion(), stringMessages.versionHeader());
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getMaster().getHost().getPublicIpAddress(), stringMessages.masterHostName());
|
||||
final SafeHtmlCell versionCell = new SafeHtmlCell();
|
||||
final Column<SailingApplicationReplicaSetDTO<String>, SafeHtml> versionColumn = new Column<SailingApplicationReplicaSetDTO<String>, SafeHtml>(versionCell) {
|
||||
@Override
|
||||
public SafeHtml getValue(SailingApplicationReplicaSetDTO<String> replicaSet) {
|
||||
final SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
final String version = replicaSet.getVersion();
|
||||
final String releaseNotesLink = getReleaseNotesLink(version);
|
||||
builder.appendHtmlConstant("<a target=\"_blank\" href=\""+releaseNotesLink+"\">");
|
||||
builder.appendEscaped(version);
|
||||
builder.appendHtmlConstant("</a>");
|
||||
return builder.toSafeHtml();
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(versionColumn, stringMessages.versionHeader(), (rs1, rs2)->new NaturalComparator().compare(rs1.getVersion(), rs2.getVersion()));
|
||||
final SafeHtmlCell masterCell = new SafeHtmlCell();
|
||||
final Column<SailingApplicationReplicaSetDTO<String>, SafeHtml> masterColumn = new Column<SailingApplicationReplicaSetDTO<String>, SafeHtml>(masterCell) {
|
||||
@Override
|
||||
public SafeHtml getValue(SailingApplicationReplicaSetDTO<String> replicaSet) {
|
||||
final SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
final String gwtStatusLink = getGwtStatusLink(replicaSet.getMaster().getHost().getPublicIpAddress(), replicaSet.getMaster().getPort());
|
||||
builder.appendHtmlConstant("<a target=\"_blank\" href=\""+gwtStatusLink+"\">");
|
||||
builder.appendEscaped(replicaSet.getMaster().getHost().getPublicIpAddress());
|
||||
builder.appendHtmlConstant("</a>");
|
||||
return builder.toSafeHtml();
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(masterColumn, stringMessages.masterHostName(), (rs1, rs2)->new NaturalComparator().compare(rs1.getMaster().getHost().getPublicIpAddress(), rs2.getMaster().getHost().getPublicIpAddress()));
|
||||
applicationReplicaSetsTable.addColumn(rs->Integer.toString(rs.getMaster().getPort()), stringMessages.masterPort());
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getHostname(), stringMessages.hostname());
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getMaster().getHost().getInstanceId(), stringMessages.masterInstanceId());
|
||||
final SafeHtmlCell hostnameCell = new SafeHtmlCell();
|
||||
final Column<SailingApplicationReplicaSetDTO<String>, SafeHtml> hostnameColumn = new Column<SailingApplicationReplicaSetDTO<String>, SafeHtml>(hostnameCell) {
|
||||
@Override
|
||||
public SafeHtml getValue(SailingApplicationReplicaSetDTO<String> replicaSet) {
|
||||
final SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
final String hostnameLink = "https://"+replicaSet.getHostname();
|
||||
builder.appendHtmlConstant("<a target=\"_blank\" href=\""+hostnameLink+"\">");
|
||||
builder.appendEscaped(replicaSet.getHostname());
|
||||
builder.appendHtmlConstant("</a>");
|
||||
return builder.toSafeHtml();
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(hostnameColumn, stringMessages.hostname(), (rs1, rs2)->new NaturalComparator().compare(rs1.getMaster().getHost().getInstanceId(), rs2.getMaster().getHost().getInstanceId()));
|
||||
final SafeHtmlCell masterInstanceIdCell = new SafeHtmlCell();
|
||||
final Column<SailingApplicationReplicaSetDTO<String>, SafeHtml> masterInstanceIdColumn = new Column<SailingApplicationReplicaSetDTO<String>, SafeHtml>(masterInstanceIdCell) {
|
||||
@Override
|
||||
public SafeHtml getValue(SailingApplicationReplicaSetDTO<String> replicaSet) {
|
||||
final SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
final String instanceId = replicaSet.getMaster().getHost().getInstanceId();
|
||||
appendEc2InstanceLink(builder, instanceId);
|
||||
return builder.toSafeHtml();
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(masterInstanceIdColumn, stringMessages.masterInstanceId());
|
||||
applicationReplicaSetsTable.addColumn(rs->""+rs.getMaster().getStartTimePoint(), stringMessages.startTimePoint(),
|
||||
(rs1, rs2)->rs1.getMaster().getStartTimePoint().compareTo(rs2.getMaster().getStartTimePoint()));
|
||||
applicationReplicaSetsTable.addColumn(rs->Util.joinStrings(", ", Util.map(rs.getReplicas(),
|
||||
r->r.getHost().getPublicIpAddress()+":"+r.getPort()+" ("+r.getServerName()+", "+r.getHost().getInstanceId()+")")),
|
||||
stringMessages.replicas());
|
||||
final SafeHtmlCell replicasCell = new SafeHtmlCell();
|
||||
final Column<SailingApplicationReplicaSetDTO<String>, SafeHtml> replicasColumn = new Column<SailingApplicationReplicaSetDTO<String>, SafeHtml>(replicasCell) {
|
||||
@Override
|
||||
public SafeHtml getValue(SailingApplicationReplicaSetDTO<String> replicaSet) {
|
||||
SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
for (final SailingAnalyticsProcessDTO replica : replicaSet.getReplicas()) {
|
||||
final String gwtStatusLink = getGwtStatusLink(replica.getHost().getPublicIpAddress(), replica.getPort());
|
||||
builder.appendHtmlConstant("<a target=\"_blank\" href=\""+gwtStatusLink+"\">");
|
||||
builder.appendEscaped(replica.getHost().getPublicIpAddress()+":"+replica.getPort());
|
||||
builder.appendHtmlConstant("</a>");
|
||||
final String replicaInstanceId = replica.getHost().getInstanceId();
|
||||
builder.appendEscaped(" (");
|
||||
builder.appendEscaped(replica.getServerName());
|
||||
builder.appendEscaped(", ");
|
||||
appendEc2InstanceLink(builder, replicaInstanceId);
|
||||
builder.appendEscaped(")");
|
||||
builder.appendHtmlConstant("<br>");
|
||||
}
|
||||
return builder.toSafeHtml();
|
||||
}
|
||||
};
|
||||
applicationReplicaSetsTable.addColumn(replicasColumn, stringMessages.replicas());
|
||||
applicationReplicaSetsTable.addColumn(rs->rs.getDefaultRedirectPath(), stringMessages.defaultRedirectPath());
|
||||
final ActionsColumn<SailingApplicationReplicaSetDTO<String>, ApplicationReplicaSetsImagesBarCell> applicationReplicaSetsActionColumn = new ActionsColumn<SailingApplicationReplicaSetDTO<String>, ApplicationReplicaSetsImagesBarCell>(
|
||||
new ApplicationReplicaSetsImagesBarCell(stringMessages), /* permission checker */ (applicationReplicaSet, action)->true);
|
||||
@@ -256,9 +327,17 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_CREATE_LOAD_BALANCER_MAPPING,
|
||||
applicationReplicaSetForWhichToDefineLoadBalancerMapping -> createDefaultLoadBalancerMappings(stringMessages,
|
||||
regionsTable.getSelectionModel().getSelectedObject(), applicationReplicaSetForWhichToDefineLoadBalancerMapping));
|
||||
applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_LAUNCH_ANOTHER_REPLICA_SET_ON_THIS_MASTER,
|
||||
applicationReplicaSetForWhichToDefineLoadBalancerMapping -> createApplicationReplicaSetWithMasterOnExistingHost(stringMessages,
|
||||
regionsTable.getSelectionModel().getSelectedObject(), applicationReplicaSetForWhichToDefineLoadBalancerMapping.getMaster().getHost()));
|
||||
applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_REMOVE,
|
||||
applicationReplicaSetToRemove -> removeApplicationReplicaSet(stringMessages,
|
||||
regionsTable.getSelectionModel().getSelectedObject(), applicationReplicaSetToRemove));
|
||||
applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_ENSURE_ONE_REPLICA_THEN_STOP_REPLICATING_AND_REMOVE_MASTER_FROM_TARGET_GROUPS,
|
||||
applicationReplicaSetForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups ->
|
||||
ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(stringMessages,
|
||||
regionsTable.getSelectionModel().getSelectedObject(),
|
||||
Collections.singleton(applicationReplicaSetForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups)));
|
||||
applicationReplicaSetsTable.addColumn(applicationReplicaSetsActionColumn, stringMessages.actions());
|
||||
final CaptionPanel applicationReplicaSetsCaptionPanel = new CaptionPanel(stringMessages.applicationReplicaSets());
|
||||
final VerticalPanel applicationReplicaSetsVerticalPanel = new VerticalPanel();
|
||||
@@ -275,6 +354,11 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
e->upgradeApplicationReplicaSet(stringMessages, regionsTable.getSelectionModel().getSelectedObject(),
|
||||
applicationReplicaSetsTable.getSelectionModel().getSelectedSet()));
|
||||
applicationReplicaSetsButtonPanel.add(upgradeApplicationReplicaSetButton);
|
||||
final SelectedElementsCountingButton<SailingApplicationReplicaSetDTO<String>> stopReplicatingAndUnregisterMasterButton = new SelectedElementsCountingButton<>(
|
||||
stringMessages.stopReplicating(), applicationReplicaSetsTable.getSelectionModel(),
|
||||
e->ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(stringMessages, regionsTable.getSelectionModel().getSelectedObject(),
|
||||
applicationReplicaSetsTable.getSelectionModel().getSelectedSet()));
|
||||
applicationReplicaSetsButtonPanel.add(stopReplicatingAndUnregisterMasterButton);
|
||||
applicationReplicaSetsCaptionPanel.add(applicationReplicaSetsVerticalPanel);
|
||||
applicationReplicaSetsVerticalPanel.add(applicationReplicaSetsTable);
|
||||
applicationReplicaSetsBusy = new SimpleBusyIndicator();
|
||||
@@ -321,13 +405,62 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
storeRegionSelection(userService, selectedRegion);
|
||||
});
|
||||
// TODO try to identify archive servers
|
||||
// TODO support archiving of an application server cluster
|
||||
// TODO support deploying a new app server process instance onto an existing app server host (multi-instance)
|
||||
// TODO support archive server upgrade
|
||||
// TODO support upgrading all app server instances in a region
|
||||
// TODO upon region selection show RabbitMQ, and Central Reverse Proxy clusters in region
|
||||
}
|
||||
|
||||
private String getGwtStatusLink(final String host, int port) {
|
||||
return (port == 443 ? "https" : "http") + "://" + host + ":" + port + "/gwt/status";
|
||||
}
|
||||
|
||||
private String getReleaseNotesLink(final String version) {
|
||||
return "https://releases.sapsailing.com/"+version+"/release-notes.txt";
|
||||
}
|
||||
|
||||
private void ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(
|
||||
StringMessages stringMessages, String selectedObject,
|
||||
Iterable<SailingApplicationReplicaSetDTO<String>> applicationReplicaSetsForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups) {
|
||||
final String selectedRegion = regionsTable.getSelectionModel().getSelectedObject();
|
||||
new EnsureReplicaStopReplicatingRemoveMasterFromTargetGroupsDialog(stringMessages, errorReporter, new DialogCallback<String>() {
|
||||
@Override
|
||||
public void ok(String replicaReplicationBearerToken) {
|
||||
applicationReplicaSetsBusy.setBusy(true);
|
||||
final int[] howManyMoreToGo = new int[] { Util.size(applicationReplicaSetsForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups) };
|
||||
for (final SailingApplicationReplicaSetDTO<String> applicationReplicaSetForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups :
|
||||
applicationReplicaSetsForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups) {
|
||||
landscapeManagementService.ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(
|
||||
selectedRegion, applicationReplicaSetForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups,
|
||||
sshKeyManagementPanel.getSelectedKeyPair().getName(),
|
||||
sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption() != null
|
||||
? sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption().getBytes() : null,
|
||||
replicaReplicationBearerToken, new AsyncCallback<Boolean>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
decrementHowManyMoreToGoAndSetNonBusyIfDone(howManyMoreToGo);
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
decrementHowManyMoreToGoAndSetNonBusyIfDone(howManyMoreToGo);
|
||||
Notification.notify(stringMessages.successfullyStoppedReplicatingAndRemovedMasterFromTargetGroups(
|
||||
applicationReplicaSetForWhichToEnsureAtLeastOneReplicaStopReplicatingAndRemoveMasterFromTargetGroups.getName()),
|
||||
NotificationType.SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void cancel() {}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void decrementHowManyMoreToGoAndSetNonBusyIfDone(int[] howManyMoreToGo) {
|
||||
if (--howManyMoreToGo[0] <= 0) {
|
||||
applicationReplicaSetsBusy.setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void defineLandingPage(StringMessages stringMessages, String selectedRegion,
|
||||
SailingApplicationReplicaSetDTO<String> applicationReplicaSetToDefineLandingPageFor) {
|
||||
if (sshKeyManagementPanel.getSelectedKeyPair() == null) {
|
||||
@@ -391,7 +524,55 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption() != null
|
||||
? sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption().getBytes() : null,
|
||||
instructions.getMasterReplicationBearerToken(), instructions.getReplicaReplicationBearerToken(),
|
||||
instructions.getOptionalDomainName(),
|
||||
instructions.getOptionalDomainName(), instructions.getOptionalMemoryInMegabytesOrNull(),
|
||||
instructions.getOptionalMemoryTotalSizeFactorOrNull(),
|
||||
new AsyncCallback<SailingApplicationReplicaSetDTO<String>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
applicationReplicaSetsBusy.setBusy(false);
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(SailingApplicationReplicaSetDTO<String> result) {
|
||||
applicationReplicaSetsBusy.setBusy(false);
|
||||
Notification.notify(stringMessages.successfullyCreatedReplicaSet(instructions.getName()), NotificationType.SUCCESS);
|
||||
if (result != null) {
|
||||
applicationReplicaSetsTable.getFilterPanel().add(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createApplicationReplicaSetWithMasterOnExistingHost(StringMessages stringMessages, String regionId, AwsInstanceDTO hostToDeployTo) {
|
||||
landscapeManagementService.getReleases(new AsyncCallback<ArrayList<ReleaseDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ArrayList<ReleaseDTO> result) {
|
||||
new CreateApplicationReplicaSetDialog(landscapeManagementService, result.stream().map(r->r.getName())::iterator,
|
||||
stringMessages, errorReporter, new DialogCallback<CreateApplicationReplicaSetDialog.CreateApplicationReplicaSetInstructions>() {
|
||||
@Override
|
||||
public void ok(CreateApplicationReplicaSetInstructions instructions) {
|
||||
applicationReplicaSetsBusy.setBusy(true);
|
||||
landscapeManagementService.deployApplicationToExistingHost(regionId, instructions.getName(), hostToDeployTo,
|
||||
instructions.getInstanceType(), instructions.isDynamicLoadBalancerMapping(),
|
||||
instructions.getReleaseNameOrNullForLatestMaster(), sshKeyManagementPanel.getSelectedKeyPair()==null?null:sshKeyManagementPanel.getSelectedKeyPair().getName(),
|
||||
sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption() != null
|
||||
? sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption().getBytes() : null,
|
||||
instructions.getMasterReplicationBearerToken(), instructions.getReplicaReplicationBearerToken(),
|
||||
instructions.getOptionalDomainName(), instructions.getOptionalMemoryInMegabytesOrNull(), instructions.getOptionalMemoryTotalSizeFactorOrNull(),
|
||||
new AsyncCallback<SailingApplicationReplicaSetDTO<String>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
@@ -682,17 +863,13 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
new AsyncCallback<SailingApplicationReplicaSetDTO<String>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
if (--howManyMoreToGo[0] == 0) {
|
||||
applicationReplicaSetsBusy.setBusy(false);
|
||||
}
|
||||
decrementHowManyMoreToGoAndSetNonBusyIfDone(howManyMoreToGo);
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(SailingApplicationReplicaSetDTO<String> result) {
|
||||
if (--howManyMoreToGo[0] == 0) {
|
||||
applicationReplicaSetsBusy.setBusy(false);
|
||||
}
|
||||
decrementHowManyMoreToGoAndSetNonBusyIfDone(howManyMoreToGo);
|
||||
if (result != null) {
|
||||
Notification.notify(stringMessages.successfullyUpgradedApplicationReplicaSet(
|
||||
result.getName(), result.getVersion()), NotificationType.SUCCESS);
|
||||
@@ -961,4 +1138,17 @@ public class LandscapeManagementPanel extends SimplePanel {
|
||||
RemoteServiceMappingConstants.landscapeManagementServiceRemotePath, HEADER_FORWARD_TO_MASTER);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getEc2ConsoleLinkForInstanceId(String instanceId) {
|
||||
return "https://"+regionsTable.getSelectionModel().getSelectedObject()+
|
||||
".console.aws.amazon.com/ec2/v2/home?region="+regionsTable.getSelectionModel().getSelectedObject()+
|
||||
"#Instances:search="+instanceId;
|
||||
}
|
||||
|
||||
private void appendEc2InstanceLink(final SafeHtmlBuilder builder, final String instanceId) {
|
||||
final String ec2Link = getEc2ConsoleLinkForInstanceId(instanceId);
|
||||
builder.appendHtmlConstant("<a target=\"_blank\" href=\""+ec2Link+"\">");
|
||||
builder.appendEscaped(instanceId);
|
||||
builder.appendHtmlConstant("</a>");
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -9,12 +9,12 @@ import com.sap.sailing.landscape.ui.shared.AwsInstanceDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoScalingInstructionsDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ProcessDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.RedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ReleaseDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SailingApplicationReplicaSetDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SerializationDummyDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.landscape.aws.common.shared.RedirectDTO;
|
||||
|
||||
public interface LandscapeManagementWriteService extends RemoteService {
|
||||
ArrayList<String> getRegions();
|
||||
@@ -71,7 +71,8 @@ public interface LandscapeManagementWriteService extends RemoteService {
|
||||
|
||||
SailingApplicationReplicaSetDTO<String> createApplicationReplicaSet(String regionId, String name, String masterInstanceType,
|
||||
boolean dynamicLoadBalancerMapping, String releaseNameOrNullForLatestMaster, String optionalKeyName,
|
||||
byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken, String replicaReplicationBearerToken, String optionalDomainName) throws Exception;
|
||||
byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken, String replicaReplicationBearerToken, String optionalDomainName,
|
||||
Integer optionalMemoryInMegabytesOrNull, Integer optionalMemoryTotalSizeFactorOrNull) throws Exception;
|
||||
|
||||
void defineDefaultRedirect(String regionId, String hostname, RedirectDTO redirect, String keyName, String passphraseForPrivateKeyDecryption);
|
||||
|
||||
@@ -96,4 +97,15 @@ public interface LandscapeManagementWriteService extends RemoteService {
|
||||
int maxNumberOfCompareServerAttempts, boolean removeApplicationReplicaSet,
|
||||
MongoEndpointDTO moveDatabaseHere, String optionalKeyName, byte[] passphraseForPrivateKeyDecryption)
|
||||
throws Exception;
|
||||
|
||||
SailingApplicationReplicaSetDTO<String> deployApplicationToExistingHost(String regionId, String replicaSetName,
|
||||
AwsInstanceDTO hostToDeployTo, String replicaInstanceType, boolean dynamicLoadBalancerMapping,
|
||||
String releaseNameOrNullForLatestMaster, String optionalKeyName, byte[] privateKeyEncryptionPassphrase,
|
||||
String masterReplicationBearerToken, String replicaReplicationBearerToken, String optionalDomainName,
|
||||
Integer optionalMemoryInMegabytesOrNull, Integer optionalMemoryTotalSizeFactorOrNull) throws Exception;
|
||||
|
||||
|
||||
Boolean ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(String regionId,
|
||||
SailingApplicationReplicaSetDTO<String> applicationReplicaSet, String optionalKeyName,
|
||||
byte[] privateKeyEncryptionPassphrase, String replicaReplicationBearerToken) throws Exception;
|
||||
}
|
||||
|
||||
+29
-3
@@ -9,12 +9,12 @@ import com.sap.sailing.landscape.ui.shared.AwsInstanceDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoScalingInstructionsDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ProcessDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.RedirectDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.ReleaseDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SailingApplicationReplicaSetDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SerializationDummyDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.landscape.aws.common.shared.RedirectDTO;
|
||||
|
||||
public interface LandscapeManagementWriteServiceAsync {
|
||||
void getRegions(AsyncCallback<ArrayList<String>> callback);
|
||||
@@ -89,8 +89,9 @@ public interface LandscapeManagementWriteServiceAsync {
|
||||
|
||||
void createApplicationReplicaSet(String regionId, String name, String masterInstanceType,
|
||||
boolean dynamicLoadBalancerMapping, String releaseNameOrNullForLatestMaster, String optionalKeyName,
|
||||
byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken, String replicaReplicationBearerToken,
|
||||
String optionalDomainName,
|
||||
byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken,
|
||||
String replicaReplicationBearerToken, String optionalDomainName, Integer optionalMemoryInMegabytesOrNull,
|
||||
Integer optionalMemoryTotalSizeFactorOrNull,
|
||||
AsyncCallback<SailingApplicationReplicaSetDTO<String>> callback);
|
||||
|
||||
void serializationDummy(ProcessDTO mongoProcessDTO, AwsInstanceDTO awsInstanceDTO,
|
||||
@@ -187,4 +188,29 @@ public interface LandscapeManagementWriteServiceAsync {
|
||||
int maxNumberOfCompareServerAttempts, boolean removeApplicationReplicaSet,
|
||||
MongoEndpointDTO moveDatabaseHere, String optionalKeyName, byte[] passphraseForPrivateKeyDecryption,
|
||||
AsyncCallback<UUID> callback);
|
||||
|
||||
/**
|
||||
* Like {@link #createApplicationReplicaSet(String, String, String, boolean, String, String, byte[], String, String, String, AsyncCallback)},
|
||||
* only that in this case the master is deployed on an already existing host ({@code hostToDeployTo}) using the next available combination
|
||||
* of ports, and only for the replicas an instance type ({@code replicaInstanceType}) can be specified. The minimum number of replicas is
|
||||
* set to 0 (instead of 1), so the resulting application replica set is created in "low availability" ("economy") mode.
|
||||
*/
|
||||
void deployApplicationToExistingHost(String regionId, String replicaSetName, AwsInstanceDTO hostToDeployTo,
|
||||
String replicaInstanceType, boolean dynamicLoadBalancerMapping, String releaseNameOrNullForLatestMaster,
|
||||
String optionalKeyName, byte[] privateKeyEncryptionPassphrase, String masterReplicationBearerToken,
|
||||
String replicaReplicationBearerToken, String optionalDomainName, Integer optionalMemoryInMegabytesOrNull,
|
||||
Integer optionalMemoryTotalSizeFactorOrNull, AsyncCallback<SailingApplicationReplicaSetDTO<String>> callback);
|
||||
|
||||
/**
|
||||
* For the given replica set ensures there is at least one healthy replica, then stops replicating on all replicas and
|
||||
* removes the master from the public and master target groups. This can be used as a preparatory action for upgrading
|
||||
* the master while keeping one or more replicas available to handle read traffic.<p>
|
||||
*
|
||||
* Other than de-registering the master from the replica set's target groups this method does nothing to the master
|
||||
* process/host.
|
||||
*/
|
||||
void ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups(String regionId,
|
||||
SailingApplicationReplicaSetDTO<String> applicationReplicaSet, String optionalKeyName,
|
||||
byte[] privateKeyEncryptionPassphrase, String replicaReplicationBearerToken,
|
||||
AsyncCallback<Boolean> callback);
|
||||
}
|
||||
|
||||
+6
@@ -108,4 +108,10 @@ com.sap.sse.gwt.adminconsole.StringMessages {
|
||||
String numberOfMinutesBeforeAndBetweenCompareServers();
|
||||
String numberOfCompareServersAttempts();
|
||||
String replicaReplicationBearerToken();
|
||||
String memoryInMegabytes();
|
||||
String memoryTotalSizeFactor();
|
||||
String launchAnotherReplicaSetOnThisMaster();
|
||||
String ensureAtLeastOneReplicaExistsStopReplicatingAndRemoveMasterFromTargetGroups();
|
||||
String successfullyStoppedReplicatingAndRemovedMasterFromTargetGroups(String replicaSetName);
|
||||
String stopReplicating();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user