Merge branch 'master' into bug2775

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
	java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
This commit is contained in:
Steffen Schaefer committed 2016-07-11 11:18:46 +02:00
commit 793c2c0341
215 files changed
+11209 -6198

No files matched your search

@@ -1,47 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp"> <launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> <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"/> <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.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/> <stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES"> <listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/> <listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute> </listAttribute>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/> <booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/> <stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/> <listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/> <listEntry value="4"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/> <listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute> </listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> <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 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&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.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/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.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.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.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.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 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 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.google.gwt.osgi/lib/gwt-servlet.jar&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.google.gwt.osgi/lib/gwt-servlet.jar&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.google.gwt.osgi/lib/gwt-user.jar&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.google.gwt.osgi/lib/gwt-user.jar&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.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;/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;/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;"/> <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> </listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/> <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"/> <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.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.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 9998 com.sap.sailing.dashboards.gwt.RibDashboard"/> <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 9998 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.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
</launchConfiguration> </launchConfiguration>
@@ -1,47 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp"> <launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> <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"/> <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.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/> <stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
<booleanAttribute key="com.google.gwt.eclipse.core.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"> <listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/> <listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
</listAttribute> </listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/> <stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.dashboards.gwt"/> <listEntry value="/com.sap.sailing.dashboards.gwt"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/> <listEntry value="4"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/> <listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute> </listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> <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 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&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.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/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.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.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.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.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 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 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.google.gwt.osgi/lib/gwt-servlet.jar&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.google.gwt.osgi/lib/gwt-servlet.jar&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.google.gwt.osgi/lib/gwt-user.jar&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.google.gwt.osgi/lib/gwt-user.jar&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.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;/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;/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;"/> <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> </listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/> <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"/> <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.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.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.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.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
</launchConfiguration> </launchConfiguration>
@@ -21,6 +21,7 @@ Export-Package: com.sap.sailing.domain.common,
com.sap.sailing.domain.common.racelog.utils, com.sap.sailing.domain.common.racelog.utils,
com.sap.sailing.domain.common.scalablevalue.impl, com.sap.sailing.domain.common.scalablevalue.impl,
com.sap.sailing.domain.common.security, com.sap.sailing.domain.common.security,
com.sap.sailing.domain.common.sensordata,
com.sap.sailing.domain.common.trackfiles, com.sap.sailing.domain.common.trackfiles,
com.sap.sailing.domain.common.tracking, com.sap.sailing.domain.common.tracking,
com.sap.sailing.domain.common.tracking.impl com.sap.sailing.domain.common.tracking.impl
@@ -17,7 +17,7 @@ import java.io.Serializable;
public enum DetailType implements Serializable { public enum DetailType implements Serializable {
DISTANCE_TRAVELED(0, ASCENDING), DISTANCE_TRAVELED_INCLUDING_GATE_START(0, ASCENDING), DISTANCE_TRAVELED(0, ASCENDING), DISTANCE_TRAVELED_INCLUDING_GATE_START(0, ASCENDING),
AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING), REGATTA_RANK(0, ASCENDING), OVERALL_RANK(0, ASCENDING), AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING), REGATTA_RANK(0, ASCENDING), OVERALL_RANK(0, ASCENDING),
RANK_GAIN(0, ASCENDING), NUMBER_OF_MANEUVERS(0, ASCENDING), CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RANK_GAIN(0, ASCENDING), NUMBER_OF_MANEUVERS(0, ASCENDING), CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING), ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING),
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING), GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING),
SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING), SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
@@ -39,7 +39,7 @@ public enum DetailType implements Serializable {
MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING), MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING),
TIME_ON_TIME_FACTOR(4, DESCENDING), TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE(0, ASCENDING), TIME_ON_TIME_FACTOR(4, DESCENDING), TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE(0, ASCENDING),
TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING), TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
RACE_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS_SUM(2, ASCENDING), RACE_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS_SUM(2, ASCENDING),
RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(1, ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING), RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(1, ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING),
RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING), RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING),
@@ -35,6 +35,13 @@ public class LegEntryDTO implements Serializable {
*/ */
public Double currentSpeedOverGroundInKnots; public Double currentSpeedOverGroundInKnots;
/**
* If the competitor has already finished the leg at the time point for which this entry was created, contains the
* <em>average</em> ride height (foiling), otherwise the current ride height. If the competitor hasn't started the
* leg yet, it's <code>null</code>.
*/
public Double currentRideHeightInMeters;
/** /**
* If the competitor has already finished the leg at the time point for which this entry was created, contains the * If the competitor has already finished the leg at the time point for which this entry was created, contains the
* <em>average</em> VMG, otherwise the current VMG. If the competitor hasn't started the leg yet, it's * <em>average</em> VMG, otherwise the current VMG. If the competitor hasn't started the leg yet, it's
@@ -68,6 +75,7 @@ public class LegEntryDTO implements Serializable {
result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode()); result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode());
result = prime * result result = prime * result
+ ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode()); + ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode());
result = prime * result + ((currentRideHeightInMeters == null) ? 0 : currentRideHeightInMeters.hashCode());
result = prime * result result = prime * result
+ ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode()); + ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode());
result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode()); result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode());
@@ -122,6 +130,11 @@ public class LegEntryDTO implements Serializable {
return false; return false;
} else if (!averageSpeedOverGroundInKnots.equals(other.averageSpeedOverGroundInKnots)) } else if (!averageSpeedOverGroundInKnots.equals(other.averageSpeedOverGroundInKnots))
return false; return false;
if (currentRideHeightInMeters == null) {
if (other.currentRideHeightInMeters != null)
return false;
} else if (!currentRideHeightInMeters.equals(other.currentRideHeightInMeters))
return false;
if (currentSpeedOverGroundInKnots == null) { if (currentSpeedOverGroundInKnots == null) {
if (other.currentSpeedOverGroundInKnots != null) if (other.currentSpeedOverGroundInKnots != null)
return false; return false;
@@ -0,0 +1,43 @@
package com.sap.sailing.domain.common.sensordata;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Metadata that defines the column structure of {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}es when imported.
*/
public enum BravoSensorDataMetadata {
INSTANCE;
private final String RIDE_HEIGHT = "RideHeight";
private final int HEADER_COLUMN_OFFSET = 3;
private final List<String> columns = Collections.unmodifiableList(Arrays.asList(RIDE_HEIGHT, "RideHeightPortHull",
"RideHeightStbdHull", "Heel", "Trim", "ImuSensor_GyroX", "ImuSensor_GyroY", "ImuSensor_GyroZ",
"ImuSensor_Pitch", "ImuSensor_Roll", "ImuSensor_Yaw", "ImuSensor_LinearAccX", "ImuSensor_LinearAccY",
"ImuSensor_LinearAccZ", "Hb_Z", "Dn_Z", "Db_Z", "LKF_ride_hgh", "LKF_ride_hgh_Position",
"LKF_ride_hgh_Velocity", "LKF_ride_hgh_Acceleration", "LKF_ride_hgh_PositionError",
"LKF_ride_hgh_VelocityError", "LKF_ride_hgh_AccelerationError", "Gps_Gga_PosFixTime", "Gps_Gga_Lat",
"Gps_Gga_Lon", "Gps_Gga_QI", "Gps_Gga_HDOP", "Gps_Gga_AntHeight", "Gps_Vtg_TMG", "Gps_Vtg_SOGKnots",
"Gps_Rmc_MagVar", "Gps_EastVelocity", "Gps_NorthVelocity", "Gps_UpVelocity", "BravoNet_Node0x0A0_Ch0",
"BravoNet_Node0x0A0_Voltage", "BravoNet_Node0x0A0_Temperature", "BravoNet_Node0x0A0_Current"));
public final int rideHeightColumn = getColumnIndex(RIDE_HEIGHT);
public List<String> getColumns() {
return columns;
}
public boolean hasColumn(String columnName) {
return columns.contains(columnName);
}
public int getColumnIndex(String columnName) {
return columns.indexOf(columnName);
}
public int getHeaderColumnOffset() {
return HEADER_COLUMN_OFFSET;
}
}
@@ -0,0 +1,14 @@
package com.sap.sailing.domain.common.tracking;
/**
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
*/
public interface BravoFix extends SensorFix {
/**
* Gets the ride height (foiling) for this fix.
*
* @return the ride height
*/
double getRideHeight();
}
@@ -0,0 +1,12 @@
package com.sap.sailing.domain.common.tracking;
import com.sap.sse.common.Timed;
/**
* A fix that simply holds an array of double values. The interpretation of the data depends on the concrete mapping in
* the RegattaLog.
*/
public interface DoubleVectorFix extends Timed {
double[] get();
double get(int index);
}
@@ -0,0 +1,23 @@
package com.sap.sailing.domain.common.tracking;
import com.sap.sse.common.Timed;
/**
* A fix having sensor data provided by a device. This kind of fix is intended to be used in a way so that all data can
* be accessed in a generic way by using names for the specific values. The available value names are provided by the
* associated SensorFixTrack.
*
* {@link SensorFix} implementation typically don't directly have their data but wrap a
* {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
*/
public interface SensorFix extends Timed {
/**
* Gets a value by its name.
* The available value names are provided by the associated SensorFixTrack.
*
* @param valueName the name of the value to get from this fix
* @return the value
* @throws IllegalArgumentException if the valueName isn't known by this fix
*/
double get(String valueName) throws IllegalArgumentException;
}
@@ -0,0 +1,39 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sse.common.TimePoint;
/**
* Implementation of {@link BravoFix} that wraps a {@link DoubleVectorFix} which holds the actual sensor data.
*/
public class BravoFixImpl implements BravoFix {
private static final long serialVersionUID = 2033254212013221160L;
private final DoubleVectorFix fix;
public BravoFixImpl(DoubleVectorFix fix) {
this.fix = fix;
}
@Override
public double get(String valueName) {
int index = BravoSensorDataMetadata.INSTANCE.getColumnIndex(valueName);
if (index < 0) {
throw new IllegalArgumentException("Unknown value \"" + valueName + "\" for " + getClass().getSimpleName());
}
return fix.get(index);
}
@Override
public TimePoint getTimePoint() {
return fix.getTimePoint();
}
@Override
public double getRideHeight() {
return fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn);
}
}
@@ -0,0 +1,37 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sse.common.TimePoint;
/**
* Implementation of {@link DoubleVectorFix}.
*/
public class DoubleVectorFixImpl implements DoubleVectorFix {
private static final long serialVersionUID = -425848879310273855L;
private final double[] fixData;
private final TimePoint timePoint;
public DoubleVectorFixImpl(TimePoint timePoint, double[] fixData) {
this.timePoint = timePoint;
this.fixData = fixData;
}
@Override
public TimePoint getTimePoint() {
return timePoint;
}
@Override
public double get(int index) {
return fixData[index];
}
@Override
public double[] get() {
// TODO defensive copy?
return fixData;
}
}
@@ -103,6 +103,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitor
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpenEndedDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpenEndedDeviceMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; 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.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
@@ -1656,6 +1657,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
String eventClass = (String) dbObject.get(FieldNames.REGATTA_LOG_EVENT_CLASS.name()); String eventClass = (String) dbObject.get(FieldNames.REGATTA_LOG_EVENT_CLASS.name());
if (eventClass.equals(RegattaLogDeviceCompetitorMappingEvent.class.getSimpleName())) { if (eventClass.equals(RegattaLogDeviceCompetitorMappingEvent.class.getSimpleName())) {
return loadRegattaLogDeviceCompetitorMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); return loadRegattaLogDeviceCompetitorMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o);
} else if (eventClass.equals(RegattaLogDeviceCompetitorBravoMappingEventImpl.class.getSimpleName())) {
return loadRegattaLogDeviceCompetitorBravoMappingEvent(createdAt, author, logicalTimePoint, id, dbObject,
regattaLogIdentifier, o);
} else if (eventClass.equals(RegattaLogDeviceMarkMappingEvent.class.getSimpleName())) { } else if (eventClass.equals(RegattaLogDeviceMarkMappingEvent.class.getSimpleName())) {
return loadRegattaLogDeviceMarkMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o); return loadRegattaLogDeviceMarkMappingEvent(createdAt, author, logicalTimePoint, id, dbObject, regattaLogIdentifier, o);
} else if (eventClass.equals(RegattaLogCloseOpenEndedDeviceMappingEvent.class.getSimpleName())) { } else if (eventClass.equals(RegattaLogCloseOpenEndedDeviceMappingEvent.class.getSimpleName())) {
@@ -1840,6 +1844,43 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result; return result;
} }
private RegattaLogDeviceCompetitorBravoMappingEventImpl loadRegattaLogDeviceCompetitorBravoMappingEvent(
TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id,
final DBObject dbObject, RegattaLikeIdentifier regattaLogIdentifier, DBObject outerDBObject) {
DeviceIdentifier device = null;
try {
device = loadDeviceId(deviceIdentifierServiceFinder, (DBObject) dbObject.get(FieldNames.DEVICE_ID.name()));
} catch (Exception e) {
logger.log(Level.WARNING, "Could not load deviceId for RaceLogEvent", e);
e.printStackTrace();
}
Competitor mappedTo = baseDomainFactory.getExistingCompetitorById((Serializable) dbObject
.get(FieldNames.COMPETITOR_ID.name()));
@SuppressWarnings("deprecation")
// used only for auto-migration; may be removed in future releases
final FieldNames deprecatedFromFieldName = FieldNames.RACE_LOG_FROM;
@SuppressWarnings("deprecation")
// used only for auto-migration; may be removed in future releases
final FieldNames deprecatedToFieldName = FieldNames.RACE_LOG_TO;
Triple<TimePoint, TimePoint, Boolean> times = loadFromToTimePoint(dbObject, FieldNames.REGATTA_LOG_FROM,
deprecatedFromFieldName, FieldNames.REGATTA_LOG_TO, deprecatedToFieldName);
final TimePoint from = times.getA();
final TimePoint to = times.getB();
final boolean needsMigration = times.getC();
final RegattaLogDeviceCompetitorBravoMappingEventImpl result = new RegattaLogDeviceCompetitorBravoMappingEventImpl(
createdAt, logicalTimePoint, author, id, mappedTo, device, from, to);
if (needsMigration) {
// remove old version of mapping event
WriteResult removeResult = database.getCollection(CollectionNames.REGATTA_LOGS.name())
.remove(outerDBObject);
assert removeResult.getN() == 1;
// and then insert using the fixed storage implementation
new MongoObjectFactoryImpl(database, serviceFinderFactory).storeRegattaLogEvent(regattaLogIdentifier,
result);
}
return result;
}
/** /**
* The old field name WAYPOINT_PASSINGSIDE has been replaced by WAYPOINT_PASSINGINSTRUCTIONS. If a race with the old * The old field name WAYPOINT_PASSINGSIDE has been replaced by WAYPOINT_PASSINGINSTRUCTIONS. If a race with the old
* field is loaded, the value of PASSINGSIDE is used and then migrated to PASSINGINSTRUCTION. If the first or last * field is loaded, the value of PASSINGSIDE is used and then migrated to PASSINGINSTRUCTION. If the first or last
@@ -153,5 +153,8 @@ public enum FieldNames {
VIDEO_TAGS, VIDEO_THUMBNAIL_URL, VIDEO_LENGTH_IN_SECONDS, VIDEO_TAGS, VIDEO_THUMBNAIL_URL, VIDEO_LENGTH_IN_SECONDS,
// sailors info fileds // sailors info fileds
SAILORS_INFO_URL, SAILORS_INFO_LOCALE; SAILORS_INFO_URL, SAILORS_INFO_LOCALE,
// double vector fixes
FIX;
} }
@@ -52,12 +52,14 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.ControlPointWithTwoMarks; import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
@@ -168,9 +170,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeTimePoint(timePoint, result, field.name()); storeTimePoint(timePoint, result, field.name());
} }
public static void storeTimeRange(TimeRange timeRange, DBObject result, FieldNames field) { public static void storeTimeRange(TimeRange timeRange, BasicDBObject result, FieldNames field) {
if (timeRange != null) { if (timeRange != null) {
DBObject timeRangeObj = new BasicDBObject(); BasicDBObject timeRangeObj = new BasicDBObject();
storeTimePoint(timeRange.from(), timeRangeObj, FieldNames.FROM_MILLIS); storeTimePoint(timeRange.from(), timeRangeObj, FieldNames.FROM_MILLIS);
storeTimePoint(timeRange.to(), timeRangeObj, FieldNames.TO_MILLIS); storeTimePoint(timeRange.to(), timeRangeObj, FieldNames.TO_MILLIS);
result.put(field.name(), timeRangeObj); result.put(field.name(), timeRangeObj);
@@ -1379,6 +1381,15 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeRegattaLogEvent(regattaLikeId, result); storeRegattaLogEvent(regattaLikeId, result);
} }
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId,
RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogDeviceCompetitorBravoMappingEventImpl.class.getSimpleName());
storeDeviceMappingEvent(event, result, FieldNames.REGATTA_LOG_FROM, FieldNames.REGATTA_LOG_TO);
result.put(FieldNames.COMPETITOR_ID.name(), event.getMappedTo().getId());
storeRegattaLogEvent(regattaLikeId, result);
}
public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogDeviceMarkMappingEvent event) { public void storeRegattaLogEvent(RegattaLikeIdentifier regattaLikeId, RegattaLogDeviceMarkMappingEvent event) {
DBObject result = createBasicRegattaLogEventDBObject(event); DBObject result = createBasicRegattaLogEventDBObject(event);
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogDeviceMarkMappingEvent.class.getSimpleName()); result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogDeviceMarkMappingEvent.class.getSimpleName());
@@ -1484,4 +1495,4 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.SAILORS_INFO_LOCALE.name(), locale != null ? locale.toLanguageTag() : null); result.put(FieldNames.SAILORS_INFO_LOCALE.name(), locale != null ? locale.toLanguageTag() : null);
return result; return result;
} }
} }
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
@@ -42,6 +43,10 @@ public class MongoRegattaLogStoreVisitor implements RegattaLogEventVisitor {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event); mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
} }
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
}
@Override @Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) { public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event); mongoObjectFactory.storeRegattaLogEvent(regattaLikeIdentifier, event);
@@ -2,15 +2,14 @@ package com.sap.sailing.domain.persistence.racelog.tracking;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import com.sap.sailing.domain.common.racelog.tracking.TransformationHandler; import com.sap.sailing.domain.common.racelog.tracking.TransformationHandler;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sse.common.Timed;
/** /**
* For GPS Fixes, the fully qualified class name is the type used to identify * For fixes, the fully qualified class name is the type used to identify
* the handlers, as the fixes are domain objects and are not intended to carry * the handlers, as the fixes are domain objects and are not intended to carry
* some separate type attribute. * some separate type attribute.
* *
* @author Fredrik Teschke * @author Fredrik Teschke
*
*/ */
public interface GPSFixMongoHandler extends TransformationHandler<GPSFix, DBObject> { public interface FixMongoHandler<T extends Timed> extends TransformationHandler<T, DBObject> {
} }
@@ -1,7 +0,0 @@
package com.sap.sailing.domain.persistence.racelog.tracking;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
public interface MongoGPSFixStore extends GPSFixStore {
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.persistence.racelog.tracking;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
public interface MongoSensorFixStore extends SensorFixStore {
}
@@ -5,17 +5,17 @@ import java.net.UnknownHostException;
import com.mongodb.MongoException; import com.mongodb.MongoException;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoGPSFixStoreFactoryImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreFactoryImpl;
import com.sap.sse.common.TypeBasedServiceFinderFactory; import com.sap.sse.common.TypeBasedServiceFinderFactory;
public interface MongoGPSFixStoreFactory { public interface MongoSensorFixStoreFactory {
MongoGPSFixStoreFactory INSTANCE = new MongoGPSFixStoreFactoryImpl(); MongoSensorFixStoreFactory INSTANCE = new MongoSensorFixStoreFactoryImpl();
/** /**
* Gets the default Mongo store based on the properties mongo.hostname[=localhost], mongo.port[=27017] * Gets the default Mongo store based on the properties mongo.hostname[=localhost], mongo.port[=27017]
* and mongo.dbName[=&lt;contents of constant {@link #DEFAULT_DB_NAME}&gt;] specified in the bundle context * and mongo.dbName[=&lt;contents of constant {@link #DEFAULT_DB_NAME}&gt;] specified in the bundle context
*/ */
MongoGPSFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory, MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory,
TypeBasedServiceFinderFactory serviceFinderFactory) TypeBasedServiceFinderFactory serviceFinderFactory)
throws UnknownHostException, MongoException; throws UnknownHostException, MongoException;
@@ -0,0 +1,58 @@
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
import java.util.List;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.FieldNames;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sse.common.TimePoint;
public class DoubleVectorFixMongoHandlerImpl implements FixMongoHandler<DoubleVectorFix> {
private final MongoObjectFactoryImpl mof;
private final DomainObjectFactoryImpl dof;
public DoubleVectorFixMongoHandlerImpl(MongoObjectFactory mof, DomainObjectFactory dof) {
this.mof = (MongoObjectFactoryImpl) mof;
this.dof = (DomainObjectFactoryImpl) dof;
}
@Override
public DBObject transformForth(DoubleVectorFix fix) throws IllegalArgumentException {
DBObject result = new BasicDBObject();
mof.storeTimed(fix, result);
result.put(FieldNames.FIX.name(), toDBObject(fix.get()));
return result;
}
@Override
public DoubleVectorFix transformBack(DBObject dbObject) {
TimePoint timePoint = dof.loadTimePoint(dbObject);
return new DoubleVectorFixImpl(timePoint, fromDBObject((DBObject) dbObject.get(FieldNames.FIX.name())));
}
private DBObject toDBObject(double[] data) {
BasicDBList result = new BasicDBList();
for (double value : data) {
result.add(value);
}
return result;
}
private double[] fromDBObject(DBObject dbObject) {
@SuppressWarnings("unchecked")
List<Double> dbValues = (List<Double>) dbObject;
double[] result = new double[dbValues.size()];
for(int i = 0 ; i < dbValues.size() ; i++) {
result[i] = dbValues.get(i);
}
return result;
}
}
@@ -9,10 +9,10 @@ import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
public class GPSFixMongoHandlerImpl implements GPSFixMongoHandler { public class GPSFixMongoHandlerImpl implements FixMongoHandler<GPSFix> {
private final MongoObjectFactoryImpl mof; private final MongoObjectFactoryImpl mof;
private final DomainObjectFactoryImpl dof; private final DomainObjectFactoryImpl dof;
@@ -4,17 +4,16 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
public class GPSFixMovingMongoHandlerImpl implements GPSFixMongoHandler { public class GPSFixMovingMongoHandlerImpl implements FixMongoHandler<GPSFixMoving> {
private final MongoObjectFactoryImpl mof; private final MongoObjectFactoryImpl mof;
private final DomainObjectFactoryImpl dof; private final DomainObjectFactoryImpl dof;
@@ -24,16 +23,16 @@ public class GPSFixMovingMongoHandlerImpl implements GPSFixMongoHandler {
} }
@Override @Override
public DBObject transformForth(GPSFix fix) throws IllegalArgumentException { public DBObject transformForth(GPSFixMoving fix) throws IllegalArgumentException {
DBObject result = new BasicDBObject(); DBObject result = new BasicDBObject();
mof.storeTimed(fix, result); mof.storeTimed(fix, result);
mof.storePositioned(fix, result); mof.storePositioned(fix, result);
mof.storeSpeedWithBearing(((GPSFixMoving) fix).getSpeed(), result); mof.storeSpeedWithBearing(fix.getSpeed(), result);
return result; return result;
} }
@Override @Override
public GPSFix transformBack(DBObject dbObject) { public GPSFixMoving transformBack(DBObject dbObject) {
TimePoint timePoint = dof.loadTimePoint(dbObject); TimePoint timePoint = dof.loadTimePoint(dbObject);
Position position = dof.loadPosition(dbObject); Position position = dof.loadPosition(dbObject);
SpeedWithBearing speed = dof.loadSpeedWithBearing(dbObject); SpeedWithBearing speed = dof.loadSpeedWithBearing(dbObject);
@@ -1,264 +0,0 @@
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.FieldNames;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixReceivedListener;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.common.TypeBasedServiceFinderFactory;
import com.sap.sse.common.Util;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.TimeRangeImpl;
/**
* At the moment, the timerange covered by the fixes for a device, and the number of
* fixes for a device are stored in a metadata collection. Should be changed, see bug 1982.
* @author Fredrik Teschke
*
*/
public class MongoGPSFixStoreImpl implements MongoGPSFixStore {
private static final Logger logger = Logger.getLogger(MongoGPSFixStore.class.getName());
private final TypeBasedServiceFinder<GPSFixMongoHandler> fixServiceFinder;
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceServiceFinder;
private final DBCollection fixesCollection;
private final DBCollection metadataCollection;
private final MongoObjectFactoryImpl mongoOF;
private final Map<DeviceIdentifier, Set<GPSFixReceivedListener>> listeners = new HashMap<>();
public MongoGPSFixStoreImpl(MongoObjectFactory mongoObjectFactory,
DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory) {
mongoOF = (MongoObjectFactoryImpl) mongoObjectFactory;
if (serviceFinderFactory != null) {
fixServiceFinder = serviceFinderFactory.createServiceFinder(GPSFixMongoHandler.class);
deviceServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class);
} else {
fixServiceFinder = null;
deviceServiceFinder = null;
}
fixesCollection = mongoOF.getGPSFixCollection();
metadataCollection = mongoOF.getGPSFixMetadataCollection();
}
private GPSFix loadGPSFix(DBObject object) throws TransformationException, NoCorrespondingServiceRegisteredException {
String type = (String) object.get(FieldNames.GPSFIX_TYPE.name());
DBObject fixObject = (DBObject) object.get(FieldNames.GPSFIX.name());
return fixServiceFinder.findService(type).transformBack(fixObject);
}
private <FixT extends GPSFix> void loadTrack(DynamicGPSFixTrack<?, FixT> track, DeviceIdentifier device,
TimePoint from, TimePoint to, boolean inclusive) throws NoCorrespondingServiceRegisteredException,
TransformationException {
Object dbDeviceId = MongoObjectFactoryImpl.storeDeviceId(deviceServiceFinder, device);
final QueryBuilder queryBuilder = QueryBuilder.start(FieldNames.DEVICE_ID.name()).is(dbDeviceId)
.and(FieldNames.TIME_AS_MILLIS.name());
if (inclusive) {
queryBuilder.greaterThanEquals(from.asMillis()).and(FieldNames.TIME_AS_MILLIS.name()).lessThanEquals(to.asMillis());
} else {
queryBuilder.greaterThan(from.asMillis()).and(FieldNames.TIME_AS_MILLIS.name()).lessThan(to.asMillis());
}
DBObject query = queryBuilder.get();
DBCursor result = fixesCollection.find(query);
for (DBObject fixObject : result) {
try {
@SuppressWarnings("unchecked")
FixT fix = (FixT) loadGPSFix(fixObject);
track.add(fix);
} catch (TransformationException e) {
logger.log(Level.WARNING, "Could not read fix from MongoDB: " + fixObject);
} catch (ClassCastException e) {
String type = (String) fixObject.get(FieldNames.GPSFIX_TYPE.name());
logger.log(Level.WARNING, "Unexpected fix type (" + type + ") encountered when trying to load track for " + track.getTrackedItem());
}
}
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, RegattaLog log, Competitor competitor)
throws NoCorrespondingServiceRegisteredException, TransformationException{
List<DeviceMapping<Competitor>> mappings = new RegattaLogDeviceCompetitorMappingFinder(log).analyze().get(competitor);
if (mappings != null) {
for (DeviceMapping<Competitor> mapping : mappings) {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
}
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, RegattaLog log, Mark mark)
throws NoCorrespondingServiceRegisteredException, TransformationException{
List<DeviceMapping<Mark>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze().get(mark);
if (mappings != null) {
for (DeviceMapping<Mark> mapping : mappings) {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
}
}
@Override
public void storeFix(DeviceIdentifier device, GPSFix fix) {
try {
Object dbDeviceId = MongoObjectFactoryImpl.storeDeviceId(deviceServiceFinder, device);
String type = fix.getClass().getName();
Object fixObject = fixServiceFinder.findService(type).transformForth(fix);
DBObject entry = new BasicDBObjectBuilder().add(FieldNames.DEVICE_ID.name(), dbDeviceId)
.add(FieldNames.GPSFIX_TYPE.name(), type).add(FieldNames.GPSFIX.name(), fixObject).get();
mongoOF.storeTimed(fix, entry);
fixesCollection.insert(entry);
TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
long oldNumFixes = getNumberOfFixes(device);
DBObject newMetadata = new BasicDBObject();
newMetadata.put(FieldNames.DEVICE_ID.name(), MongoObjectFactoryImpl.storeDeviceId(deviceServiceFinder, device));
newMetadata.put(FieldNames.NUM_FIXES.name(), oldNumFixes + 1);
TimePoint newFrom = fix.getTimePoint();
TimePoint newTo = fix.getTimePoint();
if (oldTimeRange != null) {
if (oldTimeRange.from().before(newFrom)) {
newFrom = oldTimeRange.from();
}
if (oldTimeRange.to().after(newTo)) {
newTo = oldTimeRange.to();
}
}
MongoObjectFactoryImpl.storeTimeRange(new TimeRangeImpl(newFrom, newTo), newMetadata, FieldNames.TIMERANGE);
metadataCollection.update(getDeviceQuery(device), newMetadata, /*create if not existent*/ true,
/*update multiple*/ false);
} catch (TransformationException e) {
logger.log(Level.WARNING, "Could not store fix in MongoDB");
e.printStackTrace();
}
notifyListeners(device, fix);
}
private void notifyListeners(DeviceIdentifier device, GPSFix fix) {
for (GPSFixReceivedListener listener : Util.get(listeners, device, Collections.<GPSFixReceivedListener>emptySet())) {
listener.fixReceived(device, fix);
}
}
@Override
public synchronized void addListener(GPSFixReceivedListener listener, DeviceIdentifier device) {
Util.addToValueSet(listeners, device, listener);
}
@Override
public synchronized void removeListener(GPSFixReceivedListener listener) {
for (Set<GPSFixReceivedListener> set : listeners.values()) {
set.remove(listener);
}
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, DeviceMapping<Competitor> mapping)
throws TransformationException, NoCorrespondingServiceRegisteredException {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, RegattaLog log,
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException {
List<DeviceMapping<Competitor>> mappings = new RegattaLogDeviceCompetitorMappingFinder(log).analyze().get(competitor);
if (mappings != null) {
for (DeviceMapping<Competitor> mapping : mappings) {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
}
}
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, DeviceMapping<Mark> mapping)
throws TransformationException, NoCorrespondingServiceRegisteredException {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, RegattaLog log, Mark mark,
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
List<DeviceMapping<Mark>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze().get(mark);
if (mappings != null) {
for (DeviceMapping<Mark> mapping : mappings) {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
}
}
}
@Override
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
throws NoCorrespondingServiceRegisteredException, TransformationException {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true);
}
private DBObject getDeviceQuery(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
Object dbDeviceId = MongoObjectFactoryImpl.storeDeviceId(deviceServiceFinder, device);
DBObject query = QueryBuilder.start(FieldNames.DEVICE_ID.name()).is(dbDeviceId).get();
return query;
}
private DBObject findMetadataObject(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject query = getDeviceQuery(device);
DBObject result = metadataCollection.findOne(query);
return result;
}
@Override
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject result = findMetadataObject(device);
if (result == null) {
return null;
}
return DomainObjectFactoryImpl.loadTimeRange(result, FieldNames.TIMERANGE);
}
@Override
public long getNumberOfFixes(DeviceIdentifier device) throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject result = findMetadataObject(device);
if (result == null) {
return 0;
}
return ((Number) result.get(FieldNames.NUM_FIXES.name())).longValue();
}
}
@@ -5,15 +5,15 @@ import java.net.UnknownHostException;
import com.mongodb.MongoException; import com.mongodb.MongoException;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStore; import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStoreFactory; import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStoreFactory;
import com.sap.sse.common.TypeBasedServiceFinderFactory; import com.sap.sse.common.TypeBasedServiceFinderFactory;
public class MongoGPSFixStoreFactoryImpl implements MongoGPSFixStoreFactory { public class MongoSensorFixStoreFactoryImpl implements MongoSensorFixStoreFactory {
@Override @Override
public MongoGPSFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, public MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory,
DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory) DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory)
throws UnknownHostException, MongoException { throws UnknownHostException, MongoException {
return new MongoGPSFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory); return new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory);
} }
} }
@@ -0,0 +1,239 @@
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
import static com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl.storeDeviceId;
import static com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl.storeTimeRange;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.FieldNames;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
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.TimeRangeImpl;
/**
* At the moment, the timerange covered by the fixes for a device, and the number of fixes for a device are stored in a
* metadata collection. Should be changed, see bug 1982.
*
* @author Fredrik Teschke
*
*/
public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
private static final Logger logger = Logger.getLogger(MongoSensorFixStoreImpl.class.getName());
private final TypeBasedServiceFinder<FixMongoHandler<?>> fixServiceFinder;
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceServiceFinder;
private final DBCollection fixesCollection;
private final DBCollection metadataCollection;
private final MongoObjectFactoryImpl mongoOF;
private final Map<DeviceIdentifier, Set<FixReceivedListener<? extends Timed>>> listeners = new HashMap<>();
public MongoSensorFixStoreImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory,
TypeBasedServiceFinderFactory serviceFinderFactory) {
mongoOF = (MongoObjectFactoryImpl) mongoObjectFactory;
if (serviceFinderFactory != null) {
fixServiceFinder = createFixServiceFinder(serviceFinderFactory);
deviceServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class);
} else {
fixServiceFinder = null;
deviceServiceFinder = null;
}
fixesCollection = mongoOF.getGPSFixCollection();
metadataCollection = mongoOF.getGPSFixMetadataCollection();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private TypeBasedServiceFinder<FixMongoHandler<?>> createFixServiceFinder(
TypeBasedServiceFinderFactory serviceFinderFactory) {
return (TypeBasedServiceFinder) serviceFinderFactory.createServiceFinder(FixMongoHandler.class);
}
public <T extends Timed> T loadFix(DBObject object)
throws TransformationException, NoCorrespondingServiceRegisteredException {
String type = (String) object.get(FieldNames.GPSFIX_TYPE.name());
DBObject fixObject = (DBObject) object.get(FieldNames.GPSFIX.name());
return this.<T> findService(type).transformBack(fixObject);
}
@Override
public <FixT extends Timed> void loadFixes(Consumer<FixT> consumer, DeviceIdentifier device, TimePoint from,
TimePoint to, boolean inclusive) throws NoCorrespondingServiceRegisteredException, TransformationException {
final TimePoint loadFixesFrom = from == null ? TimePoint.BeginningOfTime : from;
final TimePoint loadFixesTo = to == null ? TimePoint.EndOfTime : to;
Object dbDeviceId = storeDeviceId(deviceServiceFinder, device);
final QueryBuilder queryBuilder = QueryBuilder.start(FieldNames.DEVICE_ID.name()).is(dbDeviceId)
.and(FieldNames.TIME_AS_MILLIS.name());
if (inclusive) {
queryBuilder.greaterThanEquals(loadFixesFrom.asMillis()).and(FieldNames.TIME_AS_MILLIS.name())
.lessThanEquals(loadFixesTo.asMillis());
} else {
queryBuilder.greaterThan(loadFixesFrom.asMillis()).and(FieldNames.TIME_AS_MILLIS.name()).lessThan(loadFixesTo.asMillis());
}
DBObject query = queryBuilder.get();
DBCursor result = fixesCollection.find(query);
for (DBObject fixObject : result) {
try {
@SuppressWarnings("unchecked")
FixT fix = (FixT) loadFix(fixObject);
consumer.accept(fix);
} catch (TransformationException e) {
logger.log(Level.WARNING, "Could not read fix from MongoDB: " + fixObject);
} catch (ClassCastException e) {
String type = (String) fixObject.get(FieldNames.GPSFIX_TYPE.name());
logger.log(Level.WARNING,
"Unexpected fix type (" + type + ") encountered when trying to load track for " + device);
}
}
}
/**
* Store fixes in batches, reducing metadata storage update.
*/
@Override
public <FixT extends Timed> void storeFixes(DeviceIdentifier device, Iterable<FixT> fixes) {
if (Util.isEmpty(fixes)) {
return;
}
try {
final Object dbDeviceId = storeDeviceId(deviceServiceFinder, device);
final int nrOfTotalFixes = Util.size(fixes);
final ArrayList<DBObject> dbFixes = new ArrayList<>(nrOfTotalFixes);
TimePoint newFrom = null;
TimePoint newTo = null;
for (FixT fix : fixes) {
String type = fix.getClass().getName();
FixMongoHandler<FixT> mongoHandler = findService(type);
Object fixObject = mongoHandler.transformForth(fix);
DBObject entry = new BasicDBObjectBuilder().add(FieldNames.DEVICE_ID.name(), dbDeviceId)
.add(FieldNames.GPSFIX_TYPE.name(), type).add(FieldNames.GPSFIX.name(), fixObject).get();
mongoOF.storeTimed(fix, entry);
dbFixes.add(entry);
TimePoint fixTP = fix.getTimePoint();
if (newFrom == null || newFrom.after(fixTP)) {
newFrom = fixTP;
}
if (newTo == null || newTo.before(fixTP)) {
newTo = fixTP;
}
}
fixesCollection.insert(dbFixes);
final BasicDBObject updateOperation = new BasicDBObject();
final BasicDBObject newMetadata = new BasicDBObject();
newMetadata.put(FieldNames.DEVICE_ID.name(), dbDeviceId);
TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
if (oldTimeRange != null) {
newFrom = oldTimeRange.from().before(newFrom) ? oldTimeRange.from() : newFrom;
newTo = oldTimeRange.to().after(newTo) ? oldTimeRange.to() : newTo;
}
final TimeRange newTimeRange = new TimeRangeImpl(newFrom, newTo);
storeTimeRange(newTimeRange, newMetadata, FieldNames.TIMERANGE);
updateOperation.append("$set", newMetadata);
updateOperation.append("$inc", new BasicDBObject(FieldNames.NUM_FIXES.name(), nrOfTotalFixes));
metadataCollection.update(getDeviceQuery(device), updateOperation, /* create if not existent */ true,
/* update multiple */ false);
} catch (TransformationException e) {
logger.log(Level.WARNING, "Could not store fix in MongoDB");
e.printStackTrace();
}
for (FixT fix : fixes) {
notifyListeners(device, fix);
}
}
@Override
public <FixT extends Timed> void storeFix(DeviceIdentifier device, FixT fix) {
storeFixes(device, Collections.singletonList(fix));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <FixT extends Timed> void notifyListeners(DeviceIdentifier device, FixT fix) {
for (FixReceivedListener<FixT> listener : Util.<DeviceIdentifier, Set<FixReceivedListener<FixT>>> get(
(Map) listeners, device, Collections.emptySet())) {
listener.fixReceived(device, fix);
}
}
@Override
public synchronized void addListener(FixReceivedListener<? extends Timed> listener, DeviceIdentifier device) {
Util.addToValueSet(listeners, device, listener);
}
@Override
public synchronized void removeListener(FixReceivedListener<? extends Timed> listener) {
for (Set<FixReceivedListener<? extends Timed>> set : listeners.values()) {
set.remove(listener);
}
}
private DBObject getDeviceQuery(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
Object dbDeviceId = storeDeviceId(deviceServiceFinder, device);
DBObject query = QueryBuilder.start(FieldNames.DEVICE_ID.name()).is(dbDeviceId).get();
return query;
}
private DBObject findMetadataObject(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject query = getDeviceQuery(device);
DBObject result = metadataCollection.findOne(query);
return result;
}
@Override
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject result = findMetadataObject(device);
if (result == null) {
return null;
}
return DomainObjectFactoryImpl.loadTimeRange(result, FieldNames.TIMERANGE);
}
@Override
public long getNumberOfFixes(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
DBObject result = findMetadataObject(device);
if (result == null) {
return 0;
}
return ((Number) result.get(FieldNames.NUM_FIXES.name())).longValue();
}
@SuppressWarnings("unchecked")
private <FixT extends Timed> FixMongoHandler<FixT> findService(String type) {
return (FixMongoHandler<FixT>) fixServiceFinder.findService(type);
}
}
@@ -1,94 +1,95 @@
package com.sap.sailing.domain.racelogtracking.test; package com.sap.sailing.domain.racelogtracking.test;
import static com.sap.sse.common.Util.size; import static com.sap.sse.common.Util.size;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import java.util.UUID; import java.util.UUID;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; 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.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl; import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoGPSFixStoreImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore; 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.MockDeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory; 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.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.deserialization.impl.DeviceAndSessionIdentifierWithGPSFixesDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.DeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.server.impl.RacingEventServiceImpl; import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class AbstractGPSFixStoreTest { public class AbstractGPSFixStoreTest {
protected RacingEventService service; protected RacingEventService service;
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory(); protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
DeviceAndSessionIdentifierWithGPSFixesDeserializer deserializer = DeviceAndSessionIdentifierWithGPSFixesDeserializer deserializer =
new MockDeviceAndSessionIdentifierWithGPSFixesDeserializer(); new MockDeviceAndSessionIdentifierWithGPSFixesDeserializer();
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a"); protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
protected RaceLog raceLog; protected RaceLog raceLog;
protected RegattaLog regattaLog; protected RegattaLog regattaLog;
protected GPSFixStore store; protected SensorFixStore store;
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null); 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 mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0); protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
protected GPSFixMoving createFix(long millis, double lat, double lng, double knots, double degrees) { protected GPSFixMoving createFix(long millis, double lat, double lng, double knots, double degrees) {
return new GPSFixMovingImpl(new DegreePosition(lat, lng), return new GPSFixMovingImpl(new DegreePosition(lat, lng),
new MillisecondsTimePoint(millis), new KnotSpeedWithBearingImpl(knots, new DegreeBearingImpl(degrees))); new MillisecondsTimePoint(millis), new KnotSpeedWithBearingImpl(knots, new DegreeBearingImpl(degrees)));
} }
@Before @Before
public void setServiceAndRaceLog() { public void setServiceAndRaceLog() {
service = new RacingEventServiceImpl(null, null, serviceFinderFactory); service = new RacingEventServiceImpl(null, null, serviceFinderFactory);
raceLog = new RaceLogImpl("racelog"); raceLog = new RaceLogImpl("racelog");
regattaLog = new RegattaLogImpl("regattalog"); regattaLog = new RegattaLogImpl("regattalog");
store = new MongoGPSFixStoreImpl(service.getMongoObjectFactory(), service.getDomainObjectFactory(),
serviceFinderFactory); store = new MongoSensorFixStoreImpl(service.getMongoObjectFactory(), service.getDomainObjectFactory(),
} serviceFinderFactory);
}
@After
public void after() { @After
MongoObjectFactoryImpl mongoOF = (MongoObjectFactoryImpl) service.getMongoObjectFactory(); public void after() {
mongoOF.getGPSFixCollection().drop(); MongoObjectFactoryImpl mongoOF = (MongoObjectFactoryImpl) service.getMongoObjectFactory();
mongoOF.getGPSFixMetadataCollection().drop(); mongoOF.getGPSFixCollection().drop();
} mongoOF.getGPSFixMetadataCollection().drop();
}
protected void map(RegattaLog regattaLog, Competitor comp, DeviceIdentifier device, long from, long to) {
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), protected void map(RegattaLog regattaLog, Competitor comp, DeviceIdentifier device, long from, long to) {
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) {
map(regattaLog, comp, device, from, to); protected void map(Competitor comp, DeviceIdentifier device, long from, long to) {
} map(regattaLog, comp, device, from, to);
}
protected void map(Mark mark, DeviceIdentifier device, long from, long to) {
regattaLog.add(new RegattaLogDeviceMarkMappingEventImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now(), author, UUID.randomUUID(), protected void map(Mark mark, DeviceIdentifier device, long from, long to) {
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 testNumberOfRawFixes(Track<?> track, long expected) {
track.lockForRead(); protected void testNumberOfRawFixes(Track<?> track, long expected) {
assertEquals(expected, size(track.getRawFixes())); track.lockForRead();
track.unlockAfterRead(); assertEquals(expected, size(track.getRawFixes()));
} track.unlockAfterRead();
} }
}
@@ -16,6 +16,7 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
@@ -42,13 +43,15 @@ import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore; 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.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier; import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
@@ -66,14 +69,17 @@ public class CreateAndTrackWithRaceLogTest {
private RaceLogTrackingAdapter adapter; private RaceLogTrackingAdapter adapter;
private Regatta regatta; private Regatta regatta;
private AbstractLogEventAuthor author; private AbstractLogEventAuthor author;
private GPSFixStore gpsFixStore; private SensorFixStore sensorFixStore;
private long time = 0; private long time = 0;
@Rule
public Timeout CreateAndTrackWithRaceLogTestTimeout = new Timeout(3 * 60 * 1000);
@Before @Before
public void setup() { public void setup() {
service = new RacingEventServiceImpl(true, new MockSmartphoneImeiServiceFinderFactory()); service = new RacingEventServiceImpl(true, new MockSmartphoneImeiServiceFinderFactory());
gpsFixStore = service.getGPSFixStore(); sensorFixStore = service.getSensorFixStore();
service.getMongoObjectFactory().getDatabase().dropDatabase(); service.getMongoObjectFactory().getDatabase().dropDatabase();
author = service.getServerAuthor(); author = service.getServerAuthor();
Series series = new SeriesImpl("series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.emptySet(), Series series = new SeriesImpl("series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.emptySet(),
@@ -118,9 +124,9 @@ public class CreateAndTrackWithRaceLogTest {
private void addFixes0(DeviceIdentifier dev1) throws TransformationException, private void addFixes0(DeviceIdentifier dev1) throws TransformationException,
NoCorrespondingServiceRegisteredException { NoCorrespondingServiceRegisteredException {
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(5), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(5), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); 10, new DegreeBearingImpl(5))));
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(15), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(15), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); 10, new DegreeBearingImpl(5))));
} }
@@ -130,9 +136,9 @@ public class CreateAndTrackWithRaceLogTest {
testSize(race.getTrack(comp1), 1); testSize(race.getTrack(comp1), 1);
// further fix arrives in race // further fix arrives in race
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(7), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(7), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); 10, new DegreeBearingImpl(5))));
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(14), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(14), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); // outside mapping range 10, new DegreeBearingImpl(5)))); // outside mapping range
testSize(race.getTrack(comp1), 2); testSize(race.getTrack(comp1), 2);
} }
@@ -143,7 +149,7 @@ public class CreateAndTrackWithRaceLogTest {
testSize(race.getTrack(comp1), 4); testSize(race.getTrack(comp1), 4);
// add another fix in new mapping range // add another fix in new mapping range
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(18), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(18), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); 10, new DegreeBearingImpl(5))));
testSize(race.getTrack(comp1), 5); testSize(race.getTrack(comp1), 5);
} }
@@ -151,7 +157,7 @@ public class CreateAndTrackWithRaceLogTest {
private void addFixes3(TrackedRace race, Competitor comp1, DeviceIdentifier dev1) throws TransformationException, private void addFixes3(TrackedRace race, Competitor comp1, DeviceIdentifier dev1) throws TransformationException,
NoCorrespondingServiceRegisteredException { NoCorrespondingServiceRegisteredException {
// stop tracking, then no more fixes arrive at race // stop tracking, then no more fixes arrive at race
gpsFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(8), new KnotSpeedWithBearingImpl( sensorFixStore.storeFix(dev1, new GPSFixMovingImpl(new DegreePosition(0, 0), t(8), new KnotSpeedWithBearingImpl(
10, new DegreeBearingImpl(5)))); 10, new DegreeBearingImpl(5))));
testSize(race.getTrack(comp1), 5); testSize(race.getTrack(comp1), 5);
} }
@@ -179,20 +185,25 @@ public class CreateAndTrackWithRaceLogTest {
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
// start tracking // start tracking
adapter.startTracking(service, leaderboard, column, fleet); adapter.startTracking(service, leaderboard, column, fleet);
// now there is a tracked race // now there is a tracked race
TrackedRace race = column.getTrackedRace(fleet); TrackedRace race = column.getTrackedRace(fleet);
assertNotNull(race); assertNotNull(race);
RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager((DynamicTrackedRace) race, sensorFixStore, null);
race.waitForLoadingFromGPSFixStoreToFinishRunning(regattaLog); race.waitForLoadingToFinish();
addFixes1(race, comp1, dev1); addFixes1(race, comp1, dev1);
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, 0, comp1, dev1, t(11), t(20))); regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, 0, comp1, dev1, t(11), t(20)));
race.waitForLoadingToFinish();
// add another mapping on the fly, other old fixes should be loaded // add another mapping on the fly, other old fixes should be loaded
addFixes2(race, comp1, dev1); addFixes2(race, comp1, dev1);
// stop tracking, then no more fixes arrive at race // stop tracking, then no more fixes arrive at race
service.getRaceTrackerById(raceLog.getId()).stop(false); service.getRaceTrackerById(raceLog.getId()).stop(false);
raceLogFixTrackerManager.stop(false);
addFixes3(race, comp1, dev1); addFixes3(race, comp1, dev1);
} }
@@ -220,17 +231,21 @@ public class CreateAndTrackWithRaceLogTest {
// now there is a trackedrace // now there is a trackedrace
TrackedRace race = column.getTrackedRace(fleet); TrackedRace race = column.getTrackedRace(fleet);
assertNotNull(race); assertNotNull(race);
RaceLogFixTrackerManager raceLogFixTrackerManager = new RaceLogFixTrackerManager((DynamicTrackedRace) race, sensorFixStore, null);
race.waitForLoadingFromGPSFixStoreToFinishRunning(regattaLog); race.waitForLoadingToFinish();
addFixes1(race, comp1, dev1); addFixes1(race, comp1, dev1);
// add another mapping on the fly, other old fixes should be loaded // add another mapping on the fly, other old fixes should be loaded
regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, UUID.randomUUID(), comp1, dev1, regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(t(), t(), author, UUID.randomUUID(), comp1, dev1,
t(11), t(20))); t(11), t(20)));
race.waitForLoadingToFinish();
addFixes2(race, comp1, dev1); addFixes2(race, comp1, dev1);
// stop tracking, then no more fixes arrive at race // stop tracking, then no more fixes arrive at race
service.getRaceTrackerById(raceLog.getId()).stop(/* preemptive */ false); service.getRaceTrackerById(raceLog.getId()).stop(/* preemptive */ false);
raceLogFixTrackerManager.stop(false);
addFixes3(race, comp1, dev1); addFixes3(race, comp1, dev1);
} }
} }
@@ -6,7 +6,9 @@ import static org.mockito.Mockito.mock;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import org.junit.Rule;
import org.junit.Test; 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.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
@@ -26,10 +28,12 @@ import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier; import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; 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.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.TrackedRegatta; 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.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
@@ -40,6 +44,9 @@ import com.sap.sse.common.impl.TimeRangeImpl;
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest { public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er"); private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
@Rule
public Timeout TrackedRaceLoadsFixesTestTimeout = new Timeout(3 * 60 * 1000);
@Test @Test
public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException, public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException { NoCorrespondingServiceRegisteredException, InterruptedException {
@@ -62,18 +69,20 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40)); store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40));
store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40)); store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40));
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000)); trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000));
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2000)); trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2000));
new FixLoaderAndTracker(trackedRace, store, null);
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog); trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(regattaLog); trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getTrack(comp), 1); testNumberOfRawFixes(trackedRace.getTrack(comp), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1); testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
@@ -98,8 +107,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
DeviceIdentifier device3 = new SmartphoneImeiIdentifier("imei3"); DeviceIdentifier device3 = new SmartphoneImeiIdentifier("imei3");
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark), Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) })); new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(comp, comp2));
comp2 }));
map(comp, device, 0, 20000); map(comp, device, 0, 20000);
map(comp2, device2, 0, 600); map(comp2, device2, 0, 600);
@@ -117,16 +125,18 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
store.storeFix(device, createFix(i + 1000, 10, 20, 30, 40)); store.storeFix(device, createFix(i + 1000, 10, 20, 30, 40));
} }
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, DynamicTrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
null, /* endDate */null, null, null, "a", null)); /* startDate */ null, /* endDate */null, null, null, "a", null));
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class));
new FixLoaderAndTracker(trackedRace, store, null);
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog); trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(regattaLog); trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getTrack(comp), 10002); testNumberOfRawFixes(trackedRace.getTrack(comp), 10002);
testNumberOfRawFixes(trackedRace.getTrack(comp2), 1); testNumberOfRawFixes(trackedRace.getTrack(comp2), 1);
@@ -72,7 +72,8 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
setStartAndEndOfRaceInRaceLog(10000, 20000); setStartAndEndOfRaceInRaceLog(10000, 20000);
@@ -80,7 +81,6 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
trackedRace.attachRegattaLog(regattaLog); trackedRace.attachRegattaLog(regattaLog);
// test inference via race start/end time in racelog // test inference via race start/end time in racelog
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(regattaLog);
MillisecondsTimePoint startOfRaceInRaceLog = new MillisecondsTimePoint(10000); MillisecondsTimePoint startOfRaceInRaceLog = new MillisecondsTimePoint(10000);
TimePoint expectedStartOfTracking = startOfRaceInRaceLog.minus(Duration.ONE_MINUTE.times(TrackedRaceImpl.TRACKING_BUFFER_IN_MINUTES)); TimePoint expectedStartOfTracking = startOfRaceInRaceLog.minus(Duration.ONE_MINUTE.times(TrackedRaceImpl.TRACKING_BUFFER_IN_MINUTES));
MillisecondsTimePoint endOfRaceInRaceLog = new MillisecondsTimePoint(20000); MillisecondsTimePoint endOfRaceInRaceLog = new MillisecondsTimePoint(20000);
@@ -134,7 +134,8 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2]; final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2];
@@ -171,7 +172,8 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2]; final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2];
trackedRace.addListener(new AbstractRaceChangeListener() { trackedRace.addListener(new AbstractRaceChangeListener() {
@@ -212,19 +214,20 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
assertTrue(regatta.getRegatta().useStartTimeInference()); assertTrue(regatta.getRegatta().useStartTimeInference());
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog); trackedRace.attachRegattaLog(regattaLog);
final TimePoint[] newStartAndEndOfTrackingNotifiedByRace = new TimePoint[2]; final TimePoint[] newStartAndEndOfTrackingNotifiedByRace = new TimePoint[2];
trackedRace.addListener(new AbstractRaceChangeListener() { trackedRace.addListener(new AbstractRaceChangeListener() {
@Override @Override
public void startOfTrackingChanged(TimePoint startOfTracking) { public void startOfTrackingChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
newStartAndEndOfTrackingNotifiedByRace[0] = startOfTracking; newStartAndEndOfTrackingNotifiedByRace[0] = newStartOfRace;
} }
@Override @Override
public void endOfTrackingChanged(TimePoint endOfTracking) { public void endOfTrackingChanged(TimePoint oldEndOfTracking, TimePoint newEndOfTracking) {
newStartAndEndOfTrackingNotifiedByRace[1] = endOfTracking; newStartAndEndOfTrackingNotifiedByRace[1] = newEndOfTracking;
} }
}); });
assertNull(trackedRace.getStartOfTracking()); assertNull(trackedRace.getStartOfTracking());
@@ -314,7 +317,8 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */ EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null)); null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0, 0, 0,
/* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2]; final TimePoint[] oldAndNewStartTimeNotifiedByRace = new TimePoint[2];
@@ -10,19 +10,19 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl;
import com.sap.sse.common.TypeBasedServiceFinder; import com.sap.sse.common.TypeBasedServiceFinder;
public class MockGPSFixMongoServiceFinder implements TypeBasedServiceFinder<GPSFixMongoHandler> { public class MockGPSFixMongoServiceFinder implements TypeBasedServiceFinder<FixMongoHandler<?>> {
private final MongoObjectFactoryImpl mof = new MongoObjectFactoryImpl(null); private final MongoObjectFactoryImpl mof = new MongoObjectFactoryImpl(null);
private final DomainObjectFactoryImpl dof = new DomainObjectFactoryImpl(null, null); private final DomainObjectFactoryImpl dof = new DomainObjectFactoryImpl(null, null);
private final GPSFixMongoHandler movingHandler = new GPSFixMovingMongoHandlerImpl(mof, dof); private final FixMongoHandler<?> movingHandler = new GPSFixMovingMongoHandlerImpl(mof, dof);
private final GPSFixMongoHandler handler = new GPSFixMongoHandlerImpl(mof, dof); private final FixMongoHandler<?> handler = new GPSFixMongoHandlerImpl(mof, dof);
@Override @Override
public GPSFixMongoHandler findService(String fixType) { public FixMongoHandler<?> findService(String fixType) {
if (fixType.equals(GPSFixMovingImpl.class.getName()) || if (fixType.equals(GPSFixMovingImpl.class.getName()) ||
fixType.equals(CompactGPSFixMovingImpl.class.getName())) return movingHandler; fixType.equals(CompactGPSFixMovingImpl.class.getName())) return movingHandler;
if (fixType.equals(GPSFixImpl.class.getName()) || if (fixType.equals(GPSFixImpl.class.getName()) ||
@@ -31,11 +31,11 @@ public class MockGPSFixMongoServiceFinder implements TypeBasedServiceFinder<GPSF
} }
@Override @Override
public void setFallbackService(GPSFixMongoHandler fallback) { public void setFallbackService(FixMongoHandler<?> fallback) {
} }
@Override @Override
public Set<GPSFixMongoHandler> findAllServices() { public Set<FixMongoHandler<?>> findAllServices() {
return new HashSet<GPSFixMongoHandler>(Arrays.asList(movingHandler, handler)); return new HashSet<FixMongoHandler<?>>(Arrays.asList(movingHandler, handler));
} }
} }
@@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler; import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler; import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
import com.sap.sse.common.TypeBasedServiceFinder; import com.sap.sse.common.TypeBasedServiceFinder;
@@ -24,7 +24,7 @@ public class MockSmartphoneImeiServiceFinderFactory implements TypeBasedServiceF
public MockSmartphoneImeiServiceFinderFactory() { public MockSmartphoneImeiServiceFinderFactory() {
serviceFinders.put(DeviceIdentifierMongoHandler.class, new MockServiceFinder<>(new SmartphoneImeiMongoHandler())); serviceFinders.put(DeviceIdentifierMongoHandler.class, new MockServiceFinder<>(new SmartphoneImeiMongoHandler()));
serviceFinders.put(DeviceIdentifierJsonHandler.class, new MockServiceFinder<>(new SmartphoneImeiJsonHandler())); serviceFinders.put(DeviceIdentifierJsonHandler.class, new MockServiceFinder<>(new SmartphoneImeiJsonHandler()));
serviceFinders.put(GPSFixMongoHandler.class, new MockGPSFixMongoServiceFinder()); serviceFinders.put(FixMongoHandler.class, new MockGPSFixMongoServiceFinder());
serviceFinders.put(GPSFixJsonHandler.class, new MockServiceFinder<>(new MockGPSFixJsonHandler())); serviceFinders.put(GPSFixJsonHandler.class, new MockServiceFinder<>(new MockGPSFixJsonHandler()));
} }
@@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.GPSFixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDMongoHandler; import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDMongoHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler; import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler; import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
@@ -26,7 +26,7 @@ public class MockSmartphoneUuidServiceFinderFactory implements TypeBasedServiceF
public MockSmartphoneUuidServiceFinderFactory() { public MockSmartphoneUuidServiceFinderFactory() {
serviceFinders.put(DeviceIdentifierMongoHandler.class, new MockServiceFinder<>(new SmartphoneUUIDMongoHandler())); serviceFinders.put(DeviceIdentifierMongoHandler.class, new MockServiceFinder<>(new SmartphoneUUIDMongoHandler()));
serviceFinders.put(DeviceIdentifierJsonHandler.class, new MockServiceFinder<>(new SmartphoneUUIDJsonHandler())); serviceFinders.put(DeviceIdentifierJsonHandler.class, new MockServiceFinder<>(new SmartphoneUUIDJsonHandler()));
serviceFinders.put(GPSFixMongoHandler.class, new MockGPSFixMongoServiceFinder()); serviceFinders.put(FixMongoHandler.class, new MockGPSFixMongoServiceFinder());
serviceFinders.put(GPSFixJsonHandler.class, new MockServiceFinder<>(new MockGPSFixJsonHandler())); serviceFinders.put(GPSFixJsonHandler.class, new MockServiceFinder<>(new MockGPSFixJsonHandler()));
} }
@@ -21,6 +21,7 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.mail com.sap.sse.mail
Import-Package: com.google.zxing;version="3.1.0", Import-Package: com.google.zxing;version="3.1.0",
com.mongodb, com.mongodb,
com.sap.sse.concurrent,
com.sap.sse.shared.media, com.sap.sse.shared.media,
javax.mail;version="1.4.0", javax.mail;version="1.4.0",
javax.mail.internet;version="1.4.0", javax.mail.internet;version="1.4.0",
@@ -28,7 +29,8 @@ Import-Package: com.google.zxing;version="3.1.0",
javax.servlet;version="2.6.0", javax.servlet;version="2.6.0",
org.json.simple, org.json.simple,
org.json.simple.parser, org.json.simple.parser,
org.osgi.framework;version="1.6.0" org.osgi.framework;version="1.6.0",
org.osgi.util.tracker;version="1.5.1"
Export-Package: com.sap.sailing.domain.racelogtracking, 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"
Bundle-Activator: com.sap.sailing.domain.racelogtracking.impl.Activator Bundle-Activator: com.sap.sailing.domain.racelogtracking.impl.Activator
@@ -0,0 +1,16 @@
package com.sap.sailing.domain.racelogsensortracking;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.Timed;
public interface SensorFixMapperFactory {
<FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Competitor>
createCompetitorMapper(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType);
// <FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Mark>
// createMarkMapper(RegattaLogDeviceMarkMappingEvent event);
}
@@ -0,0 +1,34 @@
package com.sap.sailing.domain.racelogsensortracking.impl;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.Timed;
public class SensorFixMapperFactoryImpl implements SensorFixMapperFactory {
private final ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> tracker;
public SensorFixMapperFactoryImpl(ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> tracker) {
this.tracker = tracker;
}
@Override
@SuppressWarnings("unchecked")
public <FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Competitor>
createCompetitorMapper(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
for (ServiceReference<SensorFixMapper<?, ?, ?>> serviceReference : tracker.getServiceReferences()) {
SensorFixMapper<?, ?, ?> service = tracker.getService(serviceReference);
if (service != null && service.isResponsibleFor(eventType)) {
return (SensorFixMapper<FixT, TrackT, Competitor>) service;
}
}
throw new RuntimeException("No SensorFixMapper found for event type: " + eventType);
}
}
@@ -21,7 +21,6 @@ import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelogtracking.impl.RaceLogRaceTracker; import com.sap.sailing.domain.racelogtracking.impl.RaceLogRaceTracker;
import com.sap.sailing.domain.tracking.RaceHandle; import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -10,15 +10,21 @@ import java.util.logging.Logger;
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration; import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelogsensortracking.impl.SensorFixMapperFactoryImpl;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler; import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl; import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier; 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.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
import com.sap.sailing.server.MasterDataImportClassLoaderService; import com.sap.sailing.server.MasterDataImportClassLoaderService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.GPSFixJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.GPSFixJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer;
@@ -30,12 +36,16 @@ import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonH
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.GPSFixJsonHandlerImpl; import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.GPSFixJsonHandlerImpl;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler; import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler;
import com.sap.sse.common.TypeBasedServiceFinder; import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.replication.Replicable;
import com.sap.sse.util.ServiceTrackerFactory;
public class Activator implements BundleActivator { public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName()); private static final Logger logger = Logger.getLogger(Activator.class.getName());
private static BundleContext context; private static BundleContext context;
private ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
private ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> sensorFixMapperTracker;
public static BundleContext getContext() { public static BundleContext getContext() {
return context; return context;
} }
@@ -76,14 +86,31 @@ public class Activator implements BundleActivator {
registrations.add(context.registerService(MasterDataImportClassLoaderService.class, registrations.add(context.registerService(MasterDataImportClassLoaderService.class,
new MasterDataImportClassLoaderServiceImpl(), null)); new MasterDataImportClassLoaderServiceImpl(), null));
registrations.add(context.registerService(SensorFixMapper.class, new BravoDataFixMapper(), null));
sensorFixMapperTracker = createSensorFixMapperServiceTracker(context);
racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
RegattaLogFixTrackerRegattaListener regattaLogSensorDataTrackerTrackedRegattaListener = new RegattaLogFixTrackerRegattaListener(
racingEventServiceTracker, new SensorFixMapperFactoryImpl(sensorFixMapperTracker));
registrations.add(context.registerService(TrackedRegattaListener.class,
regattaLogSensorDataTrackerTrackedRegattaListener, null));
registrations.add(context.registerService(Replicable.class,
regattaLogSensorDataTrackerTrackedRegattaListener, null));
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName()); logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName());
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
private ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> createSensorFixMapperServiceTracker(BundleContext context) {
return (ServiceTracker) ServiceTrackerFactory.createAndOpen(context, SensorFixMapper.class);
}
@Override @Override
public void stop(BundleContext context) throws Exception { public void stop(BundleContext context) throws Exception {
Activator.context = null; Activator.context = null;
racingEventServiceTracker.close();
sensorFixMapperTracker.close();
for (ServiceRegistration<?> reg : registrations) { for (ServiceRegistration<?> reg : registrations) {
reg.unregister(); reg.unregister();
} }
@@ -0,0 +1,36 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorBravoMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.impl.BravoFixImpl;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
/**
* {@link SensorFixMapper} to handle {@link BravoFix}es.
*
*/
public class BravoDataFixMapper implements SensorFixMapper<DoubleVectorFix, DynamicSensorFixTrack<Competitor, BravoFix>, Competitor> {
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME));
}
@Override
public void addFix(DynamicSensorFixTrack<Competitor, BravoFix> track, DoubleVectorFix fix) {
track.add(new BravoFixImpl(fix));
}
@Override
public boolean isResponsibleFor(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
return RegattaLogDeviceCompetitorBravoMappingEventImpl.class.isAssignableFrom(eventType);
}
}
@@ -19,7 +19,6 @@ import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException; import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException; import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.HasRegattaLike;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.RaceTracker;
@@ -54,13 +53,13 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
@Override @Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) { RaceLogResolver raceLogResolver) {
return createRaceTracker(regatta, trackedRegattaRegistry, windStore, gpsFixStore, raceLogResolver); return createRaceTracker(regatta, trackedRegattaRegistry, windStore, raceLogResolver);
} }
@Override @Override
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) { WindStore windStore, RaceLogResolver raceLogResolver) {
if (regatta == null) { if (regatta == null) {
BoatClass boatClass = new RaceInformationFinder(getRaceLog()).analyze().getBoatClass(); BoatClass boatClass = new RaceInformationFinder(getRaceLog()).analyze().getBoatClass();
regatta = service.getOrCreateDefaultRegatta( regatta = service.getOrCreateDefaultRegatta(
@@ -71,7 +70,7 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
throw new RaceNotCreatedException("No regatta for race-log tracked race"); throw new RaceNotCreatedException("No regatta for race-log tracked race");
} }
DynamicTrackedRegatta trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); DynamicTrackedRegatta trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta);
return new RaceLogRaceTracker(trackedRegatta, this, windStore, gpsFixStore, raceLogResolver); return new RaceLogRaceTracker(trackedRegatta, this, windStore, raceLogResolver);
} }
@Override @Override
@@ -8,8 +8,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -36,18 +34,13 @@ import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTr
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor; import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
@@ -58,35 +51,21 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState; import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException; import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.GPSFixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.regattalike.IsRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.AbstractRaceTrackerBaseImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceHandle; import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
import difflib.PatchFailedException; import difflib.PatchFailedException;
@@ -100,33 +79,26 @@ import difflib.PatchFailedException;
* Otherwise, the tracker waits until a {@link RaceLogStartTrackingEvent} is received to perform these tasks. * Otherwise, the tracker waits until a {@link RaceLogStartTrackingEvent} is received to perform these tasks.
* *
* @author Fredrik Teschke * @author Fredrik Teschke
*
*/ */
public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener { public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
private static final String LOGGER_AND_LOGAUTHOR_NAME = RaceLogRaceTracker.class.getName();
private static final Logger logger = Logger.getLogger(LOGGER_AND_LOGAUTHOR_NAME);
private final AbstractLogEventAuthor raceLogEventAuthor = new LogEventAuthorImpl(LOGGER_AND_LOGAUTHOR_NAME, 0);
private final Map<AbstractLog<?, ?>, Object> visitors = new HashMap<AbstractLog<?, ?>, Object>();
private final RaceLogConnectivityParams params; private final RaceLogConnectivityParams params;
private final WindStore windStore; private final WindStore windStore;
private final GPSFixStore gpsFixStore;
private final DynamicTrackedRegatta regatta; private final DynamicTrackedRegatta regatta;
private final RaceLogResolver raceLogResolver; private final RaceLogResolver raceLogResolver;
private final Map<AbstractLog<?, ?>, Object> visitors = new HashMap<AbstractLog<?, ?>, Object>();
private final ConcurrentMap<Competitor, List<DeviceMapping<Competitor>>> competitorMappings = new ConcurrentHashMap<>();
private final ConcurrentMap<Mark, List<DeviceMapping<Mark>>> markMappings = new ConcurrentHashMap<>();
private final Map<DeviceIdentifier, List<DeviceMapping<Mark>>> markMappingsByDevices = new HashMap<>();
private final Map<DeviceIdentifier, List<DeviceMapping<Competitor>>> competitorMappingsByDevices = new HashMap<>();
private DynamicTrackedRace trackedRace; private DynamicTrackedRace trackedRace;
private final AbstractLogEventAuthor raceLogEventAuthor = new LogEventAuthorImpl(
RaceLogRaceTracker.class.getName(), 0);
private static final Logger logger = Logger.getLogger(RaceLogRaceTracker.class.getName());
public RaceLogRaceTracker(DynamicTrackedRegatta regatta, RaceLogConnectivityParams params, WindStore windStore, public RaceLogRaceTracker(DynamicTrackedRegatta regatta, RaceLogConnectivityParams params, WindStore windStore,
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) { RaceLogResolver raceLogResolver) {
this.params = params; this.params = params;
this.windStore = windStore; this.windStore = windStore;
this.gpsFixStore = gpsFixStore;
this.regatta = regatta; this.regatta = regatta;
this.raceLogResolver = raceLogResolver; this.raceLogResolver = raceLogResolver;
@@ -168,20 +140,11 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
((RaceLog) log).addListener(visitor); ((RaceLog) log).addListener(visitor);
} else if (log instanceof RegattaLog) { } else if (log instanceof RegattaLog) {
RegattaLogEventVisitor visitor = new BaseRegattaLogEventVisitor() { RegattaLogEventVisitor visitor = new BaseRegattaLogEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
RaceLogRaceTracker.this.onDeviceCompetitorMappingEvent(event);
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
RaceLogRaceTracker.this.onDeviceMarkMappingEvent(event);
}
@Override @Override
public void visit(RegattaLogDefineMarkEvent event) { public void visit(RegattaLogDefineMarkEvent event) {
RaceLogRaceTracker.this.onDefineMarkEvent(event); RaceLogRaceTracker.this.onDefineMarkEvent(event);
} }
}; };
visitors.put(log, visitor); visitors.put(log, visitor);
((RegattaLog) log).addListener(visitor); ((RegattaLog) log).addListener(visitor);
@@ -198,7 +161,7 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
} }
@Override @Override
public void stop(boolean preemptive) { protected void onStop(boolean preemptive) {
RaceLog raceLog = params.getRaceLog(); RaceLog raceLog = params.getRaceLog();
final Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> trackingTimes = new TrackingTimesFinder(raceLog).analyze(); final Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> trackingTimes = new TrackingTimesFinder(raceLog).analyze();
if (trackingTimes == null || trackingTimes.getB() == null || trackingTimes.getB().getTimePoint() == null) { if (trackingTimes == null || trackingTimes.getB() == null || trackingTimes.getB().getTimePoint() == null) {
@@ -207,18 +170,11 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
raceLog.add(new RaceLogEndOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, /* passId */ 0)); raceLog.add(new RaceLogEndOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, /* passId */ 0));
} }
// mark passing calculator is automatically stopped, when the race status is set to {@link
// TrackedRaceStatusEnum#FINISHED}
trackedRace.setStatus(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, 100));
// remove listeners on logs // remove listeners on logs
for (Entry<AbstractLog<?, ?>, Object> visitor : visitors.entrySet()) { for (Entry<AbstractLog<?, ?>, Object> visitor : visitors.entrySet()) {
visitor.getKey().removeListener(visitor.getValue()); visitor.getKey().removeListener(visitor.getValue());
} }
// remove listener for fixes
gpsFixStore.removeListener(this);
logger.info(String.format("Stopped tracking race-log race %s %s %s", params.getLeaderboard(), logger.info(String.format("Stopped tracking race-log race %s %s %s", params.getLeaderboard(),
params.getRaceColumn(), params.getFleet())); params.getRaceColumn(), params.getFleet()));
} }
@@ -253,107 +209,10 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
return windStore; return windStore;
} }
@Override
public GPSFixStore getGPSFixStore() {
return gpsFixStore;
}
@Override @Override
public Object getID() { public Object getID() {
return params.getRaceLog().getId(); return params.getRaceLog().getId();
} }
private Map<Competitor, List<DeviceMapping<Competitor>>> getNewCompetitorMappings() throws DoesNotHaveRegattaLogException {
return new RegattaLogDeviceCompetitorMappingFinder(params.getRegattaLog()).analyze();
}
private Map<Mark, List<DeviceMapping<Mark>>> getNewMarkMappings() throws DoesNotHaveRegattaLogException {
return new RegattaLogDeviceMarkMappingFinder(params.getRegattaLog()).analyze();
}
private <ItemT extends WithID, FixT extends GPSFix> boolean hasMappingAlreadyBeenLoaded(
DeviceMapping<ItemT> newMapping, List<DeviceMapping<ItemT>> oldMappings) {
if (oldMappings == null) {
return false;
}
for (DeviceMapping<ItemT> oldMapping : oldMappings) {
if (newMapping.getDevice() == oldMapping.getDevice()
&& newMapping.getTimeRange().liesWithin(oldMapping.getTimeRange())) {
return true;
}
}
return false;
}
private <ItemT extends WithID, FixT extends GPSFix> Map<DeviceIdentifier, List<DeviceMapping<ItemT>>> transformToMappingsByDevice(
Map<ItemT, List<DeviceMapping<ItemT>>> mappings) {
Map<DeviceIdentifier, List<DeviceMapping<ItemT>>> result = new HashMap<>();
for (ItemT item : mappings.keySet()) {
for (DeviceMapping<ItemT> mapping : mappings.get(item)) {
List<DeviceMapping<ItemT>> list = result.get(mapping.getDevice());
if (list == null) {
list = new ArrayList<>();
result.put(mapping.getDevice(), list);
}
list.add(mapping);
}
}
return result;
}
/**
* Adjusts the contents of {@link #markMappings} according to the device mappings for marks found in the regatta
* log. Afterwards, the start end end of tracking is {@link #updateStartAndEndOfTracking(boolean) updated}.
*
* @param loadIfNotCovered
* If <code>true</code>, for additional time ranges added compared to the previous contents of
* {@link #markMappings}, their fixes are loaded from the {@link #gpsFixStore}.
* @throws DoesNotHaveRegattaLogException
*/
private void updateMarkMappings(boolean loadIfNotCovered) throws DoesNotHaveRegattaLogException {
assert trackedRace != null;
// TODO remove fixes, if mappings have been removed
// check if there are new time ranges not covered so far
Map<Mark, List<DeviceMapping<Mark>>> newMappings = getNewMarkMappings();
if (loadIfNotCovered) {
for (Mark mark : newMappings.keySet()) {
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
List<DeviceMapping<Mark>> oldMappings = markMappings.get(mark);
if (oldMappings != null) {
for (DeviceMapping<Mark> newMapping : newMappings.get(mark)) {
if (!hasMappingAlreadyBeenLoaded(newMapping, oldMappings)) {
try {
gpsFixStore.loadMarkTrack(track, newMapping);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load mark track " + newMapping.getMappedTo());
}
}
}
}
}
}
markMappings.clear();
markMappings.putAll(newMappings);
markMappingsByDevices.clear();
markMappingsByDevices.putAll(transformToMappingsByDevice(markMappings));
}
private void onDeviceMarkMappingEvent(RegattaLogDeviceMarkMappingEvent event) {
if (trackedRace != null) {
try {
updateMarkMappings(true);
} catch (DoesNotHaveRegattaLogException e) {
logger.warning("Could not load update mark mappings as RegattaLog couldn't be found");
}
gpsFixStore.addListener(this, event.getDevice());
}
}
/** /**
* When a log is attached to it, the tracked race creates mark tracks for all marks either defined or with a device * When a log is attached to it, the tracked race creates mark tracks for all marks either defined or with a device
@@ -367,56 +226,6 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
} }
} }
private void onDeviceCompetitorMappingEvent(RegattaLogDeviceCompetitorMappingEvent event) {
if (trackedRace != null) {
try {
updateCompetitorMappings(true);
} catch (DoesNotHaveRegattaLogException e) {
logger.warning("Could not load update competitor mappings as RegattaLog couldn't be found");
}
gpsFixStore.addListener(this, event.getDevice());
}
}
/**
* Adjusts the {@link #competitorMappings} map according to the competitor registrations for the race managed by
* this tracked, either from the regatta log or the race log. Then, the {@link TrackedRace}'s start and end of
* tracking time frame is {@link #updateStartAndEndOfTracking(boolean) updated} from the mapping intervals.
*
* @param loadIfNotCovered
* if <code>true</code>, the GPS fixes for the mappings will be loaded based on a comparison of the
* previous mappings from {@link #competitorMappings} and the new mappings.
*
* @throws DoesNotHaveRegattaLogException
*/
private void updateCompetitorMappings(boolean loadIfNotCovered) throws DoesNotHaveRegattaLogException {
// TODO remove fixes, if mappings have been removed
// check if there are new time ranges not covered so far
Map<Competitor, List<DeviceMapping<Competitor>>> newMappings = getNewCompetitorMappings();
if (loadIfNotCovered) {
for (Competitor competitor : newMappings.keySet()) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
List<DeviceMapping<Competitor>> oldMappings = competitorMappings.get(competitor);
for (DeviceMapping<Competitor> newMapping : newMappings.get(competitor)) {
if (!hasMappingAlreadyBeenLoaded(newMapping, oldMappings)) {
try {
gpsFixStore.loadCompetitorTrack(track, newMapping);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load competitor track " + newMapping.getMappedTo());
}
}
}
}
}
competitorMappings.clear();
competitorMappings.putAll(newMappings);
competitorMappingsByDevices.clear();
competitorMappingsByDevices.putAll(transformToMappingsByDevice(competitorMappings));
}
private void onStartTrackingEvent(RaceLogStartTrackingEvent event) { private void onStartTrackingEvent(RaceLogStartTrackingEvent event) {
if (trackedRace == null) { if (trackedRace == null) {
startTracking(event); startTracking(event);
@@ -457,8 +266,7 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
RaceLogDenoteForTrackingEvent denoteEvent = new RaceInformationFinder(raceLog).analyze(); RaceLogDenoteForTrackingEvent denoteEvent = new RaceInformationFinder(raceLog).analyze();
final Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> trackingTimes = new TrackingTimesFinder(raceLog).analyze(); final Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> trackingTimes = new TrackingTimesFinder(raceLog).analyze();
if (trackingTimes == null || trackingTimes.getA() == null || trackingTimes.getA().getTimePoint() == null) { if (trackingTimes == null || trackingTimes.getA() == null || trackingTimes.getA().getTimePoint() == null) {
// the start of tracking interval is unset or set to null; enter "now" as start of tracking // the start of tracking interval is unset or set to null; enter "now" as start of tracking into the race log
// into the race log
raceLog.add(new RaceLogStartOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, /* passId */ 0)); raceLog.add(new RaceLogStartOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, /* passId */ 0));
} }
BoatClass boatClass = denoteEvent.getBoatClass(); BoatClass boatClass = denoteEvent.getBoatClass();
@@ -487,87 +295,13 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
// set race definition, so race is linked to leaderboard automatically // set race definition, so race is linked to leaderboard automatically
regatta.getRegatta().addRace(raceDef); regatta.getRegatta().addRace(raceDef);
raceColumn.setRaceIdentifier(fleet, regatta.getRegatta().getRaceIdentifier(raceDef)); raceColumn.setRaceIdentifier(fleet, regatta.getRegatta().getRaceIdentifier(raceDef));
trackedRace = regatta.createTrackedRace(raceDef, sidelines, windStore, gpsFixStore, trackedRace = regatta.createTrackedRace(raceDef, sidelines, windStore,
params.getDelayToLiveInMillis(), WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, params.getDelayToLiveInMillis(), WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver); boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver);
trackedRace.setStatus(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 0));
//attach regatta logs
for (AbstractLog<?, ?> log : params.getLogHierarchy()) {
if (log instanceof RegattaLog) {
trackedRace.attachRegattaLog((RegattaLog) log);
}
}
// update the device mappings (without loading the fixes, as the TrackedRace does this itself on startup)
try {
updateCompetitorMappings(false);
updateMarkMappings(false);
} catch (DoesNotHaveRegattaLogException e) {
logger.warning("Could not load update mark and competitor mappings as RegattaLog couldn't be found");
}
// add listeners for devices in mappings already present
addListeners(competitorMappings);
addListeners(markMappings);
logger.info(String.format("Started tracking race-log race (%s)", raceLog)); logger.info(String.format("Started tracking race-log race (%s)", raceLog));
// this wakes up all waiting race handles // this wakes up all waiting race handles
synchronized (this) { synchronized (this) {
this.notifyAll(); this.notifyAll();
} }
} }
private <T extends WithID> void addListeners(Map<T, List<DeviceMapping<T>>> mappings) {
for (List<DeviceMapping<T>> list : mappings.values()) {
for (DeviceMapping<T> mapping : list) {
gpsFixStore.addListener(this, mapping.getDevice());
}
}
}
public RaceLogConnectivityParams getParams() {
return params;
}
@Override
public void fixReceived(DeviceIdentifier device, GPSFix fix) {
TimePoint timePoint = fix.getTimePoint();
if (markMappingsByDevices.get(device) != null) {
for (DeviceMapping<Mark> mapping : markMappingsByDevices.get(device)) {
Mark mark = mapping.getMappedTo();
if (mapping.getTimeRange().includes(timePoint)) {
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
final GPSFix firstFixAtOrAfter;
boolean forceFix;
markTrack.lockForRead();
try {
forceFix = Util.isEmpty(markTrack.getRawFixes()) ||
(firstFixAtOrAfter=markTrack.getFirstFixAtOrAfter(timePoint)) != null &&
firstFixAtOrAfter.getTimePoint().equals(timePoint);
} finally {
markTrack.unlockAfterRead();
}
if (forceFix) {
trackedRace.recordFix(mark, fix, /* only when in tracking interval */ false); // force fix into track
} else {
trackedRace.recordFix(mark, fix);
}
}
}
}
if (competitorMappingsByDevices.get(device) != null) {
for (DeviceMapping<Competitor> mapping : competitorMappingsByDevices.get(device)) {
Competitor comp = mapping.getMappedTo();
if (mapping.getTimeRange().includes(timePoint)) {
if (fix instanceof GPSFixMoving) {
trackedRace.recordFix(comp, (GPSFixMoving) fix);
} else {
logger.log(
Level.WARNING,
String.format(
"Could not add fix for competitor (%s) in race (%s), as it is no GPSFixMoving, meaning it is missing COG/SOG values",
comp, params.getRaceLog()));
}
}
}
}
}
} }
@@ -56,7 +56,6 @@ import com.sap.sailing.domain.common.racelog.tracking.NotDenotableForRaceLogTrac
import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException; import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException;
import com.sap.sailing.domain.common.racelog.tracking.RaceLogRaceTrackerExistsException; import com.sap.sailing.domain.common.racelog.tracking.RaceLogRaceTrackerExistsException;
import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState; import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
@@ -250,8 +249,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
now, service.getServerAuthor(), UUID.randomUUID(), mark, device, timePoint, timePoint); now, service.getServerAuthor(), UUID.randomUUID(), mark, device, timePoint, timePoint);
log.add(mapping); log.add(mapping);
try { try {
service.getGPSFixStore().storeFix(device, gpsFix); service.getSensorFixStore().storeFix(device, gpsFix);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) { } catch (NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not ping mark " + mark); logger.log(Level.WARNING, "Could not ping mark " + mark);
} }
} }
@@ -0,0 +1,362 @@
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.racelog.impl.GPSFixStoreImpl;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.TimeRangeImpl;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
/**
* This class listens to RaceLog Events, changes to the race and fix loading events and properly handles mappings and
* fix loading.
*
* <ul>
* <li>{@link RaceChangeListener},</li>
* <li>{@link RegattaLogEventVisitor}</li>
* <li>{@link FixReceivedListener}</li>
* </ul>
*
*/
public class FixLoaderAndTracker implements TrackingDataLoader {
private static final Logger logger = Logger.getLogger(FixLoaderAndTracker.class.getName());
protected final DynamicTrackedRace trackedRace;
private final NamedReentrantReadWriteLock loadingFromFixStoreLock;
private final SensorFixStore sensorFixStore;
private final GPSFixStore gpsFixStore;
private RegattaLogDeviceMappings<WithID> deviceMappings;
private final AtomicInteger activeLoaders = new AtomicInteger();
private final SensorFixMapperFactory sensorFixMapperFactory;
private AtomicBoolean preemptiveStopRequested = new AtomicBoolean(false);
private AtomicBoolean stopRequested = new AtomicBoolean(false);
private final AbstractRaceChangeListener raceChangeListener = new AbstractRaceChangeListener() {
@Override
public void startOfTrackingChanged(TimePoint oldStartOfTracking, TimePoint newStartOfTracking) {
if ((newStartOfTracking == null
|| (oldStartOfTracking != null && newStartOfTracking.before(oldStartOfTracking)))) {
loadFixesForExtendedTimeRange(newStartOfTracking, oldStartOfTracking);
}
}
@Override
public void endOfTrackingChanged(TimePoint oldEndOfTracking, TimePoint newEndOfTracking) {
if (newEndOfTracking == null || (oldEndOfTracking != null && newEndOfTracking.after(oldEndOfTracking))) {
loadFixesForExtendedTimeRange(oldEndOfTracking, newEndOfTracking);
}
}
public void regattaLogAttached(RegattaLog regattaLog) {
deviceMappings.addRegattaLog(regattaLog);
}
};
private final FixReceivedListener<Timed> listener = new FixReceivedListener<Timed>() {
@Override
public void fixReceived(DeviceIdentifier device, Timed fix) {
if (preemptiveStopRequested.get()) {
return;
}
final TimePoint timePoint = fix.getTimePoint();
deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), (mapping) -> {
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
}
SensorFixMapper<DoubleVectorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
.createCompetitorMapper(event.getClass());
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
event.getMappedTo());
if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
mapper.addFix(track, (DoubleVectorFix) fix);
}
}
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
}
Competitor comp = event.getMappedTo();
if (fix instanceof GPSFixMoving) {
trackedRace.recordFix(comp, (GPSFixMoving) fix);
} else {
logger.log(Level.WARNING,
String.format(
"Could not add fix for competitor (%s) in race (%s), as it"
+ " is no GPSFixMoving, meaning it is missing COG/SOG values",
comp, trackedRace.getRace().getName()));
}
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
if (preemptiveStopRequested.get()) {
return;
}
Mark mark = event.getMappedTo();
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();
}
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
}
});
});
}
};
public FixLoaderAndTracker(DynamicTrackedRace trackedRace, SensorFixStore sensorFixStore,
SensorFixMapperFactory sensorFixMapperFactory) {
this.sensorFixStore = sensorFixStore;
this.gpsFixStore = new GPSFixStoreImpl(sensorFixStore);
this.sensorFixMapperFactory = sensorFixMapperFactory;
this.trackedRace = trackedRace;
loadingFromFixStoreLock = new NamedReentrantReadWriteLock(
"Loading from SensorFix store lock for tracked race " + trackedRace.getRace().getName(), false);
startTracking();
}
private void loadFixes(TimeRange timeRangeToLoad, DeviceMappingWithRegattaLogEvent<? extends WithID> mapping) {
if (preemptiveStopRequested.get()) {
return;
}
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
SensorFixMapper<Timed, Track<?>, Competitor> mapper = sensorFixMapperFactory
.createCompetitorMapper(event.getClass());
Track<?> track = mapper.getTrack(trackedRace, event.getMappedTo());
try {
sensorFixStore.loadFixes((DoubleVectorFix fix) -> mapper.addFix(track, fix), mapping.getDevice(),
timeRangeToLoad.from(), timeRangeToLoad.to(), true);
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
logger.log(Level.WARNING, "Could not load track for competitor: " + mapping.getMappedTo()
+ "; device: " + mapping.getDevice());
}
}
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(event.getMappedTo());
try {
@SuppressWarnings({ "unchecked" })
DeviceMapping<Competitor> competitorMapping = (DeviceMapping<Competitor>) mapping;
gpsFixStore.loadCompetitorTrack(track, competitorMapping, getStartOfTracking(),
getEndOfTracking());
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load competitor track " + mapping.getMappedTo());
}
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
try {
@SuppressWarnings("unchecked")
DeviceMapping<Mark> markMapping = (DeviceMapping<Mark>) mapping;
TimePoint from = getStartOfTracking();
TimePoint to = getEndOfTracking();
gpsFixStore.loadMarkTrack(track, markMapping, from, to);
if (track.getFirstRawFix() == null) {
logger.fine("Loading mark positions from outside of start/end of tracking interval (" + from
+ ".." + 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());
}
}
});
}
private TimeRange getTrackingTimeRange() {
return new TimeRangeImpl(getStartOfTracking() == null ? TimePoint.BeginningOfTime : getStartOfTracking(),
getEndOfTracking() == null ? TimePoint.EndOfTime : getEndOfTracking());
}
public void stop(boolean preemptive) {
preemptiveStopRequested.set(preemptive);
stopRequested.set(true);
trackedRace.removeListener(raceChangeListener);
deviceMappings.stop();
synchronized (this) {
if (activeLoaders.get() == 0) {
setStatusAndProgress(TrackedRaceStatusEnum.FINISHED, 1.0);
}
}
sensorFixStore.removeListener(listener);
if (preemptiveStopRequested.get()) {
waitForLoadingToFinishRunning();
}
}
protected void startTracking() {
trackedRace.addListener(raceChangeListener);
this.deviceMappings = new FixLoaderDeviceMappings(trackedRace.getAttachedRegattaLogs());
}
private synchronized void waitForLoadingToFinishRunning() {
try {
while (activeLoaders.get() > 0) {
wait();
}
} catch (InterruptedException e) {
logger.log(Level.WARNING, "Interrupted while waiting for Fixes to be loaded", e);
}
}
// TEMP: kommt von RaceLogSensorFixTracker
protected TimePoint getStartOfTracking() {
return trackedRace.getStartOfTracking();
}
// TEMP: kommt von RaceLogSensorFixTracker
protected TimePoint getEndOfTracking() {
return trackedRace.getEndOfTracking();
}
protected void loadFixesForExtendedTimeRange(TimePoint loadFixesFrom, TimePoint loadFixesTo) {
final TimeRangeImpl extendedTimeRange = new TimeRangeImpl(loadFixesFrom, loadFixesTo);
deviceMappings.forEachMapping((mapping) -> {
TimeRange timeRangeToLoad = extendedTimeRange.intersection(mapping.getTimeRange());
if (timeRangeToLoad != null && !preemptiveStopRequested.get()) {
loadFixes(timeRangeToLoad, mapping);
}
});
}
private void updateConcurrent(final Runnable updateCallback) {
synchronized (FixLoaderAndTracker.this) {
activeLoaders.incrementAndGet();
setStatusAndProgress(TrackedRaceStatusEnum.LOADING, 0.5);
}
Thread t = new Thread(
this.getClass().getSimpleName() + " loader for tracked race " + trackedRace.getRace().getName()) {
@Override
public void run() {
try {
if (!preemptiveStopRequested.get()) {
trackedRace.lockForSerializationRead();
setStatusAndProgress(TrackedRaceStatusEnum.LOADING, 0.5);
LockUtil.lockForWrite(loadingFromFixStoreLock);
synchronized (FixLoaderAndTracker.this) {
FixLoaderAndTracker.this.notifyAll();
}
updateCallback.run();
}
} finally {
LockUtil.unlockAfterWrite(loadingFromFixStoreLock);
synchronized (FixLoaderAndTracker.this) {
int currentActiveLoaders;
currentActiveLoaders = activeLoaders.decrementAndGet();
FixLoaderAndTracker.this.notifyAll();
if (currentActiveLoaders == 0) {
setStatusAndProgress(stopRequested.get() ? TrackedRaceStatusEnum.FINISHED
: TrackedRaceStatusEnum.TRACKING, 1.0);
}
}
trackedRace.unlockAfterSerializationRead();
logger.info("Thread " + getName() + " done.");
}
}
};
t.start();
}
/**
* Tells if currently the race is loading GPS fixes from the {@link GPSFixStore}. Clients may {@link Object#wait()}
* on <code>this</code> object and will be notified whenever a change of this flag's value occurs.
*/
public boolean isLoadingFromGPSFixStore() {
return activeLoaders.get() > 0;
}
private void setStatusAndProgress(TrackedRaceStatusEnum status, double progress) {
trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(status, progress));
}
private class FixLoaderDeviceMappings extends RegattaLogDeviceMappings<WithID> {
public FixLoaderDeviceMappings(Iterable<RegattaLog> initialRegattaLogs) {
super(initialRegattaLogs);
}
@Override
protected void updateMappings() {
updateConcurrent(() -> {
FixLoaderDeviceMappings.super.updateMappings();
// add listeners for devices in mappings already present
forEachDevice((device) -> sensorFixStore.addListener(listener, device));
});
}
@Override
protected void mappingRemoved(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
// TODO if tracks are always associated to only one device mapping, we could remove tracks here
// TODO remove listener from store if there is no mapping left for the DeviceIdentifier
}
@Override
protected void mappingAdded(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
loadFixes(getTrackingTimeRange().intersection(mapping.getTimeRange()), mapping);
}
@Override
protected void mappingChanged(DeviceMappingWithRegattaLogEvent<WithID> oldMapping,
DeviceMappingWithRegattaLogEvent<WithID> newMapping) {
// TODO can the new time range be bigger than the old one? In this case we would need to load the
// additional time range.
}
}
}
@@ -0,0 +1,153 @@
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
import java.security.acl.Owner;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTrackingStateAnalyzer;
import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
/**
* This class manages the lifecycle of the {@link FixLoaderAndTracker} by listening to
* {@link RaceLogDenoteForTrackingEvent}, race log attached and stop tracking race changes.
*
* Once the race is stopped, it notifies its own {@link Owner} so that all reference to this instance can be cleanly
* removed to prevent memory leaks.
*/
public class RaceLogFixTrackerManager implements TrackingDataLoader {
private static final Logger logger = Logger.getLogger(RaceLogFixTrackerManager.class.getName());
private final DynamicTrackedRace trackedRace;
private final SensorFixStore sensorFixStore;
private final SensorFixMapperFactory sensorFixMapperFactory;
/**
* We maintain our own collection that holds the RaceLogs. The known RaceLogs should by in sync with the ones that
* can be obtained from the TrackedRace. When stopping, there could be a concurrency issue that leads to a listener
* not being removed. This is prevented by remembering all RaceLogs to which we attached a listener. So we can be
* sure to not produce a memory leak.
*/
private final Set<RaceLog> knownRaceLogs = new HashSet<>();
private FixLoaderAndTracker tracker;
private final RaceChangeListener raceChangeListener = new AbstractRaceChangeListener() {
public void raceLogAttached(RaceLog raceLog) {
addRaceLog(raceLog);
}
public void raceLogDetached(RaceLog raceLog) {
removeRaceLog(raceLog);
}
};
private final RaceLogEventVisitor raceLogEventVisitor = new BaseRaceLogEventVisitor() {
@Override
public void visit(RaceLogDenoteForTrackingEvent event) {
updateDenotationState();
}
@Override
public void visit(RaceLogRevokeEvent event) {
updateDenotationState();
}
};
public RaceLogFixTrackerManager(DynamicTrackedRace trackedRace, SensorFixStore sensorFixStore,
SensorFixMapperFactory sensorFixMapperFactory) {
this.trackedRace = trackedRace;
this.sensorFixStore = sensorFixStore;
this.sensorFixMapperFactory = sensorFixMapperFactory;
trackedRace.addListener(raceChangeListener);
synchronized (knownRaceLogs) {
for (RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
addRaceLogUnlocked(raceLog);
}
}
updateDenotationState();
}
private synchronized void updateDenotationState() {
if (isForTracking()) {
startTrackerIfNotAlreadyStarted();
} else {
stopTrackerIfStillRunning(false);
}
}
private void addRaceLog(RaceLog raceLog) {
synchronized (knownRaceLogs) {
addRaceLogUnlocked(raceLog);
}
updateDenotationState();
}
private void removeRaceLog(RaceLog raceLog) {
synchronized (knownRaceLogs) {
removeRaceLogUnlocked(raceLog);
}
updateDenotationState();
}
private void addRaceLogUnlocked(RaceLog raceLog) {
knownRaceLogs.add(raceLog);
raceLog.addListener(raceLogEventVisitor);
}
private void removeRaceLogUnlocked(RaceLog raceLog) {
raceLog.removeListener(raceLogEventVisitor);
knownRaceLogs.remove(raceLog);
}
public void stop(boolean preemptive) {
stopTrackerIfStillRunning(preemptive);
trackedRace.removeListener(raceChangeListener);
synchronized (knownRaceLogs) {
for (RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
removeRaceLogUnlocked(raceLog);
}
}
}
private synchronized void startTrackerIfNotAlreadyStarted() {
if (tracker == null) {
logger.fine("Starting fix tracker for TrackedRace: " + trackedRace.getRaceIdentifier());
tracker = new FixLoaderAndTracker(trackedRace, sensorFixStore, sensorFixMapperFactory);
}
}
private synchronized void stopTrackerIfStillRunning(boolean preemptive) {
if (tracker != null) {
logger.fine("Stopping fix tracker for TrackedRace: " + trackedRace.getRaceIdentifier());
tracker.stop(preemptive);
tracker = null;
}
}
boolean isForTracking() {
for (RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
RaceLogTrackingState raceLogTrackingState = new RaceLogTrackingStateAnalyzer(raceLog).analyze();
if (raceLogTrackingState.isForTracking()) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,331 @@
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.WithID;
/**
* Holds DeviceMappings to make it possible to track changes. This makes it possible to only process mappings that
* weren't processed before.
*
* @param <ItemT> The type of items the DeviceMappings are mapped to
*/
public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
private static final Logger logger = Logger.getLogger(RegattaLogDeviceMappings.class.getName());
/**
* We maintain our own collection that holds the RegattaLogs. The known RegattaLogs should by in sync with the ones that
* can be obtained from the TrackedRace. When stopping, there could be a concurrency issue that leads to a listener
* not being removed. This is prevented by remembering all RegattaLogs to which we attached a listener. So we can be
* sure to not produce a memory leak.
*/
private final Set<RegattaLog> knownRegattaLogs = new HashSet<>();
private final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappings = new HashMap<>();
private final Map<DeviceIdentifier, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappingsByDevice = new HashMap<>();
private final RegattaLogEventVisitor regattaLogEventVisitor = new BaseRegattaLogEventVisitor() {
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
logger.log(Level.FINE, "New mapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@Override
public void visit(RegattaLogDeviceMarkMappingEvent event) {
logger.log(Level.FINE,
"New DeviceMarkMapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@Override
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
logger.log(Level.FINE,
"New DeviceCompetitorMapping for: " + event.getMappedTo() + "; device: " + event.getDevice());
updateMappings();
}
@Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
logger.log(Level.FINE, "CloseOpenEndedDeviceMapping closed: " + event.getDeviceMappingEventId());
updateMappings();
}
@Override
public void visit(RegattaLogRevokeEvent event) {
logger.log(Level.FINE, "Mapping revoked for: " + event.getRevokedEventId());
updateMappings();
};
};
public RegattaLogDeviceMappings(Iterable<RegattaLog> initialRegattaLogs) {
final boolean hasRegattaLogs;
synchronized (knownRegattaLogs) {
initialRegattaLogs.forEach(this::addRegattaLogUnlocked);
hasRegattaLogs = !knownRegattaLogs.isEmpty();
}
if (hasRegattaLogs) {
updateMappings();
}
}
public void addRegattaLog(RegattaLog regattaLog) {
synchronized (knownRegattaLogs) {
addRegattaLogUnlocked(regattaLog);
}
updateMappings();
}
private void addRegattaLogUnlocked(RegattaLog log) {
log.addListener(regattaLogEventVisitor);
knownRegattaLogs.add(log);
}
public void stop() {
synchronized (knownRegattaLogs) {
knownRegattaLogs.forEach((log) -> log.removeListener(regattaLogEventVisitor));
knownRegattaLogs.clear();
}
}
protected void updateMappings() {
try {
updateMappings(true);
} catch (Exception e) {
logger.warning("Could not update device mappings");
}
}
/**
* Used internally to access {@link #mappings} with a defensive copy to not run into concurrency issues if this is
* called while an update is done.
*
* @return the mappings
*/
private synchronized Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> getDeviceMappings() {
return new HashMap<>(mappings);
}
/**
* Used internally to access {@link #mappingsByDevice} with a defensive copy to not run into concurrency issues if
* this is called while an update is done.
*
* @return the mappings
*/
private synchronized Map<DeviceIdentifier, List<DeviceMappingWithRegattaLogEvent<ItemT>>> getMappingsByDevice() {
return new HashMap<>(mappingsByDevice);
}
/**
* Calls the given callback for every known mapping that's currently known.
*
* @param callback the callback to call for every known mapping
*/
public void forEachMapping(Consumer<DeviceMappingWithRegattaLogEvent<ItemT>> callback) {
forEachMapping((item, mapping) -> callback.accept(mapping));
}
/**
* Calls the given callback for every device mapped by at least one of the known mappings.
*
* @param callback the callback to call for every mapped device
*/
public void forEachDevice(Consumer<DeviceIdentifier> callback) {
getMappingsByDevice().keySet().forEach(callback::accept);
}
/**
* Calls the given callback for every known mapping that's currently known.
*
* @param callback the callback to call for every known mapping
*/
public void forEachMapping(BiConsumer<ItemT, DeviceMappingWithRegattaLogEvent<ItemT>> callback) {
for (Map.Entry<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> entry : getDeviceMappings().entrySet()) {
ItemT item = entry.getKey();
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : entry.getValue()) {
callback.accept(item, mapping);
}
}
}
/**
* Calls the given callback for every DeviceMapping that is known for the given {@link DeviceIdentifier} that
* includes the given {@link TimePoint}.
*
* @param device
* the device to get the mappings for
* @param timePoint
* the TimePoint to check DeviceMappings for
* @param callback
* the callback to call for every known mapping of the given {@link DeviceIdentifier} that includes the
* given {@link TimePoint}.
*/
public void forEachMappingOfDeviceIncludingTimePoint(DeviceIdentifier device, TimePoint timePoint,
Consumer<DeviceMappingWithRegattaLogEvent<ItemT>> callback) {
List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDevice;
synchronized (this) {
mappingsForDevice = mappingsByDevice.get(device);
}
if (mappingsForDevice != null) {
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappingsForDevice) {
if (mapping.getTimeRange().includes(timePoint)) {
callback.accept(mapping);
}
}
}
}
/**
* To be implemented by subclasses to calculate the current {@link DeviceMapping}s.
*
* @return All currently known DeviceMappings
*/
protected Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> calculateMappings() {
Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> result = new HashMap<>();
forEachRegattaLog(
(log) -> result.putAll(new RegattaLogDeviceMappingFinder<ItemT>(log).analyze()));
return result;
}
protected void forEachRegattaLog(Consumer<RegattaLog> regattaLogConsumer) {
synchronized (knownRegattaLogs) {
knownRegattaLogs.forEach(regattaLogConsumer);
}
}
/**
* Adjusts the {@link #mappings} map according to the device mappings provided from the {@link #calculateMappings()}
* method. Afterwards, the start end end of tracking is {@link #updateStartAndEndOfTracking() updated} from the
* mapping intervals.
*
* @param loadIfNotCovered
* if <code>true</code>, the fixes for the mappings will be loaded based on a comparison of the previous
* mappings and the new mappings.
*
* @throws DoesNotHaveRegattaLogException
*/
private final <FixT extends Timed, TrackT extends DynamicTrack<FixT>> void updateMappings(boolean loadIfNotCovered) {
// TODO remove fixes, if mappings have been removed
// check if there are new time ranges not covered so far
Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings = calculateMappings();
updateMappings(newMappings, loadIfNotCovered);
}
/**
* Used internally to update the internal set of mappings to the state of the new mappings.
* This ensures that the internal state is updated and all new and changed mappings are correctly processed.
*
* @param newMappings the new mappings
* @param loadIfNotCovered true to inform about new/changed/removed mappings
*/
private synchronized void updateMappings(Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings,
boolean loadIfNotCovered) {
if (loadIfNotCovered) {
Set<ItemT> itemsToProcess = new HashSet<ItemT>(mappings.keySet());
itemsToProcess.addAll(newMappings.keySet());
for(ItemT item : itemsToProcess) {
if(!newMappings.containsKey(item)) {
mappings.get(item).forEach(this::mappingRemoved);
} else {
final List<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings = mappings.containsKey(item)
? mappings.get(item) : Collections.emptyList();
for (DeviceMappingWithRegattaLogEvent<ItemT> newMapping : newMappings.get(item)) {
DeviceMappingWithRegattaLogEvent<ItemT> oldMapping = findAndRemoveMapping(newMapping,
oldMappings);
if (oldMapping == null) {
mappingAdded(newMapping);
} else if (newMapping.getTimeRange().equals(oldMapping.getTimeRange())) {
mappingChanged(oldMapping, newMapping);
}
}
oldMappings.forEach(this::mappingRemoved);
}
}
}
mappings.clear();
mappings.putAll(newMappings);
mappingsByDevice.clear();
for (ItemT item : newMappings.keySet()) {
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : newMappings.get(item)) {
List<DeviceMappingWithRegattaLogEvent<ItemT>> list = mappingsByDevice.get(mapping.getDevice());
if (list == null) {
list = new ArrayList<>();
mappingsByDevice.put(mapping.getDevice(), list);
}
list.add(mapping);
}
}
}
/**
* Called when a {@link DeviceMapping} was removed.
*
* @param mapping the removed mapping
*/
protected abstract void mappingRemoved(DeviceMappingWithRegattaLogEvent<ItemT> mapping);
/**
* Called when a {@link DeviceMapping} was added.
*
* @param mapping the new mapping
*/
protected abstract void mappingAdded(DeviceMappingWithRegattaLogEvent<ItemT> mapping);
/**
* 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.
*
* @param oldMapping the old mapping
* @param newMapping the new mapping
*/
protected abstract void mappingChanged(DeviceMappingWithRegattaLogEvent<ItemT> oldMapping,
DeviceMappingWithRegattaLogEvent<ItemT> newMapping);
private DeviceMappingWithRegattaLogEvent<ItemT> findAndRemoveMapping(
DeviceMappingWithRegattaLogEvent<ItemT> mappingToFind,
List<DeviceMappingWithRegattaLogEvent<ItemT>> newItemsToProcess) {
for (Iterator<DeviceMappingWithRegattaLogEvent<ItemT>> iterator = newItemsToProcess.iterator(); iterator
.hasNext();) {
DeviceMappingWithRegattaLogEvent<ItemT> deviceMapping = iterator.next();
if(isSame(mappingToFind, deviceMapping)) {
iterator.remove();
return deviceMapping;
}
}
return null;
}
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());
}
}
@@ -0,0 +1,123 @@
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.server.RacingEventService;
/**
* An instance of this class is created for every {@link TrackedRegatta} by {@link RegattaLogFixTrackerRegattaListener}.
* This observes the given {@link TrackedRegatta} to get to know about every {@link TrackedRace} of the
* {@link TrackedRegatta}.
*
* An instance of {@link RaceLogFixTrackerManager} is created for every {@link TrackedRace}. It is ensured that
* {@link RaceLogFixTrackerManager} are stopped when a {@link TrackedRace} is removed from the {@link TrackedRegatta}.
*/
public class RegattaLogFixTrackerRaceListener {
private final Map<RegattaAndRaceIdentifier, DynamicTrackedRace> knownTrackedRaces = new ConcurrentHashMap<>();
private final Map<RegattaAndRaceIdentifier, RaceLogFixTrackerManager> dataTrackers = new ConcurrentHashMap<>();
private final DynamicTrackedRegatta trackedRegatta;
private final RaceListener raceListener;
private final RacingEventService racingEventService;
private final SensorFixMapperFactory sensorFixMapperFactory;
public RegattaLogFixTrackerRaceListener(final DynamicTrackedRegatta trackedRegatta, ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker,
final SensorFixMapperFactory sensorFixMapperFactory) {
this.trackedRegatta = trackedRegatta;
this.racingEventService = racingEventServiceTracker.getService();
this.sensorFixMapperFactory = sensorFixMapperFactory;
this.raceListener = new RaceListener() {
@Override
public void raceRemoved(TrackedRace trackedRace) {
RegattaLogFixTrackerRaceListener.this.raceRemoved(trackedRace);
}
@Override
public void raceAdded(TrackedRace trackedRace) {
RegattaLogFixTrackerRaceListener.this.raceAdded(trackedRace);
}
};
trackedRegatta.addRaceListener(raceListener);
}
public void raceRemoved(TrackedRace trackedRace) {
removeRaceLogSensorDataTracker(trackedRace.getRaceIdentifier());
}
public synchronized void raceAdded(TrackedRace trackedRace) {
if (trackedRace instanceof DynamicTrackedRace) {
DynamicTrackedRace dynamicTrackedRace = (DynamicTrackedRace) trackedRace;
RegattaAndRaceIdentifier raceIdentifier = dynamicTrackedRace.getRaceIdentifier();
DynamicTrackedRace existingRace = knownTrackedRaces.get(raceIdentifier);
if (existingRace != null) {
removeRaceLogSensorDataTracker(raceIdentifier);
}
racingEventService.getRaceTrackerByRegattaAndRaceIdentifier(raceIdentifier, (raceTracker) -> {
if (raceTracker != null) {
boolean added = raceTracker.add(new RaceTracker.Listener() {
@Override
public void onTrackerWillStop(boolean preemptive) {
raceTracker.remove(this);
removeRaceLogSensorDataTracker(raceIdentifier, preemptive);
}
});
// if !added, the RaceTracker is already stopped, so we are not allowed to start fix tracking
if (added) {
RaceLogFixTrackerManager trackerManager = new RaceLogFixTrackerManager(
(DynamicTrackedRace) trackedRace, racingEventService.getSensorFixStore(),
sensorFixMapperFactory);
RaceLogFixTrackerManager oldInstance = dataTrackers.put(raceIdentifier, trackerManager);
if (oldInstance != null) {
oldInstance.stop(true);
}
}
}
});
}
}
private void trackerStopped(RegattaAndRaceIdentifier raceIdentifier, RaceLogFixTrackerManager trackerManager) {
dataTrackers.remove(raceIdentifier, trackerManager);
}
/**
* Called by {@link RegattaLogFixTrackerRegattaListener} when the {@link TrackedRegatta} was removed or the
* {@link TrackedRegatta} shouldn't be tracked anymore (this is e.g. the case in replication state.
*/
public synchronized void stop() {
trackedRegatta.removeRaceListener(raceListener);
knownTrackedRaces.keySet().forEach(this::removeRaceLogSensorDataTracker);
knownTrackedRaces.clear();
dataTrackers.clear();
}
private void removeRaceLogSensorDataTracker(RegattaAndRaceIdentifier raceIdentifier) {
removeRaceLogSensorDataTracker(raceIdentifier, false);
}
private void removeRaceLogSensorDataTracker(RegattaAndRaceIdentifier raceIdentifier, boolean preemptive) {
RaceLogFixTrackerManager currentActiveDataTracker = dataTrackers.get(raceIdentifier);
if (currentActiveDataTracker != null) {
currentActiveDataTracker.stop(preemptive);
trackerStopped(raceIdentifier, currentActiveDataTracker);
}
}
@Override
public String toString() {
return "RegattaLogSensorDataTracker [regattaId=" + trackedRegatta.getRegatta().getId() + "]";
}
}
@@ -0,0 +1,180 @@
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.replication.OperationExecutionListener;
import com.sap.sse.replication.OperationWithResult;
import com.sap.sse.replication.ReplicationMasterDescriptor;
import com.sap.sse.replication.impl.OperationWithResultWithIdWrapper;
import com.sap.sse.replication.impl.ReplicableWithObjectInputStream;
/**
* This is the main entry point of the {@link SensorFixStore} based fix tracking.
*
* This listener is informed about every {@link TrackedRegatta} by {@link RacingEventService} via the implemented
* {@link TrackedRegattaListener}. For every known {@link TrackedRegatta}, a {@link RegattaLogFixTrackerRaceListener} is
* started.
*
* In addition this is a {@link ReplicableWithObjectInputStream} because we need to know if the current node is a
* replica. Replicas must not do any fix tracking because fixes are being loaded on the master and transferred to the
* replicas through the replication mechanism. That's why in replication state, no
* {@link RegattaLogFixTrackerRaceListener} instances are created at all.
*/
public class RegattaLogFixTrackerRegattaListener implements TrackedRegattaListener,
ReplicableWithObjectInputStream<RegattaLogFixTrackerRegattaListener, OperationWithResult<RegattaLogFixTrackerRegattaListener, ?>> {
private static final Logger log = Logger.getLogger(RegattaLogFixTrackerRegattaListener.class.getName());
private final Map<Serializable, RegattaLogFixTrackerRaceListener> registeredTrackers = new HashMap<>();
private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
private final SensorFixMapperFactory sensorFixMapperFactory;
public RegattaLogFixTrackerRegattaListener(
ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker,
SensorFixMapperFactory sensorFixMapperFactory) {
this.racingEventServiceTracker = racingEventServiceTracker;
this.sensorFixMapperFactory = sensorFixMapperFactory;
}
@Override
public synchronized void regattaAdded(TrackedRegatta trackedRegatta) {
final Serializable regattaId = trackedRegatta.getRegatta().getId();
if (!isReplica()) {
RegattaLogFixTrackerRaceListener tracker = new RegattaLogFixTrackerRaceListener((DynamicTrackedRegatta)
trackedRegatta, racingEventServiceTracker, sensorFixMapperFactory);
this.stopIfNotNull(registeredTrackers.put(regattaId, tracker));
log.fine("Added sensor data tracker to tracked regatta: " + trackedRegatta.getRegatta().getName());
} else {
log.warning("Regatta already known, not adding sensor twice");
}
}
@Override
public synchronized void regattaRemoved(TrackedRegatta trackedRegatta) {
final Serializable regattaId = trackedRegatta.getRegatta().getId();
try {
this.stopIfNotNull(registeredTrackers.get(regattaId));
} finally {
registeredTrackers.remove(regattaId);
}
}
private void stopIfNotNull(RegattaLogFixTrackerRaceListener tracker) {
if (tracker != null) {
try {
tracker.stop();
} catch (Exception exc) {
log.log(Level.SEVERE, "Stopping of tracker failed: " + tracker, exc);
}
}
}
// Replication related methods and fields
private final ConcurrentHashMap<OperationExecutionListener<RegattaLogFixTrackerRegattaListener>, OperationExecutionListener<RegattaLogFixTrackerRegattaListener>> operationExecutionListeners = new ConcurrentHashMap<>();
private final ThreadLocal<Boolean> currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
private final Set<OperationWithResultWithIdWrapper<RegattaLogFixTrackerRegattaListener, ?>> operationsSentToMasterForReplication = new HashSet<>();
private ReplicationMasterDescriptor master;
@Override
public Serializable getId() {
return getClass().getName();
}
@Override
public ReplicationMasterDescriptor getMasterDescriptor() {
return master;
}
@Override
public void startedReplicatingFrom(ReplicationMasterDescriptor master) {
this.master = master;
}
@Override
public void stoppedReplicatingFrom(ReplicationMasterDescriptor master) {
this.master = null;
}
@Override
public Iterable<OperationExecutionListener<RegattaLogFixTrackerRegattaListener>> getOperationExecutionListeners() {
return operationExecutionListeners.keySet();
}
@Override
public void addOperationExecutionListener(
OperationExecutionListener<RegattaLogFixTrackerRegattaListener> listener) {
this.operationExecutionListeners.put(listener, listener);
}
@Override
public void removeOperationExecutionListener(
OperationExecutionListener<RegattaLogFixTrackerRegattaListener> listener) {
this.operationExecutionListeners.remove(listener);
}
@Override
public boolean isCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster() {
return currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster.get();
}
@Override
public void setCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster(boolean b) {
this.currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster.set(b);
}
@Override
public void addOperationSentToMasterForReplication(
OperationWithResultWithIdWrapper<RegattaLogFixTrackerRegattaListener, ?> operationWithResultWithIdWrapper) {
this.operationsSentToMasterForReplication.add(operationWithResultWithIdWrapper);
}
@Override
public boolean hasSentOperationToMaster(
OperationWithResult<RegattaLogFixTrackerRegattaListener, ?> operation) {
return operationsSentToMasterForReplication.remove(operation);
}
@Override
public ObjectInputStream createObjectInputStreamResolvingAgainstCache(InputStream is) throws IOException {
return new ObjectInputStream(is);
}
@Override
public synchronized void initiallyFillFromInternal(ObjectInputStream is)
throws IOException, ClassNotFoundException, InterruptedException {
}
@Override
public void serializeForInitialReplicationInternal(ObjectOutputStream objectOutputStream) throws IOException {
}
@Override
public synchronized void clearReplicaState() throws MalformedURLException, IOException, InterruptedException {
this.registeredTrackers.values().forEach(this::stopIfNotNull);
this.registeredTrackers.clear();
}
private boolean isReplica() {
return master != null;
}
}
@@ -8,8 +8,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import junit.framework.Assert;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
@@ -22,10 +20,12 @@ import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException; import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier; import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping; import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
import junit.framework.Assert;
public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest { public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
@@ -33,7 +33,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
private int time = 0; private int time = 0;
private List<DeviceMapping<Competitor>> getMappings() { private List<DeviceMappingWithRegattaLogEvent<Competitor>> getMappings() {
return new RegattaLogDeviceCompetitorMappingFinder(log).analyze().get(competitor); return new RegattaLogDeviceCompetitorMappingFinder(log).analyze().get(competitor);
} }
@@ -66,24 +66,24 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log); final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
addMapping(author, device, 100l, 200l, competitor); addMapping(author, device, 100l, 200l, competitor);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(2, deviceMappings.size()); assertEquals(2, deviceMappings.size());
boolean found100 = false; boolean found100 = false;
for (final DeviceMapping<Competitor> mapping : deviceMappings) { for (final DeviceMappingWithRegattaLogEvent<Competitor> mapping : deviceMappings) {
if (mapping.getTimeRange().from().asMillis() == 100) { if (mapping.getTimeRange().from().asMillis() == 100) {
found100 = true; found100 = true;
assertEquals(149, mapping.getTimeRange().to().asMillis()); assertEquals(149, mapping.getTimeRange().to().asMillis());
@@ -102,23 +102,23 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final Serializable mappingId = addMapping(author, device, 100l, null, competitor); final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
closeMapping(author, device, mappingId, 200); closeMapping(author, device, mappingId, 200);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(2, deviceMappings.size()); assertEquals(2, deviceMappings.size());
for (final DeviceMapping<Competitor> mapping : deviceMappings) { for (final DeviceMappingWithRegattaLogEvent<Competitor> mapping : deviceMappings) {
if (mapping.getTimeRange().from().asMillis() == 100) { if (mapping.getTimeRange().from().asMillis() == 100) {
assertEquals(149, mapping.getTimeRange().to().asMillis()); assertEquals(149, mapping.getTimeRange().to().asMillis());
} else { } else {
@@ -134,21 +134,21 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log); final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
addMapping(author, device, 100l, 200l, competitor); addMapping(author, device, 100l, 200l, competitor);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis()); assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis()); assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
@@ -161,21 +161,21 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final Serializable mappingId = addMapping(author, device, 100l, null, competitor); final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
closeMapping(author, device, mappingId, 200); closeMapping(author, device, mappingId, 200);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis()); assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis()); assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
@@ -187,21 +187,21 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log); final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
addMapping(author, device, 100l, 200l, competitor); addMapping(author, device, 100l, 200l, competitor);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis()); assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis()); assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
@@ -214,21 +214,21 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final Serializable mappingId = addMapping(author, device, 100l, null, competitor); final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
closeMapping(author, device, mappingId, 200); closeMapping(author, device, mappingId, 200);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis()); assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis()); assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
@@ -240,18 +240,18 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log); final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
addMapping(author, device, 100l, 100l, competitor); addMapping(author, device, 100l, 100l, competitor);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertTrue(mappings.isEmpty()); assertTrue(mappings.isEmpty());
} }
} }
@@ -262,18 +262,18 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
final Serializable mappingId = addMapping(author, device, 100l, null, competitor); final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
closeMapping(author, device, mappingId, 100); closeMapping(author, device, mappingId, 100);
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
assertTrue(mappings.containsKey(competitor)); assertTrue(mappings.containsKey(competitor));
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor); final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
assertEquals(1, deviceMappings.size()); assertEquals(1, deviceMappings.size());
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next(); final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to()); assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
} }
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100)); finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
{ {
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze(); final Map<Competitor, List<DeviceMappingWithRegattaLogEvent<Competitor>>> mappings = finder.analyze();
assertTrue(mappings.isEmpty()); assertTrue(mappings.isEmpty());
} }
} }
@@ -297,9 +297,9 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
//close one range //close one range
Serializable id = addMapping(author, device, 10L, null, competitor); Serializable id = addMapping(author, device, 10L, null, competitor);
closeMapping(author, device, id, 20); closeMapping(author, device, id, 20);
List<DeviceMapping<Competitor>> mappings = getMappings(); List<DeviceMappingWithRegattaLogEvent<Competitor>> mappings = getMappings();
assertEquals(1, mappings.size()); assertEquals(1, mappings.size());
DeviceMapping<Competitor> mapping = mappings.get(0); DeviceMappingWithRegattaLogEvent<Competitor> mapping = mappings.get(0);
assertEquals(10, mapping.getTimeRange().from().asMillis()); assertEquals(10, mapping.getTimeRange().from().asMillis());
assertEquals(20, mapping.getTimeRange().to().asMillis()); assertEquals(20, mapping.getTimeRange().to().asMillis());
@@ -0,0 +1,19 @@
package com.sap.sailing.domain.abstractlog.regatta;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
/**
* Used to dynamically dispatch calls for {@link RegattaLogDeviceMappingEvent}s depending on the concrete type.
*/
public interface MappingEventVisitor {
void visit(RegattaLogDeviceMarkMappingEvent event);
void visit(RegattaLogDeviceCompetitorMappingEvent event);
void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event);
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.abstractlog.regatta;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
@@ -16,6 +17,8 @@ public interface RegattaLogEventVisitor {
void visit(RegattaLogDeviceCompetitorMappingEvent event); void visit(RegattaLogDeviceCompetitorMappingEvent event);
void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event);
void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event); void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event);
void visit(RegattaLogRegisterCompetitorEvent event); void visit(RegattaLogRegisterCompetitorEvent event);
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.base.Competitor;
public interface RegattaLogDeviceCompetitorSensorDataMappingEvent extends RegattaLogDeviceMappingEvent<Competitor> {
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.regatta.events; package com.sap.sailing.domain.abstractlog.regatta.events;
import com.sap.sailing.domain.abstractlog.Revokable; import com.sap.sailing.domain.abstractlog.Revokable;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer; import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
@@ -37,4 +38,11 @@ public interface RegattaLogDeviceMappingEvent<ItemType extends WithID> extends R
* In that case the device should be mapped to the item for any timepoint after that specified by {@link #getFrom()}. * In that case the device should be mapped to the item for any timepoint after that specified by {@link #getFrom()}.
*/ */
TimePoint getTo(); TimePoint getTo();
/**
* Visitor patter implementation for event mapping handling differentiation.
*
* @param visitor
*/
void accept(MappingEventVisitor visitor);
} }
@@ -0,0 +1,42 @@
package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
* Mapping event for devices mapped for Bravo type fixes.
*/
public class RegattaLogDeviceCompetitorBravoMappingEventImpl extends RegattaLogDeviceMappingEventImpl<Competitor>
implements RegattaLogDeviceCompetitorSensorDataMappingEvent {
private static final long serialVersionUID = -1494030544804758753L;
public RegattaLogDeviceCompetitorBravoMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
TimePoint from, TimePoint to) {
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
}
public RegattaLogDeviceCompetitorBravoMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
Competitor mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
super(logicalTimePoint, author, mappedTo, device, from, to);
}
@Override
public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this);
}
@Override
public void accept(MappingEventVisitor visitor) {
visitor.visit(this);
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable; import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
@@ -29,4 +30,9 @@ public class RegattaLogDeviceCompetitorMappingEventImpl extends RegattaLogDevice
public void accept(RegattaLogEventVisitor visitor) { public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@Override
public void accept(MappingEventVisitor visitor) {
visitor.visit(this);
}
} }
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.abstractlog.regatta.events.impl;
import java.io.Serializable; import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
@@ -29,4 +30,9 @@ public class RegattaLogDeviceMarkMappingEventImpl extends RegattaLogDeviceMappin
public void accept(RegattaLogEventVisitor visitor) { public void accept(RegattaLogEventVisitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@Override
public void accept(MappingEventVisitor visitor) {
visitor.visit(this);
}
} }
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
@@ -24,6 +25,10 @@ public class BaseRegattaLogEventVisitor implements RegattaLogEventVisitor {
public void visit(RegattaLogDeviceCompetitorMappingEvent event) { public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
} }
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
}
@Override @Override
public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) { public void visit(RegattaLogCloseOpenEndedDeviceMappingEvent event) {
} }
@@ -5,6 +5,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
@@ -48,9 +49,14 @@ public abstract class RegattaLogEventListener implements RegattaLogEventVisitor
public void visit(RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent event) { public void visit(RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEvent event) {
eventAdded(event); eventAdded(event);
} }
@Override @Override
public void visit(RegattaLogDefineMarkEvent event) { public void visit(RegattaLogDefineMarkEvent event) {
eventAdded(event); eventAdded(event);
} }
@Override
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
eventAdded(event);
}
} }
@@ -0,0 +1,79 @@
package com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.TimeRangeImpl;
/**
* Extracts the {@link DeviceMapping}s with the appropriate {@link TimeRange}s from the {@link RaceLog}.
* Based on the {@link RegattaLogDeviceMappingEvent}s found in the {@code RaceLog} for the tracked {@code items}
* ({@code Competitor}s and {@code Marks}), the actual mappings are created.
* <p>
* Mappings can be defined with an open-ended time range (e.g. track this from now on), which should then be
* closed by a corresponding {@link RegattaLogCloseOpenEndedDeviceMappingEvent}.
* <p>
* The process of removing conflicts between mappings to ensure that only one mapping was active for
* one tracked item has been removed. Instead, every {@code DeviceMappingEvent} now results directly in one
* {@code DeviceMapping}, the only changes being introduced by closing open time ranges in cases where an
* according {@code CloseOpenEndedDeviceMappingEvent} exists.
*/
public abstract class BaseRegattaLogDeviceMappingFinder<ItemT extends WithID>
extends RegattaLogDeviceMappingFinder<ItemT> {
public BaseRegattaLogDeviceMappingFinder(RegattaLog log) {
super(log);
}
/**
* By revoking existing mappings for {@code item} and replacing them by mappings that exclude {@code fixTimePoint},
* any fix at {@code fixTimePoint} will no longer be linked to {@code item}. If a mapping is limited to the exact
* fix time point, the mapping is only revoked and not replaced. If one boundary is exactly the fix time point, the
* interval is revoked and replaced by a single interval with that border adjusted so it excludes the fix.
*/
public void removeTimePointFromMapping(ItemT item, TimePoint fixTimePoint) throws NotRevokableException {
Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>>();
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent>();
findUnrevokedMappingAndClosingEvents(events, closingEvents);
for (final RegattaLogDeviceMappingEvent<ItemT> event : events.get(item)) {
final TimePoint from = event.getFrom();
final RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = closingEvents.get(event.getId());
final TimePoint to = closingEvent != null ? closingEvent.getClosingTimePoint() : event.getTo();
final TimeRange mappingTimeRange = new TimeRangeImpl(from, to);
if (mappingTimeRange.includes(fixTimePoint)) {
if (closingEvent != null) {
log.revokeEvent(closingEvent.getAuthor(), closingEvent,
"removing single time point " + fixTimePoint + " from mapping for " + item);
}
log.revokeEvent(event.getAuthor(), event,
"removing single time point " + fixTimePoint + " from mapping for " + item);
final TimePoint endOfFirstHalf = fixTimePoint.minus(1);
final TimePoint startOfSecondHalf = fixTimePoint.plus(1);
if (!endOfFirstHalf.before(from)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), from, endOfFirstHalf, event.getDevice()));
}
if (!to.before(startOfSecondHalf)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, to,
event.getDevice()));
}
}
}
}
protected abstract RegattaLogDeviceMappingEvent<ItemT> createDeviceMappingEvent(ItemT item,
AbstractLogEventAuthor author, TimePoint plus, TimePoint to, DeviceIdentifier deviceId);
}
@@ -12,7 +12,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class RegattaLogDeviceCompetitorMappingFinder extends RegattaLogDeviceMappingFinder<Competitor> { public class RegattaLogDeviceCompetitorMappingFinder extends BaseRegattaLogDeviceMappingFinder<Competitor> {
public RegattaLogDeviceCompetitorMappingFinder(RegattaLog log) { public RegattaLogDeviceCompetitorMappingFinder(RegattaLog log) {
super(log); super(log);
@@ -8,16 +8,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping; import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingWithRegattaLogEventImpl;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange; import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID; import com.sap.sse.common.WithID;
@@ -36,7 +35,8 @@ import com.sap.sse.common.impl.TimeRangeImpl;
* {@code DeviceMapping}, the only changes being introduced by closing open time ranges in cases where an * {@code DeviceMapping}, the only changes being introduced by closing open time ranges in cases where an
* according {@code CloseOpenEndedDeviceMappingEvent} exists. * according {@code CloseOpenEndedDeviceMappingEvent} exists.
*/ */
public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extends RegattaLogAnalyzer<Map<ItemT, List<DeviceMapping<ItemT>>>> { public class RegattaLogDeviceMappingFinder<ItemT extends WithID>
extends RegattaLogAnalyzer<Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>>> {
private static Logger logger = Logger.getLogger(RegattaLogDeviceMappingFinder.class.getName()); private static Logger logger = Logger.getLogger(RegattaLogDeviceMappingFinder.class.getName());
public RegattaLogDeviceMappingFinder(RegattaLog log) { public RegattaLogDeviceMappingFinder(RegattaLog log) {
@@ -47,10 +47,10 @@ public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extend
return mapping instanceof RegattaLogDeviceMappingEvent; return mapping instanceof RegattaLogDeviceMappingEvent;
} }
protected DeviceMapping<ItemT> createMapping(DeviceIdentifier device, ItemT item, protected DeviceMappingWithRegattaLogEvent<ItemT> createMapping(DeviceIdentifier device, ItemT item,
TimePoint from, TimePoint to, Serializable originalEventId) { TimePoint from, TimePoint to, Serializable originalEventId, RegattaLogDeviceMappingEvent<ItemT> event) {
return new DeviceMappingImpl<ItemT>(item, device, new TimeRangeImpl(from, to), return new DeviceMappingWithRegattaLogEventImpl<ItemT>(item, device, new TimeRangeImpl(from, to),
Collections.singletonList(originalEventId)); Collections.singletonList(originalEventId), event);
} }
private List<RegattaLogDeviceMappingEvent<ItemT>> getMappingEventsForItem(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> map, ItemT item) { private List<RegattaLogDeviceMappingEvent<ItemT>> getMappingEventsForItem(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> map, ItemT item) {
@@ -63,18 +63,18 @@ public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extend
} }
@Override @Override
protected Map<ItemT, List<DeviceMapping<ItemT>>> performAnalysis() { protected Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> performAnalysis() {
Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>>(); Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<>();
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent>(); Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<>();
findUnrevokedMappingAndClosingEvents(events, closingEvents); findUnrevokedMappingAndClosingEvents(events, closingEvents);
Map<ItemT, List<DeviceMapping<ItemT>>> mappings = new HashMap<ItemT, List<DeviceMapping<ItemT>>>(); Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappings = new HashMap<>();
for (ItemT item : events.keySet()) { for (ItemT item : events.keySet()) {
mappings.put(item, closeOpenRanges(events.get(item), item, closingEvents)); mappings.put(item, closeOpenRanges(events.get(item), item, closingEvents));
} }
return mappings; return mappings;
} }
private void findUnrevokedMappingAndClosingEvents(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events, protected void findUnrevokedMappingAndClosingEvents(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events,
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) { Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) {
for (RegattaLogEvent e : getLog().getUnrevokedEvents()) { for (RegattaLogEvent e : getLog().getUnrevokedEvents()) {
if (e instanceof RegattaLogDeviceMappingEvent && isValidMapping(((RegattaLogDeviceMappingEvent<?>) e))) { if (e instanceof RegattaLogDeviceMappingEvent && isValidMapping(((RegattaLogDeviceMappingEvent<?>) e))) {
@@ -89,9 +89,10 @@ public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extend
} }
} }
private List<DeviceMapping<ItemT>> closeOpenRanges(List<RegattaLogDeviceMappingEvent<ItemT>> events, ItemT item, private List<DeviceMappingWithRegattaLogEvent<ItemT>> closeOpenRanges(
List<RegattaLogDeviceMappingEvent<ItemT>> events, ItemT item,
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) { Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) {
List<DeviceMapping<ItemT>> result = new ArrayList<DeviceMapping<ItemT>>(); List<DeviceMappingWithRegattaLogEvent<ItemT>> result = new ArrayList<>();
for (final RegattaLogDeviceMappingEvent<ItemT> event : events) { for (final RegattaLogDeviceMappingEvent<ItemT> event : events) {
TimePoint from = event.getFrom(); TimePoint from = event.getFrom();
TimePoint to = event.getTo(); TimePoint to = event.getTo();
@@ -103,42 +104,9 @@ public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extend
if (to == null) { if (to == null) {
to = closingTimePoint; to = closingTimePoint;
} }
result.add(createMapping(event.getDevice(), item, from, to, event.getId())); result.add(createMapping(event.getDevice(), item, from, to, event.getId(), event));
} }
return result; return result;
} }
/**
* By revoking existing mappings for {@code item} and replacing them by mappings that exclude {@code fixTimePoint}, any
* fix at {@code fixTimePoint} will no longer be linked to {@code item}. If a mapping is limited to the exact fix time point,
* the mapping is only revoked and not replaced. If one boundary is exactly the fix time point, the interval is revoked and
* replaced by a single interval with that border adjusted so it excludes the fix.
*/
public void removeTimePointFromMapping(ItemT item, TimePoint fixTimePoint) throws NotRevokableException {
Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>>();
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent>();
findUnrevokedMappingAndClosingEvents(events, closingEvents);
for (final RegattaLogDeviceMappingEvent<ItemT> event : events.get(item)) {
final TimePoint from = event.getFrom();
final RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = closingEvents.get(event.getId());
final TimePoint to = closingEvent!= null ? closingEvent.getClosingTimePoint() : event.getTo();
final TimeRange mappingTimeRange = new TimeRangeImpl(from, to);
if (mappingTimeRange.includes(fixTimePoint)) {
if (closingEvent != null) {
log.revokeEvent(closingEvent.getAuthor(), closingEvent, "removing single time point "+fixTimePoint+" from mapping for "+item);
}
log.revokeEvent(event.getAuthor(), event, "removing single time point "+fixTimePoint+" from mapping for "+item);
final TimePoint endOfFirstHalf = fixTimePoint.minus(1);
final TimePoint startOfSecondHalf = fixTimePoint.plus(1);
if (!endOfFirstHalf.before(from)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), from, endOfFirstHalf, event.getDevice()));
}
if (!to.before(startOfSecondHalf)) {
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, to, event.getDevice()));
}
}
}
}
protected abstract RegattaLogDeviceMappingEvent<ItemT> createDeviceMappingEvent(ItemT item, AbstractLogEventAuthor author, TimePoint plus, TimePoint to, DeviceIdentifier deviceId);
} }
@@ -12,7 +12,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class RegattaLogDeviceMarkMappingFinder extends RegattaLogDeviceMappingFinder<Mark> { public class RegattaLogDeviceMarkMappingFinder extends BaseRegattaLogDeviceMappingFinder<Mark> {
public RegattaLogDeviceMarkMappingFinder(RegattaLog log) { public RegattaLogDeviceMarkMappingFinder(RegattaLog log) {
super(log); super(log);
@@ -25,4 +25,7 @@ public interface DeviceMapping<ItemType extends WithID> extends Timed {
DeviceIdentifier getDevice(); DeviceIdentifier getDevice();
TimeRange getTimeRange(); TimeRange getTimeRange();
Class<?> getEventType();
} }
@@ -0,0 +1,20 @@
package com.sap.sailing.domain.racelogtracking;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
import com.sap.sse.common.WithID;
/**
* @see RegattaLogDeviceMappingFinder {@code DeviceMappings} are derived from {@code DeviceMappingEvents}. The
* {@link Timed} interface is to be implemented as providing the {@link TimeRange#from()} time point as the answer
* to {@link Timed#getTimePoint()}.
*
* This instance provides access to the originating event.
*
*/
public interface DeviceMappingWithRegattaLogEvent<ItemType extends WithID> extends DeviceMapping<ItemType> {
RegattaLogDeviceMappingEvent<ItemType> getRegattaLogEvent();
}
@@ -19,20 +19,24 @@ public class DeviceMappingImpl<ItemType extends WithID> implements DeviceMapping
private final DeviceIdentifier device; private final DeviceIdentifier device;
private final TimeRange timeRange; private final TimeRange timeRange;
private final List<Serializable> originalRaceLogEventIds = new ArrayList<Serializable>(); private final List<Serializable> originalRaceLogEventIds = new ArrayList<Serializable>();
private final Class<?> eventType;
public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange) {
public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange, Class<?> eventType) {
this.mappedTo = mappedTo; this.mappedTo = mappedTo;
this.device = device; this.device = device;
this.timeRange = timeRange; this.timeRange = timeRange;
this.eventType = eventType;
} }
public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange, Serializable originalRaceLogEventId) { public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange,
this(mappedTo, device, timeRange); Serializable originalRaceLogEventId, Class<?> eventType) {
this(mappedTo, device, timeRange, eventType);
if (originalRaceLogEventId != null) originalRaceLogEventIds.add(originalRaceLogEventId); if (originalRaceLogEventId != null) originalRaceLogEventIds.add(originalRaceLogEventId);
} }
public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange, List<? extends Serializable> originalRaceLogEventIds) { public DeviceMappingImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange,
this(mappedTo, device, timeRange); List<? extends Serializable> originalRaceLogEventIds, Class<?> eventType) {
this(mappedTo, device, timeRange, eventType);
if (originalRaceLogEventIds != null) this.originalRaceLogEventIds.addAll(originalRaceLogEventIds); if (originalRaceLogEventIds != null) this.originalRaceLogEventIds.addAll(originalRaceLogEventIds);
} }
@@ -56,9 +60,14 @@ public class DeviceMappingImpl<ItemType extends WithID> implements DeviceMapping
return timeRange.from(); return timeRange.from();
} }
@Override
public Class<?> getEventType() {
return eventType;
}
public static <T extends WithID> DeviceMapping<T> convertToDeviceMapping(RegattaLogDeviceMappingEvent<T> event) { public static <T extends WithID> DeviceMapping<T> convertToDeviceMapping(RegattaLogDeviceMappingEvent<T> event) {
return new DeviceMappingImpl<T>(event.getMappedTo(), event.getDevice(), new TimeRangeImpl(event.getFrom(), event.getTo()), return new DeviceMappingImpl<T>(event.getMappedTo(), event.getDevice(), new TimeRangeImpl(event.getFrom(), event.getTo()),
event.getId()); event.getId(), event.getClass());
} }
@Override @Override
@@ -0,0 +1,34 @@
package com.sap.sailing.domain.racelogtracking.impl;
import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
public class DeviceMappingWithRegattaLogEventImpl<ItemType extends WithID> extends DeviceMappingImpl<ItemType>
implements DeviceMappingWithRegattaLogEvent<ItemType> {
private static final long serialVersionUID = 1495968054831506408L;
private final RegattaLogDeviceMappingEvent<ItemType> event;
public DeviceMappingWithRegattaLogEventImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange,
RegattaLogDeviceMappingEvent<ItemType> event) {
super(mappedTo, device, timeRange, event.getClass());
this.event = event;
}
public DeviceMappingWithRegattaLogEventImpl(ItemType mappedTo, DeviceIdentifier device, TimeRange timeRange,
List<Serializable> originalRaceLogEventIds, RegattaLogDeviceMappingEvent<ItemType> event) {
super(mappedTo, device, timeRange, originalRaceLogEventIds, event.getClass());
this.event = event;
}
@Override
public RegattaLogDeviceMappingEvent<ItemType> getRegattaLogEvent() {
return event;
}
}
@@ -8,7 +8,6 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingFactoryImpl; import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingFactoryImpl;
import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.RaceTracker;
@@ -53,14 +52,14 @@ public interface SwissTimingFactory {
SwissTimingRaceTracker createRaceTracker(String raceID, String raceName, String raceDescription, SwissTimingRaceTracker createRaceTracker(String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws InterruptedException, UnknownHostException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws InterruptedException, UnknownHostException,
IOException, ParseException; IOException, ParseException;
RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription, RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
WindStore windStore, GPSFixStore gpsFixStore, boolean useInternalMarkPassingAlgorithm, WindStore windStore, boolean useInternalMarkPassingAlgorithm,
DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws UnknownHostException, DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws UnknownHostException,
InterruptedException, IOException, ParseException; InterruptedException, IOException, ParseException;
@@ -11,7 +11,6 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.Race; import com.sap.sailing.domain.swisstimingadapter.Race;
@@ -91,23 +90,24 @@ public class SwissTimingFactoryImpl implements SwissTimingFactory {
@Override @Override
public SwissTimingRaceTracker createRaceTracker(String raceID, String raceName, String raceDescription, public SwissTimingRaceTracker createRaceTracker(String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws InterruptedException, UnknownHostException, IOException, ParseException { throws InterruptedException, UnknownHostException, IOException, ParseException {
return new SwissTimingRaceTrackerImpl(raceID, raceName, raceDescription, boatClass, hostname, port, startList, return new SwissTimingRaceTrackerImpl(raceID, raceName, raceDescription, boatClass, hostname, port, startList,
raceLogStore, regattaLogStore, windStore, gpsFixStore, domainFactory, this, raceLogStore, regattaLogStore, windStore, domainFactory, this,
trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm); trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm);
} }
@Override @Override
public RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription, public RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis,
WindStore windStore, GPSFixStore gpsFixStore, boolean useInternalMarkPassingAlgorithm, WindStore windStore, boolean useInternalMarkPassingAlgorithm,
DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws UnknownHostException, InterruptedException, IOException, ParseException { throws UnknownHostException, InterruptedException, IOException, ParseException {
return new SwissTimingRaceTrackerImpl(regatta, raceID, raceName, raceDescription, boatClass, hostname, port, return new SwissTimingRaceTrackerImpl(regatta, raceID, raceName, raceDescription, boatClass, hostname, port,
startList, windStore, gpsFixStore, domainFactory, this, trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm); startList, windStore, domainFactory, this, trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis,
useInternalMarkPassingAlgorithm);
} }
@Override @Override
@@ -36,7 +36,6 @@ import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.Course; import com.sap.sailing.domain.swisstimingadapter.Course;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
@@ -60,6 +59,7 @@ import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus; import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
@@ -69,7 +69,8 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
import difflib.PatchFailedException; import difflib.PatchFailedException;
public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implements SwissTimingRaceTracker, SailMasterListener { public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
implements SwissTimingRaceTracker, SailMasterListener, TrackingDataLoader {
private static final Logger logger = Logger.getLogger(SwissTimingRaceTrackerImpl.class.getName()); private static final Logger logger = Logger.getLogger(SwissTimingRaceTrackerImpl.class.getName());
private final SailMasterConnector connector; private final SailMasterConnector connector;
@@ -81,7 +82,6 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
private final com.sap.sse.common.Util.Triple<String, String, Integer> id; private final com.sap.sse.common.Util.Triple<String, String, Integer> id;
private final Regatta regatta; private final Regatta regatta;
private final WindStore windStore; private final WindStore windStore;
private final GPSFixStore gpsFixStore;
private final boolean startListFromManage2Sail; private final boolean startListFromManage2Sail;
private final boolean useInternalMarkPassingAlgorithm; private final boolean useInternalMarkPassingAlgorithm;
@@ -111,18 +111,19 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
protected SwissTimingRaceTrackerImpl(String raceID, String raceName, String raceDescription, BoatClass boatClass, protected SwissTimingRaceTrackerImpl(String raceID, String raceName, String raceDescription, BoatClass boatClass,
String hostname, int port, StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String hostname, int port, StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, DomainFactory domainFactory, SwissTimingFactory factory, WindStore windStore, DomainFactory domainFactory, SwissTimingFactory factory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException,
UnknownHostException, IOException, ParseException { UnknownHostException, IOException, ParseException {
this(domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, raceID, this(domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, raceID,
boatClass, trackedRegattaRegistry), raceID, raceName, boatClass, trackedRegattaRegistry), raceID, raceName,
raceDescription, boatClass, hostname, port, startList, windStore, gpsFixStore, domainFactory, factory, trackedRegattaRegistry, raceDescription, boatClass, hostname, port, startList, windStore, domainFactory, factory,
trackedRegattaRegistry,
raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm); raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm);
} }
protected SwissTimingRaceTrackerImpl(Regatta regatta, String raceID, String raceName, String raceDescription, protected SwissTimingRaceTrackerImpl(Regatta regatta, String raceID, String raceName, String raceDescription,
BoatClass boatClass, String hostname, int port, StartList startList, WindStore windStore, BoatClass boatClass, String hostname, int port, StartList startList, WindStore windStore,
GPSFixStore gpsFixStore, DomainFactory domainFactory, SwissTimingFactory factory, DomainFactory domainFactory, SwissTimingFactory factory,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver,
long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, UnknownHostException, IOException, long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, UnknownHostException, IOException,
ParseException { ParseException {
@@ -139,13 +140,13 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
this.raceDescription = raceDescription; this.raceDescription = raceDescription;
this.boatClass = boatClass; this.boatClass = boatClass;
this.windStore = windStore; this.windStore = windStore;
this.gpsFixStore = gpsFixStore;
this.id = createID(raceID, hostname, port); this.id = createID(raceID, hostname, port);
connector.addSailMasterListener(this); connector.addSailMasterListener(this);
trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta);
this.delayToLiveInMillis = delayToLiveInMillis; this.delayToLiveInMillis = delayToLiveInMillis;
this.competitorsByBoatId = new HashMap<String, Competitor>(); this.competitorsByBoatId = new HashMap<String, Competitor>();
this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm;
} }
@Override @Override
@@ -158,10 +159,10 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
} }
@Override @Override
public void stop(boolean preemptive) throws MalformedURLException, IOException, InterruptedException { protected void onStop(boolean preemptive) throws MalformedURLException, IOException, InterruptedException {
if (isTrackedRaceStillReachable()) { if (isTrackedRaceStillReachable()) {
TrackedRaceStatus newStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, 1.0); TrackedRaceStatus newStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, 1.0);
trackedRace.setStatus(newStatus); trackedRace.onStatusChanged(this, newStatus);
} }
connector.removeSailMasterListener(this); connector.removeSailMasterListener(this);
} }
@@ -238,11 +239,6 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
return windStore; return windStore;
} }
@Override
public GPSFixStore getGPSFixStore() {
return gpsFixStore;
}
@Override @Override
public Regatta getRegatta() { public Regatta getRegatta() {
return regatta; return regatta;
@@ -416,7 +412,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
} else { } else {
newStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, progress); newStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, progress);
} }
trackedRace.setStatus(newStatus); trackedRace.onStatusChanged(this, newStatus);
} }
} }
@@ -472,7 +468,8 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
competitorsByBoatId.put(c.getBoatID(), existingCompetitor); competitorsByBoatId.put(c.getBoatID(), existingCompetitor);
} }
} }
trackedRace = getTrackedRegatta().createTrackedRace(race, Collections.<Sideline> emptyList(), windStore, gpsFixStore, delayToLiveInMillis, trackedRace = getTrackedRegatta().createTrackedRace(race, Collections.<Sideline> emptyList(), windStore,
delayToLiveInMillis,
WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
/* time over which to average speed */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), /* time over which to average speed */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
new DynamicRaceDefinitionSet() { new DynamicRaceDefinitionSet() {
@@ -5,7 +5,6 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.StartList; import com.sap.sailing.domain.swisstimingadapter.StartList;
@@ -52,17 +51,18 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo
@Override @Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws Exception { RaceLogResolver raceLogResolver) throws Exception {
return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port, return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port,
startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore, gpsFixStore, useInternalMarkPassingAlgorithm, startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore,
useInternalMarkPassingAlgorithm,
domainFactory, trackedRegattaRegistry, raceLogResolver); domainFactory, trackedRegattaRegistry, raceLogResolver);
} }
@Override @Override
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws Exception { WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname, return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname,
port, startList, delayToLiveInMillis, windStore, gpsFixStore, useInternalMarkPassingAlgorithm, port, startList, delayToLiveInMillis, windStore, useInternalMarkPassingAlgorithm,
domainFactory, trackedRegattaRegistry, raceLogResolver); domainFactory, trackedRegattaRegistry, raceLogResolver);
} }
@@ -38,7 +38,6 @@ import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus;
@@ -48,6 +47,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
@@ -70,7 +70,7 @@ import difflib.PatchFailedException;
* @author Axel Uhl (D043530) * @author Axel Uhl (D043530)
* *
*/ */
public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter { public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter implements TrackingDataLoader {
private static final int THRESHOLD_FOR_EARLIEST_MARK_PASSING_BEFORE_START_IN_MILLIS = 30000; private static final int THRESHOLD_FOR_EARLIEST_MARK_PASSING_BEFORE_START_IN_MILLIS = 30000;
private static final Logger logger = Logger.getLogger(SwissTimingReplayToDomainAdapter.class.getName()); private static final Logger logger = Logger.getLogger(SwissTimingReplayToDomainAdapter.class.getName());
@@ -318,11 +318,12 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter {
currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition); currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition);
racePerRaceID.put(currentRaceID, race); racePerRaceID.put(currentRaceID, race);
DynamicTrackedRace trackedRace = trackedRegattaRegistry.getOrCreateTrackedRegatta(myRegatta). DynamicTrackedRace trackedRace = trackedRegattaRegistry.getOrCreateTrackedRegatta(myRegatta).
createTrackedRace(race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS, createTrackedRace(race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS,
WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
/* raceDefinitionSetToUpdate */ null, useInternalMarkPassingAlgorithm, raceLogResolver); /* raceDefinitionSetToUpdate */ null, useInternalMarkPassingAlgorithm, raceLogResolver);
trackedRace.setStatus(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0)); trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0));
TimePoint bestStartTimeKnownSoFar = bestStartTimePerRaceID.get(currentRaceID); TimePoint bestStartTimeKnownSoFar = bestStartTimePerRaceID.get(currentRaceID);
if (bestStartTimeKnownSoFar != null) { if (bestStartTimeKnownSoFar != null) {
trackedRace.setStartTimeReceived(bestStartTimeKnownSoFar); trackedRace.setStartTimeReceived(bestStartTimeKnownSoFar);
@@ -392,7 +393,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter {
} else { } else {
newStatus = TrackedRaceStatusEnum.LOADING; newStatus = TrackedRaceStatusEnum.LOADING;
} }
trackedRace.setStatus(new TrackedRaceStatusImpl(newStatus, progress)); trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(newStatus, progress));
} }
} }
} }
@@ -31,7 +31,6 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.NauticalMileDistance; import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.markpassingcalculation.impl.WaypointPositionAndDistanceCache; import com.sap.sailing.domain.markpassingcalculation.impl.WaypointPositionAndDistanceCache;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegatta;
@@ -76,7 +75,7 @@ public class WaypointPositionAndDistanceCacheTest {
final Regatta regatta = mock(Regatta.class); final Regatta regatta = mock(Regatta.class);
when(regatta.getName()).thenReturn("Regatta name"); when(regatta.getName()).thenReturn("Regatta name");
when(trackedRegatta.getRegatta()).thenReturn(regatta); when(trackedRegatta.getRegatta()).thenReturn(regatta);
trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.emptyList(), EmptyWindStore.INSTANCE,
/* delayToLiveInMillis */ 8000, /* millisecondsOverWhichToAverageWind */ 30000, /* delayToLiveInMillis */ 8000, /* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 15000, /* delayForCacheInvalidationOfWindEstimation */ 10000, /* millisecondsOverWhichToAverageSpeed */ 15000, /* delayForCacheInvalidationOfWindEstimation */ 10000,
/* useInternalMarkPassingAlgorithm */ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)); /* useInternalMarkPassingAlgorithm */ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class));
@@ -41,7 +41,6 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.TrackBasedTest; import com.sap.sailing.domain.test.TrackBasedTest;
@@ -110,7 +109,7 @@ public class TestFarthestAheadWithSkippedMarkPassing {
Course course = new CourseImpl("Test Course", waypoints); Course course = new CourseImpl("Test Course", waypoints);
RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors); RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors);
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 0, /* delayToLiveInMillis */ 0,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000,
/* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false, /* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false,
tr->new OneDesignRankingMetric(tr), mock(RaceLogResolver.class)); tr->new OneDesignRankingMetric(tr), mock(RaceLogResolver.class));
@@ -41,7 +41,6 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.TrackBasedTest; import com.sap.sailing.domain.test.TrackBasedTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -88,7 +87,7 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg {
Course course = new CourseImpl("Test Course", waypoints); Course course = new CourseImpl("Test Course", waypoints);
RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors); RaceDefinition race = new RaceDefinitionImpl("Test Race", course, boatClass, competitors);
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 0, /* delayToLiveInMillis */ 0,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000,
/* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false, /* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false,
tr->new TimeOnTimeAndDistanceRankingMetric(tr, tr->new TimeOnTimeAndDistanceRankingMetric(tr,
@@ -33,7 +33,6 @@ import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLeg;
@@ -304,7 +303,7 @@ public class CourseTest {
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new DynamicTrackedRegattaImpl( DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new DynamicTrackedRegattaImpl(
new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null, "test", null, OneDesignRankingMetric::new)), new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null, "test", null, OneDesignRankingMetric::new)),
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso), new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso),
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,/* delayToLiveInMillis */3000, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */3000,
/* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageWind */30000,
/* millisecondsOverWhichToAverageSpeed */8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, /* millisecondsOverWhichToAverageSpeed */8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class)); mock(RaceLogResolver.class));
@@ -336,7 +335,7 @@ public class CourseTest {
new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null, "test", null, OneDesignRankingMetric::new)), new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null, "test", null, OneDesignRankingMetric::new)),
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true), new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true),
hasso), Collections.<Sideline> emptyList(), hasso), Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, /* millisecondsOverWhichToAverageSpeed */ 8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class)); mock(RaceLogResolver.class));
@@ -37,7 +37,6 @@ import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.BoatClassMasterdata; import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegatta;
@@ -81,7 +80,7 @@ public class CourseUpdateDuringNonAtomicSerializationTest implements Serializabl
TrackedRegatta trackedRegatta = new TrackedRegattaImpl(regatta); TrackedRegatta trackedRegatta = new TrackedRegattaImpl(regatta);
RaceDefinition race = new RaceDefinitionImpl("Test Race", course, regatta.getBoatClass(), Collections.<Competitor>emptySet()); RaceDefinition race = new RaceDefinitionImpl("Test Race", course, regatta.getBoatClass(), Collections.<Competitor>emptySet());
trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptySet(), trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptySet(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, EmptyWindStore.INSTANCE,
/* delayToLiveInMillis */10000, /* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageSpeed */ /* delayToLiveInMillis */10000, /* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageSpeed */
7000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)) { 7000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)) {
private static final long serialVersionUID = 9114777576548711763L; private static final long serialVersionUID = 9114777576548711763L;
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl; import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sse.common.Duration; import com.sap.sse.common.Duration;
@@ -43,10 +44,11 @@ public class DistanceCacheInvalidationAfterLoadingFinishedTest extends TrackBase
@Test @Test
public void testDistanceCalculationWhileLoading() { public void testDistanceCalculationWhileLoading() {
final TimePoint now = MillisecondsTimePoint.now(); final TimePoint now = MillisecondsTimePoint.now();
final TrackingDataLoader tdl = new TrackingDataLoader() {};
trackedRace.updateMarkPassings(competitor, Collections.singleton(new MarkPassingImpl(now, trackedRace.getRace().getCourse().getFirstWaypoint(), competitor))); trackedRace.updateMarkPassings(competitor, Collections.singleton(new MarkPassingImpl(now, trackedRace.getRace().getCourse().getFirstWaypoint(), competitor)));
// no distance traveled yet because there is no fix yet; this may be cached for now // no distance traveled yet because there is no fix yet; this may be cached for now
assertEquals(Distance.NULL, trackedRace.getDistanceTraveled(competitor, now)); assertEquals(Distance.NULL, trackedRace.getDistanceTraveled(competitor, now));
trackedRace.setStatus(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, /* progress */ 0.1)); trackedRace.onStatusChanged(tdl, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, /* progress */ 0.1));
assertEquals(Distance.NULL, trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2)))); assertEquals(Distance.NULL, trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))));
final Position startPos = new DegreePosition(0, 0); final Position startPos = new DegreePosition(0, 0);
final DegreeBearingImpl bearing = new DegreeBearingImpl(10); final DegreeBearingImpl bearing = new DegreeBearingImpl(10);
@@ -54,7 +56,7 @@ public class DistanceCacheInvalidationAfterLoadingFinishedTest extends TrackBase
trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos, now, speed)); trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos, now, speed));
final Distance distance = speed.travel(Duration.ONE_SECOND); final Distance distance = speed.travel(Duration.ONE_SECOND);
trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos.translateGreatCircle(bearing, distance), now.plus(Duration.ONE_SECOND), speed)); trackedRace.recordFix(competitor, new GPSFixMovingImpl(startPos.translateGreatCircle(bearing, distance), now.plus(Duration.ONE_SECOND), speed));
trackedRace.setStatus(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* progress */ 1.0)); trackedRace.onStatusChanged(tdl, new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* progress */ 1.0));
assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.000001); // ask 1s after the second fix assertEquals(distance.getMeters(), trackedRace.getDistanceTraveled(competitor, now.plus(Duration.ONE_SECOND.times(2))).getMeters(), 0.000001); // ask 1s after the second fix
} }
} }
@@ -47,7 +47,6 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
@@ -139,7 +138,8 @@ public class LeaderboardCourseChangeTest {
TrackedRegatta mockedTrackedRegatta = createMockedTrackedRegatta(regatta); TrackedRegatta mockedTrackedRegatta = createMockedTrackedRegatta(regatta);
RaceDefinition mockedRace = createMockedRace(course, boatClass); RaceDefinition mockedRace = createMockedRace(course, boatClass);
TrackedRace spyedTrackedRace = spy(new DynamicTrackedRaceImpl(mockedTrackedRegatta, mockedRace, TrackedRace spyedTrackedRace = spy(new DynamicTrackedRaceImpl(mockedTrackedRegatta, mockedRace,
new HashSet<Sideline>(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 5000, 20000, 20000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, new HashSet<Sideline>(), EmptyWindStore.INSTANCE, 5000, 20000, 20000,
/* useMarkPassingCalculator */ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class))); mock(RaceLogResolver.class)));
return spyedTrackedRace; return spyedTrackedRace;
@@ -3,8 +3,8 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.same; import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -38,6 +38,7 @@ public class MarkPositionTimeFilterTest {
track = new DynamicGPSFixTrackImpl<Mark>(m, /* millisecondsOverWhichToAverage */ 5000); track = new DynamicGPSFixTrackImpl<Mark>(m, /* millisecondsOverWhichToAverage */ 5000);
when(trackedRace.getOrCreateTrack(m)).thenReturn(track); when(trackedRace.getOrCreateTrack(m)).thenReturn(track);
doCallRealMethod().when(trackedRace).recordFix(same(m), (GPSFixMoving) anyObject(), anyBoolean()); doCallRealMethod().when(trackedRace).recordFix(same(m), (GPSFixMoving) anyObject(), anyBoolean());
doCallRealMethod().when(trackedRace).isWithinStartAndEndOfTracking(anyObject());
} }
@Test @Test
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNotSame;
import static org.mockito.Mockito.mock;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@@ -10,15 +11,12 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import static org.mockito.Mockito.mock;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -62,7 +60,7 @@ public class MultipleClassesInRegattaTest {
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI), new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
kiwotest2 = domainFactory kiwotest2 = domainFactory
@@ -73,7 +71,7 @@ public class MultipleClassesInRegattaTest {
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI), new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
kiwotest3 = domainFactory kiwotest3 = domainFactory
@@ -84,7 +82,7 @@ public class MultipleClassesInRegattaTest {
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI), new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
weym470may112014_2 = domainFactory weym470may112014_2 = domainFactory
@@ -95,7 +93,7 @@ public class MultipleClassesInRegattaTest {
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI), new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
@@ -34,6 +34,7 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
@@ -63,7 +64,7 @@ import com.tractrac.subscription.lib.api.event.IStoredDataEvent;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest { public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest implements TrackingDataLoader {
private final Logger logger = Logger.getLogger(OnlineTracTracBasedTest.class.getName()); private final Logger logger = Logger.getLogger(OnlineTracTracBasedTest.class.getName());
private DomainFactoryImpl domainFactory; private DomainFactoryImpl domainFactory;
private Regatta domainEvent; private Regatta domainEvent;
@@ -137,20 +138,20 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest {
logger.info("Stored data begin"); logger.info("Stored data begin");
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0);
if (getTrackedRace() != null) { if (getTrackedRace() != null) {
getTrackedRace().setStatus(lastStatus); getTrackedRace().onStatusChanged(OnlineTracTracBasedTest.this, lastStatus);
} }
break; break;
case End: case End:
logger.info("Stored data end"); logger.info("Stored data end");
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1);
if (getTrackedRace() != null) { if (getTrackedRace() != null) {
getTrackedRace().setStatus(lastStatus); getTrackedRace().onStatusChanged(OnlineTracTracBasedTest.this, lastStatus);
} }
break; break;
case Progress: case Progress:
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, storedDataEvent.getProgress()); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, storedDataEvent.getProgress());
if (getTrackedRace() != null) { if (getTrackedRace() != null) {
getTrackedRace().setStatus(lastStatus); getTrackedRace().onStatusChanged(OnlineTracTracBasedTest.this, lastStatus);
} }
break; break;
default: default:
@@ -46,7 +46,6 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassing;
@@ -108,7 +107,7 @@ public class ReachingLegTest extends TrackBasedTest {
Course course = new CourseImpl(raceName, waypoints); Course course = new CourseImpl(raceName, waypoints);
RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors); RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors);
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 0, /* delayToLiveInMillis */ 0,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000,
/* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false, /* delay for wind estimation cache invalidation */ 0, /*useMarkPassingCalculator*/ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class)); OneDesignRankingMetric::new, mock(RaceLogResolver.class));
@@ -19,7 +19,6 @@ import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.TimeOnTimeAndDistanceRankingMetric; import com.sap.sailing.domain.ranking.TimeOnTimeAndDistanceRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
@@ -38,7 +37,7 @@ public class SerializeRankingMetricTest extends AbstractSerializationTest {
new LowPoint(), UUID.randomUUID(), /* courseArea */ null)), new LowPoint(), UUID.randomUUID(), /* courseArea */ null)),
new RaceDefinitionImpl("Race", new CourseImpl("Course", Collections.<Waypoint>emptyList()) , new RaceDefinitionImpl("Race", new CourseImpl("Course", Collections.<Waypoint>emptyList()) ,
/* boatClass */ _49er, Collections.<Competitor>emptySet()), /* boatClass */ _49er, Collections.<Competitor>emptySet()),
Collections.<Sideline>emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 0l, 0l, 0l, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, 0l, 0l, 0l,
false, TimeOnTimeAndDistanceRankingMetric::new, null); false, TimeOnTimeAndDistanceRankingMetric::new, null);
RankingMetric clone = cloneBySerialization(trackedRace.getRankingMetric(), DomainFactory.INSTANCE); RankingMetric clone = cloneBySerialization(trackedRace.getRankingMetric(), DomainFactory.INSTANCE);
assertNotNull(clone); assertNotNull(clone);
@@ -88,7 +88,7 @@ public class StarbordSideOfStartLineRecognitionTest {
private static final long serialVersionUID = -8007932232555073829L; private static final long serialVersionUID = -8007932232555073829L;
public MockedTrackedRaceImpl() { public MockedTrackedRaceImpl() {
super(null, null, Collections.<Sideline> emptyList(), null, null, 0, 0, 0, 0, false, OneDesignRankingMetric::new, super(null, null, Collections.<Sideline> emptyList(), null, 0, 0, 0, 0, false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class)); mock(RaceLogResolver.class));
} }
@@ -54,7 +54,6 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassing;
@@ -160,7 +159,7 @@ public abstract class TrackBasedTest {
Course course = new CourseImpl(raceName, waypoints); Course course = new CourseImpl(raceName, waypoints);
RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors); RaceDefinition race = new RaceDefinitionImpl(raceName, course, boatClass, competitors);
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 0, /* delayToLiveInMillis */ 0,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000,
/* delay for wind estimation cache invalidation */ 0, useMarkPassingCalculator, /* delay for wind estimation cache invalidation */ 0, useMarkPassingCalculator,
OneDesignRankingMetric::new, raceLogResolver); OneDesignRankingMetric::new, raceLogResolver);
@@ -1,7 +1,8 @@
package com.sap.sailing.domain.test; package com.sap.sailing.domain.test;
import java.io.BufferedReader;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import java.io.BufferedReader;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -19,7 +20,6 @@ import org.junit.rules.Timeout;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -69,7 +69,7 @@ public class UnicodeCharactersInCompetitorNamesTest {
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, "tracTest", "tracTest", "", "", EmptyWindStore.INSTANCE, "tracTest", "tracTest", "", "",
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
Iterable<Competitor> competitors = fourtyninerYellow_2.getRacesHandle().getRace().getCompetitors(); Iterable<Competitor> competitors = fourtyninerYellow_2.getRacesHandle().getRace().getCompetitors();
@@ -22,7 +22,6 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.MarkImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -46,7 +45,8 @@ public class UpdateMarkPassingTest {
when(race.getCompetitors()).thenReturn(Collections.singleton(competitor)); when(race.getCompetitors()).thenReturn(Collections.singleton(competitor));
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl( DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(
/* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null, /* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null,
"test", null, OneDesignRankingMetric::new)), race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, "test", null, OneDesignRankingMetric::new)),
race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE,
/* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000, /* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000,
/* millisecondsOverWhichToAverageSpeed */30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class)); /* millisecondsOverWhichToAverageSpeed */30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, mock(RaceLogResolver.class));
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
@@ -48,7 +48,6 @@ import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager; import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory; import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -287,7 +286,7 @@ public class WindTest {
new RaceDefinitionImpl("Test Race", new RaceDefinitionImpl("Test Race",
new CourseImpl("Test Course", Arrays.asList(new Waypoint[] { w1, w2, w3 })), new CourseImpl("Test Course", Arrays.asList(new Waypoint[] { w1, w2, w3 })),
boatClass, Collections.singleton(competitor)), Collections.<Sideline> emptyList(), boatClass, Collections.singleton(competitor)), Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 1000, EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 1000,
/* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, /* millisecondsOverWhichToAverageSpeed */ 30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new,
mock(RaceLogResolver.class)); mock(RaceLogResolver.class));
@@ -1,12 +1,12 @@
package com.sap.sailing.domain.test.markpassing; package com.sap.sailing.domain.test.markpassing;
import static org.mockito.Mockito.mock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.mockito.Mockito.mock;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPointWithTwoMarks; import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
@@ -34,7 +34,6 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -72,7 +71,9 @@ public class AbstractMockedRaceMarkPassingTest {
new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new); new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new);
Course course = new CourseImpl("course", waypoints); Course course = new CourseImpl("course", waypoints);
RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben)); RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben));
race = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), raceDef, new ArrayList<Sideline>(), new EmptyWindStore(), EmptyGPSFixStore.INSTANCE, 0, 10000, 10000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new, race = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), raceDef, new ArrayList<Sideline>(),
new EmptyWindStore(), 0, 10000, 10000, /* useMarkPassingCalculator */ false,
OneDesignRankingMetric::new,
mock(RaceLogResolver.class)); mock(RaceLogResolver.class));
race.setStartTimeReceived(new MillisecondsTimePoint(10000)); race.setStartTimeReceived(new MillisecondsTimePoint(10000));
TimePoint t = new MillisecondsTimePoint(30000); TimePoint t = new MillisecondsTimePoint(30000);
@@ -42,10 +42,10 @@ import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.ranking.RankingMetricConstructor; import com.sap.sailing.domain.ranking.RankingMetricConstructor;
@@ -55,6 +55,7 @@ import com.sap.sailing.domain.regattalike.RegattaLikeListener;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener; import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -65,11 +66,14 @@ import com.sap.sailing.domain.tracking.RaceAbortedListener;
import com.sap.sailing.domain.tracking.RaceChangeListener; import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.SensorFixTrack;
import com.sap.sailing.domain.tracking.StartTimeChangedListener; import com.sap.sailing.domain.tracking.StartTimeChangedListener;
import com.sap.sailing.domain.tracking.TrackFactory;
import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus; import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache; import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode; import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindStore;
@@ -428,7 +432,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override @Override
public void setStartDate(TimePoint startDate) { public void setStartDate(TimePoint startDate) {
} }
@Override @Override
@@ -438,7 +442,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override @Override
public void setEndDate(TimePoint startDate) { public void setEndDate(TimePoint startDate) {
} }
@Override @Override
@@ -447,11 +451,11 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void addListener(RegattaLikeListener listener) { public void addListener(RegattaLikeListener listener) {
} }
@Override @Override
public void removeListener(RegattaLikeListener listener) { public void removeListener(RegattaLikeListener listener) {
} }
@Override @Override
@@ -504,7 +508,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void registerCompetitor(Competitor competitor) { public void registerCompetitor(Competitor competitor) {
} }
@Override @Override
@@ -559,9 +563,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override @Override
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines,
WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingcalculator, RaceLogResolver raceLogResolver) { DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingcalculator,
RaceLogResolver raceLogResolver) {
return null; return null;
} }
@@ -642,7 +647,8 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode) {
return null; return null;
} }
@@ -806,7 +812,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void setStatus(TrackedRaceStatus newStatus) { public void onStatusChanged(TrackingDataLoader loader, TrackedRaceStatus status) {
} }
@Override @Override
@@ -915,9 +921,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null; return null;
} }
public GPSFixStore getGPSFixStore() {
return null;
}
@Override @Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
@@ -960,7 +963,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RegattaLog fromRegattaLog) throws InterruptedException { public void waitForLoadingToFinish() throws InterruptedException {
} }
@Override @Override
@@ -1048,4 +1051,46 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override @Override
public void setFinishedTime(TimePoint newFinishedTime) { public void setFinishedTime(TimePoint newFinishedTime) {
} }
@Override
public <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(
Competitor competitor, String trackName) {
return null;
}
@Override
public <FixT extends SensorFix, TrackT extends DynamicSensorFixTrack<Competitor, FixT>> TrackT getDynamicSensorTrack(
Competitor competitor, String trackName) {
return null;
}
@Override
public <FixT extends SensorFix, TrackT extends DynamicSensorFixTrack<Competitor, FixT>> TrackT getOrCreateSensorTrack(
Competitor competitor, String trackName, TrackFactory<TrackT> newTrackFactory) {
return null;
}
@Override
public boolean isWithinStartAndEndOfTracking(TimePoint timePoint) {
return true;
}
@Override
public Iterable<RegattaLog> getAttachedRegattaLogs() {
return Collections.emptySet();
}
@Override
public void recordSensorFix(Competitor competitor, String trackName, SensorFix fix,
boolean onlyWhenInTrackingTimeInterval) {
}
@Override
public void addSensorTrack(Competitor trackedItem, String trackName, DynamicSensorFixTrack<Competitor, ?> track) {
}
@Override
public Iterable<RaceLog> getAttachedRaceLogs() {
return Collections.emptySet();
}
} }
@@ -32,9 +32,9 @@ import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener; import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
@@ -45,6 +45,7 @@ import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceAbortedListener; import com.sap.sailing.domain.tracking.RaceAbortedListener;
import com.sap.sailing.domain.tracking.RaceChangeListener; import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.SensorFixTrack;
import com.sap.sailing.domain.tracking.StartTimeChangedListener; import com.sap.sailing.domain.tracking.StartTimeChangedListener;
import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
@@ -560,11 +561,6 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
return null; return null;
} }
@Override
public GPSFixStore getGPSFixStore() {
return null;
}
@Override @Override
public void addRaceAbortedListener(RaceAbortedListener listener) { public void addRaceAbortedListener(RaceAbortedListener listener) {
} }
@@ -579,7 +575,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
} }
@Override @Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RegattaLog fromRegattaLog) throws InterruptedException { public void waitForLoadingToFinish() throws InterruptedException {
} }
@Override @Override
@@ -664,4 +660,20 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override @Override
public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad) { public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad) {
} }
@Override
public <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(
Competitor competitor, String trackName) {
return null;
}
@Override
public Iterable<RegattaLog> getAttachedRegattaLogs() {
return Collections.emptySet();
}
@Override
public Iterable<RaceLog> getAttachedRaceLogs() {
return Collections.emptySet();
}
} }
@@ -0,0 +1,125 @@
package com.sap.sailing.domain.tracking.impl;
import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.impl.BravoFixImpl;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.test.TrackBasedTest;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class BravoFixTrackSerializationTest {
private DynamicBravoFixTrack<Competitor> track;
@Before
public void setUp() {
Competitor competitor = TrackBasedTest.createCompetitor("SAP Extreme Sailing Team");
track = new BravoFixTrackImpl<Competitor>(competitor, BravoFixTrack.TRACK_NAME);
}
@Test
public void testWithZeroValueFix() throws ClassNotFoundException, IOException {
addToTrackAndAssertRideHeight(new TestFixData(now().minus(Duration.ONE_MINUTE), 0.0));
}
@Test
public void testWithSingleFix() throws ClassNotFoundException, IOException {
addToTrackAndAssertRideHeight(new TestFixData(now().minus(Duration.ONE_MINUTE), 0.6543));
}
@Test
public void testWithMultipleFixes() throws ClassNotFoundException, IOException {
addToTrackAndAssertRideHeight(new TestFixData(now().minus(Duration.ONE_MINUTE), 1.0249),
new TestFixData(now().minus(Duration.ONE_MINUTE.times(2)), 0.9140),
new TestFixData(now().minus(Duration.ONE_MINUTE.times(3)), 0.3721),
new TestFixData(now().minus(Duration.ONE_MINUTE.times(4)), 0.7942),
new TestFixData(now().minus(Duration.ONE_MINUTE.times(5)), 0.6203));
}
@Test
public void testWithTwoFixesWithSameTimePointNoReplacement() throws ClassNotFoundException, IOException {
TimePoint timePoint = now().minus(Duration.ONE_MINUTE);
TestFixData testFixData1 = new TestFixData(timePoint, 1.0000);
TestFixData testFixData2 = new TestFixData(timePoint, 0.5000);
Arrays.asList(testFixData1, testFixData2).forEach(TestFixData::addBravoFixToTrack);
assertRideHeight(getDeserializedTrack(), testFixData1);
}
@Test
public void testWithTwoFixesWithSameTimePointWithReplacement() throws ClassNotFoundException, IOException {
TimePoint timePoint = now().minus(Duration.ONE_MINUTE);
TestFixData testFixData1 = new TestFixData(timePoint, 1.0000);
TestFixData testFixData2 = new TestFixData(timePoint, 0.5000);
Arrays.asList(testFixData1, testFixData2).forEach(TestFixData::replaceBravoFixOnTrack);
assertRideHeight(getDeserializedTrack(), testFixData2);
}
private void addToTrackAndAssertRideHeight(TestFixData... testFixData) throws ClassNotFoundException, IOException {
List<TestFixData> testFixDataList = Arrays.asList(testFixData);
testFixDataList.forEach(TestFixData::addBravoFixToTrack);
BravoFixTrack<Competitor> track = getDeserializedTrack();
testFixDataList.forEach(testData -> assertRideHeight(track, testData));
}
private void assertRideHeight(BravoFixTrack<Competitor> track, TestFixData testFixData) {
assertRideHeight(track, testFixData.timePoint, testFixData.rideHeight);
}
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expected) {
assertEquals(expected, track.getRideHeight(timePoint), 0.0);
}
@SuppressWarnings("unchecked")
private BravoFixTrack<Competitor> getDeserializedTrack() throws ClassNotFoundException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream ous = new ObjectOutputStream(baos);
ous.writeObject(track);
ous.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
BravoFixTrack<Competitor> deserializedInstance = (BravoFixTrack<Competitor>) ois.readObject();
ois.close();
return deserializedInstance;
}
private class TestFixData {
private final TimePoint timePoint;
private final double rideHeight;
private TestFixData(TimePoint timePoint, double rideHeight) {
this.timePoint = timePoint;
this.rideHeight = rideHeight;
}
private void addBravoFixToTrack() {
addOrReplaceBravoFixToTrack(false);
}
private void replaceBravoFixOnTrack() {
addOrReplaceBravoFixToTrack(true);
}
private void addOrReplaceBravoFixToTrack(boolean replace) {
double[] fixData = new double[BravoSensorDataMetadata.INSTANCE.getColumns().size()];
fixData[BravoSensorDataMetadata.INSTANCE.rideHeightColumn] = rideHeight;
track.add(new BravoFixImpl(new DoubleVectorFixImpl(timePoint, fixData)), replace);
}
}
}
@@ -27,7 +27,6 @@ import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -142,7 +141,7 @@ public interface DomainFactory {
TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword, WindStore windStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException; throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException;
@@ -156,7 +155,7 @@ public interface DomainFactory {
RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword, WindStore windStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException; SubscriberInitializationException;
@@ -53,8 +53,6 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -524,7 +522,7 @@ public class DomainFactoryImpl implements DomainFactory {
// add to existing regatta only if boat class matches // add to existing regatta only if boat class matches
if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) { if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) {
trackedRegatta.getRegatta().addRace(raceDefinition); trackedRegatta.getRegatta().addRace(raceDefinition);
trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore, EmptyGPSFixStore.INSTANCE, trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore,
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, ignoreTracTracMarkPassings, delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, ignoreTracTracMarkPassings,
raceLogResolver); raceLogResolver);
logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()); logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta());
@@ -562,11 +560,11 @@ public class DomainFactoryImpl implements DomainFactory {
} }
private DynamicTrackedRace createTrackedRace(TrackedRegatta trackedRegatta, RaceDefinition race, private DynamicTrackedRace createTrackedRace(TrackedRegatta trackedRegatta, RaceDefinition race,
Iterable<Sideline> sidelines, WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis, Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) { boolean useMarkPassingCalculator, RaceLogResolver raceLogResolver) {
return trackedRegatta.createTrackedRace(race, sidelines, return trackedRegatta.createTrackedRace(race, sidelines,
windStore, gpsFixStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver); raceDefinitionSetToUpdate, useMarkPassingCalculator, raceLogResolver);
} }
@@ -655,27 +653,28 @@ public class DomainFactoryImpl implements DomainFactory {
public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException,
URISyntaxException, CreateModelException, SubscriberInitializationException { URISyntaxException, CreateModelException, SubscriberInitializationException {
return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry,
raceLogResolver);
} }
@Override @Override
public RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, public RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException { SubscriberInitializationException {
return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, courseDesignUpdateURI, return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver); raceVisibility, trackedRegattaRegistry, raceLogResolver);
} }
@@ -18,7 +18,6 @@ import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -225,7 +224,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
private DynamicTrackedRace createTrackedRace(RaceDefinition race, Iterable<Sideline> sidelines) { private DynamicTrackedRace createTrackedRace(RaceDefinition race, Iterable<Sideline> sidelines) {
DynamicTrackedRace trackedRace = getTrackedRegatta().createTrackedRace(race, sidelines, DynamicTrackedRace trackedRace = getTrackedRegatta().createTrackedRace(race, sidelines,
windStore, EmptyGPSFixStore.INSTANCE, delayToLiveInMillis, millisecondsOverWhichToAverageWind, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver); raceDefinitionSetToUpdate, useInternalMarkPassingAlgorithm, raceLogResolver);
getDomainFactory().addTracTracUpdateHandlers(tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, race, trackedRace); getDomainFactory().addTracTracUpdateHandlers(tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, race, trackedRace);
@@ -9,7 +9,6 @@ import java.net.URL;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
@@ -65,21 +64,21 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
@Override @Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, URISyntaxException, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, URISyntaxException,
CreateModelException, SubscriberInitializationException { CreateModelException, SubscriberInitializationException {
RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI, RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver); raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker; return tracker;
} }
@Override @Override
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws Exception { WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI, RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm,
raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver); raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker; return tracker;
} }
@@ -30,7 +30,6 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl; import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
@@ -41,6 +40,7 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus; import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
@@ -69,7 +69,8 @@ import com.tractrac.subscription.lib.api.event.ILiveDataEvent;
import com.tractrac.subscription.lib.api.event.IStoredDataEvent; import com.tractrac.subscription.lib.api.event.IStoredDataEvent;
import com.tractrac.subscription.lib.api.race.IRacesListener; import com.tractrac.subscription.lib.api.race.IRacesListener;
public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements IConnectionStatusListener, TracTracRaceTracker, DynamicRaceDefinitionSet { public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
implements IConnectionStatusListener, TracTracRaceTracker, DynamicRaceDefinitionSet, TrackingDataLoader {
private static final Logger logger = Logger.getLogger(TracTracRaceTrackerImpl.class.getName()); private static final Logger logger = Logger.getLogger(TracTracRaceTrackerImpl.class.getName());
/** /**
@@ -193,7 +194,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
private final Set<Receiver> receivers; private final Set<Receiver> receivers;
private final DomainFactory domainFactory; private final DomainFactory domainFactory;
private final WindStore windStore; private final WindStore windStore;
private final GPSFixStore gpsFixStore;
private final Set<RaceDefinition> races; private final Set<RaceDefinition> races;
private final DynamicTrackedRegatta trackedRegatta; private final DynamicTrackedRegatta trackedRegatta;
private TrackedRaceStatus lastStatus; private TrackedRaceStatus lastStatus;
@@ -255,26 +255,28 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
protected TracTracRaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, protected TracTracRaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword, WindStore windStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException, throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException,
SubscriberInitializationException { SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), domainFactory, paramURL, liveURI, this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), domainFactory, paramURL, liveURI,
storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore,
windStore, tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
} }
private TracTracRaceTrackerImpl(IRace tractracRace, DomainFactory domainFactory, URL paramURL, URI liveURI, private TracTracRaceTrackerImpl(IRace tractracRace, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
FileNotFoundException, SubscriberInitializationException { FileNotFoundException, SubscriberInitializationException {
this(tractracRace, null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, this(tractracRace, null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry,
raceLogResolver);
} }
/** /**
@@ -286,13 +288,14 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI, protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException,
MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException { MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), regatta, domainFactory, paramURL, this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), regatta, domainFactory, paramURL,
liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername, offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore,
tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
} }
@@ -310,7 +313,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
private TracTracRaceTrackerImpl(IRace tractracRace, final Regatta regatta, DomainFactory domainFactory, private TracTracRaceTrackerImpl(IRace tractracRace, final Regatta regatta, DomainFactory domainFactory,
URL paramURL, URI liveURI, URI storedURI, URI tracTracUpdateURI, TimePoint startOfTracking, URL paramURL, URI liveURI, URI storedURI, URI tracTracUpdateURI, TimePoint startOfTracking,
TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
FileNotFoundException, SubscriberInitializationException { FileNotFoundException, SubscriberInitializationException {
@@ -321,7 +324,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.id = createID(paramURL, liveURI, storedURI); this.id = createID(paramURL, liveURI, storedURI);
isLiveTracking = liveURI != null; isLiveTracking = liveURI != null;
this.races = new HashSet<RaceDefinition>(); this.races = new HashSet<RaceDefinition>();
this.gpsFixStore = gpsFixStore;
this.domainFactory = domainFactory; this.domainFactory = domainFactory;
this.lastProgressPerID = new HashMap<>(); this.lastProgressPerID = new HashMap<>();
if (offsetToStartTimeOfSimulatedRace != null) { if (offsetToStartTimeOfSimulatedRace != null) {
@@ -537,7 +539,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
} }
@Override @Override
public void stop(boolean stopReceiversPreemtively) throws InterruptedException { protected void onStop(boolean stopReceiversPreemtively) throws InterruptedException {
if (!stopped) { if (!stopped) {
stopped = true; stopped = true;
raceSubscriber.stop(); raceSubscriber.stop();
@@ -601,7 +603,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
} else { } else {
status = lastStatus; status = lastStatus;
} }
trackedRace.setStatus(status); trackedRace.onStatusChanged(this, status);
} }
} }
@@ -623,7 +625,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.ERROR, 0.0); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.ERROR, 0.0);
updateStatusOfTrackedRaces(); updateStatusOfTrackedRaces();
return; return;
} catch (InterruptedException e) { } catch (Exception e) {
logger.severe("Exception stopping race tracker: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} else { } else {
@@ -709,8 +712,4 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
}; };
} }
@Override
public GPSFixStore getGPSFixStore() {
return gpsFixStore;
}
} }
@@ -106,6 +106,10 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
} }
} }
if (trackedRace != null) { if (trackedRace != null) {
RegattaLog regattaLog = getRegattaLog();
if(regattaLog != null) {
trackedRace.attachRegattaLog(regattaLog);
}
final RaceLog raceLog = getRaceLog(fleet); final RaceLog raceLog = getRaceLog(fleet);
if (raceLog != null) { if (raceLog != null) {
trackedRace.attachRaceLog(raceLog); trackedRace.attachRaceLog(raceLog);
@@ -136,6 +136,6 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
@Override @Override
public RegattaLog getRegattaLog() { public RegattaLog getRegattaLog() {
Regatta regatta = getRegatta(); Regatta regatta = getRegatta();
return regatta.getRegattaLog(); return regatta == null ? null : regatta.getRegattaLog();
} }
} }
File diff suppressed because it is too large. Load diff
@@ -16,7 +16,6 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
@@ -30,21 +29,15 @@ import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException; import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.TimeRangeImpl;
/** /**
* Holds all information needed for a master data import. * Holds all information needed for a master data import.
@@ -62,15 +55,16 @@ public class TopLevelMasterData implements Serializable {
private final Set<LeaderboardGroup> leaderboardGroups; private final Set<LeaderboardGroup> leaderboardGroups;
private final Set<WindTrackMasterData> windTrackMasterData; private final Set<WindTrackMasterData> windTrackMasterData;
private final Map<LeaderboardGroup, Set<Event>> eventForLeaderboardGroup; private final Map<LeaderboardGroup, Set<Event>> eventForLeaderboardGroup;
private final Map<DeviceIdentifier, Set<GPSFix>> raceLogTrackingFixes; private final Map<DeviceIdentifier, Set<Timed>> raceLogTrackingFixes;
private final Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations; private final Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations;
public TopLevelMasterData(final Set<LeaderboardGroup> groupsToExport, final Iterable<Event> allEvents, public TopLevelMasterData(final Set<LeaderboardGroup> groupsToExport, final Iterable<Event> allEvents,
final Map<String, Regatta> regattaForRaceIdString, final Iterable<MediaTrack> allMediaTracks, final Map<String, Regatta> regattaForRaceIdString, final Iterable<MediaTrack> allMediaTracks,
GPSFixStore gpsFixStore, boolean exportWind, Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations) { SensorFixStore sensorFixStore, boolean exportWind,
Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations) {
this.raceIdStringsForRegatta = convertToRaceIdStringsForRegattaMap(regattaForRaceIdString); this.raceIdStringsForRegatta = convertToRaceIdStringsForRegattaMap(regattaForRaceIdString);
this.leaderboardGroups = groupsToExport; this.leaderboardGroups = groupsToExport;
this.raceLogTrackingFixes = getAllRelevantRaceLogTrackingFixes(gpsFixStore); this.raceLogTrackingFixes = getAllRelevantRaceLogTrackingFixes(sensorFixStore);
if (exportWind) { if (exportWind) {
this.windTrackMasterData = fillWindMap(groupsToExport); this.windTrackMasterData = fillWindMap(groupsToExport);
} else { } else {
@@ -82,8 +76,8 @@ public class TopLevelMasterData implements Serializable {
filterMediaTracks(allMediaTracks, this.filteredMediaTracks); filterMediaTracks(allMediaTracks, this.filteredMediaTracks);
} }
private Map<DeviceIdentifier, Set<GPSFix>> getAllRelevantRaceLogTrackingFixes(GPSFixStore gpsFixStore) { private Map<DeviceIdentifier, Set<Timed>> getAllRelevantRaceLogTrackingFixes(SensorFixStore sensorFixStore) {
Map<DeviceIdentifier, Set<GPSFix>> relevantFixes = new HashMap<>(); Map<DeviceIdentifier, Set<Timed>> relevantFixes = new HashMap<>();
//Add fixes for regatta log mappings //Add fixes for regatta log mappings
for (Regatta regatta : getAllRegattas()) { for (Regatta regatta : getAllRegattas()) {
@@ -91,7 +85,7 @@ public class TopLevelMasterData implements Serializable {
try { try {
regattaLog.lockForRead(); regattaLog.lockForRead();
for (RegattaLogEvent logEvent : regattaLog.getRawFixes()) { for (RegattaLogEvent logEvent : regattaLog.getRawFixes()) {
addAllFixesIfMappingEvent(gpsFixStore, relevantFixes, logEvent); addAllFixesIfMappingEvent(sensorFixStore, relevantFixes, logEvent);
} }
} finally { } finally {
regattaLog.unlockAfterRead(); regattaLog.unlockAfterRead();
@@ -106,7 +100,7 @@ public class TopLevelMasterData implements Serializable {
try { try {
raceLog.lockForRead(); raceLog.lockForRead();
for (RaceLogEvent logEvent : raceLog.getRawFixes()) { for (RaceLogEvent logEvent : raceLog.getRawFixes()) {
addAllFixesIfMappingEvent(gpsFixStore, relevantFixes, logEvent); addAllFixesIfMappingEvent(sensorFixStore, relevantFixes, logEvent);
} }
} finally { } finally {
raceLog.unlockAfterRead(); raceLog.unlockAfterRead();
@@ -118,12 +112,13 @@ public class TopLevelMasterData implements Serializable {
return relevantFixes; return relevantFixes;
} }
private void addAllFixesIfMappingEvent(GPSFixStore gpsFixStore, Map<DeviceIdentifier, Set<GPSFix>> relevantFixes, private void addAllFixesIfMappingEvent(SensorFixStore sensorFixStore,
Map<DeviceIdentifier, Set<Timed>> relevantFixes,
AbstractLogEvent<?> logEvent) { AbstractLogEvent<?> logEvent) {
if (logEvent instanceof RegattaLogDeviceMappingEvent<?>) { if (logEvent instanceof RegattaLogDeviceMappingEvent<?>) {
RegattaLogDeviceMappingEvent<?> mappingEvent = (RegattaLogDeviceMappingEvent<?>) logEvent; RegattaLogDeviceMappingEvent<?> mappingEvent = (RegattaLogDeviceMappingEvent<?>) logEvent;
try { try {
addAllFixesForMappingEvent(gpsFixStore, relevantFixes, mappingEvent); addAllFixesForMappingEvent(sensorFixStore, relevantFixes, mappingEvent);
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) { } catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
logger.severe("Failed to add fixes to exportdata for mapping Event"); logger.severe("Failed to add fixes to exportdata for mapping Event");
e.printStackTrace(); e.printStackTrace();
@@ -131,33 +126,16 @@ public class TopLevelMasterData implements Serializable {
} }
} }
private void addAllFixesForMappingEvent(GPSFixStore gpsFixStore, Map<DeviceIdentifier, Set<GPSFix>> relevantFixes, private void addAllFixesForMappingEvent(SensorFixStore sensorFixStore,
Map<DeviceIdentifier, Set<Timed>> relevantFixes,
RegattaLogDeviceMappingEvent<?> mappingEvent) throws NoCorrespondingServiceRegisteredException, TransformationException { RegattaLogDeviceMappingEvent<?> mappingEvent) throws NoCorrespondingServiceRegisteredException, TransformationException {
DynamicGPSFixTrack<WithID, ?> track;
if (isMarkMappingEvent(mappingEvent)) {
track = new DynamicGPSFixTrackImpl<WithID>(mappingEvent.getMappedTo(), 10000);
} else {
track = new DynamicGPSFixMovingTrackImpl<WithID>(mappingEvent.getMappedTo(), 1000);
}
DeviceIdentifier device = mappingEvent.getDevice(); DeviceIdentifier device = mappingEvent.getDevice();
gpsFixStore.loadTrack(track, new DeviceMappingImpl<WithID>(mappingEvent.getMappedTo(), device,
new TimeRangeImpl(mappingEvent.getFrom(), mappingEvent.getTo())));
if (!relevantFixes.containsKey(device)) { if (!relevantFixes.containsKey(device)) {
relevantFixes.put(device, new HashSet<>()); relevantFixes.put(device, new HashSet<>());
} }
Set<GPSFix> fixes = relevantFixes.get(device); Set<Timed> fixes = relevantFixes.get(device);
try { sensorFixStore.loadFixes(fixes::add, mappingEvent.getDevice(), mappingEvent.getFrom(), mappingEvent.getTo(),
track.lockForRead(); true);
for (GPSFix fix : track.getRawFixes()) {
fixes.add(fix);
}
} finally {
track.unlockAfterRead();
}
}
private boolean isMarkMappingEvent(RegattaLogDeviceMappingEvent<?> mappingEvent) {
return mappingEvent instanceof RegattaLogDeviceMarkMappingEvent;
} }
/** /**
@@ -335,7 +313,7 @@ public class TopLevelMasterData implements Serializable {
return raceIdentifiers; return raceIdentifiers;
} }
public Map<DeviceIdentifier, Set<GPSFix>> getRaceLogTrackingFixes() { public Map<DeviceIdentifier, Set<Timed>> getRaceLogTrackingFixes() {
return raceLogTrackingFixes; return raceLogTrackingFixes;
} }
@@ -0,0 +1,144 @@
package com.sap.sailing.domain.racelog.impl;
import java.util.List;
import java.util.function.Consumer;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Util;
import com.sap.sse.common.WithID;
/**
* At the moment, the timerange covered by the fixes for a device, and the number of
* fixes for a device are stored in a metadata collection. Should be changed, see bug 1982.
* @author Fredrik Teschke
*
*/
public class GPSFixStoreImpl implements GPSFixStore {
private final SensorFixStore sensorFixStore;
public GPSFixStoreImpl(SensorFixStore sensorFixStore) {
this.sensorFixStore = sensorFixStore;
}
private <FixT extends GPSFix> void loadTrack(DynamicGPSFixTrack<?, FixT> track, DeviceIdentifier device,
TimePoint from, TimePoint to, boolean inclusive) throws NoCorrespondingServiceRegisteredException,
TransformationException {
sensorFixStore.<FixT>loadFixes(track::add, device, from, to, inclusive);
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, RegattaLog log, Competitor competitor)
throws NoCorrespondingServiceRegisteredException, TransformationException{
List<DeviceMappingWithRegattaLogEvent<Competitor>> mappings = new RegattaLogDeviceCompetitorMappingFinder(log)
.analyze().get(competitor);
if (mappings != null) {
for (DeviceMapping<Competitor> mapping : mappings) {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
}
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, RegattaLog log, Mark mark)
throws NoCorrespondingServiceRegisteredException, TransformationException{
List<DeviceMappingWithRegattaLogEvent<Mark>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze()
.get(mark);
if (mappings != null) {
for (DeviceMapping<Mark> mapping : mappings) {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
}
}
@Override
public void storeFix(DeviceIdentifier device, GPSFix fix) {
sensorFixStore.storeFix(device, fix);
}
@Override
public synchronized void addListener(FixReceivedListener<GPSFix> listener, DeviceIdentifier device) {
sensorFixStore.addListener(listener, device);
}
@Override
public synchronized void removeListener(FixReceivedListener<GPSFix> listener) {
sensorFixStore.removeListener(listener);
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track,
DeviceMapping<Competitor> mapping, TimePoint start, TimePoint end)
throws TransformationException, NoCorrespondingServiceRegisteredException {
// loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true
// /*inclusive*/);
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /* inclusive */);
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, DeviceMapping<Mark> mapping, TimePoint start,
TimePoint end)
throws TransformationException, NoCorrespondingServiceRegisteredException {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /* inclusive */);
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, RegattaLog log, Mark mark,
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
List<DeviceMappingWithRegattaLogEvent<Mark>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze()
.get(mark);
if (mappings != null) {
for (DeviceMapping<Mark> mapping : mappings) {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
}
}
}
@Override
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
throws NoCorrespondingServiceRegisteredException, TransformationException {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true);
}
@Override
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
throws TransformationException, NoCorrespondingServiceRegisteredException {
return sensorFixStore.getTimeRangeCoveredByFixes(device);
}
@Override
public long getNumberOfFixes(DeviceIdentifier device) throws TransformationException, NoCorrespondingServiceRegisteredException {
return sensorFixStore.getNumberOfFixes(device);
}
@Override
public <FixT extends GPSFix> void loadFixes(Consumer<FixT> consumer, DeviceIdentifier deviceIdentifier,
TimePoint start, TimePoint end, boolean inclusive)
throws NoCorrespondingServiceRegisteredException, TransformationException {
sensorFixStore.loadFixes(consumer, deviceIdentifier, start, end, inclusive);
}
}
Loaded 100 of 215 files, more files were not shown because too many files have changed in this diff. Show more