Merge branch 'master' into bug2447

Conflicts:
	java/com.sap.sailing.feature/feature.xml
	java/pom.xml
This commit is contained in:
Steffen Schaefer
2017-01-09 10:05:05 +01:00
418 changed files with 11288 additions and 3801 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
# Counts the lines per committer currently in the tree, for all files with type
# .java, .MF, .properties, .swift and .xml
git ls-tree -r --name-only HEAD -- | grep "\(\.java$\)\|\(\.MF$\)\|\(\.properties$\)\|\(\.swift$\)\|\(\.xml$\)" | sed -e 's/^/"/' -e 's/$/"/' | xargs -n1 git blame HEAD | recode ISO-8859-1..UTF-8 | sed -e 's/^.* (\(.*\) [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [+-][0-9][0-9][0-9][0-9] *[0-9][0-9]*).*$/\1/' | sed -e 's/ *$//' | sort | uniq -c | sort -rn | awk 'BEGIN {s=0;} /.*/ {s=s+$1; print $0;} END { print "\n Sum: " s; }'
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
git log --shortstat --pretty="%cE" | sed 's/\(.*\)@.*/\1/' | grep -v "^$" | awk 'BEGIN { line=""; } !/^ / { if (line=="" || !match(line, $0)) {line = $0 "," line }} /^ / { print line " # " $0; line=""}' | tr '[:upper:]' '[:lower:]' | sort | sed -E 's/# //;s/ files? changed,//;s/([0-9]+) ([0-9]+ deletion)/\1 0 insertions\(+\), \2/;s/\(\+\)$/\(\+\), 0 deletions\(-\)/;s/insertions?\(\+\), //;s/ deletions?\(-\)//' | awk 'BEGIN {name=""; files=0; insertions=0; deletions=0;} {if ($1 != name && name != "") { print insertions " " name ":" files " files changed, " insertions " insertions(+), " deletions " deletions(-), " insertions-deletions " net"; files=0; insertions=0; deletions=0; name=$1; } name=$1; files+=$2; insertions+=$3; deletions+=$4} END {print name ": " files " files changed, " insertions " insertions(+), " deletions " deletions(-), " insertions-deletions " net";}' | grep -v "^[^,:]*,.*,: " | sort -n
-1
View File
@@ -1 +0,0 @@
bin
@@ -1,23 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: AutoLoad Races
Bundle-SymbolicName: com.sap.sailing.autoload
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: com.sap.sailing.autoload
Import-Package: org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.7.0",
org.osgi.util.tracker;version="1.5.1"
Require-Bundle: com.sap.sailing.gwt.ui,
com.sap.sse.common,
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.server,
com.sap.sailing.domain,
com.sap.sailing.domain.common,
com.sap.sailing.domain.persistence,
com.sap.sse.replication
Bundle-Activator: com.sap.sailing.autoload.Activator
@@ -1,32 +0,0 @@
package com.sap.sailing.autoload;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.server.RacingEventService;
public class Activator implements BundleActivator {
Logger logger = Logger.getLogger(Activator.class.getName());
@Override
public void start(BundleContext context) throws Exception {
RacingEventService racingEventService = createAndOpenRacingEventServiceTracker(context).getService();
new TracTrac(context, racingEventService);
}
@Override
public void stop(BundleContext context) throws Exception {
}
protected ServiceTracker<RacingEventService, RacingEventService> createAndOpenRacingEventServiceTracker(
BundleContext context) {
ServiceTracker<RacingEventService, RacingEventService> result = new ServiceTracker<RacingEventService, RacingEventService>(
context, RacingEventService.class.getName(), null);
result.open();
return result;
}
}
@@ -1,141 +0,0 @@
package com.sap.sailing.autoload;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tractracadapter.RaceRecord;
import com.sap.sailing.domain.tractracadapter.TracTracAdapter;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
/**
* Auto load TracTrac races from a parameter called <code>tractrac.autoload.json_urls</code>. This
* parameter contains a list of all event json urls from which you want to load races from. Only
* races in state <code>REPLAY</code> will be loaded.
*
* @author Simon Marcel Pamies
*/
public class TracTrac {
Logger logger = Logger.getLogger(TracTrac.class.getName());
private final TracTracAdapter tracTracAdapter;
private final RacingEventService racingEventService;
public TracTrac(BundleContext context, RacingEventService racingEventService) {
this.racingEventService = racingEventService;
this.tracTracAdapter = createAndOpenTracTracAdapterTracker(context).getService().getOrCreateTracTracAdapter(this.racingEventService.getBaseDomainFactory());
try {
autoLoadTrackTrackRaces();
} catch (Exception e) {
e.printStackTrace();
}
}
private TracTracAdapter getTracTracAdapter() {
return this.tracTracAdapter;
}
private RacingEventService getService() {
return this.racingEventService;
}
private void autoLoadTrackTrackRaces() throws Exception {
String jsonUrls = System.getProperty("tractrac.autoload.json_urls");
if (jsonUrls != null) {
String[] splitJsonUrls = jsonUrls.split(",");
for (String jsonUrl : splitJsonUrls) {
logger.info("Loading races from " + jsonUrl);
Pair<String, List<TracTracRaceRecordDTO>> racesThatCanBeTracked = listTracTracRacesInEvent(jsonUrl, /*listHiddenRaces*/false);
for (TracTracRaceRecordDTO raceRecord : racesThatCanBeTracked.getB()) {
RaceRecord record = getTracTracAdapter().getSingleTracTracRaceRecord(new URL(raceRecord.jsonURL), raceRecord.id, /*loadClientParams*/true);
if (record.getRaceStatus().equals(TracTracConnectionConstants.REPLAY_STATUS) || record.getRaceVisibility().equals(TracTracConnectionConstants.REPLAY_VISIBILITY)) {
RegattaIdentifier regattaForRaceRecord = null;
if (raceRecord.hasRememberedRegatta()) {
Regatta regatta = getService().getRememberedRegattaForRace(raceRecord.id);
if (regatta != null) {
regattaForRaceRecord = regatta.getRegattaIdentifier();
}
}
RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(getService()
.getMongoObjectFactory(), getService().getDomainObjectFactory());
RegattaLogStore regattaLogStore = MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(
getService().getMongoObjectFactory(), getService().getDomainObjectFactory());
getTracTracAdapter().addTracTracRace(
(TrackerManager) getService(),
regattaForRaceRecord,
record.getParamURL(), /* effectiveLiveURI */
null,
record.getStoredURI(),
new URI(""),
new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()),
new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()),
raceLogStore, regattaLogStore,
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* offsetToStartTimeOfSimulatedRace */
null, /*useTracTracMarkPassings*/ false,
/* tracTracUsername */"", /* tracTracPassword */"", record.getRaceStatus(),
record.getRaceVisibility());
} else {
logger.info("Ignoring race " + record.getName() + " because it is in status "
+ record.getRaceStatus() + " and visibility " + record.getRaceVisibility());
}
}
}
}
}
public com.sap.sse.common.Util.Pair<String, List<TracTracRaceRecordDTO>> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws MalformedURLException, IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException {
com.sap.sse.common.Util.Pair<String,List<RaceRecord>> raceRecords;
raceRecords = getTracTracAdapter().getTracTracRaceRecords(new URL(eventJsonURL), /*loadClientParam*/ false);
List<TracTracRaceRecordDTO> result = new ArrayList<TracTracRaceRecordDTO>();
for (RaceRecord raceRecord : raceRecords.getB()) {
if (listHiddenRaces == false && raceRecord.getRaceVisibility().equals(TracTracConnectionConstants.HIDDEN_VISIBILITY)) {
continue;
}
result.add(new TracTracRaceRecordDTO(raceRecord.getID(), raceRecord.getEventName(), raceRecord.getName(),
raceRecord.getTrackingStartTime().asDate(),
raceRecord
.getTrackingEndTime().asDate(), raceRecord.getRaceStartTime().asDate(),
raceRecord.getBoatClassNames(), raceRecord.getRaceStatus(), raceRecord.getRaceVisibility(), raceRecord.getJsonURL().toString(),
hasRememberedRegatta(raceRecord.getID())));
}
return new com.sap.sse.common.Util.Pair<String, List<TracTracRaceRecordDTO>>(raceRecords.getA(), result);
}
private boolean hasRememberedRegatta(Serializable raceID) {
return racingEventService.getRememberedRegattaForRace(raceID) != null;
}
private ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> createAndOpenTracTracAdapterTracker(BundleContext context) {
ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> result = new ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory>(
context, TracTracAdapterFactory.class.getName(), null);
result.open();
result.getService().getOrCreateTracTracAdapter(racingEventService.getBaseDomainFactory());
return result;
}
}
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.autoload</name>
<name>com.sap.sailing.competitorimport</name>
<comment></comment>
<projects>
</projects>
@@ -1,12 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
@@ -1,4 +1,3 @@
#Mon Aug 29 13:20:38 CEST 2011
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,11 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Competitorimport
Bundle-SymbolicName: com.sap.sailing.competitorimport
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: com.sap.sailing.competitorimport
Require-Bundle: com.sap.sse.common,
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain.common
@@ -1,12 +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.autoload</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
<?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.competitorimport</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,47 @@
package com.sap.sailing.competitorimport;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXBException;
import com.sap.sailing.domain.common.CompetitorDescriptor;
import com.sap.sse.common.Named;
/**
* Competitor data may come from various sources, such as external regatta management systems (RMS),
* CSV files, Excel spreadsheets and the like. When creating a regatta that uses "RaceLog Tracking"
* (also known as Smartphone Tracking)---and be it only in order to manage results for the regatta
* with the race committee app---it is tedious having to enter all competitor data manually when
* that data is already available in electronic form.<p>
*
* In its simplest form, a provider will just produce an otherwise unstructured list of competitor
* entries with no hint as to the event, regatta or race that the competitor pertains to. More
* sophisticated providers may structure competitor data into events, regattas or even individual
* races so that importing these competitors into a single fleet of one race column becomes possible.
* This will be particularly useful for split-fleet racing.
*
* @author Axel Uhl (d043530)
*
*/
public interface CompetitorProvider extends Named {
/**
* @return keys are event names, values are the names of the regatta in the event for which the connector has
* competitor names. Should the value for a key be <code>null</code>, competitors for the event may still
* be available, only they may not be keyed per regatta in that case.
*/
Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException;
/**
* Obtains competitor records from the source of import
*
* @param eventName
* an event name as obtained as a key in {@link #getHasCompetitorsForRegattasInEvent()}'s result
* @param regattaName
* <code>null</code> to get the competitors for all regattas in the event specified by
* <code>eventName</code>, or a regatta name as provided in the value set for the key
* <code>eventName</code> as returned by {@link #getHasCompetitorsForRegattasInEvent()}.
*/
Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException;
}
@@ -1,46 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9874"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9874"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9874 com.sap.sailing.dashboards.gwt.RibDashboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9874"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9874"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9874 com.sap.sailing.dashboards.gwt.RibDashboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
</launchConfiguration>
@@ -1,45 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.dashboards.gwt/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.dashboards.gwt&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.dashboards.gwt}&quot; -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt.watchFileChanges=false"/>
</launchConfiguration>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:54:05Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="ja">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:27:50Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="ja">
<header>
<skl>
<internal-file crc="f38aaa3e2ebfcee22c5187ca5b77bc00" form="base64">H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA==</internal-file>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:54:05Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="ru">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:27:50Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="ru">
<header>
<skl>
<internal-file crc="f38aaa3e2ebfcee22c5187ca5b77bc00" form="base64">H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA==</internal-file>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:54:06Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="zh-CN">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:27:51Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties" source-language="en" target-language="zh-CN">
<header>
<skl>
<internal-file crc="f38aaa3e2ebfcee22c5187ca5b77bc00" form="base64">H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA==</internal-file>
@@ -16,7 +16,7 @@ public enum BoatClassMasterdata {
_49ER ("49er", true, 4.88, 1.93, BoatHullType.MONOHULL, true),
_49ERFX ("49erFX", true, 4.88, 1.93, BoatHullType.MONOHULL, true, "49FX"),
_420 ("420", true, 4.20, 1.65, BoatHullType.MONOHULL, true, "420er", "420M", "420W"),
_470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W"),
_470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W", "470 - M", "470 - W"),
_5O5 ("5O5", true, 5.03, 1.88, BoatHullType.MONOHULL, true, "505", "5o5", "505er", "5o5er"),
_5_5MR ("5.5mR", true, 9.50, 1.92, BoatHullType.MONOHULL, true, "5.5 Meter", "5.5 Metre", "5.5", "5.5M", "5.5-metre", "5.5 metre"),
A_CAT ("A-Catamaran", true, 5.49, 2.30, BoatHullType.CATAMARAN, false, "A-Cat", "ACat", "A-Class Catamaran"),
@@ -85,7 +85,7 @@ public enum BoatClassMasterdata {
RS500 ("RS 500", true, 4.34, 1.58, BoatHullType.MONOHULL, true, "RS500", "RS_500"),
RS800 ("RS 800", true, 4.80, 1.88, BoatHullType.MONOHULL, true, "RS800", "RS_800"),
RS_AERO ("RS Aero", true, 4.00, 1.40, BoatHullType.MONOHULL, false, "RSAERO", "RS_Aero"),
RS_X ("RS:X", true, 2.86, 0.93, BoatHullType.SURFERBOARD, false, "RS-X", "RSX", "RS:X"),
RS_X ("RS:X", true, 2.86, 0.93, BoatHullType.SURFERBOARD, false, "RS-X", "RSX", "RS:X", "RS:X Men", "RS:X Woman"),
RS_FEVA ("RS Feva", true, 3.64, 1.42, BoatHullType.MONOHULL, true, "RSFeva"),
SONAR ("Sonar", true, 7.01, 2.39, BoatHullType.MONOHULL, true),
SOLING ("Soling", true, 8.15, 1.91, BoatHullType.MONOHULL, true),
@@ -100,6 +100,7 @@ public enum BoatClassMasterdata {
TORNADO ("Tornado Catamaran", true, 6.10, 3.02, BoatHullType.CATAMARAN, true, "Tornado", "Tornado Cat"),
TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true),
VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true),
VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "W60", "Whitbread60", "Whitbread 60"),
VX_ONE ("VX ONE", true, 5.79, 2.19, BoatHullType.MONOHULL, true, "VX-ONE"),
X_99 ("X-99", true, 9.96, 2.95, BoatHullType.MONOHULL, true, "X99"),
@@ -0,0 +1,111 @@
package com.sap.sailing.domain.common;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.common.dto.PersonDTO;
import com.sap.sse.common.CountryCode;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Named;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedImpl;
public class CompetitorDescriptor extends NamedImpl implements Named {
private static final long serialVersionUID = -6645726854552711646L;
private final String eventName;
private final String regattaName;
private final String boatClassName;
private final String boatName;
private final String teamName;
private final String raceName;
private final String fleetName;
private final String sailNumber;
private final String name;
private final CountryCode countryCode;
private final List<PersonDTO> persons;
private final Double timeOnTimeFactor;
private final Duration timeOnDistanceAllowancePerNauticalMile;
public CompetitorDescriptor(String eventName, String regattaName, String boatClassName, String raceName,
String fleetName, String sailNumber, String name, String teamName, String boatName, CountryCode countryCode,
Iterable<PersonDTO> persons, Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile) {
super(name);
this.eventName = eventName;
this.regattaName = regattaName;
this.boatClassName = boatClassName;
this.boatName = boatName;
this.raceName = raceName;
this.fleetName = fleetName;
this.sailNumber = sailNumber;
this.name = name;
this.teamName = teamName;
this.countryCode = countryCode;
this.persons = new ArrayList<>();
Util.addAll(persons, this.persons);
this.timeOnTimeFactor = timeOnTimeFactor;
this.timeOnDistanceAllowancePerNauticalMile = timeOnDistanceAllowancePerNauticalMile;
}
public String getEventName() {
return eventName;
}
public String getRegattaName() {
return regattaName;
}
public String getBoatClassName() {
return boatClassName;
}
public String getRaceName() {
return raceName;
}
public String getFleetName() {
return fleetName;
}
public String getSailNumber() {
return sailNumber;
}
public String getName() {
return name;
}
public CountryCode getCountryCode() {
return countryCode;
}
public Iterable<PersonDTO> getPersons() {
return persons;
}
public Double getTimeOnTimeFactor() {
return timeOnTimeFactor;
}
public String getBoatName() {
return boatName;
}
public String getTeamName() {
return teamName;
}
public Duration getTimeOnDistanceAllowancePerNauticalMile() {
return timeOnDistanceAllowancePerNauticalMile;
}
@Override
public String toString() {
return "CompetitorDescriptor [eventName=" + eventName + ", regattaName=" + regattaName + ", boatClassName="
+ boatClassName + ", boatName=" + boatName + ", teamName=" + teamName + ", raceName=" + raceName
+ ", fleetName=" + fleetName + ", sailNumber=" + sailNumber + ", name=" + name + ", countryCode="
+ countryCode + ", persons=" + persons + ", timeOnTimeFactor=" + timeOnTimeFactor
+ ", timeOnDistanceAllowancePerNauticalMile=" + timeOnDistanceAllowancePerNauticalMile + "]";
}
}
@@ -20,7 +20,10 @@ public interface Position extends Serializable {
Distance getDistance(Position p);
/**
* The bearing from this position towards <code>p</code> on a great circle
* The bearing from this position towards <code>p</code> on a great circle.
*
* @return {@code null} if {@code p} is {@code null}, otherwise the bearing from {@code this} {@link Position} to
* {@code p} along a great circle
*/
Bearing getBearingGreatCircle(Position p);
@@ -0,0 +1,92 @@
package com.sap.sailing.domain.common;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
/**
* Information about the time competitors of a race are expected to need for the legs of a race. This is based on a
* {@link TrackedRace} with {@link TrackedLeg}s and
* {@link TrackedRace#getWind(com.sap.sailing.domain.common.Position, com.sap.sse.common.TimePoint) wind information}.
* Furthermore, a valid {@link PolarDataService} instance must be bound to the {@link TrackedRace} to make predictions
* possible.
* <p>
*
* In order to give a rationale for the response, in addition to the total expected duration the expected durations for
* each leg are specified, together with the {@link Wind} data used for the leg, leg distance, the true wind angle for
* the leg's direction and the leg type that this leads to.
*
* @author Axel Uhl (d043530)
*
*/
public interface TargetTimeInfo {
public interface LegTargetTimeInfo {
/**
* The distance between the waypoints delimiting the leg, at the {@link #getExpectedStartTimePoint() expected
* time point} at which the competitor starts the leg.
*/
Distance getDistance();
/**
* True wind at the position in the middle between the two positions at which the waypoints delimiting
* the leg are at {@link #getExpectedStartTimePoint()}.
*/
Wind getWind();
/**
* The bearing from the leg's start waypoint to the leg's end waypoint at {@link #getExpectedStartTimePoint()}/
*/
Bearing getLegBearing();
/**
* The angle between the wind direction (from) and the bearing from the leg's start waypoint position at
* {@link #getExpectedStartTimePoint()} and the leg's end waypoint position at
* {@link #getExpectedStartTimePoint()}. For example, if a windward mark is a few meters off to the left,
* viewed from the leeward start line, then the wind comes a little more from the starboard side compared
* to the course middle line, therefore would have a true wind angle to the leg of, say 5deg.
*/
Bearing getTrueWindAngleToLeg();
/**
* The time a competitor is expected to need to sail the leg in the {@link #getWind() wind conditions expected}.
*/
Duration getExpectedDuration();
/**
* The time point when the competitor is expected to enter this leg. For the race's first leg
* this equals the {@link TargetTimeInfo#getExpectedStartTimePoint()} result.
*/
TimePoint getExpectedStartTimePoint();
/**
* The type of the leg at {@link #getExpectedStartTimePoint()}, given the {@link #getWind() wind conditions}
* and the {@link #getLegBearing() leg bearing} at that point in time.
*/
LegType getLegType();
}
/**
* The time point when a competitor whose hypothetical race is described
* by this object starts. This time is the basis for determining wind angles
* as input for the polar / VPP calculations. Wind is taken on each leg
* during the expected time at which the competitor starts that leg.
*/
TimePoint getExpectedStartTimePoint();
/**
* The expected total duration; equals the sum of the {@link LegTargetTimeInfo#getExpectedDuration() leg durations}
* as aggregated across {@link #getLegTargetTimes()}.
*/
Duration getExpectedDuration();
/**
* The per-leg information about the expected hypothetical sail around the course. The element
* order matches that of the {@link Course#getLegs() legs in the race course}.
*/
Iterable<LegTargetTimeInfo> getLegTargetTimes();
/**
* The {@link Duration#plus(Duration) sum} of {@link LegTargetTimeInfo#getExpectedDuration() expected leg durations}
* across all legs of type {@code spentInLegsOfType}.
*/
Duration getExpectedDuration(LegType spentInLegsOfType);
}
@@ -14,24 +14,28 @@ public class BoatClassDTO extends NamedDTO implements Serializable, Comparable<B
public static final String DEFAULT_NAME = "Default";
private Distance hullLength;
private Distance hullBeam;
private String displayName;
BoatClassDTO() {}
public BoatClassDTO(String name, String displayName, Distance hullLength) {
public BoatClassDTO(String name, String displayName, Distance hullLength, Distance hullBeam) {
super(name);
this.hullLength = hullLength;
this.hullBeam = hullBeam;
}
public BoatClassDTO(String name, Distance hullLength) {
super(name);
this.hullLength = hullLength;
this.displayName = null;
public BoatClassDTO(String name, Distance hullLength, Distance hullBeam) {
this(name, /* display name */ null, hullLength, hullBeam);
}
public Distance getHullLength() {
return hullLength;
}
public Distance getHullBeam() {
return hullBeam;
}
public String getDisplayName() {
return displayName;
@@ -20,6 +20,11 @@ public interface CompetitorDTO extends Serializable, MappableToDevice {
String getSearchTag();
/**
* If the {@code searchTag} is not contained in {@link #getSearchTag()}, appends it to the search tag, separated by a space character
*/
void addToSearchTag(String searchTag);
BoatDTO getBoat();
BoatClassDTO getBoatClass();
@@ -45,5 +50,5 @@ public interface CompetitorDTO extends Serializable, MappableToDevice {
Double getTimeOnTimeFactor();
Duration getTimeOnDistanceAllowancePerNauticalMile();
}
@@ -181,22 +181,30 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
return this;
}
@Override
public Color getColor() {
return color;
}
@Override
public String getSearchTag() {
return searchTag;
}
@Override
public void addToSearchTag(String searchTag) {
if (this.searchTag == null) {
this.searchTag = searchTag;
} else if (!this.searchTag.contains(searchTag)) {
this.searchTag += " "+searchTag;
}
}
@Override
public String getEmail() {
return email;
}
public String email(){
return email;
}
@Override
public boolean hasEmail() {
return email != null && !email.isEmpty();
@@ -0,0 +1,56 @@
package com.sap.sailing.domain.common.dto;
import java.util.Date;
import java.util.Objects;
public class PersonDTO extends NamedDTO {
private static final long serialVersionUID = 7248019801310303654L;
private Date dateOfBirth;
private String description;
private String nationalityThreeLetterIOCAcronym;
PersonDTO() {} // for GWT de-serialization only
public PersonDTO(String name, Date dateOfBirth, String description, String nationalityThreeLetterIOCAcronym) {
super(name);
this.dateOfBirth = dateOfBirth;
this.description = description;
this.nationalityThreeLetterIOCAcronym = nationalityThreeLetterIOCAcronym;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public String getDescription() {
return description;
}
public String getNationalityThreeLetterIOCAcronym() {
return nationalityThreeLetterIOCAcronym;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Objects.hash(dateOfBirth, description, nationalityThreeLetterIOCAcronym);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
if (!super.equals(obj)) {
return false;
}
PersonDTO other = (PersonDTO) obj;
return Objects.equals(dateOfBirth, other.dateOfBirth) && Objects.equals(description, other.description)
&& Objects.equals(nationalityThreeLetterIOCAcronym, other.nationalityThreeLetterIOCAcronym);
}
}
@@ -112,6 +112,12 @@ public class PreviousCompetitorDTOImpl implements CompetitorDTO {
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public void addToSearchTag(String searchTag) {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
}
@Override
public String getImageURL() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
@@ -0,0 +1,116 @@
package com.sap.sailing.domain.common.impl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.TargetTimeInfo;
import com.sap.sailing.domain.common.Wind;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class TargetTimeInfoImpl implements TargetTimeInfo {
private final Iterable<TargetTimeInfo.LegTargetTimeInfo> legInfos;
public static class LegTargetTimeInfoImpl implements LegTargetTimeInfo {
private final Distance distance;
private final Wind wind;
private final Bearing legBearing;
private final Duration expectedDuration;
private final TimePoint expectedStartTimePoint;
private final LegType legType;
public LegTargetTimeInfoImpl(Distance distance, Wind wind, Bearing legBearing, Duration expectedDuration,
TimePoint expectedStartTimePoint, LegType legType) {
super();
this.distance = distance;
this.wind = wind;
this.legBearing = legBearing;
this.expectedDuration = expectedDuration;
this.expectedStartTimePoint = expectedStartTimePoint;
this.legType = legType;
}
@Override
public Distance getDistance() {
return distance;
}
@Override
public Wind getWind() {
return wind;
}
@Override
public Bearing getLegBearing() {
return legBearing;
}
@Override
public Bearing getTrueWindAngleToLeg() {
return getLegBearing().getDifferenceTo(getWind().getBearing().reverse());
}
@Override
public Duration getExpectedDuration() {
return expectedDuration;
}
@Override
public TimePoint getExpectedStartTimePoint() {
return expectedStartTimePoint;
}
@Override
public LegType getLegType() {
return legType;
}
@Override
public String toString() {
return "LegTargetTimeInfoImpl [distance=" + distance + ", wind=" + wind + ", legBearing=" + legBearing
+ ", expectedDuration=" + expectedDuration + ", expectedStartTimePoint=" + expectedStartTimePoint
+ ", legType=" + legType + "]";
}
}
public TargetTimeInfoImpl(Iterable<LegTargetTimeInfo> legInfos) {
super();
this.legInfos = legInfos;
}
@Override
public TimePoint getExpectedStartTimePoint() {
return Util.isEmpty(legInfos) ? null : legInfos.iterator().next().getExpectedStartTimePoint();
}
@Override
public Duration getExpectedDuration() {
Duration result = Duration.NULL;
for (final LegTargetTimeInfo legInfo : legInfos) {
result = result.plus(legInfo.getExpectedDuration());
}
return result;
}
@Override
public Iterable<LegTargetTimeInfo> getLegTargetTimes() {
return legInfos;
}
@Override
public Duration getExpectedDuration(final LegType spentInLegsOfType) {
Duration result = Duration.NULL;
for (final LegTargetTimeInfo legInfo : legInfos) {
if (legInfo.getLegType() == spentInLegsOfType) {
result = result.plus(legInfo.getExpectedDuration());
}
}
return result;
}
@Override
public String toString() {
return "TargetTimeInfoImpl [legInfos=" + legInfos + "]";
}
}
@@ -170,9 +170,7 @@ public class WebSocketConnectionManager implements LiveDataConnection {
@Override
public void onWebSocketText(String message) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Received "+message+" in "+this);
}
logger.finest(()->"Received "+message+" in "+this);
if (message.equals("1")) {
logger.fine("Received server heartbeat for "+this);
receivedServerHeartbeatInInterval = true;
@@ -193,9 +191,7 @@ public class WebSocketConnectionManager implements LiveDataConnection {
}
}
}
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Received fixes"+fixes+" for "+this);
}
logger.finest(()->"Received fixes"+fixes+" for "+this);
notifyListeners(fixes);
} catch (ParseException e) {
logger.log(Level.SEVERE, "Error trying to parse a web socket data package coming from Igtimi "+this, e);
@@ -1,10 +1,13 @@
package com.sap.sailing.domain.persistence;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import com.mongodb.DBObject;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
@@ -28,8 +31,14 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
import com.sap.sailing.domain.leaderboard.LeaderboardRegistry;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Util.Pair;
/**
@@ -135,4 +144,33 @@ public interface DomainObjectFactory {
Iterable<Entry<DeviceConfigurationMatcher, DeviceConfiguration>> loadAllDeviceConfigurations();
Map<String, Set<URL>> loadResultUrls();
/**
* Loads all {@link RaceTrackingConnectivityParameters} objects from the database telling how to re-load those races
* that were {@link MongoObjectFactory#addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)
* marked as to be restored} before the server got re-started. Callers that would like to use the result to
* re-connect to those races should take care not to flood servers with requests. Instead, ideally, callers would
* monitor the loading {@link TrackedRace#getStatus() status} or the resulting {@link TrackedRace}s (see
* {@link RaceTracker#getRaceHandle()}, {@link RaceHandle#getRace(long)}, {@link RaceHandle#getTrackedRegatta()} and
* {@link TrackedRegatta#getTrackedRace(RaceDefinition)}.
*
* @param callback
* invoked for each connectivity params object successfully resolved; this pattern is preferred over a
* non-{@code void} return type because resolving the connectivity parameters objects itself happens
* through a callback pattern that may be triggered asynchronously by the services for handling the
* respective parameter types becoming available only later. For example, during the OSGi startup phase,
* when the {@code RacingEventService} is launched, not all persistence bundles will have run their
* activators; some may even depend on the {@code RacingEventService} and therefore won't start their
* activators before the activator of {@code RacingEventService} has completed. This would either result
* in a {@link NoCorrespondingServiceRegisteredException} or would have to be handled by not treating
* those connectivity parameter objects which defeats the whole purpose of this method.
* @return the number of connectivity parameter objects found in the database; callers may use this to understand
* the progress with the {@code callback} invocations of which eventually there should be this many, at
* least if all handlers have ultimately become available.
*
* @see MongoObjectFactory#addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)
* @see MongoObjectFactory#removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)
*/
int loadConnectivityParametersForRacesToRestore(Consumer<RaceTrackingConnectivityParameters> callback)
throws MalformedURLException, URISyntaxException;
}
@@ -19,6 +19,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
@@ -125,6 +126,12 @@ public interface MongoObjectFactory {
*/
void storeCompetitor(Competitor competitor);
/**
* Like {@link #storeCompetitor(Competitor)}, but for a collection of competitors that are all
* expected to be new, having a unique {@link Competitor#getId() ID}.
*/
void storeCompetitors(Iterable<Competitor> competitors);
void removeAllCompetitors();
void removeCompetitor(Competitor competitor);
@@ -143,4 +150,31 @@ public interface MongoObjectFactory {
void removeResultUrl(String resultProviderName, URL url);
/**
* Updates the database such that the next call to
* {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer<RaceTrackingConnectivityParameter>)} won't return an object equivalent to
* {@code params} anymore; in other words, the race whose connectivity parameters are described by {@code params}
* will no longer be considered as to be restored.
*
* @see #addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)
*/
void removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params);
/**
* Updates the database such that the next call to
* {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer<RaceTrackingConnectivityParameter>)} will return an object equivalent to
* {@code params}; in other words, the race whose connectivity parameters are described by {@code params} will be
* considered as to be restored.
*
* @see #removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)
*/
void addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params);
/**
* Removes all {@link RaceTrackingConnectivityParameters} objects from those to restore; short for calling
* {@link #removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)} for all parameter
* objects obtained through {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer<RaceTrackingConnectivityParameter>)}.
*/
void removeAllConnectivityParametersForRacesToRestore();
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
/**
* Defines literals providing the names for MongoDB collections. The literal documentation described the semantics
@@ -100,4 +101,10 @@ public enum CollectionNames {
* URLs providing race results
*/
RESULT_URLS,
/**
* DB representations of {@link RaceTrackingConnectivityParameters} objects, serialized in a type-specific
* way by corresponding implementations of the RaceTrackingConnectivityParametersMongoHandler} interface.
*/
CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED,
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
@@ -14,6 +15,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -199,6 +201,8 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
@@ -235,8 +239,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private final DB database;
private final DomainFactory baseDomainFactory;
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder;
private final TypeBasedServiceFinderFactory serviceFinderFactory;
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder;
private final TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> raceTrackingConnectivityParamsServiceFinder;
/**
* Uses <code>null</code> as the {@link TypeBasedServiceFinder}, meaning that no {@link DeviceIdentifier}s can be loaded
@@ -252,8 +257,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (serviceFinderFactory != null) {
this.deviceIdentifierServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class);
this.deviceIdentifierServiceFinder.setFallbackService(new PlaceHolderDeviceIdentifierMongoHandler());
this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory.createServiceFinder(RaceTrackingConnectivityParametersHandler.class);
} else {
this.deviceIdentifierServiceFinder = null;
this.raceTrackingConnectivityParamsServiceFinder = null;
}
this.baseDomainFactory = baseDomainFactory;
this.competitorDeserializer = CompetitorJsonDeserializer.create(baseDomainFactory);
@@ -2276,4 +2283,29 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return modified;
}
@Override
public int loadConnectivityParametersForRacesToRestore(Consumer<RaceTrackingConnectivityParameters> callback) throws MalformedURLException, URISyntaxException {
final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name());
int count = 0;
for (final DBObject o : collection.find()) {
count++;
final String type = (String) o.get(TypeBasedServiceFinder.TYPE);
raceTrackingConnectivityParamsServiceFinder.applyServiceWhenAvailable(type, connectivityParamsPersistenceService -> {
final Map<String, Object> map = new HashMap<>();
for (final String key : o.keySet()) {
if (!key.equals(TypeBasedServiceFinder.TYPE)) {
map.put(key, o.get(key));
}
}
try {
callback.accept(connectivityParamsPersistenceService.mapTo(map));
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to load race restore connectivity parameters "
+ o + " with handler " + connectivityParamsPersistenceService, e);
}
});
}
return count;
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -104,6 +105,8 @@ import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDevic
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.WindTrack;
@@ -118,6 +121,7 @@ import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.common.TypeBasedServiceFinderFactory;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.shared.media.ImageDescriptor;
import com.sap.sse.shared.media.VideoDescriptor;
@@ -127,6 +131,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
private final DB database;
private final CompetitorJsonSerializer competitorSerializer = CompetitorJsonSerializer.create();
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder;
private final TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> raceTrackingConnectivityParamsServiceFinder;
/**
* Uses <code>null</code> for the device type service finder and hence will be unable to store device identifiers.
@@ -142,8 +147,10 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
if (serviceFinderFactory != null) {
this.deviceIdentifierServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class);
this.deviceIdentifierServiceFinder.setFallbackService(new PlaceHolderDeviceIdentifierMongoHandler());
this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory.createServiceFinder(RaceTrackingConnectivityParametersHandler.class);
} else {
this.deviceIdentifierServiceFinder = null;
this.raceTrackingConnectivityParamsServiceFinder = null;
}
}
@@ -1245,6 +1252,20 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
collection.update(query, entry, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
@Override
public void storeCompetitors(Iterable<Competitor> competitors) {
if (competitors != null && !Util.isEmpty(competitors)) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
List<DBObject> competitorsDB = new ArrayList<>();
for (Competitor competitor : competitors) {
JSONObject json = competitorSerializer.serialize(competitor);
DBObject entry = (DBObject) JSON.parse(json.toString());
competitorsDB.add(entry);
}
collection.insert(competitorsDB);
}
}
@Override
public void removeAllCompetitors() {
logger.info("Removing all persistent competitor info");
@@ -1500,4 +1521,50 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.SAILORS_INFO_LOCALE.name(), locale != null ? locale.toLanguageTag() : null);
return result;
}
@Override
public void removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params) {
if (raceTrackingConnectivityParamsServiceFinder != null) {
final String typeIdentifier = params.getTypeIdentifier();
final RaceTrackingConnectivityParametersHandler paramsPersistenceService = raceTrackingConnectivityParamsServiceFinder.findService(typeIdentifier);
if (paramsPersistenceService != null) {
final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name());
DBObject key = new BasicDBObject();
key.putAll(paramsPersistenceService.getKey(params));
collection.remove(key, WriteConcern.SAFE);
} else {
logger.warning("Couldn't find a persistence service for connectivity parameters of type "+typeIdentifier);
}
} else {
logger.warning("No connectivity parameters service finder set; unable to remove connectivity parameters from persistent store for "
+ params + " of type " + params.getTypeIdentifier());
}
}
@Override
public void addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params) {
final String typeIdentifier = params.getTypeIdentifier();
if (raceTrackingConnectivityParamsServiceFinder == null) {
logger.warning("No service finder has been configured to find connectivity parameter persistence services. Can't add connectivity parameters to DB for restore.");
} else {
try {
final RaceTrackingConnectivityParametersHandler paramsPersistenceService = raceTrackingConnectivityParamsServiceFinder.findService(typeIdentifier);
final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name());
DBObject key = new BasicDBObject();
key.putAll(paramsPersistenceService.getKey(params));
DBObject dbObject = new BasicDBObject();
dbObject.putAll(paramsPersistenceService.mapFrom(params));
collection.update(key, dbObject, /* upsert */ true, /* multi */ false, WriteConcern.SAFE);
} catch (NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Couldn't find a persistence service for connectivity parameters of type "+typeIdentifier+
". Couldn't store race "+params.getTrackerID()+" for restoring.", e);
}
}
}
@Override
public void removeAllConnectivityParametersForRacesToRestore() {
database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()).drop();
}
}
@@ -2,7 +2,9 @@ package com.sap.sailing.domain.racelogtracking.test;
import static com.sap.sse.common.Util.size;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import java.util.Collections;
import java.util.UUID;
import org.junit.After;
@@ -11,14 +13,20 @@ import org.junit.Before;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -26,12 +34,19 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockDeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.deserialization.impl.DeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
@@ -61,13 +76,17 @@ public class AbstractGPSFixStoreTest {
service = new RacingEventServiceImpl(null, null, serviceFinderFactory);
raceLog = new RaceLogImpl("racelog");
regattaLog = new RegattaLogImpl("regattalog");
dropPersistedData();
store = new MongoSensorFixStoreImpl(service.getMongoObjectFactory(), service.getDomainObjectFactory(),
serviceFinderFactory);
}
@After
public void after() {
dropPersistedData();
}
private void dropPersistedData() {
MongoObjectFactoryImpl mongoOF = (MongoObjectFactoryImpl) service.getMongoObjectFactory();
mongoOF.getGPSFixCollection().drop();
mongoOF.getGPSFixMetadataCollection().drop();
@@ -76,8 +95,9 @@ public class AbstractGPSFixStoreTest {
}
protected void map(RegattaLog regattaLog, Competitor comp, DeviceIdentifier device, long from, long to) {
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(),
comp, device, new MillisecondsTimePoint(from), new MillisecondsTimePoint(to)));
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(MillisecondsTimePoint.now(),
MillisecondsTimePoint.now(), author, UUID.randomUUID(), comp, device, new MillisecondsTimePoint(from),
new MillisecondsTimePoint(to)));
}
protected void map(Competitor comp, DeviceIdentifier device, long from, long to) {
@@ -85,8 +105,25 @@ public class AbstractGPSFixStoreTest {
}
protected void map(Mark mark, DeviceIdentifier device, long from, long to) {
regattaLog.add(new RegattaLogDeviceMarkMappingEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(),
mark, device, new MillisecondsTimePoint(from), new MillisecondsTimePoint(to)));
regattaLog.add(new RegattaLogDeviceMarkMappingEventImpl(MillisecondsTimePoint.now(),
MillisecondsTimePoint.now(), author, UUID.randomUUID(), mark, device, new MillisecondsTimePoint(from),
new MillisecondsTimePoint(to)));
}
protected void defineMarksOnRegattaLog(Mark... marks) {
for (int i = 0; i < marks.length; i++) {
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(i + 1), author,
new MillisecondsTimePoint(1), 0, marks[i]));
}
}
protected DynamicTrackedRaceImpl createDynamicTrackedRace(BoatClass boatClass, RaceDefinition raceDefinition) {
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
return new DynamicTrackedRaceImpl(regatta, raceDefinition, Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class));
}
protected void testNumberOfRawFixes(Track<?> track, long expected) {
@@ -78,7 +78,7 @@ public class CreateAndTrackWithRaceLogTest {
@Before
public void setup() {
service = new RacingEventServiceImpl(true, new MockSmartphoneImeiServiceFinderFactory());
service = new RacingEventServiceImpl(/* clearPersistentCompetitorStore */ true, new MockSmartphoneImeiServiceFinderFactory(), /* restoreTrackedRaces */ false);
sensorFixStore = service.getSensorFixStore();
service.getMongoObjectFactory().getDatabase().dropDatabase();
author = service.getServerAuthor();
@@ -113,7 +113,7 @@ public class CreateAndTrackWithRaceLogTest {
Exception {
RaceColumn column = leaderboard.getRaceColumnByName(columnName);
exception.expect(NotDenotedForRaceLogTrackingException.class);
adapter.startTracking(service, leaderboard, column, fleet);
adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
}
private void testSize(Track<?> track, int expected) {
@@ -184,7 +184,7 @@ public class CreateAndTrackWithRaceLogTest {
raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1));
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
// start tracking
adapter.startTracking(service, leaderboard, column, fleet);
adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
// now there is a tracked race
@@ -226,7 +226,7 @@ public class CreateAndTrackWithRaceLogTest {
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
// start tracking
adapter.startTracking(service, leaderboard, column, fleet);
adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
// now there is a trackedrace
TrackedRace race = column.getTrackedRace(fleet);
@@ -0,0 +1,145 @@
package com.sap.sailing.domain.racelogtracking.test.impl;
import static org.mockito.Mockito.mock;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.MongoException;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseImpl;
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.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptySensorFixStore;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sse.common.Timed;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class RaceLogFixTrackerManagerTest {
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
protected final DeviceIdentifier deviceTest = new SmartphoneImeiIdentifier("b");
protected RaceLog raceLog;
protected RaceLog raceLog2;
protected RegattaLog regattaLog;
protected SensorFixStore store;
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
protected final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
protected final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
private DynamicTrackedRace trackedRace;
protected GPSFixMoving createFix(long millis, double lat, double lng, double knots, double degrees) {
return new GPSFixMovingImpl(new DegreePosition(lat, lng), new MillisecondsTimePoint(millis),
new KnotSpeedWithBearingImpl(knots, new DegreeBearingImpl(degrees)));
}
@Before
public void setUp() throws UnknownHostException, MongoException {
raceLog = new RaceLogImpl("racelog");
raceLog2 = new RaceLogImpl("racelog2");
regattaLog = new RegattaLogImpl("regattalog");
store = new MongoSensorFixStoreImpl(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(),
PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory);
Course course = new CourseImpl("course",
Arrays.asList(new Waypoint[] { new WaypointImpl(mark), new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp));
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, 0, 0, 0, /* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class));
}
/**
* Bug 4001: A {@link ConcurrentModificationException} was thrown when stopping tracking for a race with multiple
* attached RaceLogs.
*/
@Test
public void testThatNoExceptionIsThrownWhenStoppingTrackingForRaceWithMultipleRaceLogs_bug4001() {
trackedRace.attachRegattaLog(regattaLog);
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRaceLog(raceLog2);
RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager(trackedRace,
EmptySensorFixStore.INSTANCE, new SensorFixMapperFactory() {
@Override
public <FixT extends Timed, TrackT extends DynamicTrack<FixT>> SensorFixMapper<FixT, TrackT, Competitor> createCompetitorMapper(
Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
throw new IllegalArgumentException("Unknown event type");
}
});
raceLogFixTrackerManager.stop(false);
}
/**
* Bug 4001: A {@link ConcurrentModificationException} was thrown when stopping tracking for a race with multiple
* attached RaceLogs.
*/
@Test
public void testThatNoExceptionIsThrownWhenStoppingTrackingWhenAddingSecondRaceLogWhileAlreadyTracking_bug4001() {
trackedRace.attachRegattaLog(regattaLog);
trackedRace.attachRaceLog(raceLog);
RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager(trackedRace,
EmptySensorFixStore.INSTANCE, new SensorFixMapperFactory() {
@Override
public <FixT extends Timed, TrackT extends DynamicTrack<FixT>> SensorFixMapper<FixT, TrackT, Competitor> createCompetitorMapper(
Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
throw new IllegalArgumentException("Unknown event type");
}
});
trackedRace.attachRaceLog(raceLog2);
raceLogFixTrackerManager.stop(false);
}
}
@@ -1,50 +1,50 @@
package com.sap.sailing.domain.racelogtracking.test.impl;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseImpl;
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.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Timed;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.TimeRangeImpl;
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
private static final Logger logger = Logger.getLogger(TrackedRaceLoadsFixesTest.class.getName());
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
private final Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
private final Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null,
null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Course course = new CourseImpl("course", Arrays.asList(new WaypointImpl(mark), new WaypointImpl(mark2)));
private final RaceDefinition raceDefinition = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp, comp2));
@Rule
public Timeout TrackedRaceLoadsFixesTestTimeout = new Timeout(3 * 60 * 1000);
@@ -52,15 +52,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
@Test
public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
DeviceIdentifier markDevice = new SmartphoneImeiIdentifier("imei2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
defineMarksOnRegattaLog(mark, mark2);
map(comp, device, 0, 10000);
map(mark, markDevice, 0, 10000);
@@ -71,13 +64,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40));
store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40));
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class));
final DynamicTrackedRaceImpl trackedRace = createDynamicTrackedRace(boatClass, raceDefinition);
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000));
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2000));
new FixLoaderAndTracker(trackedRace, store, null);
@@ -100,16 +87,9 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
@Test
public void areFixesStoredInDb() throws TransformationException, NoCorrespondingServiceRegisteredException,
InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark));
regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, new MillisecondsTimePoint(1), 0, mark2));
defineMarksOnRegattaLog(mark, mark2);
DeviceIdentifier device2 = new SmartphoneImeiIdentifier("imei2");
DeviceIdentifier device3 = new SmartphoneImeiIdentifier("imei3");
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp, comp2));
map(comp, device, 0, 20000);
map(comp2, device2, 0, 600);
@@ -127,12 +107,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
store.storeFix(device, createFix(i + 1000, 10, 20, 30, 40));
}
DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/* startDate */ null, /* endDate */null, null, null, "a", null));
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class));
DynamicTrackedRace trackedRace = createDynamicTrackedRace(boatClass, raceDefinition);
new FixLoaderAndTracker(trackedRace, store, null);
@@ -145,17 +120,228 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark2), 1);
}
@Test
public void testFixesForCompetitorsAreNotLoadedIfMappingDoesNotIntersectWithTrackingInterval()
throws InterruptedException {
testFixes(/* start of tracking */ 200, /* end of tracking */ 300, /* mappings and fixes */ () -> {
map(comp, device, 0, 100);
store.storeFix(device, createFix(10, 10, 20, 30, 40));
store.storeFix(device, createFix(20, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> testNumberOfRawFixes(trackedRace.getTrack(comp), 0));
}
@Test
public void testFixesForCompetitorsAreLoadedIfMappingDoesIntersectWithTrackingIntervalFixesWithinIntersectionOnly()
throws InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings and fixes */ () -> {
map(comp, device, 0, 200);
store.storeFix(device, createFix(50, 10, 20, 30, 40));
store.storeFix(device, createFix(150, 10, 20, 30, 40));
store.storeFix(device, createFix(250, 10, 20, 30, 40));
map(comp, device, 350, 500);
store.storeFix(device, createFix(400, 10, 20, 30, 40));
store.storeFix(device, createFix(450, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> testNumberOfRawFixes(trackedRace.getTrack(comp), 1));
}
@Test
public void testAddingMappingLoadsFixesPreviouslyNotCovered() throws InterruptedException {
testFixes(/* start of tracking */ 10, /* end of tracking */ 500, /* mappings and fixes */ () -> {
map(comp, device, 0, 200);
map(comp, device, 300, 400);
store.storeFix(device, createFix(50, 10, 20, 30, 40)); // in first mapping
store.storeFix(device, createFix(150, 10, 20, 30, 40)); // in first mapping
store.storeFix(device, createFix(250, 10, 20, 30, 40)); // outside both mappings
store.storeFix(device, createFix(350, 10, 20, 30, 40)); // in second mapping
}, /* tests and expectations */ trackedRace -> {
testNumberOfRawFixes(trackedRace.getTrack(comp), 3);
map(comp, device, 0, 500); // covers fix at 250ms; assert that only that fix is loaded
try {
trackedRace.waitForLoadingToFinish();
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception waiting for loading to finish", e);
}
testNumberOfRawFixes(trackedRace.getTrack(comp), 4);
});
}
@Test
public void testAddingMappingDoesNotLoadFixesAlreadyCoveredByExistingMapping() throws InterruptedException {
testFixes(/* start of tracking */ 10, /* end of tracking */ 500, /* mappings and fixes */ () -> {
map(comp, device, 0, 200);
map(comp, device, 300, 400);
store.storeFix(device, createFix(50, 10, 20, 30, 40)); // in first mapping
store.storeFix(device, createFix(150, 10, 20, 30, 40)); // in first mapping
store.storeFix(device, createFix(250, 10, 20, 30, 40)); // outside both mappings
store.storeFix(device, createFix(350, 10, 20, 30, 40)); // in second mapping
}, /* tests and expectations */ trackedRace -> {
map(comp, device, 0, 500); // covers fix at 250ms
try {
trackedRace.waitForLoadingToFinish();
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception waiting for loading to finish", e);
}
store.storeFix(device, createFix(400, 10, 20, 30, 40)); // in third mapping
testNumberOfRawFixes(trackedRace.getTrack(comp), 5);
});
}
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
@Test
public void testFixesForMarkAreLoadedIfMappingDoesNotIntersectWithTrackingInterval() throws InterruptedException {
testFixes(/* start of tracking */ 200, /* end of tracking */ 300, /* mappings and fixes */ () -> {
map(mark, device, 0, 100);
store.storeFix(device, createFix(10, 10, 20, 30, 40));
store.storeFix(device, createFix(20, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2));
}
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
@Test
public void testFixesForTwoMarksAreLoadedIfMappingsDoNotIntersectWithTrackingInterval()
throws InterruptedException {
testFixes(/* start of tracking */ 400, /* end of tracking */ 500, /* mappings and fixes */ () -> {
map(mark, device, 0, 100);
store.storeFix(device, createFix(50, 10, 20, 30, 40));
map(mark2, device, 200, 300);
store.storeFix(device, createFix(250, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> {
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark2), 1);
});
}
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
@Test
public void testFixesForMarkAreLoadedIfMappingDoesIntersectWithTrackingIntervalFixesWithinIntersectionOnly()
throws InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings and fixes */ () -> {
map(mark, device, 0, 200);
store.storeFix(device, createFix(50, 10, 20, 30, 40));
store.storeFix(device, createFix(150, 10, 20, 30, 40));
store.storeFix(device, createFix(250, 10, 20, 30, 40));
map(mark, device, 350, 500);
store.storeFix(device, createFix(400, 10, 20, 30, 40));
store.storeFix(device, createFix(450, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> {
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
@Test
public void testFixesForMarkAreLoadedIfMappingDoesIntersectWithTrackingIntervalAllFixesBecauseOfNoFixesWithIntersection()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings and fixes */ () -> {
map(mark, device, 0, 200);
store.storeFix(device, createFix(50, 10, 20, 30, 40));
store.storeFix(device, createFix(250, 10, 20, 30, 40));
map(mark, device, 350, 500);
store.storeFix(device, createFix(400, 10, 20, 30, 40));
store.storeFix(device, createFix(450, 10, 20, 30, 40));
}, /* tests and expectations */ trackedRace -> {
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 3);
});
}
@Test
public void testFixForMarkIsAddedIfBeforeTrackingIntervalAndNoOtherFixExists()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 200),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(50, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
@Test
public void testFixForMarkIsAddedIfAfterTrackingIntervalAndNoOtherFixExists()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400), /* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(350, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
@Test
public void testFixForMarkIsAddedIfBeforeTrackingIntervalAndBetterThanAnExistingOne()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(50, 10, 20, 30, 40));
store.storeFix(device, createFix(75, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
});
}
@Test
public void testFixForMarkIsNotAddedIfBeforeTrackingIntervalAndWorseThanAnExistingOne()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(75, 10, 20, 30, 40));
store.storeFix(device, createFix(50, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
@Test
public void testFixForMarkIsAddedIfAfterTrackingIntervalAndBetterThanAnExistingOne()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(375, 10, 20, 30, 40));
store.storeFix(device, createFix(350, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
});
}
@Test
public void testFixForMarkIsNotAddedIfAfterTrackingIntervalAndWorseThanAnExistingOne()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(350, 10, 20, 30, 40));
store.storeFix(device, createFix(375, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
@Test
public void testFixForMarkIsNotAddedIfNotWithinTrackingIntervalAndFixWithinTrackingIntervalExists()
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
/* fixes and tests/expectations */ trackedRace -> {
store.storeFix(device, createFix(200, 10, 20, 30, 40));
store.storeFix(device, createFix(50, 10, 20, 30, 40));
store.storeFix(device, createFix(350, 10, 20, 30, 40));
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
});
}
private void testFixes(long startOfTracking, long endOfTracking, Runnable beforeTrackingStarted,
Consumer<DynamicTrackedRace> afterTrackingStarted) throws InterruptedException {
defineMarksOnRegattaLog(mark, mark2);
raceLog.add(new RaceLogStartOfTrackingEventImpl(new MillisecondsTimePoint(startOfTracking), author, 0));
raceLog.add(new RaceLogEndOfTrackingEventImpl(new MillisecondsTimePoint(endOfTracking), author, 0));
beforeTrackingStarted.run();
DynamicTrackedRace trackedRace = createDynamicTrackedRace(boatClass, raceDefinition);
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
new FixLoaderAndTracker(trackedRace, store, null);
trackedRace.waitForLoadingToFinish();
afterTrackingStarted.accept(trackedRace);
}
@Test
public void metadataStoredInDb() throws TransformationException, NoCorrespondingServiceRegisteredException {
assertEquals(0, store.getNumberOfFixes(device));
assertEquals(null, store.getTimeRangeCoveredByFixes(device));
map(comp, device, 0, 600);
store.storeFix(device, createFix(100, 10, 20, 30, 40));
store.storeFix(device, createFix(200, 10, 20, 30, 40));
assertEquals(2, store.getNumberOfFixes(device));
assertEquals(TimeRangeImpl.create(100, 200), store.getTimeRangeCoveredByFixes(device));
}
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
import com.sap.sse.common.TypeBasedServiceFinder;
public abstract class AbstractTypeBasedServiceFinder<ServiceT> implements TypeBasedServiceFinder<ServiceT> {
@Override
public void applyServiceWhenAvailable(String type, com.sap.sse.common.TypeBasedServiceFinder.Callback<ServiceT> callback) {
final ServiceT service = findService(type);
callback.withService(service);
}
}
@@ -4,9 +4,8 @@ import java.util.Collections;
import java.util.Set;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TypeBasedServiceFinder;
public class MockEmptyServiceFinder<ServiceType> implements TypeBasedServiceFinder<ServiceType> {
public class MockEmptyServiceFinder<ServiceType> extends AbstractTypeBasedServiceFinder<ServiceType> {
private ServiceType fallback;
@Override
@@ -15,9 +15,8 @@ import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.DoubleVectorFixMongoHandlerImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl;
import com.sap.sse.common.TypeBasedServiceFinder;
public class MockGPSFixMongoServiceFinder implements TypeBasedServiceFinder<FixMongoHandler<?>> {
public class MockGPSFixMongoServiceFinder extends AbstractTypeBasedServiceFinder<FixMongoHandler<?>> {
private final MongoObjectFactoryImpl mof = new MongoObjectFactoryImpl(null);
private final DomainObjectFactoryImpl dof = new DomainObjectFactoryImpl(null, null);
private final FixMongoHandler<?> movingHandler = new GPSFixMovingMongoHandlerImpl(mof, dof);
@@ -3,9 +3,7 @@ package com.sap.sailing.domain.racelog.tracking.test.mock;
import java.util.Collections;
import java.util.Set;
import com.sap.sse.common.TypeBasedServiceFinder;
public class MockServiceFinder<T> implements TypeBasedServiceFinder<T> {
public class MockServiceFinder<T> extends AbstractTypeBasedServiceFinder<T> {
private final T handler;
private T fallback;
@@ -32,5 +32,5 @@ Import-Package: com.google.zxing;version="3.1.0",
org.osgi.framework;version="1.6.0",
org.osgi.util.tracker;version="1.5.1"
Export-Package: com.sap.sailing.domain.racelogtracking,
com.sap.sailing.domain.racelogtracking.impl;x-friends:="com.sap.sailing.domain.racelogtrackingadapter.testsupport,com.sap.sailing.gwt.ui.test"
com.sap.sailing.domain.racelogtracking.impl;x-friends:="com.sap.sailing.domain.racelogtrackingadapter.testsupport,com.sap.sailing.gwt.ui.test,com.sap.sailing.mongodb.test"
Bundle-Activator: com.sap.sailing.domain.racelogtracking.impl.Activator
@@ -5,7 +5,7 @@ iOSUsers=启动 iOS 应用
androidUsers=启动 Android 应用
alternativelyVisitThisLink=如果在已安装该应用的设备中读取邮件,只需点击以下链接即可启动:
buoyTender=浮标供应船
appStoreInstallText=如果尚无该应用,请安装:
appStoreInstallText=如果尚无该应用,请安装:
appIos=安装 iOS 应用
appAndroid=安装 Android 应用
buoyPingerAppName=SAP Sailing Buoy Pinger
@@ -5,7 +5,7 @@ iOSUsers=启动 iOS 应用
androidUsers=启动 Android 应用
alternativelyVisitThisLink=如果在已安装该应用的设备中读取邮件,只需点击以下链接即可启动:
buoyTender=浮标供应船
appStoreInstallText=如果尚无该应用,请安装:
appStoreInstallText=如果尚无该应用,请安装:
appIos=安装 iOS 应用
appAndroid=安装 Android 应用
buoyPingerAppName=SAP Sailing Buoy Pinger
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en">
<file datatype="javapropertyresourcebundle" original="/usr/sap/ljs/app/Converter_7335190261158680380/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en">
<header>
<skl>
<internal-file crc="cdbdcb19e10086d34f715e2a7da26eef" form="base64">H4sIAAAAAAAAAF2RTU/DMAyG7/0VvnGZEGekHgYSUiVgY+3gnLXeajWNo9jbqBD/naQtbOLm+PXH+zgaTN2ROxTuRGqU2D1xyKs5C5c07DlkUhv3tnnkBlfhnYR0G2zFGzyQKIal5C+mQ5BjQBj4CK05IWiL8HX3DUvvgZyosRabW6hadJDmjQV7tpbPaeM0HjhAbanuRtWS6+AoSY5jA0hvgvqWXZzOEOb1YCQ7o625x4rzjymKBRmtyq1gkLzU2AfxmcxkxjWBqbmWllNqkm2c6SL7Ce0wwlYtyXO0khf7Ec9EzICmSb40atAbssATUYMnqhHOpO34TvjGpvLhcoYFCPXeDjPrDuMREpIkN/fZ7shDha7BkKcQdIwz432pHLCYxlT4qclSw+5GLzdPCwfUBXiLRvB36X1qL1jyuftyDu9n/D/p+hzJwDqSxm/2/tX0mJfLNZSROOE/JHuTnEnMFa6kQ6v/S2HOZz95rIJYdwIAAA==</internal-file>
<internal-file crc="064477f4b22ea4cc8cbdaab36795b9a4" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA=</internal-file>
</skl>
<phase-group>
<phase company-name="SAP AG" date="2016-11-04T21:01:36Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
<phase company-name="SAP AG" date="2016-12-16T08:45:26Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
</phase-group>
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Javascript .properties Flexible File Converter" tool-version="1.1.0"/>
</header>
@@ -33,7 +33,7 @@
<source>buoy tender</source>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appStoreInstallText" resname="appStoreInstallText">
<source>If don't have the App yet, please install:</source>
<source>If you don't have the App yet, please install:</source>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appIos" resname="appIos">
<source>Install iOS App</source>
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:56:08Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="ja">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:30:12Z" original="/usr/sap/ljs/app/Converter_7335190261158680380/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="ja">
<header>
<skl>
<internal-file crc="cdbdcb19e10086d34f715e2a7da26eef" form="base64">H4sIAAAAAAAAAF2RTU/DMAyG7/0VvnGZEGekHgYSUiVgY+3gnLXeajWNo9jbqBD/naQtbOLm+PXH+zgaTN2ROxTuRGqU2D1xyKs5C5c07DlkUhv3tnnkBlfhnYR0G2zFGzyQKIal5C+mQ5BjQBj4CK05IWiL8HX3DUvvgZyosRabW6hadJDmjQV7tpbPaeM0HjhAbanuRtWS6+AoSY5jA0hvgvqWXZzOEOb1YCQ7o625x4rzjymKBRmtyq1gkLzU2AfxmcxkxjWBqbmWllNqkm2c6SL7Ce0wwlYtyXO0khf7Ec9EzICmSb40atAbssATUYMnqhHOpO34TvjGpvLhcoYFCPXeDjPrDuMREpIkN/fZ7shDha7BkKcQdIwz432pHLCYxlT4qclSw+5GLzdPCwfUBXiLRvB36X1qL1jyuftyDu9n/D/p+hzJwDqSxm/2/tX0mJfLNZSROOE/JHuTnEnMFa6kQ6v/S2HOZz95rIJYdwIAAA==</internal-file>
<internal-file crc="064477f4b22ea4cc8cbdaab36795b9a4" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA=</internal-file>
</skl>
<phase-group>
<phase company-name="SAP AG" date="2016-11-04T21:01:36Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
<phase company-name="SAP AG" date="2016-12-16T08:45:26Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
</phase-group>
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Javascript .properties Flexible File Converter" tool-version="1.1.0"/>
</header>
@@ -17,7 +17,7 @@
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="scanQRCodeOrVisitUrlToRegisterAs" resname="scanQRCodeOrVisitUrlToRegisterAs">
<source>Make sure you have the <ph id="1">{0}</ph> App installed. Then scan the following QRCode or click the link using your smartphone to register as</source>
<target state="translated"><ph id="1">{0}</ph> アプリがインストールされていることを確認してください。その上で、以下の QR コードをスキャンするか、スマートフォンを使用してリンクをクリックして登録します:</target>
<target state="signed-off"><ph id="1">{0}</ph> アプリがインストールされていることを確認してください。その上で、以下の QR コードをスキャンするか、スマートフォンを使用してリンクをクリックして登録します:</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="welcomeTo" resname="welcomeTo">
<source>Welcome to</source>
@@ -40,8 +40,8 @@
<target state="signed-off">設標船</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appStoreInstallText" resname="appStoreInstallText">
<source>If don't have the App yet, please install:</source>
<target state="signed-off">まだアプリがない場合は、インストールしてください:</target>
<source>If you don't have the App yet, please install:</source>
<target state="translated">まだアプリがない場合は、インストールしてください:</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appIos" resname="appIos">
<source>Install iOS App</source>
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:56:08Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="ru">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:30:11Z" original="/usr/sap/ljs/app/Converter_7335190261158680380/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="ru">
<header>
<skl>
<internal-file crc="cdbdcb19e10086d34f715e2a7da26eef" form="base64">H4sIAAAAAAAAAF2RTU/DMAyG7/0VvnGZEGekHgYSUiVgY+3gnLXeajWNo9jbqBD/naQtbOLm+PXH+zgaTN2ROxTuRGqU2D1xyKs5C5c07DlkUhv3tnnkBlfhnYR0G2zFGzyQKIal5C+mQ5BjQBj4CK05IWiL8HX3DUvvgZyosRabW6hadJDmjQV7tpbPaeM0HjhAbanuRtWS6+AoSY5jA0hvgvqWXZzOEOb1YCQ7o625x4rzjymKBRmtyq1gkLzU2AfxmcxkxjWBqbmWllNqkm2c6SL7Ce0wwlYtyXO0khf7Ec9EzICmSb40atAbssATUYMnqhHOpO34TvjGpvLhcoYFCPXeDjPrDuMREpIkN/fZ7shDha7BkKcQdIwz432pHLCYxlT4qclSw+5GLzdPCwfUBXiLRvB36X1qL1jyuftyDu9n/D/p+hzJwDqSxm/2/tX0mJfLNZSROOE/JHuTnEnMFa6kQ6v/S2HOZz95rIJYdwIAAA==</internal-file>
<internal-file crc="064477f4b22ea4cc8cbdaab36795b9a4" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA=</internal-file>
</skl>
<phase-group>
<phase company-name="SAP AG" date="2016-11-04T21:01:36Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
<phase company-name="SAP AG" date="2016-12-16T08:45:26Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
</phase-group>
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Javascript .properties Flexible File Converter" tool-version="1.1.0"/>
</header>
@@ -40,8 +40,8 @@
<target state="signed-off">лоцмейстерское судно</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appStoreInstallText" resname="appStoreInstallText">
<source>If don't have the App yet, please install:</source>
<target state="signed-off">Если у вас еще нет приложения, установите его:</target>
<source>If you don't have the App yet, please install:</source>
<target state="translated">Если у вас еще нет приложения, установите его:</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appIos" resname="appIos">
<source>Install iOS App</source>
@@ -1,63 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file datatype="javapropertyresourcebundle" date="2016-11-23T14:56:08Z" original="/usr/sap/ljs/app/Converter_2566045106627533633/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="zh-CN">
<file datatype="javapropertyresourcebundle" date="2016-12-21T15:30:12Z" original="/usr/sap/ljs/app/Converter_7335190261158680380/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="zh-CN">
<header>
<skl>
<internal-file crc="cdbdcb19e10086d34f715e2a7da26eef" form="base64">H4sIAAAAAAAAAF2RTU/DMAyG7/0VvnGZEGekHgYSUiVgY+3gnLXeajWNo9jbqBD/naQtbOLm+PXH+zgaTN2ROxTuRGqU2D1xyKs5C5c07DlkUhv3tnnkBlfhnYR0G2zFGzyQKIal5C+mQ5BjQBj4CK05IWiL8HX3DUvvgZyosRabW6hadJDmjQV7tpbPaeM0HjhAbanuRtWS6+AoSY5jA0hvgvqWXZzOEOb1YCQ7o625x4rzjymKBRmtyq1gkLzU2AfxmcxkxjWBqbmWllNqkm2c6SL7Ce0wwlYtyXO0khf7Ec9EzICmSb40atAbssATUYMnqhHOpO34TvjGpvLhcoYFCPXeDjPrDuMREpIkN/fZ7shDha7BkKcQdIwz432pHLCYxlT4qclSw+5GLzdPCwfUBXiLRvB36X1qL1jyuftyDu9n/D/p+hzJwDqSxm/2/tX0mJfLNZSROOE/JHuTnEnMFa6kQ6v/S2HOZz95rIJYdwIAAA==</internal-file>
<internal-file crc="064477f4b22ea4cc8cbdaab36795b9a4" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA=</internal-file>
</skl>
<phase-group>
<phase company-name="SAP AG" date="2016-11-04T21:01:36Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
<phase company-name="SAP AG" date="2016-12-16T08:45:26Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
</phase-group>
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Javascript .properties Flexible File Converter" tool-version="1.1.0"/>
</header>
<body>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="trackingInvitationFor" resname="trackingInvitationFor">
<source>Tracking Invitation for</source>
<target>跟踪邀请 - </target>
<target state="signed-off">跟踪邀请 - </target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="scanQRCodeOrVisitUrlToRegisterAs" resname="scanQRCodeOrVisitUrlToRegisterAs">
<source>Make sure you have the <ph id="1">{0}</ph> App installed. Then scan the following QRCode or click the link using your smartphone to register as</source>
<target>确保已安装 <ph id="1">{0}</ph> 应用。然后,使用智能手机扫描以下二维码或点击链接注册</target>
<target state="signed-off">确保已安装 <ph id="1">{0}</ph> 应用。然后,使用智能手机扫描以下二维码或点击链接注册</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="welcomeTo" resname="welcomeTo">
<source>Welcome to</source>
<target>欢迎使用</target>
<target state="signed-off">欢迎使用</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="iOSUsers" resname="iOSUsers">
<source>Start iOS App</source>
<target>启动 iOS 应用</target>
<target state="signed-off">启动 iOS 应用</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="androidUsers" resname="androidUsers">
<source>Start Android App</source>
<target>启动 Android 应用</target>
<target state="signed-off">启动 Android 应用</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="alternativelyVisitThisLink" resname="alternativelyVisitThisLink">
<source>If you are reading this mail on the device with the App already installed, simply click below to start:</source>
<target>如果在已安装该应用的设备中读取邮件,只需点击以下链接即可启动:</target>
<target state="signed-off">如果在已安装该应用的设备中读取邮件,只需点击以下链接即可启动:</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="buoyTender" resname="buoyTender">
<source>buoy tender</source>
<target>浮标供应船</target>
<target state="signed-off">浮标供应船</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appStoreInstallText" resname="appStoreInstallText">
<source>If don't have the App yet, please install:</source>
<target>如果尚无该应用,请安装:</target>
<source>If you don't have the App yet, please install:</source>
<target state="translated">如果尚无该应用,请安装:</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appIos" resname="appIos">
<source>Install iOS App</source>
<target>安装 iOS 应用</target>
<target state="signed-off">安装 iOS 应用</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appAndroid" resname="appAndroid">
<source>Install Android App</source>
<target>安装 Android 应用</target>
<target state="signed-off">安装 Android 应用</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="buoyPingerAppName" resname="buoyPingerAppName">
<source>SAP Sailing Buoy Pinger</source>
<target>SAP Sailing Buoy Pinger</target>
<target state="signed-off">SAP Sailing Buoy Pinger</target>
</trans-unit>
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="sailInSightAppName" resname="sailInSightAppName">
<source>SAP Sail InSight</source>
<target>SAP Sail InSight</target>
<target state="signed-off">SAP Sail InSight</target>
</trans-unit>
</body>
</file>
@@ -39,8 +39,10 @@ public interface RaceLogTrackingAdapter {
* <li>Is a {@link RaceLogRaceTracker} already listening for this racelog? If not, add one.</li>
* <li>Is a {@link RaceLogStartTrackingEvent} present in the racelog? If not, add one</li>
* </ul>
* @param trackWind TODO
* @param correctWindDirectionByMagneticDeclination TODO
*/
RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet)
RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, boolean trackWind, boolean correctWindDirectionByMagneticDeclination)
throws NotDenotedForRaceLogTrackingException, Exception;
RaceLogTrackingState getRaceLogTrackingState(RacingEventService service, RaceColumn raceColumn, Fleet fleet);
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogFixTrackerRegattaListener;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
import com.sap.sailing.server.MasterDataImportClassLoaderService;
import com.sap.sailing.server.RacingEventService;
@@ -97,7 +98,14 @@ public class Activator implements BundleActivator {
regattaLogSensorDataTrackerTrackedRegattaListener, null));
registrations.add(context.registerService(Replicable.class,
regattaLogSensorDataTrackerTrackedRegattaListener, null));
new Thread(()->{
try {
registrations.add(context.registerService(RaceTrackingConnectivityParametersHandler.class,
new RaceLogConnectivityParamsHandler(racingEventServiceTracker.waitForService(0)), getDict(RaceLogConnectivityParams.TYPE)));
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to register race log tracking connectivity params persistence handler", e);
}
}, "RaceLog tracking activator registering connectivity params persistence handler").start();
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName());
}
@@ -22,12 +22,17 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.regattalike.HasRegattaLike;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTracker;
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.AbstractRaceTrackingConnectivityParameters;
import com.sap.sailing.server.RacingEventService;
public class RaceLogConnectivityParams implements RaceTrackingConnectivityParameters {
public class RaceLogConnectivityParams extends AbstractRaceTrackingConnectivityParameters {
/**
* A type identifier that needs to be unique for the
*/
public static final String TYPE = "RACE_LOG_TRACKING";
private final RacingEventService service;
private final RaceColumn raceColumn;
private final Fleet fleet;
@@ -37,7 +42,9 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
private final DomainFactory domainFactory;
public RaceLogConnectivityParams(RacingEventService service, Regatta regatta, RaceColumn raceColumn, Fleet fleet,
Leaderboard leaderboard, long delayToLiveInMillis, DomainFactory domainFactory) throws RaceNotCreatedException {
Leaderboard leaderboard, long delayToLiveInMillis, DomainFactory domainFactory, boolean trackWind,
boolean correctWindDirectionByMagneticDeclination) throws RaceNotCreatedException {
super(trackWind, correctWindDirectionByMagneticDeclination);
this.service = service;
this.regatta = regatta;
this.raceColumn = raceColumn;
@@ -45,12 +52,16 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
this.leaderboard = leaderboard;
this.delayToLiveInMillis = delayToLiveInMillis;
this.domainFactory = domainFactory;
if (!new RaceLogTrackingStateAnalyzer(getRaceLog()).analyze().isForTracking()) {
throw new RaceNotCreatedException(String.format("Racelog (%s) is not denoted for tracking", getRaceLog()));
}
}
@Override
public String getTypeIdentifier() {
return TYPE;
}
@Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver) {
@@ -70,7 +81,7 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
throw new RaceNotCreatedException("No regatta for race-log tracked race");
}
DynamicTrackedRegatta trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta);
return new RaceLogRaceTracker(trackedRegatta, this, windStore, raceLogResolver);
return new RaceLogRaceTracker(trackedRegatta, this, windStore, raceLogResolver, this);
}
@Override
@@ -0,0 +1,71 @@
package com.sap.sailing.domain.racelogtracking.impl;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.TypeBasedServiceFinder;
/**
* Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The
* "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.<p>
*
* Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to
* its members.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceLogConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler {
private static final String FLEET_NAME = "fleetName";
private static final String RACE_COLUMN_NAME = "raceColumnName";
private static final String LEADERBOARD_NAME = "leaderboardName";
private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis";
private final DomainFactory domainFactory;
private final RacingEventService racingEventService;
public RaceLogConnectivityParamsHandler(RacingEventService racingEventService) {
super();
this.racingEventService = racingEventService;
this.domainFactory = racingEventService.getBaseDomainFactory();
}
@Override
public Map<String, Object> mapFrom(RaceTrackingConnectivityParameters params) {
assert params instanceof RaceLogConnectivityParams;
final RaceLogConnectivityParams rlParams = (RaceLogConnectivityParams) params;
final Map<String, Object> result = getKey(params);
result.put(DELAY_TO_LIVE_IN_MILLIS, rlParams.getDelayToLiveInMillis());
addWindTrackingParameters(rlParams, result);
return result;
}
@Override
public RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws MalformedURLException, URISyntaxException {
final RegattaLeaderboard leaderboard = (RegattaLeaderboard) racingEventService.getLeaderboardByName((String) map.get(LEADERBOARD_NAME));
final RaceColumn raceColumn = leaderboard.getRaceColumnByName((String) map.get(RACE_COLUMN_NAME));
return new RaceLogConnectivityParams(racingEventService, leaderboard.getRegatta(), raceColumn,
raceColumn.getFleetByName((String) map.get(FLEET_NAME)), leaderboard,
((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), domainFactory, isTrackWind(map),
isCorrectWindDirectionByMagneticDeclination(map));
}
@Override
public Map<String, Object> getKey(RaceTrackingConnectivityParameters params) {
assert params instanceof RaceLogConnectivityParams;
final RaceLogConnectivityParams rlParams = (RaceLogConnectivityParams) params;
final Map<String, Object> result = new HashMap<>();
result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier());
result.put(LEADERBOARD_NAME, rlParams.getLeaderboard().getName());
result.put(RACE_COLUMN_NAME, rlParams.getRaceColumn().getName());
result.put(FLEET_NAME, rlParams.getFleet().getName());
return result;
}
}
@@ -7,7 +7,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -98,7 +97,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
private EndOfTrackingController endOfTrackingController;
public RaceLogRaceTracker(DynamicTrackedRegatta regatta, RaceLogConnectivityParams params, WindStore windStore,
RaceLogResolver raceLogResolver) {
RaceLogResolver raceLogResolver, RaceLogConnectivityParams connectivityParams) {
super(connectivityParams);
this.params = params;
this.windStore = windStore;
this.trackedRegatta = regatta;
@@ -194,17 +194,17 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
}
@Override
public Set<RaceDefinition> getRaces() {
return trackedRace == null ? null : Collections.singleton(trackedRace.getRace());
public RaceDefinition getRace() {
return trackedRace == null ? null : trackedRace.getRace();
}
@Override
public Set<RegattaAndRaceIdentifier> getRaceIdentifiers() {
return trackedRace == null ? null : Collections.singleton(trackedRace.getRaceIdentifier());
public RegattaAndRaceIdentifier getRaceIdentifier() {
return trackedRace == null ? null : trackedRace.getRaceIdentifier();
}
@Override
public RaceHandle getRacesHandle() {
public RaceHandle getRaceHandle() {
return new RaceLogRacesHandle(this);
}
@@ -312,6 +312,7 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
trackedRace.addStartTimeChangedListener(startOfTrackingController);
endOfTrackingController = new EndOfTrackingController(trackedRace, raceLog, raceLogEventAuthor);
trackedRace.addListener(endOfTrackingController);
notifyRaceCreationListeners();
logger.info(String.format("Started tracking race-log race (%s)", raceLog));
// this wakes up all waiting race handles
synchronized (this) {
@@ -1,7 +1,5 @@
package com.sap.sailing.domain.racelogtracking.impl;
import java.util.Set;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -28,12 +26,7 @@ public class RaceLogRacesHandle implements RaceHandle {
@Override
public RaceDefinition getRace(long timeoutInMilliseconds) {
long start = System.currentTimeMillis();
Set<RaceDefinition> raceDefs = tracker.getRaces();
RaceDefinition result = null;
if (raceDefs != null && !raceDefs.isEmpty()) {
result = tracker.getRaces().iterator().next();
}
RaceDefinition result = tracker.getRace();
boolean interrupted = false;
synchronized (tracker) {
while ((timeoutInMilliseconds == -1 || System.currentTimeMillis() - start < timeoutInMilliseconds)
@@ -47,13 +40,13 @@ public class RaceLogRacesHandle implements RaceHandle {
tracker.wait(timeToWait);
}
}
result = tracker.getRaces().isEmpty() ? null : tracker.getRaces().iterator().next();
result = tracker.getRace();
} catch (InterruptedException e) {
interrupted = true;
}
}
}
return result == null ? null : result;
return result;
}
@Override
@@ -96,7 +96,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
@Override
public RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn,
Fleet fleet) throws NotDenotedForRaceLogTrackingException, Exception {
Fleet fleet, boolean trackWind, boolean correctWindDirectionByMagneticDeclination)
throws NotDenotedForRaceLogTrackingException, Exception {
RaceLog raceLog = raceColumn.getRaceLog(fleet);
RaceLogTrackingState raceLogTrackingState = new RaceLogTrackingStateAnalyzer(raceLog).analyze();
if (!raceLogTrackingState.isForTracking()) {
@@ -110,7 +111,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
}
final RaceHandle result;
if (!isRaceLogRaceTrackerAttached(service, raceLog)) {
result = addTracker(service, regatta, leaderboard, raceColumn, fleet, -1);
result = addTracker(service, regatta, leaderboard, raceColumn, fleet, -1, trackWind, correctWindDirectionByMagneticDeclination);
} else {
result = null;
}
@@ -123,16 +124,15 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
* and tracking begins. Otherwise, the {@code RaceLogRaceTracker} waits until a {@code StartTrackingEvent} is added
* to perform these actions. The race first has to be denoted for racelog tracking.
*/
private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo,
Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds)
throws RaceLogRaceTrackerExistsException, Exception {
private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo, Leaderboard leaderboard,
RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds, boolean trackWind,
boolean correctWindDirectionByMagneticDeclination) throws RaceLogRaceTrackerExistsException, Exception {
RaceLog raceLog = raceColumn.getRaceLog(fleet);
assert !isRaceLogRaceTrackerAttached(service, raceLog) : new RaceLogRaceTrackerExistsException(
leaderboard.getName() + " - " + raceColumn.getName() + " - " + fleet.getName());
Regatta regatta = regattaToAddTo == null ? null : service.getRegatta(regattaToAddTo);
RaceLogConnectivityParams params = new RaceLogConnectivityParams(service, regatta, raceColumn, fleet,
leaderboard, delayToLiveInMillis, domainFactory);
leaderboard, delayToLiveInMillis, domainFactory, trackWind, correctWindDirectionByMagneticDeclination);
return service.addRace(regattaToAddTo, params, timeoutInMilliseconds);
}
@@ -135,13 +135,56 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
final GPSFix firstFixAtOrAfter;
final boolean forceFix;
markTrack.lockForRead();
try {
forceFix = Util.isEmpty(markTrack.getRawFixes())
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
&& firstFixAtOrAfter.getTimePoint().equals(timePoint);
} finally {
markTrack.unlockAfterRead();
if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
forceFix = false;
} else {
markTrack.lockForRead();
try {
if (Util.isEmpty(markTrack.getRawFixes())
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
&& firstFixAtOrAfter.getTimePoint().equals(timePoint)) {
// either the first fix or overwriting an existing one
forceFix = true;
} else {
// checking if the given fix is "better" than an existing one
TimePoint startOfTracking = trackedRace.getStartOfTracking();
TimePoint endOfTracking = trackedRace.getStartOfTracking();
if (startOfTracking != null) {
GPSFix fixAfterStartOfTracking = markTrack
.getFirstFixAtOrAfter(startOfTracking);
if (fixAfterStartOfTracking == null
|| !trackedRace.isWithinStartAndEndOfTracking(
fixAfterStartOfTracking.getTimePoint())) {
// There is no fix in the tracking interval, so this fix could be "better"
// than ones already available in the track
// Better means closer before/after the beginning/end of the tracking
// interval
if (timePoint.before(startOfTracking)) {
// check if it is closer to the beginning of the tracking interval
GPSFix fixBeforeStartOfTracking = markTrack
.getLastFixAtOrBefore(startOfTracking);
forceFix = (fixBeforeStartOfTracking == null
|| fixBeforeStartOfTracking.getTimePoint().before(timePoint));
} else if (endOfTracking != null && timePoint.after(endOfTracking)) {
// check if it is closer to the end of the tracking interval
GPSFix fixAfterEndOfTracking = markTrack
.getFirstFixAtOrAfter(endOfTracking);
forceFix = (fixAfterEndOfTracking == null
|| fixAfterEndOfTracking.getTimePoint().after(timePoint));
} else {
forceFix = false;
}
} else {
// there is already a fix in the tracking interval
forceFix = false;
}
} else {
forceFix = false;
}
}
} finally {
markTrack.unlockAfterRead();
}
}
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
}
@@ -160,6 +203,57 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
startTracking();
}
/**
* Loading fixes for {@link Mark}s needs special handling compared with {@link Competitor}s. If there are no fixes
* available in the tracking interval, it is necessary to load other available fixes before/after the tracking
* interval. this is due to the buoy pinger app can be used in the morning to ping some marks. The resulting fixes
* need to also be available for races later on the day. This method ensures that if available, fixes are initially
* loaded when starting tracking. In contrast to that, mappings for {@link Competitor}s have no special handling.
*
* @param mappings
* the added mappings
* @param item
* the item the mappings belong to
*/
private void loadFixesForNewMappings(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, WithID item) {
TimeRange trackingTimeRange = getTrackingTimeRange();
if (item instanceof Mark) {
Mark mark = (Mark) item;
if (containsMappingThatIntersectsTimeRange(mappings, trackingTimeRange)) {
loadFixesInTimeRange(mappings, trackingTimeRange);
}
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
// load all mapped fixes if there was no fix in the tracking TimeRange
final boolean loadAllMappedFixes = (track.getFirstRawFix() == null);
if (loadAllMappedFixes) {
// either got an empty track of there is no mapping for the TimeRange of the race at all.
// try again without constraining the mapping interval by start/end of tracking to at
// least attempt to get fixes at all in case there were any within the device mapping interval specified
mappings.forEach(mapping -> loadFixes(mapping.getTimeRange(), mapping));
}
} else {
loadFixesInTimeRange(mappings, trackingTimeRange);
}
}
private void loadFixesInTimeRange(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, TimeRange trackingTimeRange) {
mappings.forEach(mapping -> loadFixes(trackingTimeRange.intersection(mapping.getTimeRange()), mapping));
}
/**
* Calls the given callback for every known mapping of the given item.
*
* @param callback the callback to call for every known mapping
*/
public boolean containsMappingThatIntersectsTimeRange(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, TimeRange timeRange) {
for (DeviceMappingWithRegattaLogEvent<WithID> mapping : mappings) {
if(timeRange.intersects(mapping.getTimeRange())) {
return true;
}
}
return false;
}
private void loadFixes(TimeRange timeRangeToLoad, DeviceMappingWithRegattaLogEvent<? extends WithID> mapping) {
if(timeRangeToLoad == null) {
return;
@@ -212,15 +306,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
@SuppressWarnings("unchecked")
DeviceMapping<Mark> markMapping = (DeviceMapping<Mark>) mapping;
gpsFixStore.loadMarkTrack(track, markMapping, timeRangeToLoad.from(), timeRangeToLoad.to());
if (track.getFirstRawFix() == null) {
logger.fine("Loading mark positions from outside of start/end of tracking interval (" + timeRangeToLoad.from()
+ ".." + timeRangeToLoad.to() + ") because no fixes were found in that interval");
// got an empty track for the mark; try again without constraining the mapping interval
// by start/end of tracking to at least attempt to get fixes at all in case there were any
// within the device mapping interval specified
gpsFixStore.loadMarkTrack(track, markMapping, /* startOfTimeWindowToLoad */ null,
/* endOfTimeWindowToLoad */ null);
}
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load mark track " + mapping.getMappedTo());
}
@@ -355,26 +440,40 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
}
@Override
protected void mappingAdded(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
// The listener is first added to not lose any fix after loading the initial fixes and adding the listener.
sensorFixStore.addListener(listener, mapping.getDevice());
loadFixes(getTrackingTimeRange().intersection(mapping.getTimeRange()), mapping);
protected void mappingsAdded(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, WithID item) {
// The listener is first added. This causes that no fix is lost but potentially fixes are being processes twice.
// There are the following cases:
// 1. A fix is added before the mapping update started
// -> the fix is dropped but during mapping update the whole mapping is being loaded including this fix.
// 2. A fix is added while the write lock is being held for the mappings
// -> The fix will be processed after the write lock is removed. In this state
// the mappings are up-to-date and in a consequence the fix is loaded into the track.
// In addition, the fix is loaded on mapping update, but both fix instances consistently have the same state.
// 3. A fix is added after a successful mapping update
// -> The fix is only added once, because the initial loading during mapping update is finished
mappings.forEach(mapping -> sensorFixStore.addListener(listener, mapping.getDevice()));
loadFixesForNewMappings(mappings, item);
}
/**
* {@link #loadFixes loads fixes} that are captured by {@code newMapping} but not by {@code oldMapping}. This
* may require up to two {@link #loadFixes} calls: one for the fixes after and one for the fixes before the old
* mapping.
*/
@Override
protected void mappingChanged(DeviceMappingWithRegattaLogEvent<WithID> oldMapping,
DeviceMappingWithRegattaLogEvent<WithID> newMapping) {
final TimeRange newTimeRange = newMapping.getTimeRange();
final TimeRange oldTimeRange = oldMapping.getTimeRange();
if(newTimeRange.endsAfter(oldTimeRange)) {
final TimePoint oldTo= oldTimeRange.to();
if (newTimeRange.endsAfter(oldTimeRange)) {
final TimePoint oldTo = oldTimeRange.to();
final TimePoint newFrom = newTimeRange.from();
final TimePoint from = oldTo.after(newFrom) ? oldTo : newFrom;
final TimeRange rangeToLoad = new TimeRangeImpl(from, newTimeRange.to());
loadFixes(rangeToLoad, newMapping);
}
if(newTimeRange.startsBefore(oldTimeRange)) {
final TimePoint oldFrom= oldTimeRange.from();
if (newTimeRange.startsBefore(oldTimeRange)) {
final TimePoint oldFrom = oldTimeRange.from();
final TimePoint newTo = newTimeRange.to();
final TimePoint to = oldFrom.after(newTo) ? oldFrom : newTo;
final TimeRange rangeToLoad = new TimeRangeImpl(newTimeRange.from(), to);
@@ -120,7 +120,8 @@ public class RaceLogFixTrackerManager implements TrackingDataLoader {
stopTrackerIfStillRunning(preemptive);
trackedRace.removeListener(raceChangeListener);
synchronized (knownRaceLogs) {
for (RaceLog raceLog : knownRaceLogs) {
final Set<RaceLog> tempSet = new HashSet<>(knownRaceLogs);
for (RaceLog raceLog : tempSet) {
removeRaceLogUnlocked(raceLog);
}
}
@@ -242,7 +242,6 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
LockUtil.lockForWrite(mappingsLock);
try {
oldMappings.putAll(mappings);
mappings.clear();
mappings.putAll(newMappings);
mappingsByDevice.clear();
@@ -266,26 +265,27 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
* Calculates <em>and applies</em> the mapping changes by removing listeners no longer needed for the mappings removed,
* and by loading and adding the fixes for extended or added mappings.
*/
private void calculateDiff(Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> previousMappings,
Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings) {
private void calculateDiff(Map<ItemT, ? extends Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> previousMappings,
Map<ItemT, ? extends Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings) {
Set<ItemT> itemsToProcess = new HashSet<ItemT>(previousMappings.keySet());
itemsToProcess.addAll(newMappings.keySet());
for (ItemT item : itemsToProcess) {
if (!newMappings.containsKey(item)) {
previousMappings.get(item).forEach(this::mappingRemovedInternal);
} else {
final List<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings = previousMappings.containsKey(item)
final Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings = previousMappings.containsKey(item)
? previousMappings.get(item) : Collections.emptyList();
final List<DeviceMappingWithRegattaLogEvent<ItemT>> addedMappings = new ArrayList<>();
for (DeviceMappingWithRegattaLogEvent<ItemT> newMapping : newMappings.get(item)) {
DeviceMappingWithRegattaLogEvent<ItemT> oldMapping = findAndRemoveMapping(newMapping,
oldMappings);
DeviceMappingWithRegattaLogEvent<ItemT> oldMapping = findAndRemoveMapping(newMapping, oldMappings);
if (oldMapping == null) {
mappingAddedInternal(newMapping);
addedMappings.add(newMapping);
} else if (!newMapping.getTimeRange().equals(oldMapping.getTimeRange())) {
mappingChangedInternal(oldMapping, newMapping);
}
}
mappingsAddedInternal(addedMappings, item);
// oldMappings now still contains those mappings that were not entirely substituted by the new mappings
oldMappings.forEach(this::mappingRemovedInternal);
}
}
@@ -309,21 +309,24 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
/**
* Called when a {@link DeviceMapping} was added.
*
* @param mapping the new mapping
* @param mappings the new mapping
*/
protected abstract void mappingAdded(DeviceMappingWithRegattaLogEvent<ItemT> mapping);
protected abstract void mappingsAdded(Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings, ItemT item);
private void mappingAddedInternal(DeviceMappingWithRegattaLogEvent<ItemT> mapping) {
private void mappingsAddedInternal(Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings, ItemT item) {
try {
mappingAdded(mapping);
mappingsAdded(mappings, item);
} catch(Exception e) {
logger.log(Level.SEVERE, "error while adding mapping " + mapping, e);
logger.log(Level.SEVERE, "error while adding mapping " + mappings, e);
}
}
/**
* Called when a {@link DeviceMapping} was changed regarding its mapped time range.
* This can occur if an open ended mapping is being closed or a close event gets revoked.
* This can occur if an open ended mapping is being closed or a close event gets revoked
* or a new mapping is added that may partly overlap with an old mapping such that only
* parts of the fixes covered by the new mapping need to be loaded (the ones not already
* covered by the old mapping).
*
* @param oldMapping the old mapping
* @param newMapping the new mapping
@@ -342,11 +345,10 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
private DeviceMappingWithRegattaLogEvent<ItemT> findAndRemoveMapping(
DeviceMappingWithRegattaLogEvent<ItemT> mappingToFind,
List<DeviceMappingWithRegattaLogEvent<ItemT>> newItemsToProcess) {
for (Iterator<DeviceMappingWithRegattaLogEvent<ItemT>> iterator = newItemsToProcess.iterator(); iterator
.hasNext();) {
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> newItemsToProcess) {
for (Iterator<DeviceMappingWithRegattaLogEvent<ItemT>> iterator = newItemsToProcess.iterator(); iterator.hasNext();) {
DeviceMappingWithRegattaLogEvent<ItemT> deviceMapping = iterator.next();
if(isSame(mappingToFind, deviceMapping)) {
if (isSame(mappingToFind, deviceMapping)) {
iterator.remove();
return deviceMapping;
}
@@ -354,9 +356,15 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
return null;
}
/**
* Compares two device mappings based on their device, the item mapped to and the race/regatta log event type
* that usually corresponds with the type of item to which the device is mapped. Note that in particular the
* mappings' time ranges are ignored for this comparison.
*/
private boolean isSame(DeviceMappingWithRegattaLogEvent<ItemT> mapping1,
DeviceMappingWithRegattaLogEvent<ItemT> mapping2) {
return mapping1.getDevice().equals(mapping2.getDevice()) && mapping1.getMappedTo().equals(mapping2.getMappedTo())
&& mapping1.getEventType().equals(mapping2.getEventType());
&& mapping1.getEventType().equals(mapping2.getEventType())
&& mapping1.getRegattaLogEvent().getId().equals(mapping2.getRegattaLogEvent().getId());
}
}
@@ -413,7 +413,7 @@ public class CurrentRaceFilterImplTest {
for (int i=0; i < numberOfRaces; i++) {
_505RaceCells.add(new RaceCellImpl(raceColumnNamePrefix+(i+firstRaceColumnNumber),
/* race log */ null, /* factor */ firstRaceColumnNumber, /* explicitFactor */ null,
/* zeroBasedIndexInFleet */ i));
/* zeroBasedIndexInFleet */ i, /* targetTime */ null));
}
final RaceRowImpl _505RaceRow = new RaceRowImpl(new FleetImpl(fleetName), _505RaceCells);
return _505RaceRow;
@@ -68,6 +68,8 @@ public interface CompetitorStore extends CompetitorFactory {
String newSailId, Nationality newNationality, URI newTeamImageUri, URI newFlagImageUri,
Double timeOnTimeFactor, Duration timeOnDistanceAllowancePerNauticalMile, String searchTag);
void addCompetitors(Iterable<Competitor> competitors);
CompetitorDTO convertToCompetitorDTO(Competitor c);
/**
@@ -110,4 +110,6 @@ public interface SharedDomainFactory extends CompetitorFactory {
ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(String id, String name, Mark left, Mark right);
RaceLogResolver getRaceLogResolver();
Mark getOrCreateMark(String name, MarkType markType);
}
@@ -48,7 +48,9 @@ public class BoatClassImpl extends NamedImpl implements BoatClass {
public BoatClassImpl(String name, boolean typicallyStartsUpwind) {
this(name, typicallyStartsUpwind, /* displayName */ null,
/* hull length */ new MeterDistance(5), /* hullBeam */ null, /* hullType */ null);
// use the typical dinghy parameters as default
/* hull length */ new MeterDistance(5), /* hullBeam */ new MeterDistance(1.8),
/* hullType */ BoatHullType.MONOHULL);
}
public BoatClassImpl(String name, BoatClassMasterdata masterData) {
@@ -154,14 +154,23 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory {
return getOrCreateMark(name, name);
}
@Override
public Mark getOrCreateMark(String name, MarkType markType) {
return getOrCreateMark(name, name, markType);
}
private Mark getOrCreateMark(Serializable id, String name, MarkType markType) {
return getOrCreateMark(id, name, markType, /* color */ null, /* shape */ null, /* pattern */ null);
}
@Override
public Mark getOrCreateMark(Serializable id, String name) {
return getOrCreateMark(id, name, null, null, null, null);
return getOrCreateMark(id, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null);
}
@Override
public Mark getOrCreateMark(String toStringRepresentationOfID, String name) {
return getOrCreateMark(toStringRepresentationOfID, name, null, null, null, null);
return getOrCreateMark(toStringRepresentationOfID, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null);
}
@Override
@@ -250,7 +250,8 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab
c.getFlagImage() == null ? null : c.getFlagImage().toString(),
new BoatDTO(c.getBoat().getName(), c.getBoat().getSailID(), c.getBoat().getColor()),
new BoatClassDTO(c.getBoat().getBoatClass()
.getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength()),
.getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength(),
c.getBoat().getBoatClass().getHullBeam()),
c.getTimeOnTimeFactor(), c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag());
weakCompetitorDTOCache.put(c, competitorDTO);
}
@@ -274,4 +275,17 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab
LockUtil.unlockAfterWrite(lock);
}
}
@Override
public void addCompetitors(Iterable<Competitor> competitors) {
LockUtil.lockForWrite(lock);
try {
for (Competitor competitor: competitors) {
competitorCache.put(competitor.getId(), competitor);
competitorsByIdAsString.put(competitor.getId().toString(), competitor);
}
} finally {
LockUtil.unlockAfterWrite(lock);
}
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.common.TargetTimeInfo;
import com.sap.sse.common.Named;
import com.sap.sse.common.Util;
@@ -33,4 +34,11 @@ public interface RaceCell extends Named {
* index 0.
*/
int getZeroBasedIndexInFleet();
/**
* If there is a tracked race for this race and we have sufficient information about wind and the boat class's
* polar / VPP data this method will return an estimated duration for how long the race will take based on the
* current course.
*/
TargetTimeInfo getTargetTime();
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.base.racegroup.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sailing.domain.common.TargetTimeInfo;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedImpl;
@@ -21,6 +22,8 @@ public class RaceCellImpl extends NamedImpl implements RaceCell {
private final Double explicitFactor;
private final int zeroBasedIndexInFleet;
private final TargetTimeInfo targetTime;
/**
* @param zeroBasedIndexInFleet
@@ -34,12 +37,13 @@ public class RaceCellImpl extends NamedImpl implements RaceCell {
* except in case the first race column is a "virtual" one that holds a non-discardable carry-forward
* result. In this case, the second Race Column, which is the first "non-virtual" one, receives index 0.
*/
public RaceCellImpl(String raceColumnName, RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
public RaceCellImpl(String raceColumnName, RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet, TargetTimeInfo targetTime) {
super(raceColumnName);
this.raceLog = raceLog;
this.factor = factor;
this.explicitFactor = explicitFactor;
this.zeroBasedIndexInFleet = zeroBasedIndexInFleet;
this.targetTime = targetTime;
}
@Override
@@ -70,9 +74,14 @@ public class RaceCellImpl extends NamedImpl implements RaceCell {
return zeroBasedIndexInFleet;
}
@Override
public TargetTimeInfo getTargetTime() {
return targetTime;
}
@Override
public String toString() {
return "RaceCellImpl [factor=" + factor + ", explicitFactor=" + explicitFactor + ", zeroBasedIndexInFleet="
+ zeroBasedIndexInFleet + ", getName()=" + getName() + "]";
+ zeroBasedIndexInFleet + ", name=" + getName() + ", targetTime: " + getTargetTime() + "]";
}
}
@@ -10,8 +10,12 @@ Require-Bundle: com.sap.sailing.domain.swisstimingadapter,
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
com.sap.sse.mongodb,
com.sap.sailing.domain,
com.sap.sailing.domain.common
com.sap.sailing.domain.common,
com.sap.sse.common,
com.sap.sailing.domain.persistence,
com.sap.sse
Export-Package: com.sap.sailing.domain.swisstimingadapter.persistence,
com.sap.sailing.domain.swisstimingadapter.persistence.impl; x-friends:="com.sap.sailing.domain.swisstimingadapter.test"
Import-Package: org.osgi.framework
com.sap.sailing.domain.swisstimingadapter.persistence.impl;x-friends:="com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.mongodb.test"
Import-Package: org.osgi.framework,
org.osgi.util.tracker;version="1.5.1"
Bundle-Activator: com.sap.sailing.domain.swisstimingadapter.persistence.impl.Activator
@@ -1,16 +1,52 @@
package com.sap.sailing.domain.swisstimingadapter.persistence.impl;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.mongodb.MongoDBService;
import com.sap.sse.util.ServiceTrackerFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
@Override
public void start(BundleContext context) throws Exception {
for (CollectionNames name : CollectionNames.values()) {
MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
}
new Thread(() -> {
final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class);
final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class);
try {
final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0);
final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0);
final Dictionary<String, Object> properties = new Hashtable<String, Object>();
final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(
domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory();
final SwissTimingConnectivityParamsHandler paramsHandler = new SwissTimingConnectivityParamsHandler(
MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory),
MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory),
domainFactory);
properties.put(TypeBasedServiceFinder.TYPE, SwissTimingTrackingConnectivityParameters.TYPE);
context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties);
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to register SwissTiming RaceTrackingConnectivityParametersHandler implementation", e);
}
}, getClass().getName() + " registering connectivity handler").start();
}
@Override
@@ -0,0 +1,192 @@
package com.sap.sailing.domain.swisstimingadapter.persistence.impl;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.CrewMember;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.StartList;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithID;
import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithoutID;
import com.sap.sailing.domain.swisstimingadapter.impl.CrewMemberImpl;
import com.sap.sailing.domain.swisstimingadapter.impl.StartListImpl;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler;
import com.sap.sse.common.TypeBasedServiceFinder;
/**
* Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The
* "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.<p>
*
* Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to
* its members.
*
* @author Axel Uhl (d043530)
*
*/
public class SwissTimingConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler {
private static final String CREW_MEMBER_POSITION = "crewMemberPosition";
private static final String CREW_MEMBER_NATIONALITY = "crewMemberNationality";
private static final String CREW_MEMBER_NAME = "crewMemberName";
private static final String COMPETITOR_CREW = "competitorCrew";
private static final String COMPETITOR_THREE_LETTER_IOC_CODE = "competitorThreeLetterIocCode";
private static final String COMPETITOR_NAME = "competitorName";
private static final String COMPETITOR_BOAT_ID = "competitorBoatId";
private static final String COMPETITOR_ID = "competitorId";
private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm";
private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis";
private static final String START_LIST = "startList";
private static final String RACE_NAME = "raceName";
private static final String RACE_ID = "raceID";
private static final String RACE_DESCRIPTION = "raceDescription";
private static final String PORT = "port";
private static final String BOAT_CLASS_NAME = "boatClassName";
private static final String HOSTNAME = "hostname";
private final RaceLogStore raceLogStore;
private final RegattaLogStore regattaLogStore;
private final DomainFactory domainFactory;
private final SwissTimingFactory swissTimingFactory;
public SwissTimingConnectivityParamsHandler(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory) {
super();
this.raceLogStore = raceLogStore;
this.regattaLogStore = regattaLogStore;
this.domainFactory = domainFactory;
this.swissTimingFactory = SwissTimingFactory.INSTANCE;
}
@Override
public Map<String, Object> mapFrom(RaceTrackingConnectivityParameters params) {
assert params instanceof SwissTimingTrackingConnectivityParameters;
final SwissTimingTrackingConnectivityParameters stParams = (SwissTimingTrackingConnectivityParameters) params;
final Map<String, Object> result = getKey(params);
result.put(BOAT_CLASS_NAME, stParams.getBoatClass()==null?null:stParams.getBoatClass().getName());
result.put(RACE_DESCRIPTION, stParams.getRaceDescription());
result.put(RACE_NAME, stParams.getRaceName());
result.put(START_LIST, createStartListDBObject(stParams));
result.put(DELAY_TO_LIVE_IN_MILLIS, stParams.getDelayToLiveInMillis());
result.put(USE_INTERNAL_MARK_PASSING_ALGORITHM, stParams.isUseInternalMarkPassingAlgorithm());
addWindTrackingParameters(stParams, result);
return result;
}
private BasicDBList createStartListDBObject(final SwissTimingTrackingConnectivityParameters stParams) {
final BasicDBList startListDbObject;
if (stParams.getStartList() != null) {
startListDbObject = new BasicDBList();
for (final Competitor competitor : stParams.getStartList().getCompetitors()) {
DBObject competitorDBObject = createCompetitorDBObject(competitor);
startListDbObject.add(competitorDBObject);
}
} else {
startListDbObject = null;
}
return startListDbObject;
}
private StartList createStartListFromDBObject(String raceId, BasicDBList startListDBObject) {
List<Competitor> competitors = new ArrayList<>();
for (final Object competitorObject : startListDBObject) {
final DBObject competitorDBObject = (DBObject) competitorObject;
competitors.add(createCompetitorFromDBObject(competitorDBObject));
}
return new StartListImpl(raceId, competitors);
}
private DBObject createCompetitorDBObject(Competitor competitor) {
final DBObject result = new BasicDBObject();
result.put(COMPETITOR_ID, competitor.getID());
result.put(COMPETITOR_BOAT_ID, competitor.getBoatID());
result.put(COMPETITOR_NAME, competitor.getName());
result.put(COMPETITOR_THREE_LETTER_IOC_CODE, competitor.getThreeLetterIOCCode());
result.put(COMPETITOR_CREW, createCrewDbObject(competitor.getCrew()));
return result;
}
private BasicDBList createCrewDbObject(List<CrewMember> crew) {
final BasicDBList result;
if (crew == null) {
result = null;
} else {
result = new BasicDBList();
for (final CrewMember crewMember : crew) {
final DBObject crewMemberDBObject = new BasicDBObject();
crewMemberDBObject.put(CREW_MEMBER_NAME, crewMember.getName());
crewMemberDBObject.put(CREW_MEMBER_NATIONALITY, crewMember.getNationality());
crewMemberDBObject.put(CREW_MEMBER_POSITION, crewMember.getPosition());
result.add(crewMemberDBObject);
}
}
return result;
}
private List<CrewMember> createCrewFromDBObject(BasicDBList crewDBObject) {
final List<CrewMember> result;
if (crewDBObject == null) {
result = null;
} else {
result = new ArrayList<>();
for (final Object o : crewDBObject) {
final DBObject crewMemberDBObject = (DBObject) o;
result.add(new CrewMemberImpl((String) crewMemberDBObject.get(CREW_MEMBER_NAME),
(String) crewMemberDBObject.get(CREW_MEMBER_NATIONALITY),
(String) crewMemberDBObject.get(CREW_MEMBER_POSITION)));
}
}
return result;
}
private Competitor createCompetitorFromDBObject(DBObject competitorDBObject) {
final Competitor result;
final String competitorId = (String) competitorDBObject.get(COMPETITOR_ID);
final String boatID = (String) competitorDBObject.get(COMPETITOR_BOAT_ID);
final String threeLetterIOCCode = (String) competitorDBObject.get(COMPETITOR_THREE_LETTER_IOC_CODE);
final String name = (String) competitorDBObject.get(COMPETITOR_NAME);
final BasicDBList crewDBObject = (BasicDBList) competitorDBObject.get(COMPETITOR_CREW);
if (competitorId == null) {
result = new CompetitorWithoutID(boatID, threeLetterIOCCode, name);
} else {
result = new CompetitorWithID(competitorId, boatID, threeLetterIOCCode, name, createCrewFromDBObject(crewDBObject));
}
return result;
}
@Override
public RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws MalformedURLException, URISyntaxException {
return new SwissTimingTrackingConnectivityParameters(
(String) map.get(HOSTNAME),
((Number) map.get(PORT)).intValue(),
(String) map.get(RACE_ID),
(String) map.get(RACE_NAME),
(String) map.get(RACE_DESCRIPTION),
domainFactory.getBaseDomainFactory().getOrCreateBoatClass((String) map.get(BOAT_CLASS_NAME)),
map.get(START_LIST) == null ? null : createStartListFromDBObject((String) map.get(RACE_ID), (BasicDBList) map.get(START_LIST)),
((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(),
swissTimingFactory, domainFactory, raceLogStore, regattaLogStore,
(boolean) map.get(USE_INTERNAL_MARK_PASSING_ALGORITHM), isTrackWind(map), isCorrectWindDirectionByMagneticDeclination(map));
}
@Override
public Map<String, Object> getKey(RaceTrackingConnectivityParameters params) {
assert params instanceof SwissTimingTrackingConnectivityParameters;
final SwissTimingTrackingConnectivityParameters stParams = (SwissTimingTrackingConnectivityParameters) params;
final Map<String, Object> result = new HashMap<>();
result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier());
result.put(HOSTNAME, stParams.getHostname());
result.put(PORT, stParams.getPort());
result.put(RACE_ID, stParams.getRaceID());
return result;
}
}
@@ -16,7 +16,7 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.xrr.schema,
com.sap.sailing.domain.shared.android
Export-Package: com.sap.sailing.domain.swisstimingadapter,
com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test"
com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test,com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.persistence"
Import-Package: com.sap.sailing.xrr.schema,
org.osgi.framework
Bundle-Activator: com.sap.sailing.domain.swisstimingadapter.impl.Activator
@@ -1,9 +1,9 @@
package com.sap.sailing.domain.swisstimingadapter;
public interface CrewMember {
public String getName();
public String getNationality();
public String getName();
public String getPosition();
public String getNationality();
public String getPosition();
}
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
@@ -31,6 +32,9 @@ public interface DomainFactory {
com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory();
/**
* @param boatClass if {@code null}, the boat class will be inferred from the Race ID
*/
Regatta getOrCreateDefaultRegatta(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String raceID, BoatClass boatClass, TrackedRegattaRegistry trackedRegattaRegistry);
Nationality getOrCreateNationality(String threeLetterIOCCode);
@@ -62,11 +66,10 @@ public interface DomainFactory {
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID,
String raceName, String raceDescription, BoatClass boatClass, StartList startList,
long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm);
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination);
ControlPoint getOrCreateControlPoint(Iterable<String> devices);
RaceDefinition createRaceDefinition(Regatta regatta, String raceID, Iterable<Competitor> competitors,
List<ControlPoint> courseDefinition);
ControlPoint getOrCreateControlPoint(Iterable<String> devices, MarkType markType);
RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable<Competitor> competitors,
List<ControlPoint> courseDefinition, String raceName, String raceIdForRaceDefinition);
}
@@ -26,10 +26,12 @@ public interface SwissTimingAdapter {
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
* @param useInternalMarkPassingAlgorithm use our own instead of the SwissTiming-provided mark passing / split times
* @param trackWind TODO
* @param correctWindDirectionByMagneticDeclination TODO
*/
RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID,
String raceName, String raceDescription, BoatClass boatClass, String hostname, int port,
StartList startList, RaceLogStore logStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, boolean useInternalMarkPassingAlgorithm)
StartList startList, RaceLogStore logStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination)
throws InterruptedException, UnknownHostException, IOException, ParseException, Exception;
StartList readStartListForRace(String raceId, RegattaResults regattaResults);
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingFactoryImpl;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindStore;
@@ -54,14 +55,15 @@ public interface SwissTimingFactory {
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws InterruptedException, UnknownHostException,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, SwissTimingTrackingConnectivityParameters connectivityParams) throws InterruptedException, UnknownHostException,
IOException, ParseException;
RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
WindStore windStore, boolean useInternalMarkPassingAlgorithm,
DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws UnknownHostException,
InterruptedException, IOException, ParseException;
WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, SwissTimingTrackingConnectivityParameters connectivityParams)
throws UnknownHostException, InterruptedException, IOException, ParseException;
Race createRace(String raceId, String raceName, String description, BoatClass boatClass);
@@ -0,0 +1,72 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
public abstract class AbstractCompetitor implements Competitor {
private final String boatID;
private final String threeLetterIOCCode;
private final String name;
public AbstractCompetitor(String boatID, String threeLetterIOCCode, String name) {
super();
this.boatID = boatID;
this.threeLetterIOCCode = threeLetterIOCCode;
this.name = name;
}
@Override
public String getBoatID() {
return boatID;
}
@Override
public String getThreeLetterIOCCode() {
return threeLetterIOCCode;
}
@Override
public String getName() {
return name;
}
public String getID() {
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((boatID == null) ? 0 : boatID.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((threeLetterIOCCode == null) ? 0 : threeLetterIOCCode.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;
AbstractCompetitor other = (AbstractCompetitor) obj;
if (boatID == null) {
if (other.boatID != null)
return false;
} else if (!boatID.equals(other.boatID))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (threeLetterIOCCode == null) {
if (other.threeLetterIOCCode != null)
return false;
} else if (!threeLetterIOCCode.equals(other.threeLetterIOCCode))
return false;
return true;
}
}
@@ -2,54 +2,61 @@ package com.sap.sailing.domain.swisstimingadapter.impl;
import java.util.List;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.CrewMember;
public class CompetitorWithID implements Competitor {
private final String boatID;
private final String threeLetterIOCCode;
private final String name;
public class CompetitorWithID extends AbstractCompetitor {
private final String id;
private List<CrewMember> crew;
public CompetitorWithID(String id, String boatID, String threeLetterIOCCode, String name, List<CrewMember> crew) {
super();
this.boatID = boatID;
this.threeLetterIOCCode = threeLetterIOCCode;
this.name = name;
super(boatID, threeLetterIOCCode, name);
this.id = id;
this.crew = crew;
}
@Override
public String getBoatID() {
return boatID;
}
@Override
public String getThreeLetterIOCCode() {
return threeLetterIOCCode;
}
@Override
public String getName() {
return name;
}
public String getID() {
return id;
}
@Override
public String toString() {
return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode=" + threeLetterIOCCode + ", name=" + name
+ ", id=" + id + "]";
}
@Override
public List<CrewMember> getCrew() {
return crew;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((crew == null) ? 0 : crew.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
CompetitorWithID other = (CompetitorWithID) obj;
if (crew == null) {
if (other.crew != null)
return false;
} else if (!crew.equals(other.crew))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
return "CompetitorWithID [boatID=" + getBoatID() + ", threeLetterIOCCode=" + getThreeLetterIOCCode() + ", name=" + getName()
+ ", id=" + id + ", crew=" + getCrew() + "]";
}
}
@@ -2,49 +2,25 @@ package com.sap.sailing.domain.swisstimingadapter.impl;
import java.util.List;
import com.sap.sailing.domain.swisstimingadapter.Competitor;
import com.sap.sailing.domain.swisstimingadapter.CrewMember;
public class CompetitorWithoutID implements Competitor {
private final String boatID;
private final String threeLetterIOCCode;
private final String name;
public class CompetitorWithoutID extends AbstractCompetitor {
public CompetitorWithoutID(String boatID, String threeLetterIOCCode, String name) {
super();
this.boatID = boatID;
this.threeLetterIOCCode = threeLetterIOCCode;
this.name = name;
super(boatID, threeLetterIOCCode, name);
}
@Override
public String getBoatID() {
return boatID;
}
@Override
public String getThreeLetterIOCCode() {
return threeLetterIOCCode;
}
@Override
public String getName() {
return name;
}
public String getID() {
return null;
}
@Override
public String toString() {
return "CompetitorWithoutID [boatID=" + boatID + ", threeLetterIOCCode="
+ threeLetterIOCCode + ", name=" + name + "]";
}
@Override
public List<CrewMember> getCrew() {
return null;
}
@Override
public List<CrewMember> getCrew() {
return null;
}
@Override
public String toString() {
return "CompetitorWithoutID [boatID=" + getBoatID() + ", threeLetterIOCCode=" + getThreeLetterIOCCode() + ", name=" + getName() + "]";
}
}
@@ -3,29 +3,70 @@ package com.sap.sailing.domain.swisstimingadapter.impl;
import com.sap.sailing.domain.swisstimingadapter.CrewMember;
public class CrewMemberImpl implements CrewMember {
private String name;
private String nationality;
private String position;
private String name;
private String nationality;
private String position;
public CrewMemberImpl(String name, String nationality, String position) {
super();
this.name = name;
this.nationality = nationality;
this.position = position;
}
public CrewMemberImpl(String name, String nationality, String position) {
super();
this.name = name;
this.nationality = nationality;
this.position = position;
}
@Override
public String getName() {
return name;
}
@Override
public String getName() {
return name;
}
@Override
public String getNationality() {
return nationality;
}
@Override
public String getNationality() {
return nationality;
}
public String getPosition() {
return position;
}
public String getPosition() {
return position;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((nationality == null) ? 0 : nationality.hashCode());
result = prime * result + ((position == null) ? 0 : position.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;
CrewMemberImpl other = (CrewMemberImpl) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (nationality == null) {
if (other.nationality != null)
return false;
} else if (!nationality.equals(other.nationality))
return false;
if (position == null) {
if (other.position != null)
return false;
} else if (!position.equals(other.position))
return false;
return true;
}
@Override
public String toString() {
return "CrewMemberImpl [name=" + name + ", nationality=" + nationality + ", position=" + position + "]";
}
}
@@ -28,6 +28,7 @@ import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
@@ -176,17 +177,17 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public RaceDefinition createRaceDefinition(Regatta regatta, String raceID, Iterable<Competitor> competitors,
List<ControlPoint> courseDefinition) {
public RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable<Competitor> competitors,
List<ControlPoint> courseDefinition, String raceName, String raceIdForRaceDefinition) {
List<Waypoint> waypoints = new ArrayList<>();
for (ControlPoint controlPoint : courseDefinition) {
Waypoint waypoint = baseDomainFactory.createWaypoint(controlPoint, /* passingInstruction */ PassingInstruction.None);
waypoints.add(waypoint);
}
com.sap.sailing.domain.base.Course domainCourse = new CourseImpl("Course", waypoints);
BoatClass boatClass = getRaceTypeFromRaceID(raceID).getBoatClass();
logger.info("Creating RaceDefinitionImpl for race "+raceID);
RaceDefinition result = new RaceDefinitionImpl(regatta.getId().toString()+"/"+raceID, domainCourse, boatClass, competitors);
BoatClass boatClass = getRaceTypeFromRaceID(swissTimingRaceID).getBoatClass();
logger.info("Creating RaceDefinitionImpl for race "+swissTimingRaceID);
RaceDefinition result = new RaceDefinitionImpl(raceName, domainCourse, boatClass, competitors, raceIdForRaceDefinition);
regatta.addRace(result);
return result;
}
@@ -264,7 +265,7 @@ public class DomainFactoryImpl implements DomainFactory {
private com.sap.sailing.domain.base.Course createCourse(String courseName, Course course) {
List<Waypoint> waypoints = new ArrayList<Waypoint>();
for (Mark mark : course.getMarks()) {
ControlPoint controlPoint = getOrCreateControlPoint(mark.getDevices());
ControlPoint controlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType()));
Waypoint waypoint = baseDomainFactory.createWaypoint(controlPoint, /* passingInstruction */ PassingInstruction.None);
waypoints.add(waypoint);
}
@@ -272,15 +273,28 @@ public class DomainFactoryImpl implements DomainFactory {
return result;
}
/**
* Converts a mark type as defined by SwissTiming into a {@link MarkType} as defined by the domain model
*/
private MarkType getMarkType(com.sap.sailing.domain.swisstimingadapter.Mark.MarkType markType) {
final MarkType result;
if (markType == null) {
result = null;
} else {
result = MarkType.BUOY;
}
return result;
}
@Override
public ControlPoint getOrCreateControlPoint(Iterable<String> devices) {
public ControlPoint getOrCreateControlPoint(Iterable<String> devices, MarkType markType) {
ControlPoint result;
synchronized (controlPointCache) {
result = controlPointCache.get(devices);
if (result == null) {
switch (Util.size(devices)) {
case 1:
result = getOrCreateMark(devices.iterator().next());
result = getOrCreateMark(devices.iterator().next(), markType);
break;
case 2:
Iterator<String> markNameIter = devices.iterator();
@@ -299,6 +313,10 @@ public class DomainFactoryImpl implements DomainFactory {
return result;
}
private ControlPoint getOrCreateMark(String name, MarkType markType) {
return baseDomainFactory.getOrCreateMark(name, markType);
}
/**
* @param trackerID
* the "device name" and the "sail number" in case of an {@link MessageType#RPD RPD} message, used as the mark's
@@ -321,7 +339,7 @@ public class DomainFactoryImpl implements DomainFactory {
List<com.sap.sse.common.Util.Pair<com.sap.sailing.domain.base.ControlPoint, PassingInstruction>> newDomainControlPoints = new ArrayList<com.sap.sse.common.Util.Pair<com.sap.sailing.domain.base.ControlPoint, PassingInstruction>>();
for (Mark mark : marks) {
// TODO bug 1043: propagate the mark names to the waypoint names
com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDevices());
com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType()));
newDomainControlPoints.add(new com.sap.sse.common.Util.Pair<>(domainControlPoint, PassingInstruction.None));
}
courseToUpdate.update(newDomainControlPoints, baseDomainFactory);
@@ -359,10 +377,10 @@ public class DomainFactoryImpl implements DomainFactory {
public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port,
String raceID, String raceName, String raceDescription, BoatClass boatClass, StartList startList,
long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm) {
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm,
boolean trackWind, boolean correctWindDirectionByMagneticDeclination) {
return new SwissTimingTrackingConnectivityParameters(hostname, port, raceID, raceName, raceDescription,
boatClass, startList, delayToLiveInMillis, swissTimingFactory, domainFactory, raceLogStore,
regattaLogStore, useInternalMarkPassingAlgorithm);
regattaLogStore, useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination);
}
}
@@ -59,94 +59,96 @@ public class SwissTimingAdapterImpl implements SwissTimingAdapter {
@Override
public List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname,
int port) throws InterruptedException, UnknownHostException, IOException,
ParseException {
int port) throws InterruptedException, UnknownHostException, IOException, ParseException {
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> result = new ArrayList<com.sap.sailing.domain.swisstimingadapter.RaceRecord>();
// TODO --> Frank: this needs to come from Manage2Sail and its JSON document somehow...
return result;
}
@Override
public RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo,
String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port,
public RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID,
String raceName, String raceDescription, BoatClass boatClass, String hostname, int port,
StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds,
boolean useInternalMarkPassingAlgorithm)
throws Exception {
return trackerManager.addRace(regattaToAddTo, swissTimingDomainFactory.createTrackingConnectivityParameters(
hostname, port, raceID, raceName, raceDescription, boatClass, startList,
DEFAULT_SWISSTIMING_LIVE_DELAY_IN_MILLISECONDS, swissTimingFactory, swissTimingDomainFactory,
raceLogStore, regattaLogStore, useInternalMarkPassingAlgorithm), timeoutInMilliseconds);
boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws Exception {
return trackerManager.addRace(regattaToAddTo,
swissTimingDomainFactory.createTrackingConnectivityParameters(hostname, port, raceID, raceName,
raceDescription, boatClass, startList, DEFAULT_SWISSTIMING_LIVE_DELAY_IN_MILLISECONDS,
swissTimingFactory, swissTimingDomainFactory, raceLogStore, regattaLogStore,
useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination),
timeoutInMilliseconds);
}
@Override
public StartList readStartListForRace(String raceId, RegattaResults regattaResults) {
StartList result = null;
StartList result = null;
List<Competitor> competitors = new ArrayList<Competitor>();
Map<String, Person> persons = new HashMap<String, Person>();
Map<String, Boat> boats = new HashMap<String, Boat>();
Map<String, Team> teams = new HashMap<String, Team>();
// collect boats, persons and teams
Map<String, Person> persons = new HashMap<String, Person>();
Map<String, Boat> boats = new HashMap<String, Boat>();
Map<String, Team> teams = new HashMap<String, Team>();
// collect boats, persons and teams
for (Object o : regattaResults.getPersonOrBoatOrTeam()) {
if(o instanceof Person) {
Person p = (Person) o;
persons.put(p.getPersonID(), p);
} else if (o instanceof Boat) {
Boat b = (Boat) o;
boats.put(b.getBoatID(), b);
} else if (o instanceof Team) {
Team t = (Team) o;
teams.put(t.getTeamID(), t);
}
if (o instanceof Person) {
Person p = (Person) o;
persons.put(p.getPersonID(), p);
} else if (o instanceof Boat) {
Boat b = (Boat) o;
boats.put(b.getBoatID(), b);
} else if (o instanceof Team) {
Team t = (Team) o;
teams.put(t.getTeamID(), t);
}
}
for (Object o : regattaResults.getPersonOrBoatOrTeam()) {
if (o instanceof Event) {
Event event = (Event) o;
Event event = (Event) o;
for (Object eventO : event.getRaceOrDivisionOrRegattaSeriesResult()) {
if (eventO instanceof Division) {
Division division = (Division) eventO;
for(Object seriesResultOrRaceResultOrTRResult: division.getSeriesResultOrRaceResultOrTRResult()) {
if(seriesResultOrRaceResultOrTRResult instanceof RaceResult) {
RaceResult raceResult = (RaceResult) seriesResultOrRaceResultOrTRResult;
if(raceResult.getRaceID().equals(raceId)) {
Team team = teams.get(raceResult.getTeamID());
if(team != null) {
Boat boat = boats.get(team.getBoatID());
if(boat != null) {
List<CrewMember> crew = new ArrayList<CrewMember>();
for(Crew crewMember: team.getCrew()) {
Person person = persons.get(crewMember.getPersonID());
crew.add(new CrewMemberImpl(person.getGivenName() + " " + person.getFamilyName(),
person.getNOC().name(), crewMember.getPosition().name()));
}
Crew firstCrewMember = team.getCrew().get(0);
Person person = persons.get(firstCrewMember.getPersonID());
String nationality = person.getNOC().name();
CompetitorWithID competitor = new CompetitorWithID(team.getTeamID(), boat.getSailNumber(), nationality, team.getTeamName(), crew);
competitors.add(competitor);
}
}
}
}
for (Object seriesResultOrRaceResultOrTRResult : division.getSeriesResultOrRaceResultOrTRResult()) {
if (seriesResultOrRaceResultOrTRResult instanceof RaceResult) {
RaceResult raceResult = (RaceResult) seriesResultOrRaceResultOrTRResult;
if (raceResult.getRaceID().equals(raceId)) {
Team team = teams.get(raceResult.getTeamID());
if (team != null) {
Boat boat = boats.get(team.getBoatID());
if (boat != null) {
List<CrewMember> crew = new ArrayList<CrewMember>();
for (Crew crewMember : team.getCrew()) {
Person person = persons.get(crewMember.getPersonID());
crew.add(new CrewMemberImpl(
person.getGivenName() + " " + person.getFamilyName(),
person.getNOC().name(), crewMember.getPosition().name()));
}
Crew firstCrewMember = team.getCrew().get(0);
Person person = persons.get(firstCrewMember.getPersonID());
String nationality = person.getNOC().name();
CompetitorWithID competitor = new CompetitorWithID(team.getTeamID(),
boat.getSailNumber(), nationality, team.getTeamName(), crew);
competitors.add(competitor);
}
}
}
}
}
}
}
}
}
if(!competitors.isEmpty()) {
result = new StartListImpl(raceId, competitors);
if (!competitors.isEmpty()) {
result = new StartListImpl(raceId, competitors);
}
return result;
}
@Override
public RegattaResults readRegattaEntryListFromXrrUrl(String xrrEntryListUrl) throws IOException, JAXBException {
// try to read the entry list from manage2sail
// try to read the entry list from manage2sail
URL regattaEntryListUrl = new URL(xrrEntryListUrl);
URLConnection regattaEntryListConn = regattaEntryListUrl.openConnection();
RegattaResults regattaResults = ParserFactory.INSTANCE.createParser((InputStream) regattaEntryListConn.getContent(), xrrEntryListUrl).parse();
RegattaResults regattaResults = ParserFactory.INSTANCE
.createParser((InputStream) regattaEntryListConn.getContent(), xrrEntryListUrl).parse();
return regattaResults;
}
}
@@ -92,22 +92,23 @@ public class SwissTimingFactoryImpl implements SwissTimingFactory {
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, SwissTimingTrackingConnectivityParameters connectivityParams)
throws InterruptedException, UnknownHostException, IOException, ParseException {
return new SwissTimingRaceTrackerImpl(raceID, raceName, raceDescription, boatClass, hostname, port, startList,
raceLogStore, regattaLogStore, windStore, domainFactory, this,
trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm);
trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams);
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
WindStore windStore, boolean useInternalMarkPassingAlgorithm,
DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, SwissTimingTrackingConnectivityParameters connectivityParams)
throws UnknownHostException, InterruptedException, IOException, ParseException {
return new SwissTimingRaceTrackerImpl(regatta, raceID, raceName, raceDescription, boatClass, hostname, port,
startList, windStore, domainFactory, this, trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis,
useInternalMarkPassingAlgorithm);
startList, windStore, domainFactory, this, trackedRegattaRegistry, raceLogStore, regattaLogStore,
raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams);
}
@Override
@@ -10,7 +10,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -112,25 +111,34 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
protected SwissTimingRaceTrackerImpl(String raceID, String raceName, String raceDescription, BoatClass boatClass,
String hostname, int port, StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, DomainFactory domainFactory, SwissTimingFactory factory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis,
boolean useInternalMarkPassingAlgorithm, SwissTimingTrackingConnectivityParameters connectivityParams) throws InterruptedException,
UnknownHostException, IOException, ParseException {
this(domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, raceID,
boatClass, trackedRegattaRegistry), raceID, raceName,
raceDescription, boatClass, hostname, port, startList, windStore, domainFactory, factory,
trackedRegattaRegistry,
raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm);
this(/* regatta */ null, raceID, raceName, raceDescription, boatClass, hostname, port, startList, windStore,
domainFactory, factory, trackedRegattaRegistry, raceLogStore, regattaLogStore, raceLogResolver,
delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams);
}
protected SwissTimingRaceTrackerImpl(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, WindStore windStore,
DomainFactory domainFactory, SwissTimingFactory factory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver,
long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, UnknownHostException, IOException,
ParseException {
super();
DomainFactory domainFactory, SwissTimingFactory factory, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, RaceLogResolver raceLogResolver,
long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm, SwissTimingTrackingConnectivityParameters connectivityParams)
throws InterruptedException, UnknownHostException, IOException, ParseException {
super(connectivityParams);
this.raceLogResolver = raceLogResolver;
this.tmdMessageQueue = new TMDMessageQueue(this);
this.regatta = regatta;
final Regatta effectiveRegatta;
// Try to find a pre-associated event based on the Race ID
if (regatta == null) {
effectiveRegatta = trackedRegattaRegistry.getRememberedRegattaForRace(raceID);
} else {
effectiveRegatta = regatta;
}
// if regatta is still null, no previous assignment of any of the races in this TracTrac event to a Regatta was
// found; in this case, create a default regatta based on the TracTrac event data
this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore,
raceID, boatClass, trackedRegattaRegistry) : effectiveRegatta;
this.connector = factory.getOrCreateSailMasterConnector(hostname, port, raceID, raceName, raceDescription, boatClass);
this.domainFactory = domainFactory;
this.raceID = raceID;
@@ -142,11 +150,10 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
this.windStore = windStore;
this.id = createID(raceID, hostname, port);
connector.addSailMasterListener(this);
trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta);
trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(this.regatta);
this.delayToLiveInMillis = delayToLiveInMillis;
this.competitorsByBoatId = new HashMap<String, Competitor>();
this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm;
}
@Override
@@ -168,13 +175,12 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
}
@Override
public Set<RaceDefinition> getRaces() {
final Set<RaceDefinition> emptySet = Collections.emptySet();
return race==null?emptySet:Collections.singleton(race);
public RaceDefinition getRace() {
return race;
}
@Override
public RaceHandle getRacesHandle() {
public RaceHandle getRaceHandle() {
return new RaceHandle() {
@Override
public Regatta getRegatta() {
@@ -479,6 +485,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
assert SwissTimingRaceTrackerImpl.this.race == race;
}
}, useInternalMarkPassingAlgorithm, raceLogResolver);
notifyRaceCreationListeners();
logger.info("Created SwissTiming RaceDefinition and TrackedRace for "+race.getName());
}
@@ -10,11 +10,13 @@ import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.StartList;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.tracking.RaceTracker;
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.AbstractRaceTrackingConnectivityParameters;
public class SwissTimingTrackingConnectivityParameters implements RaceTrackingConnectivityParameters {
public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrackingConnectivityParameters {
public static final String TYPE = "SWISS_TIMING";
private final String hostname;
private final int port;
private final String raceID;
@@ -32,8 +34,8 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
public SwissTimingTrackingConnectivityParameters(String hostname, int port, String raceID, String raceName,
String raceDescription, BoatClass boatClass, StartList startList, long delayToLiveInMillis,
SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm) {
super();
RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) {
super(trackWind, correctWindDirectionByMagneticDeclination);
this.hostname = hostname;
this.port = port;
this.raceID = raceID;
@@ -49,13 +51,18 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm;
}
@Override
public String getTypeIdentifier() {
return TYPE;
}
@Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver) throws Exception {
return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port,
startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore,
useInternalMarkPassingAlgorithm,
domainFactory, trackedRegattaRegistry, raceLogResolver);
domainFactory, trackedRegattaRegistry, raceLogResolver, this);
}
@Override
@@ -63,7 +70,7 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname,
port, startList, delayToLiveInMillis, windStore, useInternalMarkPassingAlgorithm,
domainFactory, trackedRegattaRegistry, raceLogResolver);
domainFactory, trackedRegattaRegistry, raceLogResolver, raceLogStore, regattaLogStore, this);
}
@Override
@@ -75,4 +82,36 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
public long getDelayToLiveInMillis() {
return delayToLiveInMillis;
}
public String getHostname() {
return hostname;
}
public int getPort() {
return port;
}
public String getRaceID() {
return raceID;
}
public String getRaceName() {
return raceName;
}
public String getRaceDescription() {
return raceDescription;
}
public BoatClass getBoatClass() {
return boatClass;
}
public StartList getStartList() {
return startList;
}
public boolean isUseInternalMarkPassingAlgorithm() {
return useInternalMarkPassingAlgorithm;
}
}
@@ -17,9 +17,6 @@ import java.util.Map;
import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig;
@@ -33,13 +30,13 @@ public class SwissTimingRaceConfigurationTest {
@Test
public void testLoadConfigurations() throws IOException, ParseException, org.json.simple.parser.ParseException {
InputStream inputStream = getClass().getResourceAsStream(JSON_URL);
List<SwissTimingReplayRace> races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).parseJSONObject(inputStream , JSON_URL);
List<SwissTimingReplayRace> races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).parseJSONObject(inputStream , JSON_URL);
Map<String, SwissTimingRaceConfig> configsById = new HashMap<String, SwissTimingRaceConfig>();
for (SwissTimingReplayRace race : races) {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, race.getRaceId()));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream);
SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
configsById.put(race.getRaceId(), raceConfig);
}
}
@@ -49,7 +46,7 @@ public class SwissTimingRaceConfigurationTest {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483"));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream);
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
assertNotNull(config_446483);
assertEquals("GB", config_446483.country_code);
assertNull(config_446483.event_name);
@@ -66,7 +63,7 @@ public class SwissTimingRaceConfigurationTest {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260"));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream);
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
assertNotNull(config_446483);
assertEquals("GB", config_446483.country_code);
assertEquals("London 2012 Olympic Games", config_446483.event_name);
@@ -20,6 +20,8 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayParserImpl;
@@ -35,7 +37,7 @@ public class SwissTimingReplayAdapterServiceTest {
public void testLoadRaceJson() throws Exception {
String swissTimingUrlText = "/2012_OSG.json";
InputStream inputStream = getClass().getResourceAsStream(swissTimingUrlText);
final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class));
final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE);
List<SwissTimingReplayRace> races = swissTimingReplayService.parseJSONObject(inputStream , swissTimingUrlText);
assertEquals(201, races.size());
assertEquals("446483", races.get(0).getRaceId());
@@ -179,8 +181,9 @@ public class SwissTimingReplayAdapterServiceTest {
@Test
public void testRaceData_SAW010955_20120802_WithDomainAdapter() throws Exception {
SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, DomainFactory.INSTANCE,
new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, mock(RaceLogResolver.class));
SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, /* raceName */ "SAW005906",
"SAW005906", /* boatClass */ null, DomainFactory.INSTANCE, new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false,
mock(RaceLogResolver.class), EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* tracker creator */ null);
new SwissTimingReplayParserImpl().readData(getClass().getResourceAsStream("/SAW005906.20120805.replay"), replayListener);
Iterable<? extends TrackedRace> trackedRaces = replayListener.getTrackedRaces();
assertFalse(Util.isEmpty(trackedRaces));
@@ -193,8 +196,9 @@ public class SwissTimingReplayAdapterServiceTest {
@Test
public void testStartPerformanceDetection() throws Exception {
SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, DomainFactory.INSTANCE,
new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, mock(RaceLogResolver.class));
SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, /* raceName */ "SAW005905",
"SAW005905", /* boatClass */ null, DomainFactory.INSTANCE, new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false,
mock(RaceLogResolver.class), EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* tracker creator */ null);
new SwissTimingReplayParserImpl().readData(getClass().getResourceAsStream("/SAW005905.20120805.replay"), replayListener);
Iterable<? extends TrackedRace> trackedRaces = replayListener.getTrackedRaces();
TrackedRace trackedRace = trackedRaces.iterator().next();
@@ -13,7 +13,10 @@ Require-Bundle: com.sap.sailing.domain.common,
com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sse,
com.sap.sse.common,
com.sap.sailing.domain.shared.android
Export-Package: com.sap.sailing.domain.swisstimingreplayadapter
Import-Package: org.osgi.framework;version="1.7.0"
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain.persistence
Export-Package: com.sap.sailing.domain.swisstimingreplayadapter,
com.sap.sailing.domain.swisstimingreplayadapter.impl;x-friends:="com.sap.sailing.mongodb.test"
Import-Package: org.osgi.framework;version="1.7.0",
org.osgi.util.tracker;version="1.5.1"
Bundle-Activator: com.sap.sailing.domain.swisstimingreplayadapter.impl.Activator
@@ -1,14 +1,20 @@
package com.sap.sailing.domain.swisstimingreplayadapter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.List;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackerManager;
public interface SwissTimingReplayService {
@@ -22,6 +28,30 @@ public interface SwissTimingReplayService {
DateFormat getStartTimeFormat();
/**
* @param regattaToAddTo
* the regatta to which the race shall be added; if <code>null</code>, a default regatta will be
* created/used based on the {@code regattaName} and {@code boatClassName} parameter values
* @param link
* the URL without the implicit "http://" prefix, as obtained, e.g., from
* {@link SwissTimingReplayRace#getLink()}.
* @param raceName TODO
* @param raceID
* the SwissTiming ID for the race
* @param trackerManager TODO
* @param useInternalMarkPassingAlgorithm
* use our own instead of the SwissTiming-provided mark rounding / split times
* @param raceLogStore TODO
* @param regattaLogStore TODO
* @param boatClassNameName
* only required if {@code regattaToAddTo} is {@code null}; used for the retrieval/creation of a default
* regatta
*/
void loadRaceData(RegattaIdentifier regattaToAddTo, String link, String raceName, String raceID,
String boatClassName, TrackerManager trackerManager, TrackedRegattaRegistry trackedRegattaRegistry,
boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
/**
* @param link
* the URL without the implicit "http://" prefix, as obtained, e.g., from
@@ -30,17 +60,6 @@ public interface SwissTimingReplayService {
* @param replayListener
* the listener to receive all persing events
*/
void loadRaceData(String link, SwissTimingReplayListener replayListener);
/**
* @param link
* the URL without the implicit "http://" prefix, as obtained, e.g., from
* {@link SwissTimingReplayRace#getLink()}.
* @param regatta
* the regatta to which the race shall be added; if <code>null</code>, a default regatta will be
* created/used
* @param useInternalMarkPassingAlgorithm use our own instead of the SwissTiming-provided mark rounding / split times
*/
void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, boolean useInternalMarkPassingAlgorithm);
void loadRaceData(String link, SwissTimingReplayListener listener);
}
@@ -1,12 +1,24 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.util.ServiceTrackerFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
@@ -21,6 +33,30 @@ public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
// register the racing service in the OSGi registry
context.registerService(SwissTimingReplayServiceFactory.class.getName(), swissTimingReplayServiceFactory, null);
new Thread(() -> {
final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class);
final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class);
final ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> swissTimingAdapterFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class);
final ServiceTracker<RaceLogResolver, RaceLogResolver> raceLogResolverServiceTracker = ServiceTrackerFactory.createAndOpen(context, RaceLogResolver.class);
try {
final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0);
final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0);
final SwissTimingAdapterFactory swissTimingAdapterFactory = swissTimingAdapterFactoryServiceTracker.waitForService(0);
final RaceLogResolver raceLogResolver = raceLogResolverServiceTracker.waitForService(0);
final Dictionary<String, Object> properties = new Hashtable<String, Object>();
final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = swissTimingAdapterFactory
.getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory())
.getSwissTimingDomainFactory();
final SwissTimingReplayConnectivityParamsHandler paramsHandler = new SwissTimingReplayConnectivityParamsHandler(
MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory),
MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory),
domainFactory, swissTimingReplayServiceFactory.createSwissTimingReplayService(domainFactory, raceLogResolver));
properties.put(TypeBasedServiceFinder.TYPE, SwissTimingReplayConnectivityParameters.TYPE);
context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties);
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to register SwissTiming Replay RaceTrackingConnectivityParametersHandler implementation", e);
}
}, getClass().getName() + " registering connectivity handler").start();
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName());
}
@@ -3,7 +3,7 @@ package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener;
public class SwissTimingReplayAdapter implements SwissTimingReplayListener {
public abstract class SwissTimingReplayAdapter implements SwissTimingReplayListener {
@Override
public void keyFrameIndex(int keyFrameIndex) {}
@@ -0,0 +1,192 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParameters;
import com.sap.sse.common.Util;
public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackingConnectivityParameters {
public static final String TYPE = "SWISS_TIMING_REPLAY";
private final boolean useInternalMarkPassingAlgorithm;
private final DomainFactory domainFactory;
private final String boatClassName;
private final RaceLogStore raceLogStore;
private final RegattaLogStore regattaLogStore;
private final String raceName;
private final String raceID;
private final String link;
private final SwissTimingReplayService replayService;
class SwissTimingReplayRaceTracker extends AbstractRaceTrackerImpl {
private final WindStore windStore;
private SwissTimingReplayToDomainAdapter listener;
public SwissTimingReplayRaceTracker(WindStore windStore, SwissTimingReplayToDomainAdapter listener,
SwissTimingReplayConnectivityParameters connectivityParams) {
super(connectivityParams);
this.windStore = windStore;
this.listener = listener;
}
@Override
public Regatta getRegatta() {
return listener.getRegatta();
}
@Override
public void notifyRaceCreationListeners() {
super.notifyRaceCreationListeners();
}
@Override
public RaceDefinition getRace() {
RaceDefinition race;
try {
race = listener.getRaceDefinition(raceID, 1 /* very short timeout */);
return race;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public DynamicTrackedRegatta getTrackedRegatta() {
return listener.getTrackedRegatta();
}
@Override
public RaceHandle getRaceHandle() {
return new RaceHandle() {
@Override
public Regatta getRegatta() {
return listener.getRegatta();
}
@Override
public RaceDefinition getRace() {
try {
return listener.getRaceDefinition(raceID);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public RaceDefinition getRace(long timeoutInMilliseconds) {
try {
return listener.getRaceDefinition(raceID, timeoutInMilliseconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public DynamicTrackedRegatta getTrackedRegatta() {
return SwissTimingReplayRaceTracker.this.getTrackedRegatta();
}
@Override
public RaceTracker getRaceTracker() {
return SwissTimingReplayRaceTracker.this;
}
};
}
@Override
public WindStore getWindStore() {
return windStore;
}
@Override
public Object getID() {
return getTrackerID();
}
}
public SwissTimingReplayConnectivityParameters(String link, String raceName, String raceID, String boatClassName,
boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
SwissTimingReplayService replayService, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) {
super(/* trackWind: never track live wind with a SwissTiming Replay race */ false, /* correctWindDirectionByMagneticDeclination */ false);
this.link = link;
this.raceName = raceName;
this.raceID = raceID;
this.boatClassName = boatClassName;
this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm;
this.domainFactory = domainFactory;
this.replayService = replayService;
this.raceLogStore = raceLogStore;
this.regattaLogStore = regattaLogStore;
}
@Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, final WindStore windStore,
RaceLogResolver raceLogResolver) throws Exception {
SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(/* regatta */ null, raceName,
raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), domainFactory,
trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, raceLogStore,
regattaLogStore, l->new SwissTimingReplayRaceTracker(windStore, l, this));
replayService.loadRaceData(link, listener);
return listener.getTracker();
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(regatta, raceName,
raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName),
domainFactory, trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver,
raceLogStore, regattaLogStore, l->new SwissTimingReplayRaceTracker(windStore, l, this));
replayService.loadRaceData(link, listener);
return listener.getTracker();
}
@Override
public Object getTrackerID() {
return new Util.Pair<>(link, raceID);
}
/**
* There is no live tracking with this SwissTiming replay connector
*/
@Override
public long getDelayToLiveInMillis() {
return 0;
}
@Override
public String getTypeIdentifier() {
return TYPE;
}
public boolean isUseInternalMarkPassingAlgorithm() {
return useInternalMarkPassingAlgorithm;
}
public String getBoatClassName() {
return boatClassName;
}
public String getRaceName() {
return raceName;
}
public String getRaceID() {
return raceID;
}
public String getLink() {
return link;
}
}
@@ -0,0 +1,78 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler;
import com.sap.sse.common.TypeBasedServiceFinder;
/**
* Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The
* "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.<p>
*
* Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to
* its members.
*
* @author Axel Uhl (d043530)
*
*/
public class SwissTimingReplayConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler {
private static final String BOAT_CLASS_NAME = "boatClassName";
private static final String RACE_ID = "raceId";
private static final String RACE_NAME = "raceName";
private static final String LINK = "link";
private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm";
private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis";
private final RaceLogStore raceLogStore;
private final RegattaLogStore regattaLogStore;
private final DomainFactory domainFactory;
private final SwissTimingReplayService replayService;
public SwissTimingReplayConnectivityParamsHandler(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
DomainFactory domainFactory, SwissTimingReplayService replayService) {
super();
this.raceLogStore = raceLogStore;
this.regattaLogStore = regattaLogStore;
this.domainFactory = domainFactory;
this.replayService = replayService;
}
@Override
public Map<String, Object> mapFrom(RaceTrackingConnectivityParameters params) {
assert params instanceof SwissTimingReplayConnectivityParameters;
final SwissTimingReplayConnectivityParameters stParams = (SwissTimingReplayConnectivityParameters) params;
final Map<String, Object> result = getKey(params);
result.put(DELAY_TO_LIVE_IN_MILLIS, stParams.getDelayToLiveInMillis());
result.put(USE_INTERNAL_MARK_PASSING_ALGORITHM, stParams.isUseInternalMarkPassingAlgorithm());
result.put(RACE_NAME, stParams.getRaceName());
result.put(RACE_ID, stParams.getRaceID());
result.put(BOAT_CLASS_NAME, stParams.getBoatClassName());
addWindTrackingParameters(stParams, result);
return result;
}
@Override
public RaceTrackingConnectivityParameters mapTo(Map<String, Object> map) throws MalformedURLException, URISyntaxException {
return new SwissTimingReplayConnectivityParameters((String) map.get(LINK), (String) map.get(RACE_NAME),
(String) map.get(RACE_ID), (String) map.get(BOAT_CLASS_NAME),
(boolean) map.get(USE_INTERNAL_MARK_PASSING_ALGORITHM), domainFactory, replayService, raceLogStore,
regattaLogStore);
}
@Override
public Map<String, Object> getKey(RaceTrackingConnectivityParameters params) {
assert params instanceof SwissTimingReplayConnectivityParameters;
final SwissTimingReplayConnectivityParameters stParams = (SwissTimingReplayConnectivityParameters) params;
final Map<String, Object> result = new HashMap<>();
result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier());
result.put(LINK, stParams.getLink());
return result;
}
}
@@ -15,7 +15,6 @@ public class SwissTimingReplayParserImpl implements SwissTimingReplayParser {
private static final long serialVersionUID = 1L;
public UnknownMessageIdentificationCode(byte messageIdentificationCode, long position) {
super("Unknown message identification code: " + messageIdentificationCode+" at position "+position);
}
}
@@ -19,7 +19,7 @@ public class SwissTimingReplayServiceFactoryImpl implements SwissTimingReplaySer
public SwissTimingReplayService createSwissTimingReplayService(DomainFactory domainFactory, RaceLogResolver raceLogResolver) {
SwissTimingReplayService result = servicesForDomainFactories.get(domainFactory);
if (result == null) {
result = new SwissTimingReplayServiceImpl(domainFactory, raceLogResolver);
result = new SwissTimingReplayServiceImpl(domainFactory);
servicesForDomainFactories.put(domainFactory, result);
}
return result;
@@ -1,9 +1,12 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DateFormat;
import java.text.MessageFormat;
@@ -18,13 +21,15 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sse.util.ByteArrayOutputStreamWithVisibleBuffer;
public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
@@ -35,11 +40,9 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT");
private final DomainFactory domainFactory;
private final RaceLogResolver raceLogResolver;
public SwissTimingReplayServiceImpl(DomainFactory domainFactory, RaceLogResolver raceLogResolver) {
public SwissTimingReplayServiceImpl(DomainFactory domainFactory) {
this.domainFactory = domainFactory;
this.raceLogResolver = raceLogResolver;
}
@Override
@@ -146,10 +149,11 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
}
@Override
public void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
boolean useInternalMarkPassingAlgorithm) {
SwissTimingReplayListener listener = new SwissTimingReplayToDomainAdapter(regatta, domainFactory,
trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver);
loadRaceData(link, listener);
public void loadRaceData(RegattaIdentifier regattaToAddTo, String link, String raceName, String raceID,
String boatClassName, TrackerManager trackerManager, TrackedRegattaRegistry trackedRegattaRegistry,
boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception {
trackerManager.addRace(regattaToAddTo, new SwissTimingReplayConnectivityParameters(link, raceName, raceID,
boatClassName, useInternalMarkPassingAlgorithm, domainFactory, this, raceLogStore, regattaLogStore), /* timeout */ 0);
}
}

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