mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-08 05:11:04 +00:00
Merge branch 'racecommittee_app' into racecommittee_app_startphases
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java
This commit is contained in:
commit
04e8671a11
189 files changed
+3651
-875319
No files matched your search
@@ -51,7 +51,11 @@ fi
|
||||
|
||||
cd $PROJECT_HOME
|
||||
active_branch=$(git symbolic-ref -q HEAD)
|
||||
active_branch=`basename $active_branch`
|
||||
if [[ $active_branch == "" ]]; then
|
||||
active_branch="unknown"
|
||||
else
|
||||
active_branch=`basename $active_branch`
|
||||
fi
|
||||
|
||||
HEAD_SHA=$(git show-ref --head -s | head -1)
|
||||
HEAD_DATE=$(date "+%Y%m%d%H%M")
|
||||
@@ -122,6 +126,7 @@ fi
|
||||
echo PROJECT_HOME is $PROJECT_HOME
|
||||
echo SERVERS_HOME is $SERVERS_HOME
|
||||
echo BRANCH is $active_branch
|
||||
echo VERSION is $VERSION_INFO
|
||||
|
||||
options=':bgtocprm:n:l:s:w:u'
|
||||
while getopts $options option
|
||||
@@ -129,7 +134,7 @@ do
|
||||
case $option in
|
||||
g) gwtcompile=0;;
|
||||
t) testing=0;;
|
||||
b) onegwtpermutationonly=1;;
|
||||
b) onegwtpermutationonly=1;;
|
||||
o) offline=1;;
|
||||
c) clean="";;
|
||||
p) proxy=1;;
|
||||
@@ -164,22 +169,25 @@ if [[ "$@" == "release" ]]; then
|
||||
fi
|
||||
|
||||
RELEASE_NOTES=""
|
||||
echo ""
|
||||
echo "Please provide me with some notes about this release. You can add more than"
|
||||
echo "one line. Please include major changes or new features. After your notes I will"
|
||||
echo "also include the commits of the last 4 weeks. You can save and quit by hitting ctrl+d."
|
||||
while read -e -p "> " line; do
|
||||
RELEASE_NOTES="$RELEASE_NOTES\n$line"
|
||||
done
|
||||
COMMIT_WEEK_COUNT=4
|
||||
if [ $suppress_confirmation -eq 0 ]; then
|
||||
echo ""
|
||||
echo "Please provide me with some notes about this release. You can add more than"
|
||||
echo "one line. Please include major changes or new features. After your notes I will"
|
||||
echo "also include the commits of the last 4 weeks. You can save and quit by hitting ctrl+d."
|
||||
while read -e -p "> " line; do
|
||||
RELEASE_NOTES="$RELEASE_NOTES\n$line"
|
||||
done
|
||||
|
||||
if [[ $RELEASE_NOTES == "" ]]; then
|
||||
echo -e "\nCome on - I can not release without at least some notes about this release!"
|
||||
exit
|
||||
if [[ $RELEASE_NOTES == "" ]]; then
|
||||
echo -e "\nCome on - I can not release without at least some notes about this release!"
|
||||
exit
|
||||
fi
|
||||
echo -e "\nThank you! One last thing..."
|
||||
|
||||
echo "How many weeks of commits do you want to include (0=No commits)?"
|
||||
read -p "> " -e COMMIT_WEEK_COUNT
|
||||
fi
|
||||
echo -e "\nThank you! One last thing..."
|
||||
|
||||
echo "How many weeks of commits do you want to include (0=No commits)?"
|
||||
read -p "> " -e COMMIT_WEEK_COUNT
|
||||
|
||||
mkdir -p $PROJECT_HOME/dist
|
||||
mkdir -p $PROJECT_HOME/build
|
||||
@@ -201,6 +209,7 @@ if [[ "$@" == "release" ]]; then
|
||||
cp -v $PROJECT_HOME/java/target/start $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/status $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/updateEC2Instance.sh $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/shouldIBuildOrShouldIGo.sh $ACDIR/
|
||||
|
||||
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
|
||||
@@ -220,18 +229,8 @@ if [[ "$@" == "release" ]]; then
|
||||
cp -rv $PROJECT_HOME/configuration/native-libraries $ACDIR/
|
||||
cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/
|
||||
|
||||
# make sure to save the information from env.sh
|
||||
. $ACDIR/env.sh
|
||||
|
||||
echo "$VERSION_INFO System:" > $ACDIR/configuration/jetty/version.txt
|
||||
|
||||
sed -i "/mongo.host/d" "$ACDIR/configuration/config.ini"
|
||||
sed -i "/mongo.port/d" "$ACDIR/configuration/config.ini"
|
||||
sed -i "/expedition.udp.port/d" "$ACDIR/configuration/config.ini"
|
||||
sed -i "/replication.exchangeName/d" "$ACDIR/configuration/config.ini"
|
||||
sed -i "/replication.exchangeHost/d" "$ACDIR/configuration/config.ini"
|
||||
sed -i "s/^.*jetty.port.*$/<Set name=\"port\"><Property name=\"jetty.port\" default=\"$SERVER_PORT\"\/><\/Set>/g" "$ACDIR/configuration/jetty/etc/jetty-selector.xml"
|
||||
|
||||
if [[ $OSGI_BUNDLE_NAME != "" ]]; then
|
||||
SIMPLE_VERSION_INFO=$OSGI_BUNDLE_NAME
|
||||
fi
|
||||
@@ -525,6 +524,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
|
||||
cp -v $PROJECT_HOME/java/target/start $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/status $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/updateEC2Instance.sh $ACDIR/
|
||||
cp -v $PROJECT_HOME/java/target/shouldIBuildOrShouldIGo.sh $ACDIR/
|
||||
|
||||
if [ ! -f "$ACDIR/env.sh" ]; then
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import socket, sys
|
||||
|
||||
if len(sys.argv) <= 2:
|
||||
print "Please provide ip and mode (TCP, UDP)"
|
||||
if len(sys.argv) <= 3:
|
||||
print "Please provide ip, port and mode (TCP, UDP)"
|
||||
sys.exit(1)
|
||||
|
||||
UDP_IP = sys.argv[1]
|
||||
UDP_PORT = 2013
|
||||
MODE = sys.argv[2]
|
||||
UDP_PORT = int(sys.argv[2])
|
||||
MODE = sys.argv[3]
|
||||
MESSAGE = "Hello, World!"
|
||||
|
||||
print "Target IP:", UDP_IP
|
||||
|
||||
@@ -2,12 +2,12 @@ import socket
|
||||
import sys
|
||||
|
||||
if len(sys.argv) <= 2:
|
||||
print "Please provide an IP address and mode"
|
||||
print "Please provide an IP, port and mode (UDP, TCP)"
|
||||
sys.exit(1)
|
||||
|
||||
UDP_IP = sys.argv[1]
|
||||
MODE = sys.argv[2]
|
||||
UDP_PORT = 2013
|
||||
UDP_PORT = sys.argv[2]
|
||||
MODE = sys.argv[3]
|
||||
|
||||
print "Listening on %s port %s (%s)" % (UDP_IP, UDP_PORT, MODE)
|
||||
if MODE == 'UDP':
|
||||
|
||||
-18
@@ -265,28 +265,10 @@ into one in the future.
|
||||
can be obtained; the resulting document includes the
|
||||
parameter and simulation URLs for the individual
|
||||
races that are part of the event/regatta.
|
||||
action=addevent: starts tracking all races described in the document received
|
||||
from the eventJSONURL to the server instance.
|
||||
eventJSONURL: the URL from where metadata about the entire regatta
|
||||
can be obtained; the resulting document includes the
|
||||
parameter and simulation URLs for the individual
|
||||
races that are part of the event/regatta.
|
||||
liveURI: the live data URI for the TracTrac server
|
||||
storedURI: the stored data URI for the TracTrac server; usually
|
||||
the port number of this URI is that of liveURI + 1
|
||||
action=stopregatta: stops tracking of all races of the regatta; this
|
||||
also stops all wind tracking for the races of the regatta
|
||||
regattaname: name of the regatta, as returned by the "listevents"
|
||||
action on the /moderator servlet
|
||||
action=addrace: starts tracking a single race; if other races of
|
||||
the same event/regatta are already being tracked,
|
||||
the race is added to the regatta
|
||||
paramURL: the URL from where metadata about the race
|
||||
can be obtained; the resulting document includes
|
||||
UUIDs of waypoints, competitors, etc.
|
||||
liveURI: the live data URI for the TracTrac server
|
||||
storedURI: the stored data URI for the TracTrac server; usually
|
||||
the port number of this URI is that of liveURI + 1
|
||||
action=stoprace: stops tracking of a single race, also stopping the
|
||||
wind trackers that were started for this race; other
|
||||
races of the same regatta that were being tracked will
|
||||
|
||||
-874580
File diff suppressed because it is too large.
Load diff
@@ -27,6 +27,7 @@ Like businesses, sailors need the latest information to make strategic decisions
|
||||
* [[Mobile Development|wiki/mobile-development]]
|
||||
* Information for Webdesigners
|
||||
* [[Layout repository|wiki/webdesign]]
|
||||
* [[Create boat graphics for the 2D race viewer|wiki/boatGraphicsSVG]]
|
||||
* General Information
|
||||
* [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]]
|
||||
* [[Sailing Domain Algorithms|wiki/sailing-domain-algorithms]]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.base.impl;
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.AbstractSpeedImpl;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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.7"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
Regular → Executable
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.google.gwt.user.osgi</name>
|
||||
<name>com.sap.sailing.domain.igtimiadapter.test</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
eclipse.preferences.version=1
|
||||
pluginProject.extensions=false
|
||||
resolve.requirebundle=false
|
||||
@@ -0,0 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Fragment-Host: com.sap.sailing.domain.igtimiadapter
|
||||
Bundle-Name: Test
|
||||
Bundle-SymbolicName: com.sap.sailing.domain.igtimiadapter.test
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Require-Bundle: org.junit4;bundle-version="4.8.2",
|
||||
com.sap.sailing.domain.common
|
||||
@@ -0,0 +1,4 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
@@ -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.domain.igtimiadapter.test</artifactId>
|
||||
<packaging>eclipse-test-plugin</packaging>
|
||||
</project>
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AntHrm;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualitySatCount;
|
||||
|
||||
public class FixFactoryTest {
|
||||
private static final String demoJson = "{\n"+
|
||||
"\"EA-AK-AAAG\": {\n"+
|
||||
"\"1\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"-160.123,\n"+
|
||||
"-160.1233\n"+
|
||||
"],\n"+
|
||||
"\"2\": [\n"+
|
||||
"40.43,\n"+
|
||||
"40.44\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345677000,\n"+
|
||||
"12345677500\n"+
|
||||
"]\n"+
|
||||
"},\n"+
|
||||
"\"3\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"21\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345677500\n"+
|
||||
"]\n"+
|
||||
"}\n"+
|
||||
"},\n"+
|
||||
"\"EA-AK-AAAH\": {\n"+
|
||||
"\"1\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"-160.12\n"+
|
||||
"],\n"+
|
||||
"\"2\": [\n"+
|
||||
"40.434\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345677000\n"+
|
||||
"]\n"+
|
||||
"},\n"+
|
||||
"\"13:100\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"60\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345678000\n"+
|
||||
"]\n"+
|
||||
"},\n"+
|
||||
"\"13:201\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"119,\n"+
|
||||
"120\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345677000,\n"+
|
||||
"12345677500\n"+
|
||||
"]\n"+
|
||||
"}\n"+
|
||||
"}\n"+
|
||||
"}";
|
||||
|
||||
private JSONObject json;
|
||||
|
||||
@Before
|
||||
public void setUp() throws ParseException {
|
||||
json = (JSONObject) new JSONParser().parse(demoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixProduction() {
|
||||
FixFactory fixFactory = new FixFactory();
|
||||
Iterable<Fix> fixes = fixFactory.createFixes(json);
|
||||
assertFalse(Util.isEmpty(fixes));
|
||||
final Iterator<Fix> fixIter = fixes.iterator();
|
||||
GpsLatLong firstFix = (GpsLatLong) fixIter.next();
|
||||
assertEquals(new MillisecondsTimePoint(12345677000l), firstFix.getTimePoint());
|
||||
assertEquals(-160.123, firstFix.getPosition().getLngDeg(), 0.0000001);
|
||||
assertEquals(40.43, firstFix.getPosition().getLatDeg(), 0.00000001);
|
||||
GpsLatLong secondFix = (GpsLatLong) fixIter.next();
|
||||
assertEquals(new MillisecondsTimePoint(12345677500l), secondFix.getTimePoint());
|
||||
assertEquals(-160.1233, secondFix.getPosition().getLngDeg(), 0.0000001);
|
||||
assertEquals(40.44, secondFix.getPosition().getLatDeg(), 0.00000001);
|
||||
GpsQualitySatCount satCount = (GpsQualitySatCount) fixIter.next();
|
||||
assertEquals(21, satCount.getSatCount());
|
||||
fixIter.next();
|
||||
AntHrm hrm = (AntHrm) fixIter.next();
|
||||
assertEquals("100", hrm.getSensor().getDeviceId());
|
||||
assertEquals(60, hrm.getHeartRate());
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.oauth;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.ClientImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectionFactoryImpl;
|
||||
|
||||
public class SignInTest {
|
||||
private static final Logger logger = Logger.getLogger(SignInTest.class.getName());
|
||||
|
||||
@Test
|
||||
public void testSimpleSignIn() throws ClientProtocolException, IOException, IllegalStateException,
|
||||
ParserConfigurationException, SAXException, ClassNotFoundException, InstantiationException,
|
||||
IllegalAccessException, ClassCastException, ParseException {
|
||||
// use the credentials of "Another Test App"
|
||||
final ClientImpl testAppClient = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6",
|
||||
"aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback");
|
||||
final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient);
|
||||
final String code = igtimiConnectionFactory.authorizeAndReturnAuthorizedCode("axel.uhl@gmx.de", "123456");
|
||||
logger.info("Igtimi OAuth code is "+code);
|
||||
assertNotNull(code);
|
||||
Account account = igtimiConnectionFactory.obtainAccessTokenFromAuthorizationCode(code);
|
||||
assertEquals("axel.uhl@gmx.de", account.getUser().getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleAuthorizeForAppNotYetAuthorized() throws ClientProtocolException, IOException,
|
||||
IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
|
||||
InstantiationException, IllegalAccessException, ClassCastException {
|
||||
// use the credentials of "Another Test App"
|
||||
final ClientImpl testAppClient = new ClientImpl("a4cecd8593e12d43a03433a6db0eea243a411749f93c278dce6a26d4804eebd2",
|
||||
"4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http://1.2.3.4");
|
||||
final String code = new IgtimiConnectionFactoryImpl(testAppClient).authorizeAndReturnAuthorizedCode("axel.uhl@gmx.de", "123456");
|
||||
logger.info("Igtimi OAuth code is "+code);
|
||||
assertNotNull(code);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAddToken() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
assertEquals("axel.uhl@gmx.de", account.getUser().getEmail());
|
||||
assertSame(account, connectionFactory.getAccountByEmail("axel.uhl@gmx.de"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUsers() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Iterable<User> users = connection.getUsers();
|
||||
assertEquals(1, Util.size(users));
|
||||
assertEquals(account.getUser().getId(), users.iterator().next().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResources() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Iterable<Resource> resources = connection.getResources(Permission.read, /* start time */ null, /* end time */ null,
|
||||
/* serial numbers */ Collections.singleton("GA-EN-AAEJ"), /* stream IDs */ null);
|
||||
assertTrue(resources.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceData() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Map<Type, Double> typesAndCompression = new HashMap<>();
|
||||
typesAndCompression.put(Type.gps_latlong, 0.0);
|
||||
Iterable<Fix> data = connection.getResourceData(new MillisecondsTimePoint(1384420883000l),
|
||||
new MillisecondsTimePoint(1384421639000l), Collections.singleton("GA-EN-AAEJ"), typesAndCompression);
|
||||
assertTrue(data.iterator().hasNext());
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,15 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
com.sun.jersey;bundle-version="1.17.0",
|
||||
org.objectweb.asm;bundle-version="3.3.1"
|
||||
org.objectweb.asm;bundle-version="3.3.1",
|
||||
org.apache.httpcomponents.httpclient;bundle-version="4.2.5",
|
||||
org.apache.httpcomponents.httpcore;bundle-version="4.0.0",
|
||||
org.apache.commons.logging;bundle-version="1.1.1",
|
||||
com.sap.sailing.domain.common
|
||||
Import-Package: javax.ws.rs;version="1.1.1",
|
||||
javax.ws.rs.core;version="1.1.1",
|
||||
javax.ws.rs.ext;version="1.1.1"
|
||||
javax.ws.rs.ext;version="1.1.1",
|
||||
org.apache.commons.lang,
|
||||
org.osgi.framework;version="1.7.0"
|
||||
Web-ContextPath: /igtimi
|
||||
Bundle-Activator: com.sap.sailing.domain.igtimiadapter.impl.Activator
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface Account {
|
||||
User getUser();
|
||||
}
|
||||
+9
-8
@@ -3,19 +3,20 @@ package com.sap.sailing.domain.igtimiadapter;
|
||||
import javax.security.auth.callback.Callback;
|
||||
|
||||
/**
|
||||
* Represents what the Igtimi API calls a "client." In particular, a client has an ID and a secret. The Igtimi
|
||||
* connector of which this interface is a part acts as a client. A client needs to be authorized by a user to
|
||||
* be allowed to access the user's data via the Igtimi web services API. Authorization works by directing the
|
||||
* user to a URL using his/her user agent (usually a browser) which is constructed by the
|
||||
* {@link IgtimiConnectorFactory} and encodes a callback URL that leads to the {@link Callback} servlet.
|
||||
* The servlet records the access token that can be used for Igtimi web service API calls that require
|
||||
* access to the authenticated user's data.
|
||||
* Represents what the Igtimi API or OAuth calls a "client." In particular, a client has an ID and a secret. The Igtimi
|
||||
* connector of which this interface is a part acts as a client. A client needs to be authorized by a user to be allowed
|
||||
* to access the user's data via the Igtimi web services API. Authorization works by directing the user to a URL using
|
||||
* his/her user agent (usually a browser) which is constructed by the {@link IgtimiConnectionFactory} and encodes a
|
||||
* callback URL that leads to the {@link Callback} servlet. The servlet records the access token that can be used for
|
||||
* Igtimi web service API calls that require access to the authenticated user's data.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface Client {
|
||||
String getId();
|
||||
|
||||
String getSecret();
|
||||
|
||||
String getRedirectUri();
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
|
||||
/**
|
||||
* A connection to the Igtimi system for one {@link Client} and one {@link Account}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface IgtimiConnection {
|
||||
|
||||
Iterable<User> getUsers() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* @param startTime optional; may be <code>null</code>
|
||||
* @param endTime optional; may be <code>null</code>
|
||||
* @param serialNumbers optional (may be <code>null</code>) if <code>permission</code> is {@link Permission#modify}
|
||||
* @param streamIds optional; may be <code>null</code>
|
||||
*/
|
||||
Iterable<Resource> getResources(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds) throws IllegalStateException,
|
||||
ClientProtocolException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* All arguments are mandatory.
|
||||
*/
|
||||
Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException;
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.oauth.AuthorizationCallback;
|
||||
|
||||
/**
|
||||
* A connection factory is bound to a {@link Client} and allows that client to obtain a connection to the Igtimi
|
||||
* services on behalf of an {@link Account} for which an authorization token must have been
|
||||
* {@link #registerAccountForWhichClientIsAuthorized(String) registered} with this factory previously.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface IgtimiConnectionFactory {
|
||||
/**
|
||||
* Obtains a URL that a user agent (e.g., a web browser) can be sent to in order to allow that user to authenticate
|
||||
* and then authorize this factory's {@link Client} for accessing the user's Igtimi data. The URL is chosen such that
|
||||
* it redirects to the {@link AuthorizationCallback} with the <code>code</code> response type.
|
||||
*/
|
||||
String getAuthorizationUrl();
|
||||
|
||||
/**
|
||||
* Using the "account" service, retrieves the {@link Account} information for this access token and stores the token
|
||||
* that authorizes this factory's client to access the account together with the {@link Account} data. This information is
|
||||
* used when a caller wants to {@link #connect(Account)} to a specific account on behalf of the {@link Client} to
|
||||
* which this factory belongs.
|
||||
* @return
|
||||
*/
|
||||
Account registerAccountForWhichClientIsAuthorized(String accessToken) throws ClientProtocolException,
|
||||
IllegalStateException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Matches <code>eMail</code> with the e-mail information retrieved from the "account" service earlier when an access
|
||||
* token was registered.
|
||||
*/
|
||||
Account getAccountByEmail(String eMail);
|
||||
|
||||
IgtimiConnection connect(Account account);
|
||||
|
||||
/**
|
||||
* Uses the /oauth/token service to obtain and {@link #registerAccountForWhichClientIsAuthorized(String) register}
|
||||
* an access token for an authorization code which encodes the authorization given by a user to this factory's
|
||||
* client.
|
||||
*
|
||||
* @return the account encoding the application that is authorized for a user's account
|
||||
* @throws RuntimeException in case there was an error while retrieving the token
|
||||
*/
|
||||
Account obtainAccessTokenFromAuthorizationCode(String code) throws UnsupportedEncodingException,
|
||||
ClientProtocolException, IOException, IllegalStateException, ParseException;
|
||||
|
||||
String getAccountUrl(Account account);
|
||||
|
||||
String getUsersUrl(Account account);
|
||||
|
||||
String getResourcesUrl(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds, Account account);
|
||||
|
||||
String getResourceDataUrl(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Map<Type, Double> typeAndCompression, Account account);
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectorFactoryImpl;
|
||||
|
||||
public interface IgtimiConnectorFactory {
|
||||
static IgtimiConnectorFactory INSTANCE = new IgtimiConnectorFactoryImpl();
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public enum Permission {
|
||||
read, modify;
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
public interface Resource {
|
||||
|
||||
long getId();
|
||||
|
||||
public abstract boolean isBlob();
|
||||
|
||||
public abstract Iterable<Permission> getPermissions();
|
||||
|
||||
public abstract int[] getDataTypes();
|
||||
|
||||
public abstract String getDeviceSerialNumber();
|
||||
|
||||
public abstract TimePoint getEndTime();
|
||||
|
||||
public abstract TimePoint getStartTime();
|
||||
|
||||
}
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface Sensor {
|
||||
/**
|
||||
* The ID called "sensor ID" by Igtimi. This identifies the microcontroller-like device that can mange several sensors
|
||||
* such as wind, GPS or heart rate. Together with the type code and the optional {@link #getDeviceId() device ID} as used
|
||||
* for heart rate sensors the individual sensor is uniquely defined.
|
||||
*/
|
||||
String getTransmitterId();
|
||||
|
||||
String getDeviceId();
|
||||
}
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface User {
|
||||
long getId();
|
||||
|
||||
String getFirstName();
|
||||
|
||||
String getSurname();
|
||||
|
||||
String getEmail();
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Apparent wind angle, relative to the device's or vessel's orientation
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class AWA extends Fix {
|
||||
private final Bearing apparentWindAngle;
|
||||
|
||||
public AWA(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
apparentWindAngle = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Bearing getApparentWindAngle() {
|
||||
return apparentWindAngle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "AWA: "+getApparentWindAngle();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Apparent wind speed, relative to the inertial system on which the wind speed was measured
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class AWS extends Fix {
|
||||
private final Speed apparentWindSpeed;
|
||||
|
||||
public AWS(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
apparentWindSpeed = new KnotSpeedImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Speed getApparentWindSpeed() {
|
||||
return apparentWindSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "AWS: "+getApparentWindSpeed();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Cadence value measured by an ANT sensor
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class AntCbst extends Fix {
|
||||
private final int cadence;
|
||||
|
||||
public AntCbst(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
cadence = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
}
|
||||
|
||||
public int getCadence() {
|
||||
return cadence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String localToString() {
|
||||
return "Cadence "+getCadence();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* A heart rate measurement received through a sensor supporting the ANT protocol
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class AntHrm extends Fix {
|
||||
private final int heartRate;
|
||||
|
||||
public AntHrm(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
heartRate = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
}
|
||||
|
||||
public int getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "heart rate "+getHeartRate()+"/min";
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class BatteryLevel extends Fix {
|
||||
private final double percentage;
|
||||
|
||||
public BatteryLevel(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
percentage = ((Number) valuesPerSubindex.get(1)).doubleValue();
|
||||
}
|
||||
|
||||
public double getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "Battery: "+getPercentage()+"%";
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Course over ground
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class COG extends Fix {
|
||||
private final Bearing courseOverGround;
|
||||
|
||||
public COG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
courseOverGround = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Bearing getCourseOverGround() {
|
||||
return courseOverGround;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "COG: "+getCourseOverGround();
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class File extends Fix {
|
||||
private final TimePoint start;
|
||||
private final TimePoint end;
|
||||
private final String fileName;
|
||||
private final String md5;
|
||||
private final String contentType;
|
||||
private final long size;
|
||||
|
||||
public File(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
start = timePoint;
|
||||
end = timePoint; // TODO how are start and end timepoints encoded here?
|
||||
fileName = ((String) valuesPerSubindex.get(1));
|
||||
md5 = ((String) valuesPerSubindex.get(2));
|
||||
contentType = ((String) valuesPerSubindex.get(3));
|
||||
size = ((Number) valuesPerSubindex.get(4)).longValue();
|
||||
}
|
||||
|
||||
public TimePoint getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public TimePoint getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getMd5() {
|
||||
return md5;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "File: "+getFileName()+", MD5: "+getMd5()+" content type "+getContentType()+", "+getSize()+" bytes";
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public abstract class Fix {
|
||||
private final TimePoint timePoint;
|
||||
private final Sensor sensor;
|
||||
|
||||
protected Fix(Sensor sensor, TimePoint timePoint) {
|
||||
super();
|
||||
this.timePoint = timePoint;
|
||||
this.sensor = sensor;
|
||||
}
|
||||
|
||||
public TimePoint getTimePoint() {
|
||||
return timePoint;
|
||||
}
|
||||
|
||||
public Sensor getSensor() {
|
||||
return sensor;
|
||||
}
|
||||
|
||||
abstract protected String localToString();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return localToString() + " at "+getTimePoint()+" from "+getSensor();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsAltitude extends Fix {
|
||||
private final Distance altitude;
|
||||
|
||||
public GpsAltitude(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
altitude = new MeterDistance(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Distance getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "Altitude "+getAltitude();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsLatLong extends Fix {
|
||||
private final Position position;
|
||||
|
||||
public GpsLatLong(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
position = new DegreePosition(((Number) valuesPerSubindex.get(2)).doubleValue(), ((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return position.toString();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsQualityHdop extends Fix {
|
||||
private final Distance hdop;
|
||||
|
||||
public GpsQualityHdop(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
hdop = new MeterDistance(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Distance getHdop() {
|
||||
return hdop;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "HDOP: "+getHdop();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsQualityIndicator extends Fix {
|
||||
private final int quality;
|
||||
|
||||
public GpsQualityIndicator(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
quality = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
}
|
||||
|
||||
public int getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "GPS Quality: "+getQuality();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsQualitySatCount extends Fix {
|
||||
private final int satCount;
|
||||
|
||||
public GpsQualitySatCount(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
satCount = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
}
|
||||
|
||||
public int getSatCount() {
|
||||
return satCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return ""+getSatCount()+" satellites";
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* True heading; the difference to {@link HDGM} is the local declination at the point in time the measurement was made
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class HDG extends Fix {
|
||||
private final Bearing trueHeading;
|
||||
|
||||
public HDG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
trueHeading = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Bearing getTrueHeading() {
|
||||
return trueHeading;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "HDG: "+getTrueHeading();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Magnetic heading
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class HDGM extends Fix {
|
||||
private final Bearing magnetigHeading;
|
||||
|
||||
public HDGM(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
magnetigHeading = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Bearing getMagnetigHeading() {
|
||||
return magnetigHeading;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "HDGM: "+getMagnetigHeading();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Speed over ground
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class SOG extends Fix {
|
||||
private final Speed speedOverGround;
|
||||
|
||||
public SOG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
speedOverGround = new KnotSpeedImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Speed getSpeedOverGround() {
|
||||
return speedOverGround;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "SOG: "+getSpeedOverGround();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Speed through water
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class STW extends Fix {
|
||||
private final Speed speedThroughWater;
|
||||
|
||||
public STW(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
speedThroughWater = new KnotSpeedImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
}
|
||||
|
||||
public Speed getSpeedThroughWater() {
|
||||
return speedThroughWater;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String localToString() {
|
||||
return "STW: "+getSpeedThroughWater();
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum Type {
|
||||
|
||||
gps_latlong(1, GpsLatLong.class),
|
||||
gps_quality_indicator(2, GpsQualityIndicator.class),
|
||||
gps_quality_sat_count(3, GpsQualitySatCount.class),
|
||||
gps_quality_hdop(4, GpsQualityHdop.class),
|
||||
gps_altitude(5, GpsAltitude.class),
|
||||
COG(6, COG.class),
|
||||
HDGM(7, HDGM.class),
|
||||
HDG(8, HDG.class),
|
||||
SOG(9, SOG.class),
|
||||
STW(10, STW.class),
|
||||
AWA(11, AWA.class),
|
||||
AWS(12, AWS.class),
|
||||
ant_hrm(13, AntHrm.class),
|
||||
ant_cbst(14, AntCbst.class),
|
||||
battery_level(15, BatteryLevel.class),
|
||||
File(16, File.class);
|
||||
|
||||
public static Type valueOf(int code) {
|
||||
return typeByCode.get(code);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Class<? extends Fix> getFixClass() {
|
||||
return fixClass;
|
||||
}
|
||||
|
||||
private final int code;
|
||||
private final Class<? extends Fix> fixClass;
|
||||
private final static Map<Integer, Type> typeByCode = new HashMap<>();
|
||||
|
||||
private Type(int code, Class<? extends Fix> fixClass) {
|
||||
this.code = code;
|
||||
this.fixClass = fixClass;
|
||||
}
|
||||
|
||||
static {
|
||||
for (Type type : Type.values()) {
|
||||
typeByCode.put(type.getCode(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
|
||||
public class AccountImpl implements Account {
|
||||
private final User user;
|
||||
|
||||
public AccountImpl(User user) {
|
||||
super();
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Client;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
|
||||
/**
|
||||
* Maintains data about a default {@link Client} that represents this application when interacting with the Igtimi
|
||||
* server. The corresponding default {@link IgtimiConnectionFactory} can be obtained from within this bundle using
|
||||
* {@link #getInstance()}.{@link #getConnectionFactory()}. Clients outside this bundle shall track the
|
||||
* {@link IgtimiConnectionFactory} OSGi service that this activator registeres with the OSGi system upon
|
||||
* {@link #start(BundleContext)}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class Activator implements BundleActivator {
|
||||
private static Activator INSTANCE;
|
||||
|
||||
private static final String CLIENT_ID = "d29eae61621af3057db0e638232a027e96b1d2291b1b89a1481dfcac075b0bf4";
|
||||
private static final String CLIENT_SECRET = "537dbd14a84fcb470c91d85e8c4f8f7a356ac5ffc8727594d1bfe900ee5942ef";
|
||||
private IgtimiConnectionFactory connectionFactory;
|
||||
|
||||
public Activator() {
|
||||
Client client = new ClientImpl(CLIENT_ID, CLIENT_SECRET, "http://sapsailing.com");
|
||||
connectionFactory = new IgtimiConnectionFactoryImpl(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
INSTANCE = this;
|
||||
context.registerService(IgtimiConnectionFactory.class, connectionFactory, /* properties */ null);
|
||||
}
|
||||
|
||||
public static Activator getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new Activator(); // probably non-OSGi case, as in test execution
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public IgtimiConnectionFactory getConnectionFactory() {
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Client;
|
||||
|
||||
public class ClientImpl implements Client {
|
||||
private final String id;
|
||||
private final String secret;
|
||||
private final String redirectUri;
|
||||
|
||||
public ClientImpl(String id, String secret, String redirectUri) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.secret = secret;
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
public class ConnectivityUtils {
|
||||
public static JSONObject getJsonFromResponse(HttpResponse response) throws IllegalStateException, IOException, ParseException {
|
||||
JSONParser jsonParser = new JSONParser();
|
||||
final Header contentEncoding = response.getEntity().getContentEncoding();
|
||||
final Reader reader;
|
||||
if (contentEncoding == null) {
|
||||
reader = new InputStreamReader(response.getEntity().getContent());
|
||||
} else {
|
||||
reader = new InputStreamReader(response.getEntity().getContent(), contentEncoding.getValue());
|
||||
}
|
||||
JSONObject json = (JSONObject) jsonParser.parse(reader);
|
||||
return json;
|
||||
}
|
||||
|
||||
public static String getContent(HttpResponse response) throws IOException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line;
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
|
||||
while ((line=reader.readLine()) != null) {
|
||||
result.append(line);
|
||||
result.append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static HttpResponse postForm(String baseUrl, final String action, final Map<String, String> inputFieldsToSubmit, DefaultHttpClient client, String referer)
|
||||
throws UnsupportedEncodingException, IOException, ClientProtocolException {
|
||||
HttpPost post = new HttpPost(baseUrl+action);
|
||||
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
|
||||
for (Entry<String, String> nameValue : inputFieldsToSubmit.entrySet()) {
|
||||
urlParameters.add(new BasicNameValuePair(nameValue.getKey(), nameValue.getValue()));
|
||||
}
|
||||
post.setEntity(new UrlEncodedFormEntity(urlParameters));
|
||||
// TODO check if this is necessary at all
|
||||
post.setHeader("Origin", "https://www.igtimi.com");
|
||||
post.setHeader("Referer", referer);
|
||||
post.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
|
||||
HttpResponse responseForSignIn = client.execute(post);
|
||||
return responseForSignIn;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
|
||||
public class FixFactory {
|
||||
private static final Logger logger = Logger.getLogger(FixFactory.class.getName());
|
||||
|
||||
public Iterable<Fix> createFixes(JSONObject sensorsJson) {
|
||||
List<Fix> result = new ArrayList<>();
|
||||
for (Entry<Object, Object> e : sensorsJson.entrySet()) {
|
||||
String sensorId = (String) e.getKey();
|
||||
JSONObject typesJson = (JSONObject) e.getValue();
|
||||
for (Entry<Object, Object> fixTypeAndFixesJson : typesJson.entrySet()) {
|
||||
final String[] fixTypeAndOptionalColonSeparatedSensorsSubId = ((String) fixTypeAndFixesJson.getKey()).split(":");
|
||||
int fixType = Integer.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[0]);
|
||||
JSONObject fixesJson = (JSONObject) fixTypeAndFixesJson.getValue();
|
||||
JSONArray timePointsMillis = (JSONArray) fixesJson.get("t");
|
||||
int fixIndex = 0;
|
||||
for (Object timePointMillis : timePointsMillis) {
|
||||
TimePoint timePoint = new MillisecondsTimePoint((Long) timePointMillis);
|
||||
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
|
||||
int i=1;
|
||||
JSONArray values;
|
||||
while ((values=(JSONArray) fixesJson.get(""+i)) != null) {
|
||||
valuesPerSubindex.put(i, (Number) values.get(fixIndex));
|
||||
i++;
|
||||
}
|
||||
Sensor sensor = new SensorImpl(sensorId, fixTypeAndOptionalColonSeparatedSensorsSubId.length<2?null:fixTypeAndOptionalColonSeparatedSensorsSubId[1]);
|
||||
Fix fix = createFix(sensor, Type.valueOf(fixType), timePoint, valuesPerSubindex);
|
||||
result.add(fix);
|
||||
fixIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Fix createFix(Sensor sensor, Type fixType, TimePoint timePoint, Map<Integer, Object> valuesPerSubindex) {
|
||||
try {
|
||||
Constructor<? extends Fix> constructor = fixType.getFixClass().getConstructor(TimePoint.class, Sensor.class, Map.class);
|
||||
Fix fix = constructor.newInstance(timePoint, sensor, valuesPerSubindex);
|
||||
return fix;
|
||||
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
logger.log(Level.SEVERE, "Internal error trying to find fix constructor for fix type "+fixType+" with class "+fixType.getFixClass());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.RedirectStrategy;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.client.LaxRedirectStrategy;
|
||||
import org.apache.http.impl.client.SystemDefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.Client;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
|
||||
public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
private static final Logger logger = Logger.getLogger(IgtimiConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Map<Account, String> accessTokensByAccount;
|
||||
private Map<String, Account> accountsByEmail;
|
||||
private final Client client;
|
||||
|
||||
public IgtimiConnectionFactoryImpl(Client client) {
|
||||
this.accessTokensByAccount = new HashMap<>();
|
||||
this.accountsByEmail = new HashMap<>();
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account registerAccountForWhichClientIsAuthorized(String accessToken) throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Account account = getAccount(accessToken);
|
||||
accountsByEmail.put(account.getUser().getEmail(), account);
|
||||
accessTokensByAccount.put(account, accessToken);
|
||||
return account;
|
||||
}
|
||||
|
||||
private Account getAccount(String accessToken) throws ClientProtocolException, IOException, IllegalStateException, ParseException {
|
||||
HttpClient client = new SystemDefaultHttpClient();
|
||||
HttpGet getAccount = new HttpGet(getApiV1BaseUrl()+"account?access_token="+accessToken);
|
||||
HttpResponse accountResponse = client.execute(getAccount);
|
||||
JSONObject accountJson = ConnectivityUtils.getJsonFromResponse(accountResponse);
|
||||
JSONObject userJson = (JSONObject) accountJson.get("user");
|
||||
Account account = new AccountImpl(new UserDeserializer().createUserFromJson(userJson));
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return trailing slash
|
||||
*/
|
||||
private String getApiV1BaseUrl() {
|
||||
return getBaseUrl()+"/api/v1/";
|
||||
}
|
||||
|
||||
private String getSignInUrl() {
|
||||
return "https://www.igtimi.com/users/sign_in";
|
||||
}
|
||||
|
||||
private String getOauthTokenUrl() {
|
||||
return getBaseUrl()+"/oauth/token";
|
||||
}
|
||||
|
||||
private String getOauthAuthorizeUrl() throws UnsupportedEncodingException {
|
||||
return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+"&redirect_uri="+URLEncoder.encode(getClient().getRedirectUri(), "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return no trailing slash
|
||||
*/
|
||||
private String getBaseUrl() {
|
||||
return "https://www.igtimi.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account getAccountByEmail(String eMail) {
|
||||
return accountsByEmail.get(eMail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IgtimiConnection connect(Account account) {
|
||||
return new IgtimiConnectionImpl(this, account);
|
||||
}
|
||||
|
||||
private String getAccessTokenForAccount(Account account) {
|
||||
return accessTokensByAccount.get(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccountUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"account?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsersUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"users?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceDataUrl(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Map<Type, Double> typeAndCompression, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
url.append("resources/data?start_time=");
|
||||
url.append(startTime.asMillis());
|
||||
url.append("&end_time=");
|
||||
url.append(endTime.asMillis());
|
||||
for (String serialNumber : serialNumbers) {
|
||||
url.append("&serial_numbers[]=");
|
||||
url.append(serialNumber);
|
||||
}
|
||||
for (Entry<Type, Double> e : typeAndCompression.entrySet()) {
|
||||
url.append("&types["+e.getKey().getCode()+"]="+e.getValue());
|
||||
}
|
||||
url.append("&");
|
||||
url.append(getAccessTokenUrlParameter(account));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourcesUrl(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
url.append("resources?");
|
||||
url.append("permission=");
|
||||
url.append(permission.name());
|
||||
if (startTime != null) {
|
||||
url.append("&start_time=");
|
||||
url.append(startTime.asMillis());
|
||||
}
|
||||
if (endTime != null) {
|
||||
url.append("&end_time=");
|
||||
url.append(endTime.asMillis());
|
||||
}
|
||||
if (serialNumbers != null) {
|
||||
for (String serialNumber : serialNumbers) {
|
||||
url.append("&serial_numbers[]=");
|
||||
url.append(serialNumber);
|
||||
}
|
||||
}
|
||||
if (streamIds != null) {
|
||||
for (String streamId : streamIds) {
|
||||
url.append("&stream_ids[]=");
|
||||
url.append(streamId);
|
||||
}
|
||||
}
|
||||
url.append("&");
|
||||
url.append(getAccessTokenUrlParameter(account));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private String getAccessTokenUrlParameter(Account account) {
|
||||
return "access_token="+getAccessTokenForAccount(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthorizationUrl() {
|
||||
return getBaseUrl()+"/oauth";
|
||||
}
|
||||
|
||||
private Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account obtainAccessTokenFromAuthorizationCode(String code) throws ClientProtocolException, IOException, IllegalStateException, ParseException {
|
||||
HttpClient client = new SystemDefaultHttpClient();
|
||||
HttpPost post = new HttpPost(getOauthTokenUrl());
|
||||
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
|
||||
urlParameters.add(new BasicNameValuePair("grant_type", "authorization_code"));
|
||||
urlParameters.add(new BasicNameValuePair("client_id", getClient().getId()));
|
||||
urlParameters.add(new BasicNameValuePair("client_secret", getClient().getSecret()));
|
||||
urlParameters.add(new BasicNameValuePair("code", code));
|
||||
urlParameters.add(new BasicNameValuePair("redirect_uri", getClient().getRedirectUri()));
|
||||
post.setEntity(new UrlEncodedFormEntity(urlParameters));
|
||||
HttpResponse response = client.execute(post);
|
||||
JSONObject accessTokenJson = ConnectivityUtils.getJsonFromResponse(response);
|
||||
final Account result;
|
||||
if (accessTokenJson.get("error") != null) {
|
||||
throw new RuntimeException(accessTokenJson.toString());
|
||||
} else {
|
||||
String accessToken = (String) accessTokenJson.get("access_token");
|
||||
result = registerAccountForWhichClientIsAuthorized(accessToken);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to authorize our client on behalf of a user identified by e-mail and password.
|
||||
*
|
||||
* @return the authorization code which can then be used to obtain a permanent access token to be used by our client
|
||||
* to access data owned by the user identified by e-mail and password.
|
||||
*/
|
||||
public String authorizeAndReturnAuthorizedCode(String userEmail, String userPassword)
|
||||
throws ClientProtocolException, IOException, IllegalStateException, ParserConfigurationException,
|
||||
SAXException, ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
|
||||
logger.info("Trying to authorize application client " + getClient().getId() + " for user " + userEmail);
|
||||
DefaultHttpClient client = new SystemDefaultHttpClient();
|
||||
CookieStore cookieStore = new BasicCookieStore();
|
||||
client.setCookieStore(cookieStore);
|
||||
client.setRedirectStrategy(new LaxRedirectStrategy());
|
||||
HttpGet get = new HttpGet(getOauthAuthorizeUrl());
|
||||
HttpResponse responseForAuthorize = client.execute(get);
|
||||
return signInAndReturnAuthorizationForm(client, responseForAuthorize, userEmail, userPassword);
|
||||
}
|
||||
|
||||
private String signInAndReturnAuthorizationForm(DefaultHttpClient client, HttpResponse response,
|
||||
final String userEmail, final String userPassword) throws ParserConfigurationException, SAXException,
|
||||
IOException, UnsupportedEncodingException, ClientProtocolException, IllegalStateException,
|
||||
ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
final String[] action = new String[1];
|
||||
final Map<String, String> inputFieldsToSubmit = new HashMap<>();
|
||||
try {
|
||||
String pageContent = ConnectivityUtils.getContent(response);
|
||||
String unescapedHtml = StringEscapeUtils.unescapeHtml(pageContent);
|
||||
parser.parse(new ByteArrayInputStream(unescapedHtml.getBytes("UTF-8")), new DefaultHandler() {
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
||||
throws SAXException {
|
||||
if (qName.equals("form")) {
|
||||
action[0] = attributes.getValue("action");
|
||||
} else if (qName.equals("input")) {
|
||||
if (attributes.getValue("value") != null && !attributes.getValue("value").isEmpty()) {
|
||||
inputFieldsToSubmit.put(attributes.getValue("name"), attributes.getValue("value"));
|
||||
} else if (attributes.getValue("id") != null) {
|
||||
if (attributes.getValue("id").contains("email")) {
|
||||
inputFieldsToSubmit.put(attributes.getValue("name"), userEmail);
|
||||
} else if (attributes.getValue("id").contains("pass")) {
|
||||
inputFieldsToSubmit.put(attributes.getValue("name"), userPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
}
|
||||
});
|
||||
} catch (SAXParseException e) {
|
||||
// swallow; we try to grab what we can; let's hope it was enough...
|
||||
}
|
||||
response.getEntity().getContent().close();
|
||||
final RedirectStrategy oldRedirectStrategy = client.getRedirectStrategy();
|
||||
final RedirectStrategyExtractingAuthorizationCode codeExtractor = new RedirectStrategyExtractingAuthorizationCode(oldRedirectStrategy);
|
||||
client.setRedirectStrategy(codeExtractor);
|
||||
logger.info("Posting sign-in form for user "+userEmail);
|
||||
HttpResponse authorizationForm = ConnectivityUtils.postForm(getBaseUrl(), action[0], inputFieldsToSubmit, client, getSignInUrl());
|
||||
if (codeExtractor.getCode() == null) {
|
||||
logger.info("Client app "+getClient().getId()+" doesn't seem to be authorized yet.");
|
||||
authorizeAndGetCode(authorizationForm, client);
|
||||
} else {
|
||||
logger.info("Client app "+getClient().getId()+" seems to be authorized for user "+userEmail+" already.");
|
||||
}
|
||||
return codeExtractor.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the form in the <code>autorizationForm</code> response and posts it by submitting the form that contains
|
||||
* the commit button with the value "Authorize". If a redirect strategy is set on the <code>client</code> it will
|
||||
* see the redirect URL in the <code>Location</code> header. The redirection target is closed immediately.
|
||||
* @throws ClassCastException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
private void authorizeAndGetCode(HttpResponse authorizationForm, DefaultHttpClient client)
|
||||
throws IllegalStateException, SAXException, IOException, ParserConfigurationException,
|
||||
ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
|
||||
// If the user already authorized the app, an empty document will be returned
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
final String action[] = new String[1];
|
||||
final Map<String, String> inputFieldsToSubmit = new HashMap<>();
|
||||
try {
|
||||
String pageContent = ConnectivityUtils.getContent(authorizationForm);
|
||||
String unescapedHtml = StringEscapeUtils.unescapeHtml(pageContent);
|
||||
final boolean[] completedAuthorizeForm = new boolean[1];
|
||||
parser.parse(new ByteArrayInputStream(unescapedHtml.getBytes("UTF-8")), new DefaultHandler() {
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
||||
throws SAXException {
|
||||
if (!completedAuthorizeForm[0]) {
|
||||
if (qName.equals("form")) {
|
||||
action[0] = attributes.getValue("action");
|
||||
} else if (qName.equals("input")) {
|
||||
if (attributes.getValue("value") != null && !attributes.getValue("value").isEmpty()) {
|
||||
inputFieldsToSubmit.put(attributes.getValue("name"), attributes.getValue("value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
if (qName.equals("form") && inputFieldsToSubmit.get("commit").equals("Authorize")) {
|
||||
completedAuthorizeForm[0] = true;
|
||||
}
|
||||
super.endElement(uri, localName, qName);
|
||||
}
|
||||
});
|
||||
} catch (SAXParseException e) {
|
||||
// swallow; we try to grab what we can; let's hope it was enough...
|
||||
logger.warning("The authorization form was not well-formed. Extracted the following parameters so far: "+inputFieldsToSubmit+" ("+e.getMessage()+")");
|
||||
}
|
||||
authorizationForm.getEntity().getContent().close();
|
||||
logger.info("Posting authorization form to authorize client "+getClient().getId()+" for access to data of user");
|
||||
HttpResponse authorizationResponse = ConnectivityUtils.postForm(getBaseUrl(), action[0], inputFieldsToSubmit, client, /* referer */ getSignInUrl());
|
||||
authorizationResponse.getEntity().getContent().close();
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.SystemDefaultHttpClient;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
|
||||
public class IgtimiConnectionImpl implements IgtimiConnection {
|
||||
private final Account account;
|
||||
private final IgtimiConnectionFactory connectionFactory;
|
||||
|
||||
public IgtimiConnectionImpl(IgtimiConnectionFactory connectionFactory, Account account) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<User> getUsers() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet getUsers = new HttpGet(connectionFactory.getUsersUrl(account));
|
||||
JSONObject usersJson = ConnectivityUtils.getJsonFromResponse(client.execute(getUsers));
|
||||
final List<User> result = new ArrayList<>();
|
||||
for (Object userJson : (JSONArray) usersJson.get("users")) {
|
||||
User user = new UserDeserializer().createUserFromJson((JSONObject) ((JSONObject) userJson).get("user"));
|
||||
result.add(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private HttpClient getHttpClient() {
|
||||
HttpClient client = new SystemDefaultHttpClient();
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Resource> getResources(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet getResources = new HttpGet(connectionFactory.getResourcesUrl(permission, startTime, endTime, serialNumbers, streamIds, account));
|
||||
JSONObject resourcesJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResources));
|
||||
final List<Resource> result = new ArrayList<>();
|
||||
for (Object userJson : (JSONArray) resourcesJson.get("resources")) {
|
||||
Resource user = new ResourceDeserializer().createResourceFromJson((JSONObject) ((JSONObject) userJson).get("resource"));
|
||||
result.add(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet getResourceData = new HttpGet(connectionFactory.getResourceDataUrl(startTime, endTime, serialNumbers, typeAndCompression, account));
|
||||
JSONObject resourceDataJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResourceData));
|
||||
return new FixFactory().createFixes(resourceDataJson);
|
||||
}
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectorFactory;
|
||||
|
||||
public class IgtimiConnectorFactoryImpl implements IgtimiConnectorFactory {
|
||||
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.ProtocolException;
|
||||
import org.apache.http.client.RedirectStrategy;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
public class RedirectStrategyExtractingAuthorizationCode implements RedirectStrategy {
|
||||
private static final Logger logger = Logger.getLogger(RedirectStrategyExtractingAuthorizationCode.class.getName());
|
||||
|
||||
private final RedirectStrategy wrappedRedirectStrategy;
|
||||
|
||||
private String code;
|
||||
|
||||
public RedirectStrategyExtractingAuthorizationCode(RedirectStrategy wrappedRedirectStrategy) {
|
||||
this.wrappedRedirectStrategy = wrappedRedirectStrategy;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context)
|
||||
throws ProtocolException {
|
||||
boolean result = wrappedRedirectStrategy.isRedirected(request, response, context);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
Header locationHeader = response.getFirstHeader("location");
|
||||
if (locationHeader != null) {
|
||||
switch (statusCode) {
|
||||
case HttpStatus.SC_MOVED_TEMPORARILY:
|
||||
case HttpStatus.SC_MOVED_PERMANENTLY:
|
||||
case HttpStatus.SC_TEMPORARY_REDIRECT:
|
||||
try {
|
||||
String codeParameter = getCodeFromRedirect(locationHeader);
|
||||
if (codeParameter != null) {
|
||||
code = codeParameter;
|
||||
logger.info("Found authorization code "+code+" in redirect URI "+locationHeader.getValue());
|
||||
result = false; // don't actually re-direct as the re-direct URI may be a dummy only anyway
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
logger.log(Level.INFO, "Exception while trying to obtain code from redirect URI", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context)
|
||||
throws ProtocolException {
|
||||
return wrappedRedirectStrategy.getRedirect(request, response, context);
|
||||
}
|
||||
|
||||
private String getCodeFromRedirect(final Header locationHeader) throws URISyntaxException {
|
||||
List<NameValuePair> params = URLEncodedUtils.parse(new URI(locationHeader.getValue()), "UTF-8");
|
||||
for (NameValuePair param : params) {
|
||||
if (param.getName().equals("code")) {
|
||||
return param.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
|
||||
public class ResourceDeserializer {
|
||||
public Resource createResourceFromJson(JSONObject resourceJson) {
|
||||
Boolean blob = (Boolean) resourceJson.get("blob");
|
||||
return new ResourceImpl((Long) resourceJson.get("id"),
|
||||
new MillisecondsTimePoint(((Double) resourceJson.get("start_time")).longValue()),
|
||||
new MillisecondsTimePoint(((Double) resourceJson.get("end_time")).longValue()),
|
||||
(String) resourceJson.get("device_serial_number"),
|
||||
getDataTypes((JSONArray) resourceJson.get("data_types")),
|
||||
getPermissions((JSONObject) resourceJson.get("permissions")),
|
||||
blob == null ? false : blob);
|
||||
}
|
||||
|
||||
private Iterable<Permission> getPermissions(JSONObject permissions) {
|
||||
final List<Permission> result = new ArrayList<>();
|
||||
for (Entry<Object, Object> e : permissions.entrySet()) {
|
||||
if ((Boolean) e.getValue()) {
|
||||
result.add(Permission.valueOf((String) e.getKey()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[] getDataTypes(JSONArray jsonArray) {
|
||||
final int[] result = new int[jsonArray.size()];
|
||||
int i=0;
|
||||
for (Object o : jsonArray) {
|
||||
result[i++] = ((Long) o).intValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
|
||||
public class ResourceImpl implements Resource {
|
||||
private final long id;
|
||||
private final TimePoint startTime;
|
||||
private final TimePoint endTime;
|
||||
private final String deviceSerialNumber;
|
||||
private final int[] dataTypes;
|
||||
private final Iterable<Permission> permissions;
|
||||
private final boolean blob;
|
||||
|
||||
public ResourceImpl(long id, TimePoint startTime, TimePoint endTime, String deviceSerialNumber, int[] dataTypes,
|
||||
Iterable<Permission> permissions, boolean blob) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.deviceSerialNumber = deviceSerialNumber;
|
||||
this.dataTypes = dataTypes;
|
||||
this.permissions = permissions;
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceSerialNumber() {
|
||||
return deviceSerialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getDataTypes() {
|
||||
return dataTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlob() {
|
||||
return blob;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class SensorImpl implements Sensor {
|
||||
private final String transmitterId;
|
||||
private final String deviceId;
|
||||
protected SensorImpl(String transmitterId, String deviceId) {
|
||||
super();
|
||||
this.transmitterId = transmitterId;
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransmitterId() {
|
||||
return transmitterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Transmitter "+getTransmitterId()+(getDeviceId()==null?"":(", device "+getDeviceId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((deviceId == null) ? 0 : deviceId.hashCode());
|
||||
result = prime * result + ((transmitterId == null) ? 0 : transmitterId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SensorImpl other = (SensorImpl) obj;
|
||||
if (deviceId == null) {
|
||||
if (other.deviceId != null)
|
||||
return false;
|
||||
} else if (!deviceId.equals(other.deviceId))
|
||||
return false;
|
||||
if (transmitterId == null) {
|
||||
if (other.transmitterId != null)
|
||||
return false;
|
||||
} else if (!transmitterId.equals(other.transmitterId))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
|
||||
public class UserDeserializer {
|
||||
public User createUserFromJson(JSONObject userJson) {
|
||||
Boolean blob = (Boolean) userJson.get("blob");
|
||||
return new UserImpl((Long) userJson.get("id"),
|
||||
(String) userJson.get("first_name"),
|
||||
(String) userJson.get("surname"),
|
||||
(String) userJson.get("email"),
|
||||
blob == null ? false : blob);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
|
||||
public class UserImpl implements User {
|
||||
private final long id;
|
||||
private final String firstName;
|
||||
private final String surname;
|
||||
private final String email;
|
||||
|
||||
|
||||
public UserImpl(long id, String firstName, String surname, String email, boolean blob) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.surname = surname;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.oauth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
|
||||
@Path(AuthorizationCallback.V1)
|
||||
public class AuthorizationCallback {
|
||||
private static final Logger logger = Logger.getLogger(AuthorizationCallback.class.getName());
|
||||
|
||||
private static final String AUTHORIZATIONCALLBACK = "/authorizationcallback";
|
||||
static final String V1 = "/v1";
|
||||
static final String V1_AUTHORIZATIONCALLBACK = V1 + AUTHORIZATIONCALLBACK;
|
||||
private final IgtimiConnectionFactory connectionFactory;
|
||||
|
||||
public AuthorizationCallback() {
|
||||
connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
}
|
||||
|
||||
public static String getRedirectUrl() {
|
||||
// TODO how to find out the external address of this server?
|
||||
return "http://127.0.0.1:8888/igtimi"+V1_AUTHORIZATIONCALLBACK;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path(AUTHORIZATIONCALLBACK)
|
||||
public Response obtainAccessToken(@QueryParam("code") String code) throws ClientProtocolException, IOException, IllegalStateException, ParseException, URISyntaxException {
|
||||
Response result;
|
||||
try {
|
||||
connectionFactory.obtainAccessTokenFromAuthorizationCode(code);
|
||||
result = Response.ok().build();
|
||||
} catch (Exception e) {
|
||||
result = Response.status(Status.UNAUTHORIZED).entity(e.getMessage()).build();
|
||||
logger.log(Level.SEVERE, "Error trying to obtain access token from authentication code "+code, e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("uri")
|
||||
public Response getWithUri(@Context UriInfo info) {
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.oauth;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
@Path("/v1/authorizationcallback")
|
||||
public class Callback {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("/")
|
||||
public Response getEvent(@QueryParam("access_token") String accessToken) {
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -12,6 +12,6 @@ public class RestApiApplication extends Application {
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
return new HashSet<Class<?>>(Arrays.asList(new Class<?>[] { Callback.class }));
|
||||
return new HashSet<Class<?>>(Arrays.asList(new Class<?>[] { AuthorizationCallback.class }));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -31,9 +31,9 @@ import com.sap.sailing.domain.tracking.WindTrack;
|
||||
*/
|
||||
public interface DomainObjectFactory {
|
||||
/**
|
||||
* @param regatta only needed for backward compatibility because old wind tracks used the regatta name as part of the key
|
||||
* @param regattaName only needed for backward compatibility because old wind tracks used the regatta name as part of the key
|
||||
*/
|
||||
WindTrack loadWindTrack(Regatta regatta, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage);
|
||||
WindTrack loadWindTrack(String regattaName, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage);
|
||||
|
||||
/**
|
||||
* @return the leaderboard loaded, if successful, or <code>null</code> if the leaderboard couldn't be loaded,
|
||||
@@ -78,7 +78,7 @@ public interface DomainObjectFactory {
|
||||
*/
|
||||
Iterable<Leaderboard> getLeaderboardsNotInGroup(RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry);
|
||||
|
||||
Map<? extends WindSource, ? extends WindTrack> loadWindTracks(Regatta regatta, RaceDefinition race,
|
||||
Map<? extends WindSource, ? extends WindTrack> loadWindTracks(String regattaName, RaceDefinition race,
|
||||
long millisecondsOverWhichToAverageWind);
|
||||
|
||||
Event loadEvent(String name);
|
||||
|
||||
+9
-9
@@ -707,9 +707,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack loadWindTrack(Regatta regatta, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage) {
|
||||
public WindTrack loadWindTrack(String regattaName, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage) {
|
||||
final WindTrack result;
|
||||
Map<WindSource, WindTrack> resultMap = loadWindTracks(regatta, race, windSource, millisecondsOverWhichToAverage);
|
||||
Map<WindSource, WindTrack> resultMap = loadWindTracks(regattaName, race, windSource, millisecondsOverWhichToAverage);
|
||||
if (resultMap.containsKey(windSource)) {
|
||||
result = resultMap.get(windSource);
|
||||
} else {
|
||||
@@ -722,9 +722,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(Regatta regatta, RaceDefinition race,
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(String regattaName, RaceDefinition race,
|
||||
long millisecondsOverWhichToAverageWind) {
|
||||
Map<WindSource, WindTrack> result = loadWindTracks(regatta, race, /* constrain wind source */ null, millisecondsOverWhichToAverageWind);
|
||||
Map<WindSource, WindTrack> result = loadWindTracks(regattaName, race, /* constrain wind source */ null, millisecondsOverWhichToAverageWind);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -733,7 +733,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
* if <code>null</code>, wind for all sources will be loaded; otherwise, only wind data for the wind
|
||||
* source specified by this argument will be loaded
|
||||
*/
|
||||
private Map<WindSource, WindTrack> loadWindTracks(Regatta regatta, RaceDefinition race,
|
||||
private Map<WindSource, WindTrack> loadWindTracks(String regattaName, RaceDefinition race,
|
||||
WindSource constrainToWindSource, long millisecondsOverWhichToAverageWind) {
|
||||
Map<WindSource, WindTrack> result = new HashMap<WindSource, WindTrack>();
|
||||
try {
|
||||
@@ -748,7 +748,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
loadWindFix(result, dbWind, millisecondsOverWhichToAverageWind);
|
||||
}
|
||||
BasicDBObject queryByName = new BasicDBObject();
|
||||
queryByName.put(FieldNames.EVENT_NAME.name(), regatta.getName());
|
||||
queryByName.put(FieldNames.EVENT_NAME.name(), regattaName);
|
||||
queryByName.put(FieldNames.RACE_NAME.name(), race.getName());
|
||||
if (constrainToWindSource != null) {
|
||||
queryByName.put(FieldNames.WIND_SOURCE_NAME.name(), constrainToWindSource.name());
|
||||
@@ -759,13 +759,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
for (DBObject dbWind : windFixesFoundByName) {
|
||||
Pair<Wind, WindSource> wind = loadWindFix(result, dbWind, millisecondsOverWhichToAverageWind);
|
||||
// write the wind fix with the new ID-based key and remove the legacy wind fix from the DB
|
||||
windFixesToMigrate.add(new MongoObjectFactoryImpl(database).storeWindTrackEntry(race, regatta.getName(),
|
||||
windFixesToMigrate.add(new MongoObjectFactoryImpl(database).storeWindTrackEntry(race, regattaName,
|
||||
wind.getB(), wind.getA()));
|
||||
}
|
||||
logger.info("Migrating "+windFixesFoundByName.size()+" wind fixes of regatta "+regatta.getName()+
|
||||
logger.info("Migrating "+windFixesFoundByName.size()+" wind fixes of regatta "+regattaName+
|
||||
" and race "+race.getName()+" to ID-based keys");
|
||||
windTracks.insert(windFixesToMigrate.toArray(new DBObject[windFixesToMigrate.size()]));
|
||||
logger.info("Removing "+windFixesFoundByName.size()+" wind fixes that were keyed by the names of regatta "+regatta.getName()+
|
||||
logger.info("Removing "+windFixesFoundByName.size()+" wind fixes that were keyed by the names of regatta "+regattaName+
|
||||
" and race "+race.getName());
|
||||
windTracks.remove(queryByName);
|
||||
}
|
||||
|
||||
+7
-8
@@ -10,7 +10,6 @@ import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoWindStore;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
@@ -48,26 +47,26 @@ public class MongoWindStoreImpl extends EmptyWindStore implements MongoWindStore
|
||||
* additions to the wind track will be written to the MongoDB.
|
||||
*/
|
||||
@Override
|
||||
public WindTrack getWindTrack(TrackedRegatta trackedRegatta, TrackedRace trackedRace, WindSource windSource,
|
||||
public WindTrack getWindTrack(String regattaName, TrackedRace trackedRace, WindSource windSource,
|
||||
long millisecondsOverWhichToAverage, long delayForWindEstimationCacheInvalidation) {
|
||||
WindTrack result;
|
||||
if (windSource.canBeStored()) {
|
||||
result = domainObjectFactory.loadWindTrack(trackedRegatta.getRegatta(), trackedRace.getRace(), windSource,
|
||||
result = domainObjectFactory.loadWindTrack(regattaName, trackedRace.getRace(), windSource,
|
||||
millisecondsOverWhichToAverage);
|
||||
result.addListener(new MongoWindListener(trackedRace, trackedRegatta.getRegatta().getName(), windSource, mongoObjectFactory, db));
|
||||
result.addListener(new MongoWindListener(trackedRace, regattaName, windSource, mongoObjectFactory, db));
|
||||
} else {
|
||||
result = super.getWindTrack(trackedRegatta, trackedRace, windSource, millisecondsOverWhichToAverage, delayForWindEstimationCacheInvalidation);
|
||||
result = super.getWindTrack(regattaName, trackedRace, windSource, millisecondsOverWhichToAverage, delayForWindEstimationCacheInvalidation);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(TrackedRegatta trackedRegatta,
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(String regattaName,
|
||||
TrackedRace trackedRace, long millisecondsOverWhichToAverageWind) {
|
||||
Map<? extends WindSource, ? extends WindTrack> result = domainObjectFactory.loadWindTracks(
|
||||
trackedRegatta.getRegatta(), trackedRace.getRace(), millisecondsOverWhichToAverageWind);
|
||||
regattaName, trackedRace.getRace(), millisecondsOverWhichToAverageWind);
|
||||
for (Entry<? extends WindSource, ? extends WindTrack> e : result.entrySet()) {
|
||||
e.getValue().addListener(new MongoWindListener(trackedRace, trackedRegatta.getRegatta().getName(), e.getKey(), mongoObjectFactory, db));
|
||||
e.getValue().addListener(new MongoWindListener(trackedRace, regattaName, e.getKey(), mongoObjectFactory, db));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,11 +14,11 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.swisstimingadapter.Competitor;
|
||||
|
||||
+8
-9
@@ -1,5 +1,10 @@
|
||||
package com.sap.sailing.domain.swisstimingadapter.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -57,11 +62,6 @@ import com.sap.sailing.mongodb.MongoDBService;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class EndToEndListeningStoreAndFowardTest {
|
||||
private static final Logger logger = Logger.getLogger(EndToEndListeningStoreAndFowardTest.class.getName());
|
||||
|
||||
@@ -77,7 +77,6 @@ public class EndToEndListeningStoreAndFowardTest {
|
||||
private SwissTimingAdapterPersistence swissTimingAdapterPersistence;
|
||||
private SwissTimingFactory swissTimingFactory;
|
||||
|
||||
private EmptyWindStore emptyWindStore;
|
||||
private EmptyRaceLogStore emptyRaceLogStore;
|
||||
private RacingEventService racingEventService;
|
||||
private SwissTimingAdapter swissTimingAdapter;
|
||||
@@ -111,7 +110,7 @@ public class EndToEndListeningStoreAndFowardTest {
|
||||
sendingSocket = new Socket("localhost", RECEIVE_PORT);
|
||||
sendingStream = sendingSocket.getOutputStream();
|
||||
swissTimingFactory = SwissTimingFactory.INSTANCE;
|
||||
emptyWindStore = EmptyWindStore.INSTANCE;
|
||||
EmptyWindStore emptyWindStore = EmptyWindStore.INSTANCE;
|
||||
emptyRaceLogStore = EmptyRaceLogStore.INSTANCE;
|
||||
transceiver = swissTimingFactory.createSailMasterTransceiver();
|
||||
DBCollection lastMessageCountCollection = db.getCollection(CollectionNames.LAST_MESSAGE_COUNT.name());
|
||||
@@ -122,7 +121,7 @@ public class EndToEndListeningStoreAndFowardTest {
|
||||
final com.sap.sailing.domain.base.impl.DomainFactoryImpl baseDomainFactory = new com.sap.sailing.domain.base.impl.DomainFactoryImpl();
|
||||
swissTimingAdapter = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(baseDomainFactory, swissTimingAdapterPersistence);
|
||||
racingEventService = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(
|
||||
mongoDBService, baseDomainFactory), PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoDBService), MediaDB.TEST_STUB);
|
||||
mongoDBService, baseDomainFactory), PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoDBService), MediaDB.TEST_STUB, emptyWindStore);
|
||||
raceHandles = new ArrayList<RacesHandle>();
|
||||
}
|
||||
|
||||
@@ -371,7 +370,7 @@ public class EndToEndListeningStoreAndFowardTest {
|
||||
for (String raceToTrack : racesToTrack) {
|
||||
RacesHandle raceHandle = swissTimingAdapter.addSwissTimingRace(racingEventService,
|
||||
/* regattaToAddTo */ null /* use a default regatta */, raceToTrack, /* canSendRequests */
|
||||
"localhost", CLIENT_PORT, false, emptyWindStore, emptyRaceLogStore, -1);
|
||||
"localhost", CLIENT_PORT, false, emptyRaceLogStore, -1);
|
||||
raceHandles.add(raceHandle);
|
||||
if (connector == null) {
|
||||
connector = swissTimingAdapter.getSwissTimingFactory().getOrCreateSailMasterConnector("localhost",
|
||||
|
||||
+1
-2
@@ -16,7 +16,6 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
|
||||
import difflib.PatchFailedException;
|
||||
|
||||
@@ -56,7 +55,7 @@ public interface DomainFactory {
|
||||
|
||||
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID,
|
||||
boolean canSendRequests, long delayToLiveInMillis,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore, WindStore windStore,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore,
|
||||
RaceSpecificMessageLoader messageLoader);
|
||||
|
||||
RaceType getRaceTypeFromRaceID(String raceID);
|
||||
|
||||
+1
-2
@@ -9,7 +9,6 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.tracking.RacesHandle;
|
||||
import com.sap.sailing.domain.tracking.TrackerManager;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
|
||||
public interface SwissTimingAdapter {
|
||||
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname, int port,
|
||||
@@ -25,7 +24,7 @@ public interface SwissTimingAdapter {
|
||||
* to add the races to. Otherwise, a default regatta as described above will be created and used.
|
||||
*/
|
||||
RacesHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, String hostname,
|
||||
int port, boolean canSendRequests, WindStore windStore, RaceLogStore logStore, long timeoutInMilliseconds)
|
||||
int port, boolean canSendRequests, RaceLogStore logStore, long timeoutInMilliseconds)
|
||||
throws InterruptedException, UnknownHostException, IOException, ParseException, Exception;
|
||||
|
||||
SwissTimingFactory getSwissTimingFactory();
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.ParseException;
|
||||
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
|
||||
+2
-3
@@ -50,7 +50,6 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
|
||||
|
||||
import difflib.PatchFailedException;
|
||||
@@ -329,10 +328,10 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
@Override
|
||||
public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID,
|
||||
boolean canSendRequests, long delayToLiveInMillis,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore, WindStore windStore,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore,
|
||||
RaceSpecificMessageLoader messageLoader) {
|
||||
return new SwissTimingTrackingConnectivityParameters(hostname, port, raceID, canSendRequests, delayToLiveInMillis,
|
||||
swissTimingFactory, domainFactory, raceLogStore, windStore, messageLoader);
|
||||
swissTimingFactory, domainFactory, raceLogStore, messageLoader);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,6 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
@@ -34,6 +33,7 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
|
||||
+2
-3
@@ -19,7 +19,6 @@ import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
|
||||
import com.sap.sailing.domain.tracking.RacesHandle;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackerManager;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
|
||||
public class SwissTimingAdapterImpl implements SwissTimingAdapter {
|
||||
private final SwissTimingFactory swissTimingFactory;
|
||||
@@ -65,11 +64,11 @@ public class SwissTimingAdapterImpl implements SwissTimingAdapter {
|
||||
|
||||
@Override
|
||||
public RacesHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, String hostname,
|
||||
int port, boolean canSendRequests, WindStore windStore, RaceLogStore logStore, long timeoutInMilliseconds)
|
||||
int port, boolean canSendRequests, RaceLogStore logStore, long timeoutInMilliseconds)
|
||||
throws Exception {
|
||||
return trackerManager.addRace(regattaToAddTo, swissTimingDomainFactory.createTrackingConnectivityParameters(hostname, port,
|
||||
raceID, canSendRequests, TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS, swissTimingFactory,
|
||||
swissTimingDomainFactory, logStore, windStore, raceSpecificMessageLoader), windStore,
|
||||
swissTimingDomainFactory, logStore, raceSpecificMessageLoader),
|
||||
timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -6,7 +6,6 @@ import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
+3
-5
@@ -19,12 +19,11 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
|
||||
private final SwissTimingFactory swissTimingFactory;
|
||||
private final DomainFactory domainFactory;
|
||||
private final RaceLogStore raceLogStore;
|
||||
private final WindStore windStore;
|
||||
private final RaceSpecificMessageLoader messageLoader;
|
||||
private final long delayToLiveInMillis;
|
||||
|
||||
public SwissTimingTrackingConnectivityParameters(String hostname, int port, String raceID, boolean canSendRequests, long delayToLiveInMillis,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore, WindStore windStore,
|
||||
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore,
|
||||
RaceSpecificMessageLoader messageLoader) {
|
||||
super();
|
||||
this.hostname = hostname;
|
||||
@@ -35,18 +34,17 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
|
||||
this.swissTimingFactory = swissTimingFactory;
|
||||
this.domainFactory = domainFactory;
|
||||
this.raceLogStore = raceLogStore;
|
||||
this.windStore = windStore;
|
||||
this.messageLoader = messageLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry) throws Exception {
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore) throws Exception {
|
||||
return swissTimingFactory.createRaceTracker(raceID, hostname, port, canSendRequests, delayToLiveInMillis, raceLogStore, windStore, messageLoader,
|
||||
domainFactory, trackedRegattaRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry)
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore)
|
||||
throws Exception {
|
||||
return swissTimingFactory.createRaceTracker(regatta, raceID, hostname, port, canSendRequests, delayToLiveInMillis, windStore, messageLoader,
|
||||
domainFactory, trackedRegattaRegistry);
|
||||
|
||||
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -18,6 +19,7 @@ import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
@@ -25,6 +27,7 @@ import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
@@ -33,6 +36,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
|
||||
import difflib.PatchFailedException;
|
||||
|
||||
@@ -245,12 +249,12 @@ public class CourseTest {
|
||||
Course course = new CourseImpl("Test Course", waypoints);
|
||||
assertWaypointIndexes(course);
|
||||
final Set<CompetitorImpl> hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso"));
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ null,
|
||||
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true),
|
||||
hasso), Collections.<Sideline> emptyList(),
|
||||
EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000,
|
||||
/* millisecondsOverWhichToAverageWind */ 30000,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 8000);
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new TrackedRegattaImpl(
|
||||
new RegattaImpl("test", null, new HashSet<Series>(), false, null, "test", null)),
|
||||
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso),
|
||||
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */3000,
|
||||
/* millisecondsOverWhichToAverageWind */30000,
|
||||
/* millisecondsOverWhichToAverageSpeed */8000);
|
||||
assertLegStructure(course, trackedRace);
|
||||
course.removeWaypoint(0);
|
||||
assertLegStructure(course, trackedRace);
|
||||
@@ -275,7 +279,8 @@ public class CourseTest {
|
||||
waypoints.add(wp2);
|
||||
Course course = new CourseImpl("Test Course", waypoints);
|
||||
final Set<CompetitorImpl> hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso"));
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ null,
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ new TrackedRegattaImpl(
|
||||
new RegattaImpl("test", null, new HashSet<Series>(), false, null, "test", null)),
|
||||
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true),
|
||||
hasso), Collections.<Sideline> emptyList(),
|
||||
EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000,
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.util.AutoCloseableReadLock;
|
||||
import com.sap.sailing.util.AutoCloseableWriteLock;
|
||||
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
|
||||
public class LockAsResourceTest {
|
||||
@Test
|
||||
public void testTryWithReadLockAsResource() {
|
||||
NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("Test Lock", /* fair */ false);
|
||||
try (AutoCloseableReadLock readlock = new AutoCloseableReadLock(lock)) {
|
||||
assertEquals(1, lock.getReadHoldCount());
|
||||
}
|
||||
assertEquals(0, lock.getReadHoldCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryWithWriteLockAsResource() {
|
||||
NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("Test Lock", /* fair */ false);
|
||||
try (AutoCloseableWriteLock readlock = new AutoCloseableWriteLock(lock)) {
|
||||
assertEquals(1, lock.getWriteHoldCount());
|
||||
}
|
||||
assertEquals(0, lock.getWriteHoldCount());
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import com.sap.sailing.domain.base.Boat;
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.BoatImpl;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
@@ -35,6 +34,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.impl.MeterPerSecondSpeedWithDegreeBearingImpl;
|
||||
|
||||
+10
-4
@@ -1,21 +1,25 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
@@ -24,6 +28,7 @@ import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
|
||||
public class UpdateMarkPassingTest {
|
||||
@Test
|
||||
@@ -37,9 +42,10 @@ public class UpdateMarkPassingTest {
|
||||
when(race.getBoatClass()).thenReturn(new BoatClassImpl("49er", /* typicallyStartsUpwind */ true));
|
||||
when(race.getCompetitors()).thenReturn(Collections.singleton(competitor));
|
||||
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(
|
||||
/* trackedRegatta */ null, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
|
||||
/* delayToLiveInMillis */ 1000, /* millisecondsOverWhichToAverageWind */ 30000,
|
||||
/* millisecondsOverWhichToAverageSpeed */ 30000);
|
||||
/* trackedRegatta */new TrackedRegattaImpl(new RegattaImpl("test", null, new HashSet<Series>(), false, null,
|
||||
"test", null)), race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
|
||||
/* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000,
|
||||
/* millisecondsOverWhichToAverageSpeed */30000);
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = now.plus(1000);
|
||||
trackedRace.updateMarkPassings(competitor, Arrays.asList(new MarkPassing[] { new MarkPassingImpl(now, waypoint, competitor) }));
|
||||
|
||||
+1
-1
@@ -247,7 +247,7 @@ public interface DomainFactory {
|
||||
Pair<Iterable<com.sap.sailing.domain.base.Competitor>, BoatClass> getCompetitorsAndDominantBoatClass(Race race);
|
||||
|
||||
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
|
||||
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, RaceLogStore raceLogStore, WindStore windStore,
|
||||
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, RaceLogStore raceLogStore,
|
||||
String tracTracUsername, String tracTracPassword);
|
||||
/**
|
||||
* Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ public interface TracTracAdapter {
|
||||
* is stopped; use -1 to wait forever
|
||||
*/
|
||||
RacesHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
|
||||
URI courseDesignUpdateURI, RaceLogStore raceLogStore, WindStore windStore, long timeoutInMilliseconds,
|
||||
URI courseDesignUpdateURI, RaceLogStore raceLogStore, long timeoutInMilliseconds,
|
||||
String tracTracUsername, String tracTracPassword) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ public interface TracTracAdapter {
|
||||
*/
|
||||
RacesHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI,
|
||||
URI storedURI, URI courseDesignUpdateURI, TimePoint trackingStartTime,
|
||||
TimePoint trackingEndTime, RaceLogStore raceLogStore, WindStore windStore,
|
||||
TimePoint trackingEndTime, RaceLogStore raceLogStore,
|
||||
long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow, String tracTracUsername, String tracTracPassword)
|
||||
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
|
||||
|
||||
|
||||
+2
-2
@@ -632,9 +632,9 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
@Override
|
||||
public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI,
|
||||
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
|
||||
boolean simulateWithStartTimeNow, RaceLogStore raceLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword) {
|
||||
boolean simulateWithStartTimeNow, RaceLogStore raceLogStore, String tracTracUsername, String tracTracPassword) {
|
||||
return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking,
|
||||
delayToLiveInMillis, simulateWithStartTimeNow, raceLogStore, windStore, this, tracTracUsername, tracTracPassword);
|
||||
delayToLiveInMillis, simulateWithStartTimeNow, raceLogStore, this, tracTracUsername, tracTracPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-6
@@ -6,7 +6,6 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
@@ -25,7 +24,6 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
|
||||
private final TimePoint startOfTracking;
|
||||
private final TimePoint endOfTracking;
|
||||
private final RaceLogStore raceLogStore;
|
||||
private final WindStore windStore;
|
||||
private final DomainFactory domainFactory;
|
||||
private final long delayToLiveInMillis;
|
||||
private final boolean simulateWithStartTimeNow;
|
||||
@@ -34,7 +32,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
|
||||
|
||||
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking,
|
||||
TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow,
|
||||
RaceLogStore raceLogStore, WindStore windStore, DomainFactory domainFactory, String tracTracUsername, String tracTracPassword) {
|
||||
RaceLogStore raceLogStore, DomainFactory domainFactory, String tracTracUsername, String tracTracPassword) {
|
||||
super();
|
||||
this.paramURL = paramURL;
|
||||
this.liveURI = liveURI;
|
||||
@@ -43,7 +41,6 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
|
||||
this.startOfTracking = startOfTracking;
|
||||
this.endOfTracking = endOfTracking;
|
||||
this.delayToLiveInMillis = delayToLiveInMillis;
|
||||
this.windStore = windStore;
|
||||
this.domainFactory = domainFactory;
|
||||
this.simulateWithStartTimeNow = simulateWithStartTimeNow;
|
||||
this.raceLogStore = raceLogStore;
|
||||
@@ -52,7 +49,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry) throws MalformedURLException,
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore) throws MalformedURLException,
|
||||
FileNotFoundException, URISyntaxException {
|
||||
RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
|
||||
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, raceLogStore, windStore, tracTracUsername, tracTracPassword, trackedRegattaRegistry);
|
||||
@@ -60,7 +57,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry)
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore)
|
||||
throws Exception {
|
||||
RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
|
||||
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, raceLogStore, windStore, tracTracUsername, tracTracPassword, trackedRegattaRegistry);
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
@@ -68,7 +68,7 @@ public class Simulator {
|
||||
private void startWindPlayer() {
|
||||
assert this.trackedRace != null;
|
||||
for (final Map.Entry<? extends WindSource, ? extends WindTrack> windSourceAndTrack : windStore.loadWindTracks(
|
||||
trackedRace.getTrackedRegatta(), trackedRace,
|
||||
trackedRace.getTrackedRegatta().getRegatta().getName(), trackedRace,
|
||||
/* millisecondsOverWhichToAverageWind doesn't matter because we only use raw fixes */ 10000).entrySet()) {
|
||||
new Thread("Wind simulator for wind source "+windSourceAndTrack.getKey()+" for tracked race "+trackedRace.getRace().getName()) {
|
||||
@Override
|
||||
|
||||
+6
-6
@@ -47,7 +47,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
|
||||
|
||||
@Override
|
||||
public RacesHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
|
||||
URI courseDesignUpdateURI, RaceLogStore raceLogStore, WindStore windStore, long timeoutInMilliseconds,
|
||||
URI courseDesignUpdateURI, RaceLogStore raceLogStore, long timeoutInMilliseconds,
|
||||
String tracTracUsername, String tracTracPassword) throws Exception {
|
||||
return trackerManager.addRace(
|
||||
/* regattaToAddTo */null,
|
||||
@@ -55,20 +55,20 @@ public class TracTracAdapterImpl implements TracTracAdapter {
|
||||
courseDesignUpdateURI,
|
||||
/* startOfTracking */null,
|
||||
/* endOfTracking */null, delayToLiveInMillis, /* simulateWithStartTimeNow */false,
|
||||
raceLogStore, windStore, tracTracUsername, tracTracPassword), windStore, timeoutInMilliseconds);
|
||||
raceLogStore, tracTracUsername, tracTracPassword), timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RacesHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo,
|
||||
URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking,
|
||||
TimePoint endOfTracking, RaceLogStore raceLogStore, WindStore windStore,
|
||||
TimePoint endOfTracking, RaceLogStore raceLogStore,
|
||||
long timeoutInMilliseconds, boolean simulateWithStartTimeNow, String tracTracUsername, String tracTracPassword) throws Exception {
|
||||
return trackerManager.addRace(
|
||||
regattaToAddTo,
|
||||
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
|
||||
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
|
||||
simulateWithStartTimeNow, raceLogStore, windStore, tracTracUsername, tracTracPassword),
|
||||
windStore, timeoutInMilliseconds);
|
||||
simulateWithStartTimeNow, raceLogStore, tracTracUsername, tracTracPassword),
|
||||
timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,7 +88,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
|
||||
for (RaceRecord rr : jsonService.getRaceRecords()) {
|
||||
URL paramURL = rr.getParamURL();
|
||||
regatta = addTracTracRace(trackerManager, paramURL, liveURI, storedURI, courseDesignUpdateURI, raceLogStore,
|
||||
windStore, timeoutInMilliseconds, tracTracUsername, tracTracPassword).getRegatta();
|
||||
timeoutInMilliseconds, tracTracUsername, tracTracPassword).getRegatta();
|
||||
}
|
||||
return regatta;
|
||||
}
|
||||
|
||||
+27
-12
@@ -251,7 +251,11 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
}
|
||||
addListenersForStoredDataAndStartController(typeControllers);
|
||||
// Read event data from configuration file
|
||||
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI, delayToLiveInMillis, tracTracUsername, tracTracPassword);
|
||||
synchronized (this) {
|
||||
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI,
|
||||
delayToLiveInMillis, tracTracUsername, tracTracPassword);
|
||||
notifyAll(); // the stop(boolean) method will try to cancel the controlPointPositionPoller; this may happen even before the above assignment took place; synchronize!
|
||||
}
|
||||
}
|
||||
|
||||
private URI checkForCachedStoredData(URI storedURI){
|
||||
@@ -584,12 +588,28 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
stop(/* stop receivers preemtively */ true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // explicitly calling Thread.stop in case IO thread didn't join in three seconds time
|
||||
private void stop(boolean stopReceiversPreemtively) throws InterruptedException {
|
||||
synchronized (this) {
|
||||
while (controlPointPositionPoller == null) {
|
||||
wait(); // constructor will notify all waiters once the controlPointPositionPoller is set
|
||||
}
|
||||
}
|
||||
controlPointPositionPoller.cancel(/* mayInterruptIfRunning */ false);
|
||||
new Thread("TracTrac Controller Stopper for "+getID()) {
|
||||
public void run() {
|
||||
controller.stop(/* abortStored */ true);
|
||||
try {
|
||||
ioThread.join();
|
||||
if (ioThread.isAlive()) {
|
||||
logger.severe("Tractrac IO thread in tracker "+getID()+" for race(s) "+getRaces()+" joined but is still active. Very strange.");
|
||||
} else {
|
||||
logger.info("Joined TracTrac IO thread in tracker "+getID()+" for race(s) "+getRaces());
|
||||
}
|
||||
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, /* will be ignored */ 1.0);
|
||||
updateStatusOfTrackedRaces();
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.INFO, "Interrupted while trying to join TracTrac DataController thread for "+getID());
|
||||
} // wait no more than three seconds
|
||||
}
|
||||
}.start();
|
||||
for (Receiver receiver : receivers) {
|
||||
@@ -599,15 +619,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
receiver.stopAfterProcessingQueuedEvents();
|
||||
}
|
||||
}
|
||||
ioThread.join(3000); // wait no more than three seconds
|
||||
if (ioThread.isAlive()) {
|
||||
ioThread.stop();
|
||||
logger.warning("Tractrac IO thread in tracker "+getID()+" for race(s) "+getRaces()+" didn't join in 3s. Stopped forcefully.");
|
||||
} else {
|
||||
logger.info("Joined TracTrac IO thread in tracker "+getID()+" for race(s) "+getRaces());
|
||||
}
|
||||
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, /* will be ignored */ 1.0);
|
||||
updateStatusOfTrackedRaces();
|
||||
}
|
||||
|
||||
protected DataController getController() {
|
||||
@@ -686,7 +697,9 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
|
||||
@Override
|
||||
public void storedDataProgress(float progress) {
|
||||
logger.info("Stored data progress in tracker "+getID()+" for race(s) "+getRaces()+": "+progress);
|
||||
if (lastStatus.getStatus().equals(TrackedRaceStatusEnum.ERROR)) {
|
||||
return;
|
||||
}
|
||||
Integer counter = 0;
|
||||
final Pair<Integer, Float> lastProgressPair = lastProgressPerID.get(getID());
|
||||
if (lastProgressPair != null) {
|
||||
@@ -700,6 +713,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
/* make sure to indicate that this race is erroneous */
|
||||
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.ERROR, 0.0);
|
||||
updateStatusOfTrackedRaces();
|
||||
return;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -708,6 +722,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("Stored data progress in tracker "+getID()+" for race(s) "+getRaces()+": "+progress);
|
||||
lastStatus = new TrackedRaceStatusImpl(progress==1.0 ? TrackedRaceStatusEnum.TRACKING : TrackedRaceStatusEnum.LOADING, progress);
|
||||
lastProgressPerID.put(getID(), new Pair<Integer, Float>(counter, progress));
|
||||
updateStatusOfTrackedRaces();
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.sap.sailing.util.impl.RaceColumnListeners;
|
||||
public interface RaceColumn extends Named {
|
||||
/**
|
||||
* Sets the information object used to access the race column's race logs.
|
||||
* @param information
|
||||
*/
|
||||
void setRaceLogInformation(RaceLogInformation information);
|
||||
|
||||
|
||||
+1
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.base.impl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.RadianBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
+1
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.base.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.SpeedWithConfidence;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.confidence.ScalableValue;
|
||||
|
||||
public class SpeedWithConfidenceImpl<RelativeTo> extends HasConfidenceImpl<Double, Speed, RelativeTo> implements SpeedWithConfidence<RelativeTo> {
|
||||
|
||||
+8
-1
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.domain.masterdataimport;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
|
||||
@@ -9,14 +10,16 @@ public class RaceColumnMasterData {
|
||||
private final boolean medal;
|
||||
private final Map<String, RaceIdentifier> raceIdentifiersByFleetName;
|
||||
private Double factor;
|
||||
private Set<WindTrackMasterData> windTracks;
|
||||
|
||||
public RaceColumnMasterData(String name, boolean medal, Map<String, RaceIdentifier> raceIdentifiersByFleetName,
|
||||
Double factor) {
|
||||
Double factor, Set<WindTrackMasterData> windTracks) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.medal = medal;
|
||||
this.raceIdentifiersByFleetName = raceIdentifiersByFleetName;
|
||||
this.factor = factor;
|
||||
this.windTracks = windTracks;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -34,5 +37,9 @@ public class RaceColumnMasterData {
|
||||
public Double getFactor() {
|
||||
return factor;
|
||||
}
|
||||
|
||||
public Set<WindTrackMasterData> getWindTrackMasterData() {
|
||||
return windTracks;
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.sap.sailing.domain.masterdataimport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
|
||||
public class WindTrackMasterData {
|
||||
|
||||
private final WindSourceType windSourceType;
|
||||
private final Serializable windSourceId;
|
||||
private final Set<Wind> fixes;
|
||||
private final String regattaName;
|
||||
private final String raceName;
|
||||
private final Serializable raceId;
|
||||
|
||||
public WindTrackMasterData(String windSourceTypeName, Serializable windSourceId, Set<Wind> fixes,
|
||||
String regattaName, String raceName, Serializable raceId) {
|
||||
this.windSourceId = windSourceId;
|
||||
this.fixes = fixes;
|
||||
this.regattaName = regattaName;
|
||||
this.raceName = raceName;
|
||||
this.raceId = raceId;
|
||||
this.windSourceType = WindSourceType.valueOf(windSourceTypeName);
|
||||
}
|
||||
|
||||
public WindSourceType getWindSourceType() {
|
||||
return windSourceType;
|
||||
}
|
||||
|
||||
public Serializable getWindSourceId() {
|
||||
return windSourceId;
|
||||
}
|
||||
|
||||
public Set<Wind> getFixes() {
|
||||
return fixes;
|
||||
}
|
||||
|
||||
public String getRegattaName() {
|
||||
return regattaName;
|
||||
}
|
||||
|
||||
public String getRaceName() {
|
||||
return raceName;
|
||||
}
|
||||
|
||||
public Serializable getRaceId() {
|
||||
return raceId;
|
||||
}
|
||||
|
||||
|
||||
public WindSource getWindSource() {
|
||||
WindSource source;
|
||||
if (windSourceId != null) {
|
||||
source = new WindSourceWithAdditionalID(windSourceType, windSourceId.toString());
|
||||
} else {
|
||||
source = new WindSourceImpl(windSourceType);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
+21
-18
@@ -107,26 +107,29 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable
|
||||
} else {
|
||||
GPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(competitor);
|
||||
track.lockForRead();
|
||||
Iterator<GPSFixMoving> fixesIterator = track.getFixesIterator(startTime, true);
|
||||
TimePoint lastConsideredTimePoint = null;
|
||||
if (finishedEarlyAtWaypoint != -1) {
|
||||
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
|
||||
MarkPassing lastConsideredPassing = markPassings.last();
|
||||
lastConsideredPassing = markPassings.lower(lastConsideredPassing);
|
||||
lastConsideredTimePoint = lastConsideredPassing.getTimePoint();
|
||||
}
|
||||
boolean reachedEnd = false;
|
||||
while (fixesIterator.hasNext() && reachedEnd == false) {
|
||||
GPSFixMoving fix = fixesIterator.next();
|
||||
if (fix.getTimePoint().after(endTime)
|
||||
|| (lastConsideredTimePoint != null && fix.getTimePoint().after(lastConsideredTimePoint))) {
|
||||
reachedEnd = true;
|
||||
} else {
|
||||
addFixIfValid(track, fix);
|
||||
try {
|
||||
Iterator<GPSFixMoving> fixesIterator = track.getFixesIterator(startTime, true);
|
||||
TimePoint lastConsideredTimePoint = null;
|
||||
if (finishedEarlyAtWaypoint != -1) {
|
||||
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
|
||||
MarkPassing lastConsideredPassing = markPassings.last();
|
||||
lastConsideredPassing = markPassings.lower(lastConsideredPassing);
|
||||
lastConsideredTimePoint = lastConsideredPassing.getTimePoint();
|
||||
}
|
||||
boolean reachedEnd = false;
|
||||
while (fixesIterator.hasNext() && reachedEnd == false) {
|
||||
GPSFixMoving fix = fixesIterator.next();
|
||||
if (fix.getTimePoint().after(endTime)
|
||||
|| (lastConsideredTimePoint != null && fix.getTimePoint().after(lastConsideredTimePoint))) {
|
||||
reachedEnd = true;
|
||||
} else {
|
||||
addFixIfValid(track, fix);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
done = true;
|
||||
}
|
||||
track.unlockAfterRead();
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,16 +37,20 @@ public class PolarFix {
|
||||
this.windSpeed = windSpeed;
|
||||
Set<WindSource> windSourcesToExclude;
|
||||
if (settings.useOnlyEstimatedForWindDirection()) {
|
||||
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, true);
|
||||
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, /* exclude course based */ true);
|
||||
} else {
|
||||
windSourcesToExclude = new HashSet<WindSource>();
|
||||
}
|
||||
|
||||
Wind windEstimated = race.getWind(position, fix.getTimePoint(), windSourcesToExclude);
|
||||
if (windEstimated == null) {
|
||||
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, false);
|
||||
// no estimated wind; try to include course layout
|
||||
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, /* exclude course based */ false);
|
||||
windEstimated = race.getWind(position, fix.getTimePoint(), windSourcesToExclude);
|
||||
}
|
||||
if (windEstimated == null) {
|
||||
windEstimated = windSpeed; // maybe no upwind start; need to default to measured wind speed/direction
|
||||
}
|
||||
Bearing windBearing = windEstimated.getFrom();
|
||||
angleToWind = bearing.getDifferenceTo(windBearing).getDegrees();
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ public interface RaceTrackingConnectivityParameters {
|
||||
/**
|
||||
* Starts a {@link RaceTracker} using the connectivity parameters provided by this object.
|
||||
*/
|
||||
RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry) throws Exception;
|
||||
RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore) throws Exception;
|
||||
|
||||
/**
|
||||
* Starts a {@link RaceTracker}, associating the resulting races with the {@link Regatta} passed as argument
|
||||
@@ -28,7 +28,7 @@ public interface RaceTrackingConnectivityParameters {
|
||||
* parameters. This is particularly useful if a predefined regatta with {@link Series} and {@link Fleet}s
|
||||
* is to be used.
|
||||
*/
|
||||
RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry) throws Exception;
|
||||
RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore) throws Exception;
|
||||
|
||||
/**
|
||||
* Deliver an ID object equal to that of the {@link RaceTracker#getID()} delivered by the {@link RaceTracker}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.tracking.impl.CombinedWindTrackImpl;
|
||||
import com.sap.sailing.util.impl.LockUtil;
|
||||
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
|
||||
|
||||
public abstract class TrackedRaceWithWindEssentials implements TrackedRace {
|
||||
|
||||
private static final long serialVersionUID = -3937032323498604671L;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TrackedRaceWithWindEssentials.class.getName());
|
||||
|
||||
/**
|
||||
* Serializing an instance of this class has to serialized the various data structures holding the tracked race's
|
||||
* state. When a race is currently on, these structures change very frequently, and
|
||||
* {@link ConcurrentModificationException}s during serialization will be the norm rather than the exception. To
|
||||
* avoid this, all modifications to any data structure that is not in itself synchronized obtains this lock's
|
||||
* <em>read</em> lock (note that this may be confusing at first, but we'd like to support many concurrent writers;
|
||||
* they each perform their own locking on the individual data structures they write; we only want to lock out a
|
||||
* single serialization call which with this lock is represented as the "writer"). The serialization method
|
||||
* {@link #writeObject(ObjectOutputStream)} obtains the <em>write</em> lock. Deadlocks are avoided because the
|
||||
* serialization, once it obtains this write lock, it keeps serializing and releases the write lock when it's done,
|
||||
* without doing any further synchronization or locking.
|
||||
*/
|
||||
private final NamedReentrantReadWriteLock serializationLock;
|
||||
|
||||
private transient CombinedWindTrackImpl combinedWindTrack;
|
||||
|
||||
/**
|
||||
* A tracked race can maintain a number of sources for wind information from which a client can select. As all
|
||||
* intra-leg computations are done dynamically based on wind information, selecting a different wind information
|
||||
* source can alter the intra-leg results. See {@link #currentWindSource}.
|
||||
*/
|
||||
protected final Map<WindSource, WindTrack> windTracks;
|
||||
|
||||
protected final RaceDefinition race;
|
||||
|
||||
protected transient WindStore windStore;
|
||||
|
||||
protected final TrackedRegatta trackedRegatta;
|
||||
|
||||
protected long millisecondsOverWhichToAverageWind;
|
||||
|
||||
public TrackedRaceWithWindEssentials(final RaceDefinition race, final TrackedRegatta trackedRegatta, final WindStore windStore,
|
||||
final long millisecondsOverWhichToAverageWind) {
|
||||
this.race = race;
|
||||
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
|
||||
windTracks = new ConcurrentHashMap<WindSource, WindTrack>();
|
||||
this.windStore = windStore;
|
||||
this.trackedRegatta = trackedRegatta;
|
||||
this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race "
|
||||
+ race.getName(), /* fair */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* For wind sources of the special type {@link WindSourceType#COMBINED}, emits a new {@link CombinedWindTrackImpl}
|
||||
* which will not be added to {@link #windTracks} and will not lead to the wind source being listed in
|
||||
* {@link #getWindSources()} or {@link #getWindSources(WindSourceType)}. For all other wind sources, checks
|
||||
* {@link #windTracks} for the respective source. If found, it's returned; otherwise the wind track is created
|
||||
* through the {@link #windStore} using {@link #createWindTrack(WindSource, long)} and added to {@link #windTracks}
|
||||
* before being returned.
|
||||
*
|
||||
* @param delayForWindEstimationCacheInvalidation
|
||||
* if <code>-1</code> and the parameter is accessed, it will be replaced by
|
||||
* {@link #getMillisecondsOverWhichToAverageWind()}/2
|
||||
*/
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
|
||||
WindTrack result;
|
||||
if (windSource.getType() == WindSourceType.COMBINED) {
|
||||
if (combinedWindTrack == null) {
|
||||
combinedWindTrack = new CombinedWindTrackImpl(this, WindSourceType.COMBINED.getBaseConfidence());
|
||||
}
|
||||
result = combinedWindTrack;
|
||||
} else {
|
||||
synchronized (windTracks) {
|
||||
result = windTracks.get(windSource);
|
||||
if (result == null) {
|
||||
result = createWindTrack(windSource,
|
||||
delayForWindEstimationCacheInvalidation == -1 ? getMillisecondsOverWhichToAverageWind() / 2
|
||||
: delayForWindEstimationCacheInvalidation);
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
try {
|
||||
windTracks.put(windSource, result);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(getSerializationLock());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource) {
|
||||
return getOrCreateWindTrack(windSource, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wind track for the <code>windSource</code> specified and stores it in {@link #windTracks}. The
|
||||
* averaging interval is set according to the averaging interval set for all other wind sources, or the default if
|
||||
* no other wind source exists yet.
|
||||
*/
|
||||
protected WindTrack createWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
|
||||
WindTrack result = windStore.getWindTrack(trackedRegatta.getRegatta().getName(), this, windSource, millisecondsOverWhichToAverageWind,
|
||||
delayForWindEstimationCacheInvalidation);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WindSource> getWindSources(WindSourceType type) {
|
||||
Set<WindSource> result = new HashSet<WindSource>();
|
||||
for (WindSource windSource : getWindSources()) {
|
||||
if (windSource.getType() == type) {
|
||||
result.add(windSource);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WindSource> getWindSources() {
|
||||
while (true) {
|
||||
try {
|
||||
return new HashSet<WindSource>(windTracks.keySet());
|
||||
} catch (ConcurrentModificationException cme) {
|
||||
logger.info("Caught " + cme + "; trying again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected NamedReentrantReadWriteLock getSerializationLock() {
|
||||
return serializationLock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -4,8 +4,9 @@ import java.io.FileNotFoundException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
|
||||
public interface TrackerManager {
|
||||
|
||||
@@ -18,10 +19,8 @@ public interface TrackerManager {
|
||||
* identifier; if the regatta is found, it is used to add the races to, and
|
||||
* {@link #setRegattaForRace(Regatta, RaceDefinition)} is called to remember the association
|
||||
* persistently. Otherwise, a default regatta as described above will be created and used.
|
||||
* @param windStore
|
||||
* must not be <code>null</code>, but can, e.g., be an {@link EmptyWindStore}
|
||||
*/
|
||||
RacesHandle addRace(RegattaIdentifier regattaToAddTo, RaceTrackingConnectivityParameters params, WindStore windStore, long timeoutInMilliseconds)
|
||||
RacesHandle addRace(RegattaIdentifier regattaToAddTo, RaceTrackingConnectivityParameters params, long timeoutInMilliseconds)
|
||||
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.WindSource;
|
||||
*
|
||||
*/
|
||||
public interface WindStore {
|
||||
WindTrack getWindTrack(TrackedRegatta trackedRegatta, TrackedRace trackedRace, WindSource windSource,
|
||||
WindTrack getWindTrack(String regattaName, TrackedRace trackedRace, WindSource windSource,
|
||||
long millisecondsOverWhichToAverage, long delayForWindEstimationCacheInvalidation);
|
||||
|
||||
/**
|
||||
@@ -22,6 +22,6 @@ public interface WindStore {
|
||||
*
|
||||
* @return a map that is never <code>null</code> but may be empty
|
||||
*/
|
||||
Map<? extends WindSource, ? extends WindTrack> loadWindTracks(TrackedRegatta trackedRegatta,
|
||||
Map<? extends WindSource, ? extends WindTrack> loadWindTracks(String regattaName,
|
||||
TrackedRace trackedRace, long millisecondsOverWhichToAverageWind);
|
||||
}
|
||||
+2
-3
@@ -5,7 +5,6 @@ import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
@@ -14,7 +13,7 @@ public class EmptyWindStore implements WindStore {
|
||||
public static EmptyWindStore INSTANCE = new EmptyWindStore();
|
||||
|
||||
@Override
|
||||
public WindTrack getWindTrack(TrackedRegatta trackedRegatta, TrackedRace trackedRace, WindSource windSource,
|
||||
public WindTrack getWindTrack(String regattaName, TrackedRace trackedRace, WindSource windSource,
|
||||
long millisecondsOverWhichToAverage, long delayForWindEstimationCacheInvalidation) {
|
||||
switch (windSource.getType()) {
|
||||
case COURSE_BASED:
|
||||
@@ -28,7 +27,7 @@ public class EmptyWindStore implements WindStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(TrackedRegatta trackedRegatta,
|
||||
public Map<? extends WindSource, ? extends WindTrack> loadWindTracks(String regattaName,
|
||||
TrackedRace trackedRace, long millisecondsOverWhichToAverageWind) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,6 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
|
||||
import com.sap.sailing.domain.base.SpeedWithConfidence;
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.base.impl.BearingWithConfidenceImpl;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl;
|
||||
import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
@@ -29,6 +28,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
|
||||
+1
-1
@@ -9,7 +9,6 @@ import java.util.NavigableSet;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
@@ -18,6 +17,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
+4
-123
@@ -86,8 +86,8 @@ import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceWithWindEssentials;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
@@ -100,7 +100,7 @@ import com.sap.sailing.util.impl.ArrayListNavigableSet;
|
||||
import com.sap.sailing.util.impl.LockUtil;
|
||||
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
|
||||
public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials implements CourseListener {
|
||||
private static final long serialVersionUID = -4825546964220003507L;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TrackedRaceImpl.class.getName());
|
||||
@@ -110,10 +110,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
// TODO make this variable
|
||||
private static final long DELAY_FOR_CACHE_CLEARING_IN_MILLISECONDS = 7500;
|
||||
|
||||
private final RaceDefinition race;
|
||||
|
||||
private final TrackedRegatta trackedRegatta;
|
||||
|
||||
private TrackedRaceStatus status;
|
||||
|
||||
private final Object statusNotifier;
|
||||
@@ -203,13 +199,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
*/
|
||||
private transient SmartFutureCache<Competitor, Triple<TimePoint, TimePoint, List<Maneuver>>, EmptyUpdateInterval> maneuverCache;
|
||||
|
||||
/**
|
||||
* A tracked race can maintain a number of sources for wind information from which a client can select. As all
|
||||
* intra-leg computations are done dynamically based on wind information, selecting a different wind information
|
||||
* source can alter the intra-leg results. See {@link #currentWindSource}.
|
||||
*/
|
||||
private final Map<WindSource, WindTrack> windTracks;
|
||||
|
||||
private transient Map<TimePoint, Future<Wind>> directionFromStartToNextMarkCache;
|
||||
|
||||
private final ConcurrentHashMap<Mark, GPSFixTrack<Mark, GPSFix>> markTracks;
|
||||
@@ -220,15 +209,9 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
|
||||
private final Map<Mark, StartToNextMarkCacheInvalidationListener> startToNextMarkCacheInvalidationListeners;
|
||||
|
||||
protected long millisecondsOverWhichToAverageWind;
|
||||
|
||||
private transient WindStore windStore;
|
||||
|
||||
private transient Timer cacheInvalidationTimer;
|
||||
private transient Object cacheInvalidationTimerLock;
|
||||
|
||||
private transient CombinedWindTrackImpl combinedWindTrack;
|
||||
|
||||
protected transient HashMap<Serializable, RaceLog> attachedRaceLogs;
|
||||
|
||||
/**
|
||||
@@ -244,20 +227,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
private WindLoadingState windLoadingCompleted;
|
||||
|
||||
private transient CrossTrackErrorCache crossTrackErrorCache;
|
||||
|
||||
/**
|
||||
* Serializing an instance of this class has to serialized the various data structures holding the tracked race's
|
||||
* state. When a race is currently on, these structures change very frequently, and
|
||||
* {@link ConcurrentModificationException}s during serialization will be the norm rather than the exception. To
|
||||
* avoid this, all modifications to any data structure that is not in itself synchronized obtains this lock's
|
||||
* <em>read</em> lock (note that this may be confusing at first, but we'd like to support many concurrent writers;
|
||||
* they each perform their own locking on the individual data structures they write; we only want to lock out a
|
||||
* single serialization call which with this lock is represented as the "writer"). The serialization method
|
||||
* {@link #writeObject(ObjectOutputStream)} obtains the <em>write</em> lock. Deadlocks are avoided because the
|
||||
* serialization, once it obtains this write lock, it keeps serializing and releases the write lock when it's done,
|
||||
* without doing any further synchronization or locking.
|
||||
*/
|
||||
private final NamedReentrantReadWriteLock serializationLock;
|
||||
|
||||
/**
|
||||
* Wind loading is started in a background thread during object construction. If a client needs to ensure that wind loading
|
||||
@@ -271,23 +240,18 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
public TrackedRaceImpl(final TrackedRegatta trackedRegatta, RaceDefinition race, final Iterable<Sideline> sidelines, final WindStore windStore,
|
||||
long delayToLiveInMillis, final long millisecondsOverWhichToAverageWind,
|
||||
long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) {
|
||||
super();
|
||||
super(race, trackedRegatta, windStore, millisecondsOverWhichToAverageWind);
|
||||
locksForMarkPassings = new IdentityHashMap<>();
|
||||
attachedRaceLogs = new HashMap<>();
|
||||
this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0);
|
||||
this.statusNotifier = new Object[0];
|
||||
this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race "
|
||||
+ race.getName(), /* fair */ true);
|
||||
this.windLoadingLock = new NamedReentrantReadWriteLock("Wind loading lock for tracked race "
|
||||
+ race.getName(), /* fair */ false);
|
||||
this.cacheInvalidationTimerLock = new Object();
|
||||
this.updateCount = 0;
|
||||
this.race = race;
|
||||
this.windStore = windStore;
|
||||
this.windSourcesToExclude = new HashSet<WindSource>();
|
||||
this.directionFromStartToNextMarkCache = new HashMap<TimePoint, Future<Wind>>();
|
||||
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
|
||||
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
|
||||
this.delayToLiveInMillis = delayToLiveInMillis;
|
||||
this.startToNextMarkCacheInvalidationListeners = new ConcurrentHashMap<Mark, TrackedRaceImpl.StartToNextMarkCacheInvalidationListener>();
|
||||
this.maneuverCache = createManeuverCache();
|
||||
@@ -337,7 +301,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
MarkPassingByTimeComparator.INSTANCE));
|
||||
}
|
||||
markPassingsTimes = new ArrayList<Pair<Waypoint, Pair<TimePoint, TimePoint>>>();
|
||||
windTracks = new ConcurrentHashMap<WindSource, WindTrack>();
|
||||
windLoadingCompleted = WindLoadingState.NOT_STARTED;
|
||||
// When this tracked race is to be serialized, wait for the loading of the wind tracks to complete.
|
||||
new Thread("Wind loader for tracked race " + getRace().getName()) {
|
||||
@@ -351,7 +314,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
}
|
||||
try {
|
||||
final Map<? extends WindSource, ? extends WindTrack> loadedWindTracks = windStore.loadWindTracks(
|
||||
trackedRegatta, TrackedRaceImpl.this, millisecondsOverWhichToAverageWind);
|
||||
trackedRegatta.getRegatta().getName(), TrackedRaceImpl.this, millisecondsOverWhichToAverageWind);
|
||||
windTracks.putAll(loadedWindTracks);
|
||||
updateEventTimePoints(loadedWindTracks);
|
||||
} finally {
|
||||
@@ -373,7 +336,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
WindSource trackBasedWindSource = new WindSourceImpl(WindSourceType.TRACK_BASED_ESTIMATION);
|
||||
windTracks.put(trackBasedWindSource,
|
||||
getOrCreateWindTrack(trackBasedWindSource, delayForWindEstimationCacheInvalidation));
|
||||
this.trackedRegatta = trackedRegatta;
|
||||
competitorRankings = new HashMap<TimePoint, List<Competitor>>();
|
||||
competitorRankingsLocks = new HashMap<TimePoint, NamedReentrantReadWriteLock>();
|
||||
// now wait until wind loading has at least started; then we know that the serialization lock is safely held by the loader
|
||||
@@ -1152,61 +1114,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For wind sources of the special type {@link WindSourceType#COMBINED}, emits a new {@link CombinedWindTrackImpl}
|
||||
* which will not be added to {@link #windTracks} and will not lead to the wind source being listed in
|
||||
* {@link #getWindSources()} or {@link #getWindSources(WindSourceType)}. For all other wind sources, checks
|
||||
* {@link #windTracks} for the respective source. If found, it's returned; otherwise the wind track is created
|
||||
* through the {@link #windStore} using {@link #createWindTrack(WindSource, long)} and added to {@link #windTracks}
|
||||
* before being returned.
|
||||
*
|
||||
* @param delayForWindEstimationCacheInvalidation
|
||||
* if <code>-1</code> and the parameter is accessed, it will be replaced by
|
||||
* {@link #getMillisecondsOverWhichToAverageWind()}/2
|
||||
*/
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
|
||||
WindTrack result;
|
||||
if (windSource.getType() == WindSourceType.COMBINED) {
|
||||
if (combinedWindTrack == null) {
|
||||
combinedWindTrack = new CombinedWindTrackImpl(this, WindSourceType.COMBINED.getBaseConfidence());
|
||||
}
|
||||
result = combinedWindTrack;
|
||||
} else {
|
||||
synchronized (windTracks) {
|
||||
result = windTracks.get(windSource);
|
||||
if (result == null) {
|
||||
result = createWindTrack(windSource,
|
||||
delayForWindEstimationCacheInvalidation == -1 ? getMillisecondsOverWhichToAverageWind() / 2
|
||||
: delayForWindEstimationCacheInvalidation);
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
try {
|
||||
windTracks.put(windSource, result);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(getSerializationLock());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource) {
|
||||
return getOrCreateWindTrack(windSource, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wind track for the <code>windSource</code> specified and stores it in {@link #windTracks}. The
|
||||
* averaging interval is set according to the averaging interval set for all other wind sources, or the default if
|
||||
* no other wind source exists yet.
|
||||
*/
|
||||
protected WindTrack createWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
|
||||
WindTrack result = windStore.getWindTrack(trackedRegatta, this, windSource, millisecondsOverWhichToAverageWind,
|
||||
delayForWindEstimationCacheInvalidation);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWindData() {
|
||||
boolean result = false;
|
||||
@@ -2338,28 +2245,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
return trackedLeg == null ? null : trackedLeg.getWindwardDistanceToOverallLeader(timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WindSource> getWindSources(WindSourceType type) {
|
||||
Set<WindSource> result = new HashSet<WindSource>();
|
||||
for (WindSource windSource : getWindSources()) {
|
||||
if (windSource.getType() == type) {
|
||||
result.add(windSource);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<WindSource> getWindSources() {
|
||||
while (true) {
|
||||
try {
|
||||
return new HashSet<WindSource>(windTracks.keySet());
|
||||
} catch (ConcurrentModificationException cme) {
|
||||
logger.info("Caught " + cme + "; trying again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Mark> getMarks() {
|
||||
while (true) {
|
||||
@@ -2600,10 +2485,6 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
return starboardMarkPosition;
|
||||
}
|
||||
|
||||
protected NamedReentrantReadWriteLock getSerializationLock() {
|
||||
return serializationLock;
|
||||
}
|
||||
|
||||
protected NamedReentrantReadWriteLock getWindLoadingLock() {
|
||||
return windLoadingLock;
|
||||
}
|
||||
|
||||
Loaded 100 of 189 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user