mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-10 22:30:56 +00:00
Merge branch 'master' into translation
This commit is contained in:
commit
987f17ea01
404 files changed
+4753
-3204
No files matched your search
+4
-2
@@ -54,7 +54,8 @@ public class BarbadosResultSpreadsheet {
|
||||
String crew = row.getCell(4).getStringCellValue();
|
||||
Iterable<String> names = Arrays.asList(new String[] { helm, crew });
|
||||
double scoreAfterDiscarding = row.getCell(columnNumberOfNetScore).getNumericCellValue();
|
||||
double totalPointsBeforeDiscarding = row.getCell(columnNumberOfTotalScore).getNumericCellValue();
|
||||
final Cell cellValue = row.getCell(columnNumberOfTotalScore);
|
||||
double totalPointsBeforeDiscarding = cellValue == null ? null : cellValue.getNumericCellValue();
|
||||
List<CompetitorEntry> rankAndMaxPointsReasonAndPointsAndDiscarded = new ArrayList<>();
|
||||
for (int raceNumber=0; raceNumber<numberOfRaces; raceNumber++) {
|
||||
final Cell rankOrMaxPointReasonCell = row.getCell(COLUMN_NUMBER_OF_FIRST_RACE_RANK+raceNumber);
|
||||
@@ -68,7 +69,8 @@ public class BarbadosResultSpreadsheet {
|
||||
rank = null;
|
||||
maxPointsReason = rankOrMaxPointReasonCell.getStringCellValue();
|
||||
}
|
||||
double score = row.getCell(columnNumberOfFirstRaceScore+raceNumber).getNumericCellValue();
|
||||
final Cell scoreCellContents = row.getCell(columnNumberOfFirstRaceScore+raceNumber);
|
||||
double score = scoreCellContents == null ? 0.0 : scoreCellContents.getNumericCellValue();
|
||||
if (score != 0.0) {
|
||||
CompetitorEntry entry = new DefaultCompetitorEntryImpl(rank,
|
||||
maxPointsReason, score, /* discarded */ false);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/lib/gwt-maps-api-3.9.0-build-17.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/lib/gwt-ajaxloader.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/classes"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/lib/gwt-maps-api-3.9.0-build-17.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/lib/gwt-ajaxloader.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/classes"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -1,12 +1,12 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
@@ -5,7 +5,7 @@ Bundle-SymbolicName: com.sap.sailing.dashboards.gwt
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Activator: com.sap.sailing.dashboards.gwt.server.Activator
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Require-Bundle: com.sap.sailing.domain,
|
||||
com.sap.sailing.server,
|
||||
com.google.gwt.osgi;bundle-version="2.6.1",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
|
||||
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
|
||||
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9874"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9874"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
</listAttribute>
|
||||
@@ -41,7 +42,7 @@
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9998 com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-incremental -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9874 com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
|
||||
</launchConfiguration>
|
||||
@@ -3,7 +3,7 @@
|
||||
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
|
||||
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
|
||||
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package com.sap.sailing.dashboards.gwt.client.linker;
|
||||
|
||||
import com.google.gwt.core.ext.linker.Shardable;
|
||||
import com.sap.sse.gwt.client.linker.ManifestLinker;
|
||||
import com.sap.sse.gwt.linker.ManifestLinker;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
|
||||
+86
-88
@@ -6,7 +6,8 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation.AbstracPreCalculationDataRetriever;
|
||||
import com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation.StartlineAdvantageCalculationData;
|
||||
import com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation.StartlineAdvantageCalculationDataRetriever;
|
||||
import com.sap.sailing.dashboards.gwt.shared.dispatch.DashboardDispatchContext;
|
||||
import com.sap.sailing.dashboards.gwt.shared.dto.StartLineAdvantageDTO;
|
||||
import com.sap.sailing.dashboards.gwt.shared.dto.StartlineAdvantagesWithMaxAndAverageDTO;
|
||||
@@ -28,8 +29,9 @@ import com.sap.sse.common.Util.Pair;
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDataRetriever {
|
||||
public class StartlineAdvantagesByWindCalculator {
|
||||
|
||||
private StartlineAdvantageCalculationDataRetriever startlineAdvantageCalculationDataRetriever;
|
||||
private DashboardDispatchContext dashboardDispatchContext;
|
||||
private DomainFactory domainFactory;
|
||||
private DefaultPolarWindAngleBoatSpeedFunction defaultPolarSpeedWindAngleFunction;
|
||||
@@ -37,30 +39,30 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
private final Logger logger = Logger.getLogger(StartlineAdvantagesByWindCalculator.class.getName());
|
||||
|
||||
public StartlineAdvantagesByWindCalculator(DashboardDispatchContext dashboardDispatchContext, DomainFactory domainFactory) {
|
||||
super(domainFactory);
|
||||
this.dashboardDispatchContext = dashboardDispatchContext;
|
||||
this.domainFactory = domainFactory;
|
||||
this.defaultPolarSpeedWindAngleFunction = new DefaultPolarWindAngleBoatSpeedFunction();
|
||||
this.startlineAdvantageCalculationDataRetriever = new StartlineAdvantageCalculationDataRetriever(domainFactory, dashboardDispatchContext.getPolarDataService());
|
||||
}
|
||||
|
||||
public StartlineAdvantagesWithMaxAndAverageDTO getStartLineAdvantagesAccrossLineFromTrackedRaceAtTimePoint(
|
||||
TrackedRace trackedRace, TimePoint timepoint) {
|
||||
StartlineAdvantagesWithMaxAndAverageDTO result = new StartlineAdvantagesWithMaxAndAverageDTO();
|
||||
if (trackedRace != null) {
|
||||
retrieveDataForCalculation(trackedRace, dashboardDispatchContext.getPolarDataService());
|
||||
if (getWind() != null) {
|
||||
StartlineAdvantageCalculationData startlineAdvantageCalculationData = startlineAdvantageCalculationDataRetriever.retrieveDataForTrackedRace(trackedRace);
|
||||
if (startlineAdvantageCalculationData.getWind() != null) {
|
||||
Pair<Number[][], Number[][]> startlineAdvantagesAndConfidencesAsArray = null;
|
||||
if (isStartlineCompletelyUnderneathLaylines()) {
|
||||
if (isStartlineCompletelyUnderneathLaylines(startlineAdvantageCalculationData)) {
|
||||
logger.log(Level.INFO, "Startline is completely underneath laylines");
|
||||
Pair<Double, Double> advantagesRange = new Pair<Double, Double>(0.0, getStartlineLenghtInMeters());
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculateStartlineAdvantagesUnderneathLaylinesInRange(advantagesRange);
|
||||
Pair<Double, Double> advantagesRange = new Pair<Double, Double>(0.0, startlineAdvantageCalculationData.getStartlineLenghtInMeters());
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculateStartlineAdvantagesUnderneathLaylinesInRange(advantagesRange, startlineAdvantageCalculationData);
|
||||
double maximum = getMaximumAdvantageOfStartlineAdvantageDTOs(startlineAdvantages);
|
||||
result.maximum = maximum;
|
||||
startlineAdvantagesAndConfidencesAsArray = convertStartLineAdvantageDTOListToPointAndConfidenceArrays(startlineAdvantages);
|
||||
} else if (isStartlineCompletelyAboveLaylines()) {
|
||||
} else if (isStartlineCompletelyAboveLaylines(startlineAdvantageCalculationData)) {
|
||||
logger.log(Level.INFO, "Startline is completely above laylines");
|
||||
Pair<Double, Double> advantagesRange = new Pair<Double, Double>(0.0, getStartlineLenghtInMeters());
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculatePolarBasedStartlineAdvantagesInRange(advantagesRange);
|
||||
Pair<Double, Double> advantagesRange = new Pair<Double, Double>(0.0, startlineAdvantageCalculationData.getStartlineLenghtInMeters());
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculatePolarBasedStartlineAdvantagesInRange(advantagesRange, startlineAdvantageCalculationData);
|
||||
subtractMinimumOfAllStartlineAdvantages(startlineAdvantages);
|
||||
double maximum = getMaximumAdvantageOfStartlineAdvantageDTOs(startlineAdvantages);
|
||||
result.maximum = maximum;
|
||||
@@ -68,12 +70,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
startlineAdvantagesAndConfidencesAsArray = convertStartLineAdvantageDTOListToPointAndConfidenceArrays(startlineAdvantages);
|
||||
} else {
|
||||
logger.log(Level.INFO, "Layline(s) cross startline");
|
||||
Position intersectionOfRightLaylineAndStartline = getIntersectionOfRightLaylineAndStartline();
|
||||
Position intersectionOfleftLaylineAndStartline = getIntersectionOfLeftLaylineAndStartline();
|
||||
Position intersectionOfRightLaylineAndStartline = getIntersectionOfRightLaylineAndStartline(startlineAdvantageCalculationData);
|
||||
Position intersectionOfleftLaylineAndStartline = getIntersectionOfLeftLaylineAndStartline(startlineAdvantageCalculationData);
|
||||
Pair<Double, Double> polarBasedStartlineAdvatagesRange = getStartAndEndPointOfPolarBasedStartlineAdvatagesInDistancesToRCBoat(
|
||||
intersectionOfRightLaylineAndStartline, intersectionOfleftLaylineAndStartline);
|
||||
Pair<Double, Double> pinEndStartlineAdvatagesRange = getPinEndStartlineAdvantagesRangeFromPolarAdvantagesRange(polarBasedStartlineAdvatagesRange);
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculatePolarBasedStartlineAdvantagesInRange(polarBasedStartlineAdvatagesRange);
|
||||
intersectionOfRightLaylineAndStartline, intersectionOfleftLaylineAndStartline, startlineAdvantageCalculationData);
|
||||
Pair<Double, Double> pinEndStartlineAdvatagesRange = getPinEndStartlineAdvantagesRangeFromPolarAdvantagesRange(polarBasedStartlineAdvatagesRange, startlineAdvantageCalculationData);
|
||||
List<StartLineAdvantageDTO> startlineAdvantages = calculatePolarBasedStartlineAdvantagesInRange(polarBasedStartlineAdvatagesRange, startlineAdvantageCalculationData);
|
||||
subtractMinimumOfAllStartlineAdvantages(startlineAdvantages);
|
||||
double maximum = getMaximumAdvantageOfStartlineAdvantageDTOs(startlineAdvantages);
|
||||
result.maximum = maximum;
|
||||
@@ -92,12 +94,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isBearingAboveAdvantageLines(Bearing bearing) {
|
||||
private boolean isBearingAboveAdvantageLines(Bearing bearing, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
boolean result = false;
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
- getManeuverAngle() / 2);
|
||||
Bearing bearingOfLeftLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
+ getManeuverAngle() / 2);
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
- startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
Bearing bearingOfLeftLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
+ startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
if (bearing.getDegrees() < bearingOfRightLaylineInDeg.getDegrees() && bearing.getDegrees() > 0
|
||||
|| bearing.getDegrees() > bearingOfLeftLaylineInDeg.getDegrees() && bearing.getDegrees() < 360) {
|
||||
result = true;
|
||||
@@ -105,12 +107,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isBearingUnderneathAdvantageLines(Bearing bearing) {
|
||||
private boolean isBearingUnderneathAdvantageLines(Bearing bearing, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
boolean result = false;
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
- getManeuverAngle() / 2);
|
||||
Bearing bearingOfLeftLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
+ getManeuverAngle() / 2);
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
- startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
Bearing bearingOfLeftLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
+ startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
logger.log(Level.INFO, "Underneath bearingOfRightLaylineInDeg?" + bearingOfRightLaylineInDeg);
|
||||
logger.log(Level.INFO, "Underneath bearingOfLeftLaylineInDeg?" + bearingOfLeftLaylineInDeg);
|
||||
if (bearing.getDegrees() > bearingOfRightLaylineInDeg.getDegrees()
|
||||
@@ -120,83 +122,80 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isStartlineCompletelyAboveLaylines() {
|
||||
private boolean isStartlineCompletelyAboveLaylines(StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
boolean result = false;
|
||||
Bearing bearingRCBoatToFirstMark = new DegreeBearingImpl(getStartlineAndFirstMarkPositions().firstMarkPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().startBoatPosition).getDegrees());
|
||||
Bearing bearingPinEndToFirstMark = new DegreeBearingImpl(getStartlineAndFirstMarkPositions().firstMarkPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().pinEndPosition).getDegrees());
|
||||
if (isBearingAboveAdvantageLines(bearingRCBoatToFirstMark)
|
||||
&& isBearingAboveAdvantageLines(bearingPinEndToFirstMark)) {
|
||||
Bearing bearingRCBoatToFirstMark = new DegreeBearingImpl(startlineAdvantageCalculationData.getFirstMarkPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getStartBoatPosition()).getDegrees());
|
||||
Bearing bearingPinEndToFirstMark = new DegreeBearingImpl(startlineAdvantageCalculationData.getFirstMarkPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getPinEndPosition()).getDegrees());
|
||||
if (isBearingAboveAdvantageLines(bearingRCBoatToFirstMark, startlineAdvantageCalculationData)
|
||||
&& isBearingAboveAdvantageLines(bearingPinEndToFirstMark, startlineAdvantageCalculationData)) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isStartlineCompletelyUnderneathLaylines() {
|
||||
private boolean isStartlineCompletelyUnderneathLaylines(StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
boolean result = false;
|
||||
Bearing bearingFirstMarkToRCBoat = new DegreeBearingImpl(getStartlineAndFirstMarkPositions().firstMarkPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().startBoatPosition).getDegrees());
|
||||
Bearing bearingFirstMarkToPinEnd = new DegreeBearingImpl(getStartlineAndFirstMarkPositions().firstMarkPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().pinEndPosition).getDegrees());
|
||||
Bearing bearingFirstMarkToRCBoat = new DegreeBearingImpl(startlineAdvantageCalculationData.getFirstMarkPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getStartBoatPosition()).getDegrees());
|
||||
Bearing bearingFirstMarkToPinEnd = new DegreeBearingImpl(startlineAdvantageCalculationData.getFirstMarkPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getPinEndPosition()).getDegrees());
|
||||
logger.log(Level.INFO, "Underneath RC?" + bearingFirstMarkToRCBoat);
|
||||
logger.log(Level.INFO, "Underneath PIN?" + bearingFirstMarkToPinEnd);
|
||||
if (isBearingUnderneathAdvantageLines(bearingFirstMarkToRCBoat)
|
||||
&& isBearingUnderneathAdvantageLines(bearingFirstMarkToPinEnd)) {
|
||||
if (isBearingUnderneathAdvantageLines(bearingFirstMarkToRCBoat, startlineAdvantageCalculationData)
|
||||
&& isBearingUnderneathAdvantageLines(bearingFirstMarkToPinEnd, startlineAdvantageCalculationData)) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Pair<Double, Double> getStartAndEndPointOfPolarBasedStartlineAdvatagesInDistancesToRCBoat(
|
||||
Position rightIntersection, Position leftIntersection) {
|
||||
private Pair<Double, Double> getStartAndEndPointOfPolarBasedStartlineAdvatagesInDistancesToRCBoat(Position rightIntersection, Position leftIntersection, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
Pair<Double, Double> result = null;
|
||||
if (rightIntersection != null && leftIntersection == null) {
|
||||
double distanceFromIntersectionToRCBoatInMeters = rightIntersection.getDistance(
|
||||
getStartlineAndFirstMarkPositions().startBoatPosition).getMeters();
|
||||
double distanceFromIntersectionToRCBoatInMeters = rightIntersection.getDistance(startlineAdvantageCalculationData.getStartBoatPosition()).getMeters();
|
||||
result = new Pair<Double, Double>(0.0, distanceFromIntersectionToRCBoatInMeters);
|
||||
} else if (rightIntersection == null && leftIntersection != null) {
|
||||
double distanceFromIntersectionToRCBoatInMeters = leftIntersection.getDistance(
|
||||
getStartlineAndFirstMarkPositions().startBoatPosition).getMeters();
|
||||
result = new Pair<Double, Double>(distanceFromIntersectionToRCBoatInMeters, getStartlineLenghtInMeters());
|
||||
double distanceFromIntersectionToRCBoatInMeters = leftIntersection.getDistance(startlineAdvantageCalculationData.getStartBoatPosition()).getMeters();
|
||||
result = new Pair<Double, Double>(distanceFromIntersectionToRCBoatInMeters, startlineAdvantageCalculationData.getStartlineLenghtInMeters());
|
||||
} else if (rightIntersection != null && leftIntersection != null) {
|
||||
result = new Pair<Double, Double>(0.0, getStartlineLenghtInMeters());
|
||||
result = new Pair<Double, Double>(0.0, startlineAdvantageCalculationData.getStartlineLenghtInMeters());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position getIntersectionOfRightLaylineAndStartline() {
|
||||
private Position getIntersectionOfRightLaylineAndStartline(StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
Position result = null;
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
- getManeuverAngle() / 2);
|
||||
result = calculateIntersectionPointsOfStartlineAndLaylineWithBearing(bearingOfRightLaylineInDeg);
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
- startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
result = calculateIntersectionPointsOfStartlineAndLaylineWithBearing(bearingOfRightLaylineInDeg, startlineAdvantageCalculationData);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position getIntersectionOfLeftLaylineAndStartline() {
|
||||
private Position getIntersectionOfLeftLaylineAndStartline(StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
Position result = null;
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(getWind().getBearing().getDegrees()
|
||||
+ getManeuverAngle() / 2);
|
||||
result = calculateIntersectionPointsOfStartlineAndLaylineWithBearing(bearingOfRightLaylineInDeg);
|
||||
Bearing bearingOfRightLaylineInDeg = new DegreeBearingImpl(startlineAdvantageCalculationData.getWind().getBearing().getDegrees()
|
||||
+ startlineAdvantageCalculationData.getManeuverAngle() / 2);
|
||||
result = calculateIntersectionPointsOfStartlineAndLaylineWithBearing(bearingOfRightLaylineInDeg, startlineAdvantageCalculationData);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position calculateIntersectionPointsOfStartlineAndLaylineWithBearing(Bearing bearing) {
|
||||
private Position calculateIntersectionPointsOfStartlineAndLaylineWithBearing(Bearing bearing, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
Position result = null;
|
||||
Bearing bearingOfStartlineInRad = getStartlineAndFirstMarkPositions().startBoatPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().pinEndPosition);
|
||||
Bearing bearingOfStartlineInRad = startlineAdvantageCalculationData.getStartBoatPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getPinEndPosition());
|
||||
Bearing bearingOfStartlineInDeg = new DegreeBearingImpl(bearingOfStartlineInRad.getDegrees());
|
||||
logger.log(Level.INFO, "bearingOfStartlineInDeg " + bearingOfStartlineInDeg);
|
||||
logger.log(Level.INFO, "bearingOfLaylineInDeg " + bearing);
|
||||
Position intersectionPointLaylineStartline = getStartlineAndFirstMarkPositions().firstMarkPosition.getIntersection(
|
||||
bearing, getStartlineAndFirstMarkPositions().pinEndPosition, bearingOfStartlineInDeg);
|
||||
Position intersectionPointLaylineStartline = startlineAdvantageCalculationData.getFirstMarkPosition().getIntersection(
|
||||
bearing, startlineAdvantageCalculationData.getPinEndPosition(), bearingOfStartlineInDeg);
|
||||
logger.log(Level.INFO, "rightIntersectionPointLaylineStartline " + intersectionPointLaylineStartline);
|
||||
Bearing bearingIntersectionPointToFirstMark = new DegreeBearingImpl(
|
||||
getStartlineAndFirstMarkPositions().firstMarkPosition.getBearingGreatCircle(
|
||||
startlineAdvantageCalculationData.getFirstMarkPosition().getBearingGreatCircle(
|
||||
intersectionPointLaylineStartline).getDegrees());
|
||||
if (bearingIntersectionPointToFirstMark.getDegrees() < bearing.getDegrees() + 1
|
||||
&& bearingIntersectionPointToFirstMark.getDegrees() > bearing.getDegrees() - 1
|
||||
&& isOnStartline(intersectionPointLaylineStartline)) {
|
||||
&& isPositionOnStartline(intersectionPointLaylineStartline, startlineAdvantageCalculationData)) {
|
||||
result = intersectionPointLaylineStartline;
|
||||
logger.log(Level.INFO, "Layline crosses startline");
|
||||
}
|
||||
@@ -205,10 +204,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isOnStartline(Position position) {
|
||||
/**
|
||||
* On startline mean less than one meter away
|
||||
* */
|
||||
private boolean isPositionOnStartline(Position position, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
boolean result = false;
|
||||
Distance distanceToStartline = position.getDistanceToLine(getStartlineAndFirstMarkPositions().startBoatPosition,
|
||||
getStartlineAndFirstMarkPositions().pinEndPosition);
|
||||
Distance distanceToStartline = position.getDistanceToLine(startlineAdvantageCalculationData.getStartBoatPosition(), startlineAdvantageCalculationData.getPinEndPosition());
|
||||
if (distanceToStartline.getMeters() < 1) {
|
||||
result = true;
|
||||
}
|
||||
@@ -231,18 +232,17 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return advantages;
|
||||
}
|
||||
|
||||
private Pair<Double, Double> getPinEndStartlineAdvantagesRangeFromPolarAdvantagesRange(
|
||||
Pair<Double, Double> rangePolarBasedStartlineAdvatages) {
|
||||
private Pair<Double, Double> getPinEndStartlineAdvantagesRangeFromPolarAdvantagesRange(Pair<Double, Double> rangePolarBasedStartlineAdvatages, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
Pair<Double, Double> result = null;
|
||||
double pinEndStartlineAdvantagesStart;
|
||||
double pinEndStartlineAdvantagesEnd;
|
||||
if (rangePolarBasedStartlineAdvatages.getA().doubleValue() == 0.0
|
||||
&& rangePolarBasedStartlineAdvatages.getB().doubleValue() != getStartlineLenghtInMeters()) {
|
||||
&& rangePolarBasedStartlineAdvatages.getB().doubleValue() != startlineAdvantageCalculationData.getStartlineLenghtInMeters()) {
|
||||
pinEndStartlineAdvantagesStart = rangePolarBasedStartlineAdvatages.getB().doubleValue();
|
||||
pinEndStartlineAdvantagesEnd = getStartlineLenghtInMeters();
|
||||
pinEndStartlineAdvantagesEnd = startlineAdvantageCalculationData.getStartlineLenghtInMeters();
|
||||
result = new Pair<Double, Double>(pinEndStartlineAdvantagesStart, pinEndStartlineAdvantagesEnd);
|
||||
} else if (rangePolarBasedStartlineAdvatages.getA().doubleValue() != 0.0
|
||||
&& rangePolarBasedStartlineAdvatages.getB().doubleValue() == getStartlineLenghtInMeters()) {
|
||||
&& rangePolarBasedStartlineAdvatages.getB().doubleValue() == startlineAdvantageCalculationData.getStartlineLenghtInMeters()) {
|
||||
pinEndStartlineAdvantagesStart = 0.0;
|
||||
pinEndStartlineAdvantagesEnd = rangePolarBasedStartlineAdvatages.getB().doubleValue();
|
||||
result = new Pair<Double, Double>(pinEndStartlineAdvantagesStart, pinEndStartlineAdvantagesEnd);
|
||||
@@ -250,8 +250,7 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<StartLineAdvantageDTO> calculateStartlineAdvantagesUnderneathLaylinesInRange(
|
||||
Pair<Double, Double> rangePinEndStartlineAdvantage) {
|
||||
private List<StartLineAdvantageDTO> calculateStartlineAdvantagesUnderneathLaylinesInRange(Pair<Double, Double> rangePinEndStartlineAdvantage, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
List<StartLineAdvantageDTO> result = new ArrayList<StartLineAdvantageDTO>();
|
||||
if (rangePinEndStartlineAdvantage != null) {
|
||||
logger.log(Level.INFO, "PinEnd startline advantages range " + rangePinEndStartlineAdvantage.getA() + " - "
|
||||
@@ -262,12 +261,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
StartLineAdvantageDTO leftEdgeAdvantage = new StartLineAdvantageDTO();
|
||||
leftEdgeAdvantage.confidence = 1.0;
|
||||
leftEdgeAdvantage.distanceToRCBoatInMeters = rangePinEndStartlineAdvantage.getB();
|
||||
if (getStartlineAdvantageAtPinEndInMeters() >= 0) {
|
||||
leftEdgeAdvantage.startLineAdvantage = getStartlineAdvantageAtPinEndInMeters();
|
||||
if (startlineAdvantageCalculationData.getStartlineAdvantageAtPinEndInMeters() >= 0) {
|
||||
leftEdgeAdvantage.startLineAdvantage = startlineAdvantageCalculationData.getStartlineAdvantageAtPinEndInMeters();
|
||||
rightEdgeAdvantage.startLineAdvantage = 0.0;
|
||||
} else {
|
||||
leftEdgeAdvantage.startLineAdvantage = 0.0;
|
||||
rightEdgeAdvantage.startLineAdvantage = Math.abs(getStartlineAdvantageAtPinEndInMeters());
|
||||
rightEdgeAdvantage.startLineAdvantage = Math.abs(startlineAdvantageCalculationData.getStartlineAdvantageAtPinEndInMeters());
|
||||
}
|
||||
result.add(rightEdgeAdvantage);
|
||||
result.add(leftEdgeAdvantage);
|
||||
@@ -296,27 +295,26 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<StartLineAdvantageDTO> calculatePolarBasedStartlineAdvantagesInRange(
|
||||
Pair<Double, Double> rangePinEndStartlineAdvantage) {
|
||||
private List<StartLineAdvantageDTO> calculatePolarBasedStartlineAdvantagesInRange(Pair<Double, Double> rangePinEndStartlineAdvantage, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
List<StartLineAdvantageDTO> result = new ArrayList<StartLineAdvantageDTO>();
|
||||
if (rangePinEndStartlineAdvantage != null) {
|
||||
logger.log(Level.INFO, "PolarBased startline advantages range " + rangePinEndStartlineAdvantage.getA()
|
||||
+ " - " + rangePinEndStartlineAdvantage.getB());
|
||||
Bearing bearingOfStartlineInRad = getStartlineAndFirstMarkPositions().startBoatPosition
|
||||
.getBearingGreatCircle(getStartlineAndFirstMarkPositions().pinEndPosition);
|
||||
Bearing bearingOfStartlineInRad = startlineAdvantageCalculationData.getStartBoatPosition()
|
||||
.getBearingGreatCircle(startlineAdvantageCalculationData.getPinEndPosition());
|
||||
Bearing bearingOfStartlineInDeg = new DegreeBearingImpl(bearingOfStartlineInRad.getDegrees());
|
||||
for (double i = rangePinEndStartlineAdvantage.getA().doubleValue(); i < rangePinEndStartlineAdvantage
|
||||
.getB().doubleValue() - 1; i++) {
|
||||
StartLineAdvantageDTO startlineAdvantage = new StartLineAdvantageDTO();
|
||||
startlineAdvantage.confidence = 0.5;
|
||||
startlineAdvantage.distanceToRCBoatInMeters = i;
|
||||
Position startingPosition = getStartlineAndFirstMarkPositions().startBoatPosition.translateRhumb(bearingOfStartlineInDeg, new MeterDistance(i));
|
||||
Distance startingPositionToFirstMarkDistance = startingPosition.getDistance(getStartlineAndFirstMarkPositions().firstMarkPosition);
|
||||
Bearing bearingOfFirstMarkToStartPositionPositionInRad = getStartlineAndFirstMarkPositions().firstMarkPosition.getBearingGreatCircle(startingPosition);
|
||||
Position startingPosition = startlineAdvantageCalculationData.getStartBoatPosition().translateRhumb(bearingOfStartlineInDeg, new MeterDistance(i));
|
||||
Distance startingPositionToFirstMarkDistance = startingPosition.getDistance(startlineAdvantageCalculationData.getFirstMarkPosition());
|
||||
Bearing bearingOfFirstMarkToStartPositionPositionInRad = startlineAdvantageCalculationData.getFirstMarkPosition().getBearingGreatCircle(startingPosition);
|
||||
Bearing bearingOfFirstMarkToStartPositionPositionInDeg = new DegreeBearingImpl(bearingOfFirstMarkToStartPositionPositionInRad.getDegrees());
|
||||
DegreeBearingImpl angleToWind = new DegreeBearingImpl(Math.abs(getWind().getBearing().getDifferenceTo(bearingOfFirstMarkToStartPositionPositionInDeg).getDegrees()));
|
||||
DegreeBearingImpl angleToWind = new DegreeBearingImpl(Math.abs(startlineAdvantageCalculationData.getWind().getBearing().getDifferenceTo(bearingOfFirstMarkToStartPositionPositionInDeg).getDegrees()));
|
||||
logger.log(Level.INFO, "angleToWind" + angleToWind);
|
||||
SpeedWithConfidence<Void> speedWithConfidence = getBoatSpeedWithConfidenceForWindAngleAndStrength(angleToWind);
|
||||
SpeedWithConfidence<Void> speedWithConfidence = getBoatSpeedWithConfidenceForWindAngleAndStrength(angleToWind, startlineAdvantageCalculationData);
|
||||
Speed speed = speedWithConfidence.getObject();
|
||||
logger.log(Level.INFO, "Speed: " + speed.getKnots()+" Confidence: "+speedWithConfidence.getConfidence());
|
||||
logger.log(Level.INFO, "startingPositionToFirstMarkDistance.getMeters()" + startingPositionToFirstMarkDistance.getMeters());
|
||||
@@ -369,12 +367,12 @@ public class StartlineAdvantagesByWindCalculator extends AbstracPreCalculationDa
|
||||
return result;
|
||||
}
|
||||
|
||||
private SpeedWithConfidence<Void> getBoatSpeedWithConfidenceForWindAngleAndStrength(Bearing angleToWind) {
|
||||
private SpeedWithConfidence<Void> getBoatSpeedWithConfidenceForWindAngleAndStrength(Bearing angleToWind, StartlineAdvantageCalculationData startlineAdvantageCalculationData) {
|
||||
SpeedWithConfidence<Void> result = null;
|
||||
try {
|
||||
result = dashboardDispatchContext.getPolarDataService().getSpeed(domainFactory.getOrCreateBoatClass("Extreme 40"), new KnotSpeedImpl(getWind().getBeaufort()), angleToWind);
|
||||
result = dashboardDispatchContext.getPolarDataService().getSpeed(domainFactory.getOrCreateBoatClass("Extreme 40"), new KnotSpeedImpl(startlineAdvantageCalculationData.getWind().getBeaufort()), angleToWind);
|
||||
} catch (NotEnoughDataHasBeenAddedException e) {
|
||||
result = defaultPolarSpeedWindAngleFunction.getBoatSpeedForWindAngleAndSpeed(angleToWind, new KnotSpeedImpl(getWind().getBeaufort()));
|
||||
result = defaultPolarSpeedWindAngleFunction.getBoatSpeedForWindAngleAndSpeed(angleToWind, new KnotSpeedImpl(startlineAdvantageCalculationData.getWind().getBeaufort()));
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public abstract class AbstracPreCalculationDataRetriever implements PreCalculationMarkRetriever,
|
||||
PreCalculationStartlineAdvantageRetriever,
|
||||
PreCalculationWindRetriever,
|
||||
PreCalculationPolarDataRetriever {
|
||||
private StartlineAndFirstMarkPositions startlineAndFirstMarkPositions;
|
||||
private double startlineAdvantageAtPinEndInMeters;
|
||||
private double startlineLenghtInMeters;
|
||||
private Wind wind;
|
||||
private double maneuverAngle;
|
||||
|
||||
private final DomainFactory domainFactory;
|
||||
|
||||
public AbstracPreCalculationDataRetriever(DomainFactory domainFactory) {
|
||||
super();
|
||||
this.domainFactory = domainFactory;
|
||||
}
|
||||
|
||||
protected void retrieveDataForCalculation(TrackedRace trackedRace, PolarDataService polarDataService) {
|
||||
startlineAndFirstMarkPositions = retrieveMarkPositions(trackedRace);
|
||||
startlineAdvantageAtPinEndInMeters = retrieveStartlineAdvantage(trackedRace);
|
||||
startlineLenghtInMeters = retrieveStartlineLenght(trackedRace);
|
||||
wind = retrieveWindAtPosition(startlineAndFirstMarkPositions.startBoatPosition, trackedRace);
|
||||
maneuverAngle = retrieveManouvreAngleAtWindSpeedAndBoatClass(domainFactory.getOrCreateBoatClass("Extreme 40"), ManeuverType.TACK, wind, polarDataService);
|
||||
}
|
||||
|
||||
protected StartlineAndFirstMarkPositions getStartlineAndFirstMarkPositions() {
|
||||
return startlineAndFirstMarkPositions;
|
||||
}
|
||||
|
||||
protected double getStartlineAdvantageAtPinEndInMeters() {
|
||||
return startlineAdvantageAtPinEndInMeters;
|
||||
}
|
||||
|
||||
protected double getStartlineLenghtInMeters() {
|
||||
return startlineLenghtInMeters;
|
||||
}
|
||||
|
||||
protected Wind getWind() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
protected double getManeuverAngle() {
|
||||
return maneuverAngle;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public interface PreCalculationMarkRetriever {
|
||||
|
||||
default StartlineAndFirstMarkPositions retrieveMarkPositions(TrackedRace trackedRace) {
|
||||
StartlineAndFirstMarkPositions result = new StartlineAndFirstMarkPositions();
|
||||
Course course = trackedRace.getRace().getCourse();
|
||||
if (course != null) {
|
||||
Waypoint startlineWayPoint = course.getFirstLeg().getFrom();
|
||||
Waypoint firstmarkWayPoint = course.getFirstLeg().getTo();
|
||||
if (startlineWayPoint != null && firstmarkWayPoint != null) {
|
||||
Pair<Position, Position> startlineMarkPositions = retrieveStartlineMarkPositionsFromStartLineWayPoint(startlineWayPoint, trackedRace);
|
||||
Position firstMarkPosition = retrieveFirstMarkPositionFromFirstMarkWayPoint(firstmarkWayPoint, trackedRace);
|
||||
result.startBoatPosition = startlineMarkPositions.getA();
|
||||
result.pinEndPosition = startlineMarkPositions.getB();
|
||||
result.firstMarkPosition = firstMarkPosition;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
default Pair<Position, Position> retrieveStartlineMarkPositionsFromStartLineWayPoint(Waypoint startLineWayPoint, TrackedRace trackedRace) {
|
||||
Pair<Position, Position> result = null;
|
||||
Iterator<Mark> markIterator = startLineWayPoint.getMarks().iterator();
|
||||
if (markIterator.hasNext()) {
|
||||
Mark startboat = (Mark) markIterator.next();
|
||||
if (markIterator.hasNext()) {
|
||||
Mark pinEnd = (Mark) markIterator.next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
Position startBoatPosition = getPositionFromMarkAtTimePoint(trackedRace, startboat, now);
|
||||
Position pinEndPosition = getPositionFromMarkAtTimePoint(trackedRace, pinEnd, now);
|
||||
result = new Pair<Position, Position>(startBoatPosition, pinEndPosition);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
default Position retrieveFirstMarkPositionFromFirstMarkWayPoint(Waypoint firstMarkWayPoint, TrackedRace trackedRace) {
|
||||
Position result = null;
|
||||
if (firstMarkWayPoint.getMarks().iterator().hasNext()) {
|
||||
Mark firstMark = firstMarkWayPoint.getMarks().iterator().next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
result = getPositionFromMarkAtTimePoint(trackedRace, firstMark, now);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
default Position getPositionFromMarkAtTimePoint(TrackedRace trackedRace, Mark mark, TimePoint timePoint) {
|
||||
GPSFixTrack<Mark, GPSFix> fixTrack = trackedRace.getTrack(mark);
|
||||
return fixTrack.getEstimatedPosition(timePoint, true);
|
||||
}
|
||||
|
||||
class StartlineAndFirstMarkPositions {
|
||||
public Position startBoatPosition;
|
||||
public Position pinEndPosition;
|
||||
public Position firstMarkPosition;
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.DefaultPolarValues;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public interface PreCalculationPolarDataRetriever {
|
||||
|
||||
default double retrieveManouvreAngleAtWindSpeedAndBoatClass(BoatClass boatClass, ManeuverType maneuverType, Speed windSpeed, PolarDataService polarDataService) {
|
||||
double result = DefaultPolarValues.getManouvreAngle(ManeuverType.TACK);
|
||||
try {
|
||||
if (boatClass != null && maneuverType != null && windSpeed != null) {
|
||||
BearingWithConfidence<Void> bearingWithConfidence = polarDataService.getManeuverAngle(boatClass, maneuverType, windSpeed);
|
||||
result = bearingWithConfidence.getObject().getDegrees();
|
||||
}
|
||||
} catch (NotEnoughDataHasBeenAddedException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public interface PreCalculationStartlineAdvantageRetriever {
|
||||
|
||||
default double retrieveStartlineAdvantage(TrackedRace trackedRace) {
|
||||
double result = 0;
|
||||
LineDetails startline = trackedRace.getStartLine(MillisecondsTimePoint.now());
|
||||
if (startline != null && startline.getAdvantage() != null) {
|
||||
result = startline.getAdvantage().getMeters();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
default double retrieveStartlineLenght(TrackedRace trackedRace) {
|
||||
double result = 0;
|
||||
LineDetails startline = trackedRace.getStartLine(MillisecondsTimePoint.now());
|
||||
result = startline.getLength().getMeters();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public interface PreCalculationWindRetriever {
|
||||
|
||||
default Wind retrieveWindAtPosition(Position position, TrackedRace trackedRace) {
|
||||
Wind result = null;
|
||||
result = trackedRace.getWind(position, MillisecondsTimePoint.now());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
|
||||
/**
|
||||
* In order to calculate the startline adavantage by wind, the data objects defined in this class
|
||||
* need to be defined.
|
||||
*
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public class StartlineAdvantageCalculationData {
|
||||
private Position startBoatPosition;
|
||||
private Position pinEndPosition;
|
||||
private Position firstMarkPosition;
|
||||
private Double startlineAdvantageAtPinEndInMeters;
|
||||
private Double startlineLenghtInMeters;
|
||||
private Wind wind;
|
||||
private Double maneuverAngle;
|
||||
|
||||
public StartlineAdvantageCalculationData(Position startBoatPosition, Position pinEndPosition,
|
||||
Position firstMarkPosition, Double startlineAdvantageAtPinEndInMeters, Double startlineLenghtInMeters,
|
||||
Wind wind, Double maneuverAngle) {
|
||||
this.startBoatPosition = startBoatPosition;
|
||||
this.pinEndPosition = pinEndPosition;
|
||||
this.firstMarkPosition = firstMarkPosition;
|
||||
this.startlineAdvantageAtPinEndInMeters = startlineAdvantageAtPinEndInMeters;
|
||||
this.startlineLenghtInMeters = startlineLenghtInMeters;
|
||||
this.wind = wind;
|
||||
this.maneuverAngle = maneuverAngle;
|
||||
}
|
||||
|
||||
public Position getStartBoatPosition() {
|
||||
return startBoatPosition;
|
||||
}
|
||||
|
||||
public Position getPinEndPosition() {
|
||||
return pinEndPosition;
|
||||
}
|
||||
|
||||
public Position getFirstMarkPosition() {
|
||||
return firstMarkPosition;
|
||||
}
|
||||
|
||||
public Double getStartlineAdvantageAtPinEndInMeters() {
|
||||
return startlineAdvantageAtPinEndInMeters;
|
||||
}
|
||||
|
||||
public Double getStartlineLenghtInMeters() {
|
||||
return startlineLenghtInMeters;
|
||||
}
|
||||
|
||||
public Wind getWind() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
public Double getManeuverAngle() {
|
||||
return maneuverAngle;
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.precalculation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.sap.sailing.dashboards.gwt.server.util.actions.startlineadvantage.DefaultPolarValues;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
* @author Alexander Ries (D062114)
|
||||
*
|
||||
*/
|
||||
public class StartlineAdvantageCalculationDataRetriever {
|
||||
|
||||
private PolarDataService polarDataService;
|
||||
private final DomainFactory domainFactory;
|
||||
|
||||
public StartlineAdvantageCalculationDataRetriever(DomainFactory domainFactory, PolarDataService polarDataService) {
|
||||
super();
|
||||
this.polarDataService = polarDataService;
|
||||
this.domainFactory = domainFactory;
|
||||
}
|
||||
|
||||
public StartlineAdvantageCalculationData retrieveDataForTrackedRace(TrackedRace trackedRace) {
|
||||
StartlineAdvantageCalculationData result = null;
|
||||
Position startBoatPosition = null;
|
||||
Position pinEndPosition = null;
|
||||
Position firstMarkPosition = null;
|
||||
Double startlineAdvantageAtPinEndInMeters = null;
|
||||
Double startlineLenghtInMeters = null;
|
||||
Wind wind = null;
|
||||
Double maneuverAngle = null;
|
||||
Pair<Position/* StartBoatPosition */, Position/* PinEndPosition */> startlineMarkPositions = retrieveStartlineMarksPositions(trackedRace);
|
||||
startBoatPosition = startlineMarkPositions.getA();
|
||||
pinEndPosition = startlineMarkPositions.getB();
|
||||
firstMarkPosition = retrieveFirstMarkPosition(trackedRace);
|
||||
startlineAdvantageAtPinEndInMeters = retrieveStartlineAdvantage(trackedRace);
|
||||
startlineLenghtInMeters = retrieveStartlineLenght(trackedRace);
|
||||
wind = retrieveWindAtPosition(startBoatPosition, trackedRace);
|
||||
maneuverAngle = retrieveManouvreAngleAtWindSpeedAndBoatClass(domainFactory.getOrCreateBoatClass("Extreme 40"), ManeuverType.TACK, wind);
|
||||
result = new StartlineAdvantageCalculationData(startBoatPosition, pinEndPosition, firstMarkPosition, startlineAdvantageAtPinEndInMeters, startlineLenghtInMeters, wind, maneuverAngle);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position retrieveFirstMarkPosition(TrackedRace trackedRace) {
|
||||
Position result = null;
|
||||
Course course = trackedRace.getRace().getCourse();
|
||||
if (course != null) {
|
||||
Waypoint firstmarkWayPoint = course.getFirstLeg().getTo();
|
||||
if (firstmarkWayPoint != null) {
|
||||
result = retrieveFirstMarkPositionFromFirstMarkWayPoint(firstmarkWayPoint, trackedRace);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Pair<Position/* StartBoatPosition */, Position/* PinEndPosition */> retrieveStartlineMarksPositions(TrackedRace trackedRace) {
|
||||
Pair<Position, Position> result = new Pair<Position, Position>(null, null);
|
||||
Course course = trackedRace.getRace().getCourse();
|
||||
if (course != null) {
|
||||
Waypoint startlineWayPoint = course.getFirstLeg().getFrom();
|
||||
Waypoint firstmarkWayPoint = course.getFirstLeg().getTo();
|
||||
if (startlineWayPoint != null && firstmarkWayPoint != null) {
|
||||
result = retrieveStartlineMarkPositionsFromStartLineWayPoint(startlineWayPoint, trackedRace);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Pair<Position/*StartBoatPosition*/, Position/*PinEndPosition*/> retrieveStartlineMarkPositionsFromStartLineWayPoint(Waypoint startLineWayPoint, TrackedRace trackedRace) {
|
||||
Pair<Position, Position> result = null;
|
||||
Iterator<Mark> markIterator = startLineWayPoint.getMarks().iterator();
|
||||
if (markIterator.hasNext()) {
|
||||
Mark startboat = (Mark) markIterator.next();
|
||||
if (markIterator.hasNext()) {
|
||||
Mark pinEnd = (Mark) markIterator.next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
Position startBoatPosition = getPositionFromMarkAtTimePoint(trackedRace, startboat, now);
|
||||
Position pinEndPosition = getPositionFromMarkAtTimePoint(trackedRace, pinEnd, now);
|
||||
result = new Pair<Position, Position>(startBoatPosition, pinEndPosition);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position retrieveFirstMarkPositionFromFirstMarkWayPoint(Waypoint firstMarkWayPoint, TrackedRace trackedRace) {
|
||||
Position result = null;
|
||||
if (firstMarkWayPoint.getMarks().iterator().hasNext()) {
|
||||
Mark firstMark = firstMarkWayPoint.getMarks().iterator().next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
result = getPositionFromMarkAtTimePoint(trackedRace, firstMark, now);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Position getPositionFromMarkAtTimePoint(TrackedRace trackedRace, Mark mark, TimePoint timePoint) {
|
||||
GPSFixTrack<Mark, GPSFix> fixTrack = trackedRace.getTrack(mark);
|
||||
return fixTrack.getEstimatedPosition(timePoint, true);
|
||||
}
|
||||
|
||||
private Double retrieveStartlineAdvantage(TrackedRace trackedRace) {
|
||||
Double result = null;
|
||||
LineDetails startline = trackedRace.getStartLine(MillisecondsTimePoint.now());
|
||||
if (startline != null && startline.getAdvantage() != null) {
|
||||
result = new Double(startline.getAdvantage().getMeters());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Double retrieveStartlineLenght(TrackedRace trackedRace) {
|
||||
Double result = null;
|
||||
LineDetails startline = trackedRace.getStartLine(MillisecondsTimePoint.now());
|
||||
result = new Double(startline.getLength().getMeters());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Wind retrieveWindAtPosition(Position position, TrackedRace trackedRace) {
|
||||
Wind result = null;
|
||||
result = trackedRace.getWind(position, MillisecondsTimePoint.now());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Double retrieveManouvreAngleAtWindSpeedAndBoatClass(BoatClass boatClass, ManeuverType maneuverType, Speed windSpeed) {
|
||||
Double result = DefaultPolarValues.getManouvreAngle(ManeuverType.TACK);
|
||||
try {
|
||||
if (boatClass != null && maneuverType != null && windSpeed != null) {
|
||||
BearingWithConfidence<Void> bearingWithConfidence = polarDataService.getManeuverAngle(boatClass, maneuverType, windSpeed);
|
||||
result = new Double(bearingWithConfidence.getObject().getDegrees());
|
||||
}
|
||||
} catch (NotEnoughDataHasBeenAddedException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -47,9 +47,8 @@ public class DashboardLiveRaceProvider {
|
||||
|
||||
private void setLiveRaceForLeaderboardName(String leaderboardName, TrackedRace liveRace) {
|
||||
if (liveRaceForLeaderboardNames.containsKey(leaderboardName)) {
|
||||
liveRaceForLeaderboardNames.replace(leaderboardName, liveRace);
|
||||
} else {
|
||||
liveRaceForLeaderboardNames.put(leaderboardName, liveRace);
|
||||
liveRaceForLeaderboardNames.remove(leaderboardName);
|
||||
}
|
||||
liveRaceForLeaderboardNames.put(leaderboardName, liveRace);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -29,7 +29,8 @@ public class MovingAveragesCache {
|
||||
if (averagesForStringKeys.containsKey(key)) {
|
||||
MovingAverage movingAverage = averagesForStringKeys.get(key);
|
||||
movingAverage.add(value);
|
||||
averagesForStringKeys.replace(key, movingAverage);
|
||||
averagesForStringKeys.remove(key);
|
||||
averagesForStringKeys.put(key, movingAverage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -95,7 +95,7 @@ public class SailingPredefinedQueries {
|
||||
|
||||
ModifiableStatisticQueryDefinitionDTO queryDefinition = new ModifiableStatisticQueryDefinitionDTO("default", statistic, aggregator, retrieverChain);
|
||||
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getTrackedLegOfCompetitorContext().getCompetitor().getTeam().getName()", HasGPSFixContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getTrackedLegOfCompetitorContext().getCompetitor().getName()", HasGPSFixContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
queryDefinition.appendDimensionToGroupBy(dimensionToGroupBy0);
|
||||
|
||||
FunctionDTO dimensionToGroupBy1 = new FunctionDTO(true, "getTrackedLegOfCompetitorContext().getTrackedLegContext().getLegType()", HasGPSFixContext.class.getName(), LegType.class.getName(), new ArrayList<String>(), "", 0);
|
||||
@@ -118,7 +118,7 @@ public class SailingPredefinedQueries {
|
||||
|
||||
ModifiableStatisticQueryDefinitionDTO queryDefinition = new ModifiableStatisticQueryDefinitionDTO("default", statistic, aggregator, retrieverChain);
|
||||
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getTeam().getName()", HasTrackedLegOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getName()", HasTrackedLegOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
queryDefinition.appendDimensionToGroupBy(dimensionToGroupBy0);
|
||||
|
||||
FunctionDTO dimensionToGroupBy1 = new FunctionDTO(true, "getTrackedLegContext().getLegType()", HasTrackedLegOfCompetitorContext.class.getName(), LegType.class.getName(), new ArrayList<String>(), "", 0);
|
||||
@@ -142,7 +142,7 @@ public class SailingPredefinedQueries {
|
||||
|
||||
ModifiableStatisticQueryDefinitionDTO queryDefinition = new ModifiableStatisticQueryDefinitionDTO("default", statistic, aggregator, retrieverChain);
|
||||
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getTrackedLegOfCompetitorContext().getCompetitor().getTeam().getName()", HasGPSFixContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getTrackedLegOfCompetitorContext().getCompetitor().getName()", HasGPSFixContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
queryDefinition.appendDimensionToGroupBy(dimensionToGroupBy0);
|
||||
|
||||
return queryDefinition;
|
||||
@@ -162,7 +162,7 @@ public class SailingPredefinedQueries {
|
||||
|
||||
ModifiableStatisticQueryDefinitionDTO queryDefinition = new ModifiableStatisticQueryDefinitionDTO("default", statistic, aggregator, retrieverChain);
|
||||
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getTeam().getName()", HasTrackedLegOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getName()", HasTrackedLegOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
queryDefinition.appendDimensionToGroupBy(dimensionToGroupBy0);
|
||||
|
||||
return queryDefinition;
|
||||
@@ -181,7 +181,7 @@ public class SailingPredefinedQueries {
|
||||
|
||||
ModifiableStatisticQueryDefinitionDTO queryDefinition = new ModifiableStatisticQueryDefinitionDTO("default", statistic, aggregator, retrieverChain);
|
||||
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getTeam().getName()", HasRaceOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
FunctionDTO dimensionToGroupBy0 = new FunctionDTO(true, "getCompetitor().getName()", HasRaceOfCompetitorContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
|
||||
queryDefinition.appendDimensionToGroupBy(dimensionToGroupBy0);
|
||||
|
||||
return queryDefinition;
|
||||
|
||||
+9
-7
@@ -32,14 +32,16 @@ public class DeviceConfigurationQRCodeUtils {
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
public static String composeQRContent(String deviceIdentifier, String apkUrl, String accessToken) {
|
||||
// poor man's uri fragment encoding: ' ' as '%20'
|
||||
String encodedIdentifier = deviceIdentifier.replaceAll(" ", "%20");
|
||||
return apkUrl + "#" + deviceIdentifierKey + "=" + encodedIdentifier+"&"+accessTokenKey + "=" + accessToken;
|
||||
|
||||
public static interface URLDecoder {
|
||||
String decode(String encodedURL);
|
||||
}
|
||||
|
||||
public static DeviceConfigurationDetails splitQRContent(String qrCodeContent) {
|
||||
public static String composeQRContent(String urlEncodedDeviceIdentifier, String apkUrl, String accessToken) {
|
||||
return apkUrl + "#" + deviceIdentifierKey + "=" + urlEncodedDeviceIdentifier + "&"+accessTokenKey + "=" + accessToken;
|
||||
}
|
||||
|
||||
public static DeviceConfigurationDetails splitQRContent(String qrCodeContent, URLDecoder urlDecoder) {
|
||||
int fragmentIndex = qrCodeContent.lastIndexOf('#');
|
||||
if (fragmentIndex == -1 || fragmentIndex == 0 || qrCodeContent.length() == fragmentIndex + 1) {
|
||||
throw new IllegalArgumentException("There is no server or identifier.");
|
||||
@@ -50,7 +52,7 @@ public class DeviceConfigurationQRCodeUtils {
|
||||
for (String param : params) {
|
||||
final String[] keyValue = param.split("=", 2);
|
||||
if (keyValue.length == 2) {
|
||||
paramMap.put(keyValue[0], keyValue[1].replaceAll("%20", " "));
|
||||
paramMap.put(keyValue[0], urlDecoder.decode(keyValue[1]));
|
||||
}
|
||||
}
|
||||
if (!paramMap.containsKey(deviceIdentifierKey)) {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import com.sap.sse.common.util.UrlHelper;
|
||||
* {@link DeviceMappingOnRaceQRCodeWidget}).
|
||||
* <p>
|
||||
* The structure of the URL is documented in the <a
|
||||
* href="http://wiki.sapsailing.com/wiki/tracking-app-api-v1-draft">Wiki</a>.
|
||||
* href="http://wiki.sapsailing.com/wiki/info/api/api-v1">Wiki</a>.
|
||||
* {@code http://<host>/tracking/checkin?event_id=<e>&leaderboard_name=<l>&competitor_id=<c>}
|
||||
*
|
||||
* @author Fredrik Teschke
|
||||
|
||||
+2
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.common.tracking;
|
||||
|
||||
/**
|
||||
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
|
||||
*
|
||||
* <p><b>NOTE:</b> This fix type is currently used to track ESS foiling data, only.</p>
|
||||
*/
|
||||
public interface BravoFix extends SensorFix {
|
||||
|
||||
|
||||
+1
-1
@@ -344,7 +344,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
logger.warning(String.format(
|
||||
"Setting the unique index on the %s collection failed because you have too many duplicates. "
|
||||
+ "This leads to the mongo error code %s and the following message: %s \nTo fix this follow "
|
||||
+ "the steps provided on the wiki page: http://wiki.sapsailing.com/wiki/cook-book#Remove-"
|
||||
+ "the steps provided on the wiki page: http://wiki.sapsailing.com/wiki/howto/misc/cook-book#Remove-"
|
||||
+ "duplicates-from-WIND_TRACK-collection", CollectionNames.WIND_TRACKS.name(),
|
||||
exception.getCode(), exception.getMessage()));
|
||||
} else {
|
||||
|
||||
+38
-14
@@ -37,6 +37,8 @@ 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;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* At the moment, the timerange covered by the fixes for a device, and the number of fixes for a device are stored in a
|
||||
@@ -52,6 +54,10 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
private final DBCollection fixesCollection;
|
||||
private final DBCollection metadataCollection;
|
||||
private final MongoObjectFactoryImpl mongoOF;
|
||||
/**
|
||||
* Lock object to be used when accessing {@link #listeners}.
|
||||
*/
|
||||
private final NamedReentrantReadWriteLock listenersLock = new NamedReentrantReadWriteLock("Listeners collection lock of " + MongoSensorFixStoreImpl.class.getName(), false);
|
||||
private final Map<DeviceIdentifier, Set<FixReceivedListener<? extends Timed>>> listeners = new HashMap<>();
|
||||
|
||||
public MongoSensorFixStoreImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory,
|
||||
@@ -168,9 +174,7 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
logger.log(Level.WARNING, "Could not store fix in MongoDB");
|
||||
e.printStackTrace();
|
||||
}
|
||||
for (FixT fix : fixes) {
|
||||
notifyListeners(device, fix);
|
||||
}
|
||||
notifyListeners(device, fixes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,23 +183,30 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
private <FixT extends Timed> void notifyListeners(DeviceIdentifier device, Iterable<FixT> fixes) {
|
||||
LockUtil.executeWithReadLock(listenersLock, () -> {
|
||||
for (FixT fix : fixes) {
|
||||
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);
|
||||
public void addListener(FixReceivedListener<? extends Timed> listener, DeviceIdentifier device) {
|
||||
LockUtil.executeWithWriteLock(listenersLock, () -> 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);
|
||||
}
|
||||
public void removeListener(FixReceivedListener<? extends Timed> listener) {
|
||||
LockUtil.executeWithWriteLock(listenersLock, () -> Util.removeFromAllValueSets(listeners, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(FixReceivedListener<? extends Timed> listener, DeviceIdentifier device) {
|
||||
LockUtil.executeWithWriteLock(listenersLock, () -> Util.removeFromValueSet(listeners, device, listener));
|
||||
}
|
||||
|
||||
private DBObject getDeviceQuery(DeviceIdentifier device)
|
||||
@@ -232,6 +243,19 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
return ((Number) result.get(FieldNames.NUM_FIXES.name())).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a service implementing specified type of objects interface using the {@link #fixServiceFinder}.
|
||||
* <p>
|
||||
* To support an additional fix type, an implementation of {@link FixMongoHandler} is required, which specifies how to
|
||||
* transform a fix forth to and back from database. This implementation needs to be
|
||||
* registered during OSGi bundle activator startup, providing respective type mapping properties.
|
||||
* </p>
|
||||
*
|
||||
* @param type type object to find service for
|
||||
* @return the registered {@link FixMongoHandler} implementation
|
||||
*
|
||||
* @see TypeBasedServiceFinder#findService(String)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <FixT extends Timed> FixMongoHandler<FixT> findService(String type) {
|
||||
return (FixMongoHandler<FixT>) fixServiceFinder.findService(type);
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.sap.sailing.domain.racelogtracking.test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDefineMarkEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class RevokeDeviceMappingsWithMarkDefinitionTest {
|
||||
private RegattaLog regattaLog;
|
||||
private AbstractLogEventAuthor author;
|
||||
private Mark mark;
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
regattaLog = new RegattaLogImpl("RevokeTest");
|
||||
author = new LogEventAuthorImpl("RevokeTest", 1);
|
||||
mark = new MarkImpl("mark");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevoking() {
|
||||
final PingDeviceIdentifierImpl device = new PingDeviceIdentifierImpl();
|
||||
|
||||
final RegattaLogDefineMarkEvent defineMarkEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(), author,
|
||||
MillisecondsTimePoint.now(), UUID.randomUUID(), mark);
|
||||
|
||||
regattaLog.add(defineMarkEvent);
|
||||
regattaLog.add(new RegattaLogDeviceMarkMappingEventImpl(MillisecondsTimePoint.now(), author, mark, device,
|
||||
MillisecondsTimePoint.now(), MillisecondsTimePoint.now()));
|
||||
|
||||
regattaLog.revokeDefineMarkEventAndRelatedDeviceMappings(defineMarkEvent, author, null /* the logger should not be needed */);
|
||||
|
||||
for (RegattaLogEvent event : regattaLog.getUnrevokedEvents()) {
|
||||
if (event instanceof RegattaLogDefineMarkEventImpl) {
|
||||
Assert.fail("Define mark not revoked.");
|
||||
}
|
||||
if (event instanceof RegattaLogDeviceMarkMappingEventImpl) {
|
||||
Assert.fail("Device mapping not revoked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevokingWithoutDeviceMapping() {
|
||||
final RegattaLogDefineMarkEvent defineMarkEvent = new RegattaLogDefineMarkEventImpl(MillisecondsTimePoint.now(), author,
|
||||
MillisecondsTimePoint.now(), UUID.randomUUID(), mark);
|
||||
|
||||
regattaLog.add(defineMarkEvent);
|
||||
|
||||
regattaLog.revokeDefineMarkEventAndRelatedDeviceMappings(defineMarkEvent, author, null /* the logger should not be needed */);
|
||||
|
||||
for (RegattaLogEvent event : regattaLog.getUnrevokedEvents()) {
|
||||
if (event instanceof RegattaLogDefineMarkEventImpl) {
|
||||
Assert.fail("Define mark not revoked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.sap.sailing.domain.racelogtracking.test.impl;
|
||||
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.Timeout;
|
||||
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
|
||||
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
|
||||
|
||||
public class GPSFixStoreListenerTest extends AbstractGPSFixStoreTest {
|
||||
@Rule
|
||||
public Timeout GPSFixStoreListenerTestTimeout = new Timeout(3 * 1000);
|
||||
|
||||
/**
|
||||
* {@link MongoSensorFixStoreImpl} had broken synchronization of the listeners collection (add/removeListener
|
||||
* methods were synchronized but notifyListeners was not synchronized).
|
||||
*/
|
||||
@Test(expected = TimoutRuntimeException.class)
|
||||
public void lockingOfGPSFixStoreListenersIsWorkingCorrectly() throws InterruptedException {
|
||||
CyclicBarrier barrier = new CyclicBarrier(2);
|
||||
// We need 3 listener instances to guarantee that the iterator isn't finished
|
||||
// when adding another listener in the thread below.
|
||||
store.addListener(new ListenerAwaitingBarier(barrier), device);
|
||||
store.addListener(new ListenerAwaitingBarier(barrier), device);
|
||||
store.addListener(new ListenerAwaitingBarier(barrier), device);
|
||||
|
||||
Thread thread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
barrier.await(100, TimeUnit.MILLISECONDS);
|
||||
// During iteration in the main thread this causes a modification that makes the iterator throw a
|
||||
// ConcurrentModificationException on next()
|
||||
store.addListener((DeviceIdentifier device, GPSFixMoving fix) -> {}, device);
|
||||
barrier.await(100, TimeUnit.MILLISECONDS);
|
||||
barrier.await(100, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
};
|
||||
thread.start();
|
||||
try {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
} finally {
|
||||
// This ensures that the thread is terminated when the test finishes
|
||||
// JUnit may behave crazy if there are additional tests running after the test finished
|
||||
thread.join(500);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ListenerAwaitingBarier implements FixReceivedListener<GPSFixMoving> {
|
||||
|
||||
private final CyclicBarrier barrier;
|
||||
|
||||
public ListenerAwaitingBarier(CyclicBarrier barrier) {
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixReceived(DeviceIdentifier device, GPSFixMoving fix) {
|
||||
try {
|
||||
barrier.await(100, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
throw new TimoutRuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimoutRuntimeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 6349762933223278846L;
|
||||
|
||||
public TimoutRuntimeException(TimeoutException e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -3,12 +3,12 @@ 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.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
public interface SensorFixMapperFactory {
|
||||
|
||||
<FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Competitor>
|
||||
<FixT extends Timed, TrackT extends DynamicTrack<FixT>> SensorFixMapper<FixT, TrackT, Competitor>
|
||||
createCompetitorMapper(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType);
|
||||
|
||||
// <FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Mark>
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMapping
|
||||
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.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
public class SensorFixMapperFactoryImpl implements SensorFixMapperFactory {
|
||||
@@ -20,7 +20,7 @@ public class SensorFixMapperFactoryImpl implements SensorFixMapperFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <FixT extends Timed, TrackT extends Track<?>> SensorFixMapper<FixT, TrackT, Competitor>
|
||||
public <FixT extends Timed, TrackT extends DynamicTrack<FixT>> SensorFixMapper<FixT, TrackT, Competitor>
|
||||
createCompetitorMapper(Class<? extends RegattaLogDeviceMappingEvent<?>> eventType) {
|
||||
for (ServiceReference<SensorFixMapper<?, ?, ?>> serviceReference : tracker.getServiceReferences()) {
|
||||
SensorFixMapper<?, ?, ?> service = tracker.getService(serviceReference);
|
||||
|
||||
+5
-6
@@ -13,20 +13,19 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
|
||||
|
||||
/**
|
||||
* {@link SensorFixMapper} to handle {@link BravoFix}es.
|
||||
*
|
||||
* {@link SensorFixMapper} implementation to handle {@link BravoFix}es.
|
||||
*/
|
||||
public class BravoDataFixMapper implements SensorFixMapper<DoubleVectorFix, DynamicSensorFixTrack<Competitor, BravoFix>, Competitor> {
|
||||
public class BravoDataFixMapper implements SensorFixMapper<BravoFix, 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));
|
||||
public BravoFix map(DoubleVectorFix fix) {
|
||||
return new BravoFixImpl(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-6
@@ -287,10 +287,10 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
for (Competitor competitor : competitors) {
|
||||
final String toAddress = competitor.getEmail();
|
||||
if (toAddress != null) {
|
||||
String leaderboardName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||
String leaderboardDisplayName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||
String competitorName = competitor.getName();
|
||||
String url = DeviceMappingConstants.getDeviceMappingForRegattaLogUrl(serverUrlWithoutTrailingSlash,
|
||||
event.getId().toString(), leaderboardName, DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING,
|
||||
event.getId().toString(), leaderboard.getName(), DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING,
|
||||
competitor.getId().toString(), NonGwtUrlHelper.INSTANCE);
|
||||
String logoUrl = null;
|
||||
final List<ImageDescriptor> imagesWithTag = event.findImagesWithTag(MediaTagConstants.LOGO);
|
||||
@@ -299,7 +299,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
}
|
||||
try {
|
||||
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
||||
sendInvitationEmail(locale, toAddress, leaderboardName, event.getName(), competitorName, url,
|
||||
sendInvitationEmail(locale, toAddress, leaderboardDisplayName, event.getName(), competitorName, url,
|
||||
B.get(locale, "sailInSightAppName"), iOSAppUrl, androidAppUrl, logoUrl);
|
||||
} catch (MailException e) {
|
||||
occuredExceptions.append(e.getMessage() + "\r\n");
|
||||
@@ -392,7 +392,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
String emails, String iOSAppUrl, String androidAppUrl, Locale locale) throws MailException {
|
||||
StringBuilder occuredExceptions = new StringBuilder();
|
||||
String[] emailArray = emails.split(",");
|
||||
String leaderboardName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||
String leaderboardDisplayName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||
String eventId = event.getId().toString();
|
||||
String logoUrl = null;
|
||||
List<ImageDescriptor> imagesWithTag = event.findImagesWithTag(MediaTagConstants.LOGO);
|
||||
@@ -400,12 +400,12 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
logoUrl = imagesWithTag.get(0).getURL().toString();
|
||||
}
|
||||
// http://<host>/buoy-tender/checkin?event_id=<event-id>&leaderboard_name=<leaderboard-name>
|
||||
String url = DeviceMappingConstants.getBuoyTenderInvitationUrl(serverUrlWithoutTrailingSlash, leaderboardName,
|
||||
String url = DeviceMappingConstants.getBuoyTenderInvitationUrl(serverUrlWithoutTrailingSlash, leaderboard.getName(),
|
||||
eventId, NonGwtUrlHelper.INSTANCE);
|
||||
for (String toAddress : emailArray) {
|
||||
try {
|
||||
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
||||
sendInvitationEmail(locale, toAddress, leaderboardName,
|
||||
sendInvitationEmail(locale, toAddress, leaderboardDisplayName,
|
||||
event.getName(), RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "buoyTender"), url, B.get(locale, "buoyPingerAppName"),
|
||||
iOSAppUrl, androidAppUrl, logoUrl);
|
||||
} catch (MailException e) {
|
||||
|
||||
+49
-36
@@ -30,9 +30,9 @@ 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.DynamicTrack;
|
||||
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;
|
||||
@@ -43,8 +43,6 @@ 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
|
||||
@@ -60,7 +58,6 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
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;
|
||||
@@ -102,7 +99,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
SensorFixMapper<DoubleVectorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
|
||||
SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
|
||||
.createCompetitorMapper(event.getClass());
|
||||
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
|
||||
event.getMappedTo());
|
||||
@@ -158,8 +155,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
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();
|
||||
}
|
||||
@@ -171,28 +166,38 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
SensorFixMapper<Timed, Track<?>, Competitor> mapper = sensorFixMapperFactory
|
||||
SensorFixMapper<Timed, DynamicTrack<Timed>, 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());
|
||||
DynamicTrack<Timed> track = mapper.getTrack(trackedRace, event.getMappedTo());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
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());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +249,8 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
|
||||
protected void startTracking() {
|
||||
trackedRace.addListener(raceChangeListener);
|
||||
this.deviceMappings = new FixLoaderDeviceMappings(trackedRace.getAttachedRegattaLogs());
|
||||
this.deviceMappings = new FixLoaderDeviceMappings(trackedRace.getAttachedRegattaLogs(),
|
||||
trackedRace.getRace().getName());
|
||||
}
|
||||
|
||||
private synchronized void waitForLoadingToFinishRunning() {
|
||||
@@ -277,7 +283,16 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConcurrent(final Runnable updateCallback) {
|
||||
/**
|
||||
* This method runs the given update callback in a separate {@link Thread} by handling technical concurrency aspects
|
||||
* and potential {@link #preemptiveStopRequested preemptive stop requests} internally. Thus, it separates the
|
||||
* functional updating process from technical aspects.
|
||||
*
|
||||
* @param updateCallback
|
||||
* the {@link Runnable} callback used to run the update
|
||||
*/
|
||||
// TODO Consider using a thread pool here, after merging bug3864 into master
|
||||
private void updateAsyncInternal(final Runnable updateCallback) {
|
||||
synchronized (FixLoaderAndTracker.this) {
|
||||
activeLoaders.incrementAndGet();
|
||||
setStatusAndProgress(TrackedRaceStatusEnum.LOADING, 0.5);
|
||||
@@ -290,17 +305,14 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
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();
|
||||
int currentActiveLoaders = activeLoaders.decrementAndGet();
|
||||
FixLoaderAndTracker.this.notifyAll();
|
||||
if (currentActiveLoaders == 0) {
|
||||
setStatusAndProgress(stopRequested.get() ? TrackedRaceStatusEnum.FINISHED
|
||||
@@ -328,27 +340,28 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
|
||||
private class FixLoaderDeviceMappings extends RegattaLogDeviceMappings<WithID> {
|
||||
public FixLoaderDeviceMappings(Iterable<RegattaLog> initialRegattaLogs) {
|
||||
super(initialRegattaLogs);
|
||||
public FixLoaderDeviceMappings(Iterable<RegattaLog> initialRegattaLogs, String raceNameForLock) {
|
||||
super(initialRegattaLogs, raceNameForLock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateMappings() {
|
||||
updateConcurrent(() -> {
|
||||
FixLoaderDeviceMappings.super.updateMappings();
|
||||
// add listeners for devices in mappings already present
|
||||
forEachDevice((device) -> sensorFixStore.addListener(listener, device));
|
||||
});
|
||||
updateAsyncInternal(FixLoaderDeviceMappings.super::updateMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mappingRemoved(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
|
||||
final DeviceIdentifier device = mapping.getDevice();
|
||||
if(!hasMappingForDevice(device)) {
|
||||
sensorFixStore.removeListener(listener, device);
|
||||
}
|
||||
// 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) {
|
||||
// The listener is first added to not lose any fix after loading the initial fixes and adding the listener.
|
||||
sensorFixStore.addListener(listener, mapping.getDevice());
|
||||
loadFixes(getTrackingTimeRange().intersection(mapping.getTimeRange()), mapping);
|
||||
}
|
||||
|
||||
|
||||
+96
-90
@@ -30,6 +30,8 @@ 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;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Holds DeviceMappings to make it possible to track changes. This makes it possible to only process mappings that
|
||||
@@ -46,6 +48,16 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
* sure to not produce a memory leak.
|
||||
*/
|
||||
private final Set<RegattaLog> knownRegattaLogs = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Lock object to be used when accessing {@link #knownRegattaLogs}.
|
||||
*/
|
||||
private final NamedReentrantReadWriteLock knownRegattaLogsLock;
|
||||
|
||||
/**
|
||||
* Lock object to be used when accessing {@link #mappings} or {@link #mappingsByDevice}.
|
||||
*/
|
||||
private final NamedReentrantReadWriteLock mappingsLock;
|
||||
|
||||
private final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappings = new HashMap<>();
|
||||
private final Map<DeviceIdentifier, List<DeviceMappingWithRegattaLogEvent<ItemT>>> mappingsByDevice = new HashMap<>();
|
||||
@@ -84,11 +96,16 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
};
|
||||
};
|
||||
|
||||
public RegattaLogDeviceMappings(Iterable<RegattaLog> initialRegattaLogs) {
|
||||
public RegattaLogDeviceMappings(Iterable<RegattaLog> initialRegattaLogs, String raceNameForLock) {
|
||||
mappingsLock = new NamedReentrantReadWriteLock("DeviceMapping lock for race " + raceNameForLock, false);
|
||||
knownRegattaLogsLock = new NamedReentrantReadWriteLock("Lock for known RegattaLogs of race " + raceNameForLock, false);
|
||||
final boolean hasRegattaLogs;
|
||||
synchronized (knownRegattaLogs) {
|
||||
LockUtil.lockForWrite(knownRegattaLogsLock);
|
||||
try {
|
||||
initialRegattaLogs.forEach(this::addRegattaLogUnlocked);
|
||||
hasRegattaLogs = !knownRegattaLogs.isEmpty();
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(knownRegattaLogsLock);
|
||||
}
|
||||
if (hasRegattaLogs) {
|
||||
updateMappings();
|
||||
@@ -96,9 +113,7 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
}
|
||||
|
||||
public void addRegattaLog(RegattaLog regattaLog) {
|
||||
synchronized (knownRegattaLogs) {
|
||||
addRegattaLogUnlocked(regattaLog);
|
||||
}
|
||||
LockUtil.executeWithWriteLock(knownRegattaLogsLock, () -> addRegattaLogUnlocked(regattaLog));
|
||||
updateMappings();
|
||||
}
|
||||
|
||||
@@ -108,39 +123,19 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronized (knownRegattaLogs) {
|
||||
LockUtil.executeWithWriteLock(knownRegattaLogsLock, () -> {
|
||||
knownRegattaLogs.forEach((log) -> log.removeListener(regattaLogEventVisitor));
|
||||
knownRegattaLogs.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void updateMappings() {
|
||||
try {
|
||||
updateMappings(true);
|
||||
} catch (Exception e) {
|
||||
logger.warning("Could not update device mappings");
|
||||
logger.log(Level.WARNING, "Could not update device mappings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -157,7 +152,7 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
* @param callback the callback to call for every mapped device
|
||||
*/
|
||||
public void forEachDevice(Consumer<DeviceIdentifier> callback) {
|
||||
getMappingsByDevice().keySet().forEach(callback::accept);
|
||||
LockUtil.executeWithReadLock(mappingsLock, () -> mappingsByDevice.keySet().forEach(callback::accept));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,12 +161,14 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
* @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);
|
||||
LockUtil.executeWithReadLock(mappingsLock, () -> {
|
||||
for (Map.Entry<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> entry : mappings.entrySet()) {
|
||||
ItemT item = entry.getKey();
|
||||
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : entry.getValue()) {
|
||||
callback.accept(item, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,16 +185,27 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
*/
|
||||
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);
|
||||
LockUtil.executeWithReadLock(mappingsLock, () -> {
|
||||
List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDevice = mappingsByDevice.get(device);
|
||||
if (mappingsForDevice != null) {
|
||||
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappingsForDevice) {
|
||||
if (mapping.getTimeRange().includes(timePoint)) {
|
||||
callback.accept(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there is at least one mapping for the given {@link DeviceIdentifier}, false otherwise
|
||||
*/
|
||||
public boolean hasMappingForDevice(DeviceIdentifier device) {
|
||||
LockUtil.lockForRead(mappingsLock);
|
||||
try {
|
||||
return mappingsByDevice.containsKey(device);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(mappingsLock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +222,7 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
}
|
||||
|
||||
protected void forEachRegattaLog(Consumer<RegattaLog> regattaLogConsumer) {
|
||||
synchronized (knownRegattaLogs) {
|
||||
knownRegattaLogs.forEach(regattaLogConsumer);
|
||||
}
|
||||
LockUtil.executeWithReadLock(knownRegattaLogsLock, () -> knownRegattaLogs.forEach(regattaLogConsumer));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,58 +239,58 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
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);
|
||||
}
|
||||
final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings = calculateMappings();
|
||||
final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> oldMappings = new HashMap<>();
|
||||
LockUtil.lockForWrite(mappingsLock);
|
||||
try {
|
||||
oldMappings.putAll(mappings);
|
||||
|
||||
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);
|
||||
}
|
||||
oldMappings.forEach(this::mappingRemoved);
|
||||
list.add(mapping);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(mappingsLock);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (loadIfNotCovered) {
|
||||
calculateDiff(oldMappings, newMappings);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateDiff(Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> previousMappings,
|
||||
Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings) {
|
||||
Set<ItemT> itemsToProcess = new HashSet<ItemT>(previousMappings.keySet());
|
||||
itemsToProcess.addAll(newMappings.keySet());
|
||||
for(ItemT item : itemsToProcess) {
|
||||
if(!newMappings.containsKey(item)) {
|
||||
previousMappings.get(item).forEach(this::mappingRemoved);
|
||||
} else {
|
||||
final List<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings = previousMappings.containsKey(item)
|
||||
? previousMappings.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a {@link DeviceMapping} was removed.
|
||||
*
|
||||
|
||||
+5
-1
@@ -1,6 +1,10 @@
|
||||
package com.sap.sailing.domain.abstractlog.regatta;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLog;
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
|
||||
import com.sap.sse.common.WithID;
|
||||
/**
|
||||
* This log gathers information for regattas. {@link FlexibleLeaderboard}s are considered
|
||||
@@ -9,5 +13,5 @@ import com.sap.sse.common.WithID;
|
||||
* uses the log supplied by its {@code Regatta}, whereas a {@link FlexibleLeaderboard} manages the log itself.
|
||||
*/
|
||||
public interface RegattaLog extends AbstractLog<RegattaLogEvent, RegattaLogEventVisitor>, WithID {
|
||||
|
||||
public void revokeDefineMarkEventAndRelatedDeviceMappings(final RegattaLogDefineMarkEvent event, final AbstractLogEventAuthor author, final Logger logger);
|
||||
}
|
||||
+29
@@ -1,14 +1,21 @@
|
||||
package com.sap.sailing.domain.abstractlog.regatta.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl;
|
||||
import com.sap.sailing.domain.abstractlog.impl.AllEventsOfTypeFinder;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventComparator;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
|
||||
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.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRevokeEventImpl;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sse.common.WithID;
|
||||
|
||||
public class RegattaLogImpl extends AbstractLogImpl<RegattaLogEvent, RegattaLogEventVisitor> implements RegattaLog {
|
||||
|
||||
@@ -26,4 +33,26 @@ public class RegattaLogImpl extends AbstractLogImpl<RegattaLogEvent, RegattaLogE
|
||||
protected RegattaLogEvent createRevokeEvent(AbstractLogEventAuthor author, RegattaLogEvent toRevoke, String reason) {
|
||||
return new RegattaLogRevokeEventImpl(author, toRevoke, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeDefineMarkEventAndRelatedDeviceMappings(final RegattaLogDefineMarkEvent event, final AbstractLogEventAuthor author, final Logger logger) {
|
||||
if (event != null){
|
||||
RegattaLogEvent revokeEvent = new RegattaLogRevokeEventImpl(author, event, "Revoked by AdminConsole (RaceLogTracking)");
|
||||
this.add(revokeEvent);
|
||||
|
||||
final List<RegattaLogEvent> regattaLogDeviceMarkMappingEvents = new AllEventsOfTypeFinder<>(this, /* only unrevoked */ true, RegattaLogDeviceMappingEvent.class).analyze();
|
||||
|
||||
for (RegattaLogEvent deviceMappingEvent : regattaLogDeviceMarkMappingEvents) {
|
||||
@SuppressWarnings("unchecked") // The list can only contain device mapping events
|
||||
WithID withID = ((RegattaLogDeviceMappingEvent<WithID>) deviceMappingEvent).getMappedTo();
|
||||
if (withID instanceof Mark && ((Mark) withID).getId().toString().equals(event.getMark().getId().toString())) {
|
||||
RegattaLogEvent revokeDeviceMapping = new RegattaLogRevokeEventImpl(author, deviceMappingEvent,
|
||||
"Revoked because AdminConsole (RaceLogTracking) revoked mark");
|
||||
this.add(revokeDeviceMapping);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warning("Could not revoke event for mark. Mark not found in RegattaLog.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
/**
|
||||
* {@link Track} specialization, which is mapped to a specific type of items.
|
||||
*
|
||||
* @param <ItemType>
|
||||
* the type of item this track is mapped to
|
||||
* @param <FixType>
|
||||
* the type of fix that is contained in this track
|
||||
*/
|
||||
public interface MappedTrack<ItemType, FixType extends Timed> extends Track<FixType> {
|
||||
|
||||
/**
|
||||
* @return the item this track is mapped to.
|
||||
*/
|
||||
ItemType getTrackedItem();
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.tracking.MappedTrack;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link MappedTrack} interface.
|
||||
*
|
||||
* @param <ItemType>
|
||||
* the type of item this track is mapped to
|
||||
* @param <FixType>
|
||||
* the type of fix that is contained in this track
|
||||
*/
|
||||
public class MappedTrackImpl<ItemType, FixType extends Timed> extends TrackImpl<FixType>
|
||||
implements MappedTrack<ItemType, FixType> {
|
||||
|
||||
private static final long serialVersionUID = 6165693342087329096L;
|
||||
|
||||
private final ItemType trackedItem;
|
||||
|
||||
/** @see TrackImpl#TrackImpl(String) */
|
||||
public MappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) {
|
||||
super(nameForReadWriteLock);
|
||||
this.trackedItem = trackedItem;
|
||||
}
|
||||
|
||||
/** @see TrackImpl#TrackImpl(ArrayListNavigableSet, String) */
|
||||
protected MappedTrackImpl(ItemType trackedItem, ArrayListNavigableSet<Timed> fixes, String nameForReadWriteLock) {
|
||||
super(fixes, nameForReadWriteLock);
|
||||
this.trackedItem = trackedItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemType getTrackedItem() {
|
||||
return trackedItem;
|
||||
}
|
||||
|
||||
}
|
||||
+86
@@ -2103,6 +2103,92 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 3752 comment #6: when in an earlier race a competitor does not have a fleet assigned and the other one does, but in
|
||||
* a later race in the same series both competitors are assigned to the same fleet, the authoritative answer based on the fleets
|
||||
* must be that they compare equal; an earlier default based on "extreme fleet" considerations must be discarded if fleet equality
|
||||
* is definitely established.
|
||||
*/
|
||||
@Test
|
||||
public void testTotalRankComparatorForOrderedSplitFleetsWithUnknownFleetInPreviousRaceAndSameFleetInLaterRace() throws NoWindException {
|
||||
series = new ArrayList<Series>();
|
||||
// -------- qualification series ------------
|
||||
{
|
||||
List<Fleet> qualificationFleets = new ArrayList<Fleet>();
|
||||
for (String qualificationFleetName : new String[] { "Yellow", "Blue" }) {
|
||||
qualificationFleets.add(new FleetImpl(qualificationFleetName));
|
||||
}
|
||||
List<String> qualificationRaceColumnNames = new ArrayList<String>();
|
||||
qualificationRaceColumnNames.add("Q");
|
||||
Series qualificationSeries = new SeriesImpl("Qualification", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualificationFleets, qualificationRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
// discard the one and only qualification race; it doesn't score
|
||||
qualificationSeries.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1 }));
|
||||
series.add(qualificationSeries);
|
||||
}
|
||||
|
||||
// -------- final series ------------
|
||||
{
|
||||
List<Fleet> finalFleets = new ArrayList<Fleet>();
|
||||
int fleetOrdering = 1;
|
||||
for (String finalFleetName : new String[] { "Gold", "Silver" }) {
|
||||
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
|
||||
}
|
||||
List<String> finalRaceColumnNames = Arrays.asList("F1", "F2");
|
||||
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(finalSeries);
|
||||
}
|
||||
final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("470", /* typicallyStartsUpwind */ true);
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null,
|
||||
series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* course area */null, OneDesignRankingMetric::new);
|
||||
List<Competitor> competitors = createCompetitors(12);
|
||||
final int firstYellowCompetitorIndex = 3;
|
||||
List<Competitor> yellow = new ArrayList<>(competitors.subList(firstYellowCompetitorIndex, firstYellowCompetitorIndex+6));
|
||||
List<Competitor> blue = new ArrayList<>(competitors);
|
||||
blue.removeAll(yellow);
|
||||
Collections.shuffle(yellow);
|
||||
Collections.shuffle(blue);
|
||||
final int firstGoldCompetitorIndex = 5;
|
||||
List<Competitor> gold = new ArrayList<>(competitors.subList(firstGoldCompetitorIndex, firstGoldCompetitorIndex+6));
|
||||
List<Competitor> silver = new ArrayList<>(competitors);
|
||||
silver.removeAll(gold);
|
||||
Collections.shuffle(gold);
|
||||
Collections.shuffle(silver);
|
||||
List<Competitor> lastRaceSilver = new ArrayList<>(silver);
|
||||
final Competitor theUntrackedCompetitorInLastRace = lastRaceSilver.get(lastRaceSilver.size()-1);
|
||||
lastRaceSilver.remove(theUntrackedCompetitorInLastRace); // one participant accidentally not tracked; expected to end up between silver and gold
|
||||
List<Competitor> medal = new ArrayList<>(gold.subList(0, 2)); // take two gold race participants as medal race participants
|
||||
List<Competitor> lastRaceGold = new ArrayList<>(gold);
|
||||
lastRaceGold.removeAll(medal); // no medal race participant participates in the last race's gold fleet
|
||||
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
RaceColumn qColumn = series.get(0).getRaceColumnByName("Q");
|
||||
TrackedRace qYellow = new MockedTrackedRaceWithStartTimeAndRanks(now, yellow);
|
||||
qColumn.setTrackedRace(qColumn.getFleetByName("Yellow"), qYellow);
|
||||
TrackedRace qBlue = new MockedTrackedRaceWithStartTimeAndRanks(now, blue);
|
||||
qColumn.setTrackedRace(qColumn.getFleetByName("Blue"), qBlue);
|
||||
RaceColumn f1Column = series.get(1).getRaceColumnByName("F1");
|
||||
TrackedRace f1Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, gold);
|
||||
f1Column.setTrackedRace(f1Column.getFleetByName("Gold"), f1Gold);
|
||||
final List<Competitor> f1SilverWithOneMissing = new ArrayList<>(silver);
|
||||
final Competitor missing = silver.get(silver.size()-1); // this competitor is not assigned to a fleet for F1, but for F2
|
||||
f1SilverWithOneMissing.remove(missing);
|
||||
TrackedRace f1Silver = new MockedTrackedRaceWithStartTimeAndRanks(now, f1SilverWithOneMissing);
|
||||
f1Column.setTrackedRace(f1Column.getFleetByName("Silver"), f1Silver);
|
||||
leaderboard.getScoreCorrection().correctScore(missing, f1Column, silver.size()); // set score to what it would have been
|
||||
RaceColumn f2Column = series.get(1).getRaceColumnByName("F2");
|
||||
TrackedRace f2Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, gold);
|
||||
f2Column.setTrackedRace(f2Column.getFleetByName("Gold"), f2Gold);
|
||||
TrackedRace f2Silver = new MockedTrackedRaceWithStartTimeAndRanks(now, silver);
|
||||
f2Column.setTrackedRace(f2Column.getFleetByName("Silver"), f2Silver);
|
||||
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
// the competitor missing from F1 Silver has worst score in both Silver fleet races and must rank last;
|
||||
// with the bug still present, she would have ranked between Gold and Silver
|
||||
assertEquals(missing, rankedCompetitors.get(rankedCompetitors.size()-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 3798: special discarding rule that limits the number of discards for the final series; configured
|
||||
* by {@link Series#getMaximumNumberOfDiscards()}.
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.test.compiler;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class EclipseNeonConstructorReferenceSerializationTest {
|
||||
|
||||
/**
|
||||
* We have serialization problems due to a regression in Eclipse Neon:
|
||||
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=497879
|
||||
*/
|
||||
@Test
|
||||
public void testConstructorReferenceSerializationThatFailsOnEclipseNeon() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
|
||||
SerializableSupplier factory = TypeImplementation::new;
|
||||
oos.writeObject(factory);
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
ois.readObject();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.sap.sailing.domain.test.compiler;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SerializableSupplier extends Serializable, Supplier<TypeDefinition> {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.test.compiler;
|
||||
|
||||
public interface TypeDefinition {
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.sap.sailing.domain.test.compiler;
|
||||
|
||||
public class TypeImplementation implements TypeDefinition {
|
||||
|
||||
public TypeImplementation() {
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.SensorFix;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric;
|
||||
@@ -522,6 +523,11 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
@Override
|
||||
public void deregisterCompetitors(Iterable<Competitor> competitor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -811,6 +817,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(TrackedRaceStatus newStatus) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(TrackingDataLoader loader, TrackedRaceStatus status) {
|
||||
}
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
|
||||
|
||||
@Override
|
||||
public TrackedRace getTrackedRace(Fleet fleet) {
|
||||
return trackedRaces.get(fleet);
|
||||
return trackedRaces == null ? null : trackedRaces.get(fleet);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -386,6 +386,14 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
return boatClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
if (competitorsProvider == null) {
|
||||
competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
|
||||
}
|
||||
return competitorsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getAllCompetitors() {
|
||||
Set<Competitor> result = new HashSet<Competitor>();
|
||||
|
||||
+3
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.HasRegattaLike;
|
||||
|
||||
public interface HasRaceColumnsAndRegattaLike extends HasRegattaLike, HasRaceColumns {
|
||||
|
||||
CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider();
|
||||
|
||||
}
|
||||
+1
-1
@@ -66,7 +66,7 @@ public class LeaderboardDTOCache implements LeaderboardCache {
|
||||
*/
|
||||
private final boolean waitForLatestAnalyses;
|
||||
|
||||
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
|
||||
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()/2, 3);
|
||||
/**
|
||||
* A multi-threaded executor for the currently running leaderboard requests, executing the {@link Future}s currently
|
||||
* pending.
|
||||
|
||||
+2
-1
@@ -101,7 +101,8 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
|
||||
return getOrCreateCompetitorsProvider().getAllCompetitors(raceColumn, fleet);
|
||||
}
|
||||
|
||||
private CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
@Override
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
if (competitorsProvider == null) {
|
||||
competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
|
||||
}
|
||||
|
||||
+14
-13
@@ -20,7 +20,6 @@ import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -149,20 +148,28 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private transient Map<com.sap.sse.common.Util.Pair<TrackedRace, Competitor>, RunnableFuture<RaceDetails>> raceDetailsAtEndOfTrackingCache;
|
||||
|
||||
/**
|
||||
* Used to remove all these listeners from their tracked races when this servlet is {@link #destroy() destroyed}.
|
||||
*/
|
||||
private transient Set<CacheInvalidationListener> cacheInvalidationListeners;
|
||||
|
||||
private final static int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()/2, 3);
|
||||
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
|
||||
/* maximumPoolSize */ THREAD_POOL_SIZE,
|
||||
/* keepAliveTime */ 60, TimeUnit.SECONDS,
|
||||
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
|
||||
/**
|
||||
* This executor needs to be a different one than {@link #executor} because the tasks run by {@link #executor}
|
||||
* can depend on the results of the tasks run by {@link #raceDetailsExecutor}, and an {@link Executor} doesn't
|
||||
* move a task that is blocked by waiting for another {@link FutureTask} to the side but blocks permanently,
|
||||
* ending in a deadlock (one that cannot easily be detected by the Eclipse debugger either).
|
||||
*/
|
||||
private transient Executor raceDetailsExecutor;
|
||||
private final static Executor raceDetailsExecutor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
|
||||
/* maximumPoolSize */ THREAD_POOL_SIZE,
|
||||
/* keepAliveTime */ 60, TimeUnit.SECONDS,
|
||||
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
|
||||
|
||||
/**
|
||||
* Used to remove all these listeners from their tracked races when this servlet is {@link #destroy() destroyed}.
|
||||
*/
|
||||
private transient Set<CacheInvalidationListener> cacheInvalidationListeners;
|
||||
|
||||
private transient ThreadPoolExecutor executor;
|
||||
|
||||
private transient LiveLeaderboardUpdater liveLeaderboardUpdater;
|
||||
|
||||
@@ -343,16 +350,10 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
private void initTransientFields() {
|
||||
this.raceDetailsAtEndOfTrackingCache = new HashMap<com.sap.sse.common.Util.Pair<TrackedRace,Competitor>, RunnableFuture<RaceDetails>>();
|
||||
this.raceDetailsExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true));
|
||||
this.cacheInvalidationListeners = new HashSet<CacheInvalidationListener>();
|
||||
// When many updates are triggered in a short period of time by a single thread, ensure that the single thread
|
||||
// providing the updates is not outperformed by all the re-calculations happening here. Leave at least one
|
||||
// core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur.
|
||||
final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
|
||||
executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
|
||||
/* maximumPoolSize */ THREAD_POOL_SIZE,
|
||||
/* keepAliveTime */ 60, TimeUnit.SECONDS,
|
||||
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+83
-14
@@ -11,6 +11,8 @@ import java.util.Set;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.NoWindError;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
@@ -113,6 +115,14 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
// the competitor scored in this or any subsequent columns
|
||||
boolean needToResetO1ScoreUponNextValidResult = false;
|
||||
boolean needToResetO2ScoreUponNextValidResult = false;
|
||||
// Once we have established the fleet of a competitor in a series the fleet ordering for that competitor in that series
|
||||
// cannot change anymore; if assigned to an ordered fleet, the ordering will have to remain unchanged throughout the fleet,
|
||||
// and if assigned to an unordered fleet (ordering==0) the only change may be a re-assignment to a different unordered
|
||||
// fleet but not to an ordered fleet. Therefore, once we know the fleet ordering for a competitor in a series for sure,
|
||||
// we don't have to bother computing it again. It can be cached. See also bug 3838.
|
||||
// The fleet stored here may not be the same fleet for the competitor for all columns in that series but
|
||||
// it is guaranteed to have equal ordering as all fleets that the competitor is assigned to within the key series.
|
||||
final Map<Series, Map<Competitor, Fleet>> fleetWithCorrectOrderingForCompetitorBySeries = new HashMap<>();
|
||||
int defaultFleetBasedComparisonResult = 0; // relevant if no authoritative fleet-based comparison result was determined; based on extreme fleet vs. no fleet comparison
|
||||
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
|
||||
needToResetO1ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
|
||||
@@ -174,9 +184,11 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
|
||||
}
|
||||
if (preemptiveColumnResult == 0 && raceColumn.isTotalOrderDefinedByFleet()) {
|
||||
final FleetComparisonResult compareByFleetResult = compareByFleet(raceColumn, o1, o2);
|
||||
preemptiveColumnResult = compareByFleetResult.getAuthoritativeFleetComparisonResult();
|
||||
if (defaultFleetBasedComparisonResult == 0) {
|
||||
final FleetComparisonResult compareByFleetResult = compareByFleet(raceColumn, o1, o2, fleetWithCorrectOrderingForCompetitorBySeries);
|
||||
if (compareByFleetResult.getAuthoritativeFleetComparisonResult() != null) {
|
||||
preemptiveColumnResult = compareByFleetResult.getAuthoritativeFleetComparisonResult();
|
||||
defaultFleetBasedComparisonResult = 0;
|
||||
} else if (defaultFleetBasedComparisonResult == 0) {
|
||||
defaultFleetBasedComparisonResult = compareByFleetResult.getDefaultFleetComparisonResultBasedOnUnknownFleetAssignment();
|
||||
}
|
||||
}
|
||||
@@ -231,10 +243,14 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
private static class FleetComparisonResult {
|
||||
/**
|
||||
* Is non-{@code 0} if the two competitors have been identified as having raced in different fleets in
|
||||
* {@code raceColumn} with those fleets having different {@link Fleet#getOrdering() orderings}.
|
||||
* Evaluation of further comparison criteria is not necessary in this case.
|
||||
* {@code raceColumn} with those fleets having different {@link Fleet#getOrdering() orderings}, or {@code 0}
|
||||
* if the two competitors have been identified authoritatively having raced in the same fleet in
|
||||
* {@code raceColumn}. Remains {@code null} if at least one competitor's fleet couldn't be identified.
|
||||
* Evaluation of further comparison criteria is not necessary only if an authoritative non-{@code 0}
|
||||
* answer was found. If fleet comparison has been calculated for all columns and no authoritative answer
|
||||
* was found, the {@link #defaultFleetComparisonResultBasedOnUnknownFleetAssignment} result can be used.
|
||||
*/
|
||||
private final int authoritativeFleetComparisonResult;
|
||||
private final Integer authoritativeFleetComparisonResult;
|
||||
|
||||
/**
|
||||
* When for one of the two competitors compared the fleet in which she raced in a race column
|
||||
@@ -248,14 +264,14 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
*/
|
||||
private final int defaultFleetComparisonResultBasedOnUnknownFleetAssignment;
|
||||
|
||||
public FleetComparisonResult(int authoritativeFleetComparisonResult,
|
||||
public FleetComparisonResult(Integer authoritativeFleetComparisonResult,
|
||||
int defaultFleetComparisonResultBasedOnUnknownFleetAssignment) {
|
||||
super();
|
||||
this.authoritativeFleetComparisonResult = authoritativeFleetComparisonResult;
|
||||
this.defaultFleetComparisonResultBasedOnUnknownFleetAssignment = defaultFleetComparisonResultBasedOnUnknownFleetAssignment;
|
||||
}
|
||||
|
||||
public int getAuthoritativeFleetComparisonResult() {
|
||||
public Integer getAuthoritativeFleetComparisonResult() {
|
||||
return authoritativeFleetComparisonResult;
|
||||
}
|
||||
|
||||
@@ -264,9 +280,10 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
}
|
||||
}
|
||||
|
||||
private FleetComparisonResult compareByFleet(RaceColumn raceColumn, Competitor o1, Competitor o2) {
|
||||
Fleet o1f = raceColumn.getFleetOfCompetitor(o1);
|
||||
Fleet o2f = raceColumn.getFleetOfCompetitor(o2);
|
||||
private FleetComparisonResult compareByFleet(final RaceColumn raceColumn, final Competitor o1, final Competitor o2,
|
||||
final Map<Series, Map<Competitor, Fleet>> fleetWithCorrectOrderingForCompetitorBySeries) {
|
||||
final Fleet o1f = getAFleetWithCorrectOrderingOfCompetitorFromCacheOrRaceColumnAndCache(raceColumn, o1, fleetWithCorrectOrderingForCompetitorBySeries);
|
||||
final Fleet o2f = getAFleetWithCorrectOrderingOfCompetitorFromCacheOrRaceColumnAndCache(raceColumn, o2, fleetWithCorrectOrderingForCompetitorBySeries);
|
||||
// if the fleet for both was identified because both were tracked in this column, then if the fleets
|
||||
// don't compare equal, return the fleet comparison as result immediately. Example: o1 competed in Gold fleet,
|
||||
// o2 in Silver fleet; Gold compares better to Silver, so o1 is compared better to o2.
|
||||
@@ -277,18 +294,70 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
} else {
|
||||
// check if o1's fleet is best or worst in column; in that case, o1's membership in this fleet and the fact
|
||||
// that o2 is not part of that fleet determines the result
|
||||
result = new FleetComparisonResult(0, extremeFleetComparison(raceColumn, o1f));
|
||||
result = new FleetComparisonResult(null, extremeFleetComparison(raceColumn, o1f));
|
||||
}
|
||||
} else if (o2f != null) {
|
||||
// check if o2's fleet is best or worst in column; in that case, o2's membership in this fleet and the fact
|
||||
// that o1 is not part of that fleet determines the result
|
||||
result = new FleetComparisonResult(0, -extremeFleetComparison(raceColumn, o2f));
|
||||
result = new FleetComparisonResult(null, -extremeFleetComparison(raceColumn, o2f));
|
||||
} else {
|
||||
result = new FleetComparisonResult(0, 0);
|
||||
result = new FleetComparisonResult(null, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a fleet assignment for the {@code competitor} in the {@code fleetWithCorrectOrderingForCompetitorBySeries} cache for
|
||||
* the {@link Series} corresponding with {@code RaceColumn} ({@code null} in case the column is not part of a {@link Regatta}).
|
||||
* If found, that fleet is returned, although it may not be the exact fleet assignment for the competitor in that column, but
|
||||
* at least it has the correct ordering which suffices for fleet comparisons.<p>
|
||||
*
|
||||
* If a fleet assignment is not found in the cache, the race column is {@link RaceColumn#getFleetOfCompetitor(Competitor) asked}
|
||||
* for the competitor's fleet assignment. If a result is found, it is added to the {@code fleetWithCorrectOrderingForCompetitorBySeries}
|
||||
* cache.
|
||||
*/
|
||||
private Fleet getAFleetWithCorrectOrderingOfCompetitorFromCacheOrRaceColumnAndCache(final RaceColumn raceColumn, final Competitor competitor,
|
||||
final Map<Series, Map<Competitor, Fleet>> fleetWithCorrectOrderingForCompetitorBySeries) {
|
||||
Fleet fleetWithCorrectOrdering = null;
|
||||
final Series series;
|
||||
if (raceColumn instanceof RaceColumnInSeries) {
|
||||
series = ((RaceColumnInSeries) raceColumn).getSeries();
|
||||
} else {
|
||||
series = null;
|
||||
}
|
||||
Map<Competitor, Fleet> fleetForCompetitorInSeries = fleetWithCorrectOrderingForCompetitorBySeries.get(series);
|
||||
if (fleetForCompetitorInSeries != null && fleetForCompetitorInSeries.containsKey(competitor)) {
|
||||
fleetWithCorrectOrdering = fleetForCompetitorInSeries.get(competitor);
|
||||
}
|
||||
if (fleetWithCorrectOrdering == null) {
|
||||
fleetWithCorrectOrdering = getFleetOfCompetitorFromRaceColumnAndCache(raceColumn, competitor,
|
||||
fleetWithCorrectOrderingForCompetitorBySeries, series, fleetForCompetitorInSeries);
|
||||
}
|
||||
return fleetWithCorrectOrdering;
|
||||
}
|
||||
|
||||
private Fleet getFleetOfCompetitorInRaceColumn(final RaceColumn raceColumn, final Competitor competitor) {
|
||||
for (final Fleet fleet : raceColumn.getFleets()) {
|
||||
if (Util.contains(getLeaderboard().getAllCompetitors(raceColumn, fleet), competitor)) {
|
||||
return fleet;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private Fleet getFleetOfCompetitorFromRaceColumnAndCache(final RaceColumn raceColumn, final Competitor competitor,
|
||||
final Map<Series, Map<Competitor, Fleet>> orderedFleetsForCompetitorsBySeries, final Series series,
|
||||
Map<Competitor, Fleet> fleetForCompetitorInSeries) {
|
||||
final Fleet fleetWithCorrectOrdering = getFleetOfCompetitorInRaceColumn(raceColumn, competitor);
|
||||
if (fleetWithCorrectOrdering != null) {
|
||||
if (fleetForCompetitorInSeries == null) {
|
||||
fleetForCompetitorInSeries = new HashMap<>();
|
||||
orderedFleetsForCompetitorsBySeries.put(series, fleetForCompetitorInSeries);
|
||||
}
|
||||
fleetForCompetitorInSeries.put(competitor, fleetWithCorrectOrdering);
|
||||
}
|
||||
return fleetWithCorrectOrdering;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the race column only has one fleet, no decision is made and 0 is returned. Otherwise, if <code>fleet</code> is the
|
||||
* best fleet with others in the column being worse, return "better" (lesser; -1). If <code>fleet</code> is the worst fleet
|
||||
|
||||
+5
@@ -130,4 +130,9 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
|
||||
public BoatClass getBoatClass() {
|
||||
return getRegatta().getBoatClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
return getRegatta().getOrCreateCompetitorsProvider();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -53,7 +53,7 @@ public class MarkPassingCalculator {
|
||||
private static final Logger logger = Logger.getLogger(MarkPassingCalculator.class.getName());
|
||||
private final MarkPassingUpdateListener listener;
|
||||
private final static ExecutorService executor = new ThreadPoolExecutor(/* corePoolSize */Math.max(Runtime
|
||||
.getRuntime().availableProcessors() - 1, 3),
|
||||
.getRuntime().availableProcessors()/2, 3),
|
||||
/* maximumPoolSize */Math.max(Runtime.getRuntime().availableProcessors() - 1, 3),
|
||||
/* keepAliveTime */60, TimeUnit.SECONDS,
|
||||
/* workQueue */new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY - 1, /* daemon */ true));
|
||||
|
||||
+2
-2
@@ -150,8 +150,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
*/
|
||||
public CandidateFinderImpl(DynamicTrackedRace race) {
|
||||
this(race, new ThreadPoolExecutor(/* corePoolSize */Math.max(Runtime
|
||||
.getRuntime().availableProcessors() - 1, 3),
|
||||
/* maximumPoolSize */Math.max(Runtime.getRuntime().availableProcessors() - 1, 3),
|
||||
.getRuntime().availableProcessors()/2, 3),
|
||||
/* maximumPoolSize */Math.max(Runtime.getRuntime().availableProcessors()/2, 3),
|
||||
/* keepAliveTime */60, TimeUnit.SECONDS,
|
||||
/* workQueue */new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY - 1, /* daemon */ true)));
|
||||
}
|
||||
|
||||
+4
@@ -20,6 +20,10 @@ public enum EmptySensorFixStore implements SensorFixStore {
|
||||
public void removeListener(FixReceivedListener<? extends Timed> listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(FixReceivedListener<? extends Timed> listener, DeviceIdentifier device) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device) {
|
||||
return null;
|
||||
|
||||
+19
-4
@@ -1,20 +1,24 @@
|
||||
package com.sap.sailing.domain.racelog.tracking;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.common.WithID;
|
||||
|
||||
/**
|
||||
* This is used by the fix tracking mechanism to make fixes available in a track.
|
||||
* This makes it possible to e.g. map fixes to another type before adding them to the track.
|
||||
* <p>
|
||||
*
|
||||
* Implementations need to be registered as OSGi service to be correctly found.
|
||||
*
|
||||
* @param <FixT> the type of fix this mapper handles
|
||||
* @param <TrackT> the type of track this mapper adds the fixes to
|
||||
* @param <K> the type of item the track is mapped to
|
||||
*/
|
||||
public interface SensorFixMapper<FixT extends Timed, TrackT extends Track<?>, K extends WithID> {
|
||||
public interface SensorFixMapper<FixT extends Timed, TrackT extends DynamicTrack<FixT>, K extends WithID> {
|
||||
|
||||
/**
|
||||
* Extracts the track from the TrackedRace or creates a new one if it doesn't already exist.
|
||||
@@ -26,12 +30,23 @@ public interface SensorFixMapper<FixT extends Timed, TrackT extends Track<?>, K
|
||||
TrackT getTrack(DynamicTrackedRace race, K key);
|
||||
|
||||
/**
|
||||
* Adds a fix to the given Track. This can also be used to map fixes to another type if necessary.
|
||||
* This default implementation maps the given {@link DoubleVectorFix} using the {@link #map(DoubleVectorFix)} method
|
||||
* first. Afterwards, the mapped fix will be added to the given track.
|
||||
*
|
||||
* @param track the track to add the fix to
|
||||
* @param fix the fix to add
|
||||
*/
|
||||
void addFix(TrackT track, FixT fix);
|
||||
default void addFix(TrackT track, DoubleVectorFix fix) {
|
||||
track.add(map(fix));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method maps the given {@link DoubleVectorFix} to the required fix type.
|
||||
*
|
||||
* @param fix the {@link DoubleVectorFix} to map
|
||||
* @return the mapped fix type instance
|
||||
*/
|
||||
FixT map(DoubleVectorFix fix);
|
||||
|
||||
/**
|
||||
* Checks if this mapper can be used for tracks/fixes that are associated to a given RegattaLog device mapping event.
|
||||
|
||||
+5
@@ -59,6 +59,11 @@ public interface SensorFixStore {
|
||||
*/
|
||||
void removeListener(FixReceivedListener<? extends Timed> listener);
|
||||
|
||||
/**
|
||||
* Remove the registrations of the listener for the given device.
|
||||
*/
|
||||
void removeListener(FixReceivedListener<? extends Timed> listener, DeviceIdentifier device);
|
||||
|
||||
TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device) throws TransformationException,
|
||||
NoCorrespondingServiceRegisteredException;
|
||||
|
||||
|
||||
+2
-2
@@ -473,13 +473,13 @@ public abstract class AbstractRankingMetric implements RankingMetric {
|
||||
/**
|
||||
* Get's <code>who</code>'s current tracked leg at <code>timePoint</code>, or <code>null</code> if <code>who</code> hasn't
|
||||
* started at <code>timePoint</code> yet, or <code>who</code>'s tracked leg for the last leg if <code>who</code> has
|
||||
* already finished the race at <code>timePoint</code>.
|
||||
* already finished the race at <code>timePoint</code> or
|
||||
*/
|
||||
protected TrackedLegOfCompetitor getCurrentLegOrLastLegIfAlreadyFinished(Competitor who, TimePoint timePoint) {
|
||||
TrackedLegOfCompetitor currentLegWho = getTrackedRace().getCurrentLeg(who, timePoint);
|
||||
if (currentLegWho == null) { // already finished or not yet started; if already finished, use last leg
|
||||
final Waypoint lastWaypoint = getTrackedRace().getRace().getCourse().getLastWaypoint();
|
||||
if (lastWaypoint != null) { // could be an empty course
|
||||
if (lastWaypoint != null && getTrackedRace().getRace().getCourse().getNumberOfWaypoints() > 1) { // could be an empty course
|
||||
final TrackedLeg lastTrackedLeg = getTrackedRace().getTrackedLegFinishingAt(lastWaypoint);
|
||||
TrackedLegOfCompetitor whosLastTrackedLeg = lastTrackedLeg.getTrackedLeg(who);
|
||||
if (whosLastTrackedLeg.hasFinishedLeg(timePoint)) {
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
/**
|
||||
* Extension of {@link MappedTrack} interface, which also implements {@link DynamicTrack} interface.
|
||||
*
|
||||
* @param <ItemType>
|
||||
* the type of item this track is mapped to
|
||||
* @param <FixType>
|
||||
* the type of fix that is contained in this track
|
||||
*/
|
||||
public interface DynamicMappedTrack<ItemType, FixType extends Timed>
|
||||
extends MappedTrack<ItemType, FixType>, DynamicTrack<FixType> {
|
||||
}
|
||||
+2
@@ -185,6 +185,8 @@ public interface DynamicTrackedRace extends TrackedRace {
|
||||
*/
|
||||
void setRaceIsKnownToStartUpwind(boolean raceIsKnownToStartUpwind);
|
||||
|
||||
void setStatus(TrackedRaceStatus newStatus);
|
||||
|
||||
void onStatusChanged(TrackingDataLoader source, TrackedRaceStatus status);
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.sap.sse.common.Util;
|
||||
*
|
||||
* @param <ItemType>
|
||||
*/
|
||||
public interface GPSFixTrack<ItemType, FixType extends GPSFix> extends Track<FixType> {
|
||||
public interface GPSFixTrack<ItemType, FixType extends GPSFix> extends MappedTrack<ItemType, FixType> {
|
||||
static final long DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED = 10000; // makes for a 5s half-side interval
|
||||
static final Speed DEFAULT_MAX_SPEED_FOR_SMOOTHING = new KnotSpeedImpl(40);
|
||||
|
||||
@@ -40,8 +40,6 @@ public interface GPSFixTrack<ItemType, FixType extends GPSFix> extends Track<Fix
|
||||
|
||||
void removeListener(GPSTrackListener<ItemType, FixType> listener);
|
||||
|
||||
ItemType getTrackedItem();
|
||||
|
||||
/**
|
||||
* Computes the distance traveled on the smoothened track between the
|
||||
* {@link #getEstimatedPosition(TimePoint, boolean) estimated positions} at <code>from</code> and <code>to</code>.
|
||||
|
||||
@@ -27,6 +27,10 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
* A tracker may be {@link #stop(boolean) stopped}. In this case, it will no longer receive any data at all. Stopping a tracker
|
||||
* will not modify the {@link Regatta} and the {@link TrackedRegatta} with regards to their ownership of their
|
||||
* {@link RaceDefiniion} and {@link TrackedRace}, respectively.
|
||||
* <p>
|
||||
*
|
||||
* A {@link RaceTracker} controls the lifecycle of a {@link TrackedRace} in contrast to a {@link TrackingDataLoader} which just
|
||||
* contributes to the composite status but does not control the lifecycle.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
|
||||
@@ -13,7 +13,8 @@ import com.sap.sse.common.WithID;
|
||||
* @param <ItemType> the type of item this track is mapped to
|
||||
* @param <FixT> the type of fix that is contained in this track
|
||||
*/
|
||||
public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix> extends Track<FixT> {
|
||||
public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix>
|
||||
extends MappedTrack<ItemType, FixT> {
|
||||
|
||||
/**
|
||||
* Value names to be used when reading values from {@link SensorFix}es by calling {@link SensorFix#get(String)}.
|
||||
@@ -26,11 +27,6 @@ public interface SensorFixTrack<ItemType extends WithID & Serializable, FixT ext
|
||||
|
||||
void removeListener(SensorFixTrackListener<ItemType, FixT> listener);
|
||||
|
||||
/**
|
||||
* @return the item this tracked is mapped to.
|
||||
*/
|
||||
ItemType getTrackedItem();
|
||||
|
||||
/**
|
||||
* @return the associated track name by which this track can be obtained from the {@link TrackedRace}.
|
||||
*/
|
||||
|
||||
+6
@@ -1,4 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
/**
|
||||
* Marker interface for classes that load data for a {@link TrackedRace} and therefore contribute to the composite
|
||||
* status of a tracked race. A {@link TrackingDataLoader} must update it's state in the associated {@link TrackedRace}
|
||||
* by calling {@link DynamicTrackedRace#onStatusChanged(TrackingDataLoader, TrackedRaceStatus)} with a self-reference
|
||||
* and the actual status.
|
||||
*/
|
||||
public interface TrackingDataLoader {
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.tracking.DynamicMappedTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link DynamicMappedTrack} interface.
|
||||
*
|
||||
* @param <ItemType>
|
||||
* the type of item this track is mapped to
|
||||
* @param <FixType>
|
||||
* the type of fix that is contained in this track
|
||||
*/
|
||||
public class DynamicMappedTrackImpl<ItemType, FixType extends Timed> extends MappedTrackImpl<ItemType, FixType>
|
||||
implements DynamicMappedTrack<ItemType, FixType> {
|
||||
|
||||
private static final long serialVersionUID = -8705397260656473352L;
|
||||
|
||||
public DynamicMappedTrackImpl(ItemType trackedItem, String nameForReadWriteLock) {
|
||||
super(trackedItem, nameForReadWriteLock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply makes the superclass method public as now we need to expose it via the {@link DynamicTrack} interface.
|
||||
*/
|
||||
@Override
|
||||
public boolean add(FixType fix) {
|
||||
return super.add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(FixType fix, boolean replace) {
|
||||
return super.add(fix, replace);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -211,7 +211,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStatus(TrackedRaceStatus newStatus) {
|
||||
public void setStatus(TrackedRaceStatus newStatus) {
|
||||
TrackedRaceStatus oldStatus = getStatus();
|
||||
super.setStatus(newStatus);
|
||||
notifyListeners(newStatus, oldStatus);
|
||||
|
||||
+5
-10
@@ -45,12 +45,12 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
|
||||
public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl<FixType> implements GPSFixTrack<ItemType, FixType> {
|
||||
public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends MappedTrackImpl<ItemType, FixType>
|
||||
implements GPSFixTrack<ItemType, FixType> {
|
||||
private static final Logger logger = Logger.getLogger(GPSFixTrackImpl.class.getName());
|
||||
private static final long serialVersionUID = 2115321069242514378L;
|
||||
protected final Speed maxSpeedForSmoothing;
|
||||
|
||||
private final ItemType trackedItem;
|
||||
private long millisecondsOverWhichToAverage;
|
||||
|
||||
private final TrackListenerCollection<ItemType, FixType, GPSTrackListener<ItemType, FixType>> listeners;
|
||||
@@ -131,8 +131,8 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
|
||||
* inferred speed can be matched.
|
||||
*/
|
||||
public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage, Speed maxSpeedForSmoothening) {
|
||||
super(/* nameForReadWriteLock */ GPSFixTrackImpl.class.getSimpleName()+(trackedItem==null?"":(" for "+trackedItem.toString())));
|
||||
this.trackedItem = trackedItem;
|
||||
super(trackedItem, /* nameForReadWriteLock */ GPSFixTrackImpl.class.getSimpleName()
|
||||
+ (trackedItem == null ? "" : (" for " + trackedItem.toString())));
|
||||
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
|
||||
this.maxSpeedForSmoothing = maxSpeedForSmoothening;
|
||||
this.listeners = new TrackListenerCollection<ItemType, FixType, GPSTrackListener<ItemType,FixType>>();
|
||||
@@ -166,7 +166,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
ois.defaultReadObject();
|
||||
distanceCache = new DistanceCache(trackedItem.toString());
|
||||
distanceCache = new DistanceCache(getTrackedItem().toString());
|
||||
maxSpeedCache = createMaxSpeedCache();
|
||||
}
|
||||
|
||||
@@ -247,11 +247,6 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
|
||||
FixType result = (FixType) new DummyGPSFix(timePoint);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemType getTrackedItem() {
|
||||
return trackedItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMillisecondsOverWhichToAverageSpeed() {
|
||||
|
||||
+3
-10
@@ -17,12 +17,11 @@ import com.sap.sse.common.WithID;
|
||||
* @param <FixT> the type of fix this track holds
|
||||
*/
|
||||
public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT extends SensorFix> extends
|
||||
DynamicTrackImpl<FixT> implements DynamicSensorFixTrack<ItemType, FixT> {
|
||||
DynamicMappedTrackImpl<ItemType, FixT> implements DynamicSensorFixTrack<ItemType, FixT> {
|
||||
|
||||
private static final long serialVersionUID = 6383421895429843002L;
|
||||
|
||||
private final Iterable<String> valueNames;
|
||||
private final ItemType trackedItem;
|
||||
private final String trackName;
|
||||
private final TrackListenerCollection<ItemType, FixT, SensorFixTrackListener<ItemType, FixT>> listeners;
|
||||
|
||||
@@ -34,8 +33,7 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
|
||||
*/
|
||||
public SensorFixTrackImpl(ItemType trackedItem, String trackName, Iterable<String> valueNames,
|
||||
String nameForReadWriteLock) {
|
||||
super(nameForReadWriteLock);
|
||||
this.trackedItem = trackedItem;
|
||||
super(trackedItem, nameForReadWriteLock);
|
||||
this.trackName = trackName;
|
||||
this.valueNames = valueNames;
|
||||
this.listeners = new TrackListenerCollection<>();
|
||||
@@ -48,7 +46,7 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
|
||||
try {
|
||||
final boolean firstFixInTrack = getRawFixes().isEmpty();
|
||||
result = addWithoutLocking(fix, replace);
|
||||
this.notifyListeners((listener) -> listener.fixReceived(fix, trackedItem, trackName, firstFixInTrack));
|
||||
this.notifyListeners((listener) -> listener.fixReceived(fix, getTrackedItem(), trackName, firstFixInTrack));
|
||||
} finally {
|
||||
unlockAfterWrite();
|
||||
}
|
||||
@@ -74,11 +72,6 @@ public class SensorFixTrackImpl<ItemType extends WithID & Serializable, FixT ext
|
||||
this.listeners.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemType getTrackedItem() {
|
||||
return trackedItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrackName() {
|
||||
return trackName;
|
||||
|
||||
+6
@@ -119,6 +119,7 @@ import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
@@ -298,6 +299,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
private final ConcurrentMap<Mark, GPSFixTrack<Mark, GPSFix>> markTracks;
|
||||
|
||||
/**
|
||||
* Mapping of {@link Competitor} to generic {@link DynamicTrack} implementation. Because the same competitor could
|
||||
* be mapped to several different tracks, a combined key of competitor object and track name identifier string is
|
||||
* used. This identifier is usually defined within the track interface (e.g. see {@link BravoFixTrack#TRACK_NAME}).
|
||||
*/
|
||||
private final Map<Pair<Competitor, String>, DynamicTrack<?>> sensorTracks;
|
||||
|
||||
private final Map<String, Sideline> courseSidelines;
|
||||
|
||||
+4
@@ -568,6 +568,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(TrackedRaceStatus newStatus) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(TrackingDataLoader loader, TrackedRaceStatus status) {
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
entryPointModules=
|
||||
filesCopiedToWebInfLib=gwt-servlet.jar
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDQgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48L2d3dC1jb21waWxlLXNldHRpbmdzPg\=\=
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDE1IC1zdHJpY3QgLWxvZ0xldmVsIFRSQUNFXV0+PC9leHRyYS1hcmdzPjx2bS1hcmdzPjwhW0NEQVRBWy1YbXgyMDI0bSAtRGd3dC51c2VhcmNoaXZlcz1mYWxzZV1dPjwvdm0tYXJncz48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuQWRtaW5Db25zb2xlPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC5ob21lLkhvbWU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLkxlYWRlcmJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnJlZ2F0dGFvdmVydmlldy5SZWdhdHRhT3ZlcnZpZXc8L2VudHJ5LXBvaW50LW1vZHVsZT48L2d3dC1jb21waWxlLXNldHRpbmdzPg\=\=
|
||||
@@ -448,8 +448,8 @@ img.openColumn {
|
||||
|
||||
.busyIndicator-Simple {
|
||||
position: absolute;
|
||||
top: 0.7em;
|
||||
right: 7.4em;
|
||||
top: 0.3em;
|
||||
right: 0.4em;
|
||||
}
|
||||
|
||||
.busyIndicator-Circle {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
|
||||
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
|
||||
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT" value="9876"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9876"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
|
||||
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
|
||||
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.CLASSIC_DEVMODE_CODE_SERVER_PORT_AUTO" value="true"/>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
top: -1000px;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.titleLabelWrapper {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
@@ -832,13 +836,10 @@ img.openColumn {
|
||||
}
|
||||
|
||||
.busyIndicator-Simple {
|
||||
position: absolute;
|
||||
top: 0.7em;
|
||||
right: 7.4em;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.busyIndicator-Circle {
|
||||
|
||||
}
|
||||
|
||||
.raceBoard-TitlePanel {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class AutoPlayEntryPoint extends AbstractMvpEntryPoint<StringMessages, Au
|
||||
@Override
|
||||
public void doOnModuleLoad() {
|
||||
AutoPlayAppClientFactory clientFactory = GWT.create(AutoPlayAppClientFactory.class);
|
||||
|
||||
|
||||
AutoPlayAppHistoryMapper applicationHistoryMapper = GWT.create(AutoPlayAppHistoryMapper.class);
|
||||
initMvp(clientFactory, applicationHistoryMapper, new AutoPlayAppActivityMapper(clientFactory));
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ public class DesktopApplicationClientFactory extends AbstractApplicationClientFa
|
||||
|
||||
@Override
|
||||
public StartView createStartView() {
|
||||
return new DesktopStartView(getPlaceNavigator(), getEventBus());
|
||||
return new DesktopStartView(getPlaceNavigator(), getEventBus(), getUserService());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ public class AutoPlayController implements RaceTimesInfoProviderListener, Leader
|
||||
playerView.clearContent();
|
||||
boolean withFullscreenButton = autoPlayerConfiguration.isFullscreenMode() && isInitialScreen;
|
||||
LeaderboardWithHeaderPerspective leaderboardPerspective = new LeaderboardWithHeaderPerspective(leaderboardPerspectiveLifecycleWithAllSettings,
|
||||
sailingService, asyncActionsExecutor,
|
||||
sailingService, userService, asyncActionsExecutor,
|
||||
new CompetitorSelectionModel(/* hasMultiSelection */ true), leaderboardTimer,
|
||||
autoPlayerConfiguration.getLeaderboardName(), errorReporter, StringMessages.INSTANCE,
|
||||
userAgent, withFullscreenButton);
|
||||
|
||||
+12
-9
@@ -27,6 +27,7 @@ import com.sap.sailing.gwt.autoplay.client.app.PlaceNavigator;
|
||||
import com.sap.sailing.gwt.autoplay.client.place.player.AutoPlayerConfiguration;
|
||||
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveLifecycle;
|
||||
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveSettings;
|
||||
import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication;
|
||||
import com.sap.sailing.gwt.common.client.SharedResources;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveLifecycle;
|
||||
@@ -34,15 +35,17 @@ import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.settings.Settings;
|
||||
import com.sap.sse.gwt.client.GWTLocaleUtil;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
import com.sap.sse.gwt.client.event.LocaleChangeEvent;
|
||||
import com.sap.sse.gwt.client.sapheader.SAPHeader;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeLifecycleTabbedSettingsDialog;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycle;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycleWithAllSettings;
|
||||
import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAuthentication;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
public class DesktopStartView extends Composite implements StartView {
|
||||
private static StartPageViewUiBinder uiBinder = GWT.create(StartPageViewUiBinder.class);
|
||||
@@ -50,7 +53,7 @@ public class DesktopStartView extends Composite implements StartView {
|
||||
interface StartPageViewUiBinder extends UiBinder<Widget, DesktopStartView> {
|
||||
}
|
||||
|
||||
@UiField(provided=true) SAPHeader sapHeader;
|
||||
@UiField(provided=true) SAPHeaderWithAuthentication sapHeader;
|
||||
@UiField(provided=true) ListBox localeSelectionBox;
|
||||
@UiField(provided=true) ListBox eventSelectionBox;
|
||||
@UiField(provided=true) ListBox leaderboardSelectionBox;
|
||||
@@ -73,15 +76,16 @@ public class DesktopStartView extends Composite implements StartView {
|
||||
|
||||
private final int defaultTimeToRaceStartTimeInSeconds = 180;
|
||||
|
||||
public DesktopStartView(PlaceNavigator navigator, EventBus eventBus) {
|
||||
public DesktopStartView(PlaceNavigator navigator, EventBus eventBus, UserService userService) {
|
||||
super();
|
||||
this.navigator = navigator;
|
||||
this.eventBus = eventBus;
|
||||
this.events = new ArrayList<EventDTO>();
|
||||
|
||||
sapHeader = new SAPHeaderWithAuthentication(StringMessages.INSTANCE.sapSailingAnalytics(),
|
||||
StringMessages.INSTANCE.autoplayConfiguration());
|
||||
new FixedSailingAuthentication(userService, sapHeader.getAuthenticationMenuView());
|
||||
|
||||
// sapHeader = new SAPHeaderWithAuthentication(StringMessages.INSTANCE.sapSailingAnalytics(), StringMessages.INSTANCE.autoplayConfiguration());
|
||||
sapHeader = new SAPHeader(StringMessages.INSTANCE.sapSailingAnalytics());
|
||||
sapHeader.setHeaderTitle(StringMessages.INSTANCE.autoplayConfiguration());
|
||||
eventSelectionBox = new ListBox();
|
||||
eventSelectionBox.setMultipleSelect(false);
|
||||
leaderboardSelectionBox = new ListBox();
|
||||
@@ -140,13 +144,12 @@ public class DesktopStartView extends Composite implements StartView {
|
||||
public void setEvents(List<EventDTO> events) {
|
||||
this.events.clear();
|
||||
this.events.addAll(events);
|
||||
|
||||
eventSelectionBox.addItem(StringMessages.INSTANCE.pleaseSelectAnEvent());
|
||||
for(EventDTO event: events) {
|
||||
for (EventDTO event : Util.sortNamedCollection(events)) {
|
||||
eventSelectionBox.addItem(event.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@UiHandler("eventSelectionBox")
|
||||
void onEventSelectionChange(ChangeEvent event) {
|
||||
EventDTO selectedEvent = getSelectedEvent();
|
||||
|
||||
+8
-6
@@ -3,24 +3,26 @@ package com.sap.sailing.gwt.autoplay.client.shared.header;
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication;
|
||||
import com.sap.sailing.gwt.common.client.FullscreenUtil;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.sapheader.SAPHeader;
|
||||
import com.sap.sse.gwt.client.shared.components.AbstractCompositeComponent;
|
||||
import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAuthentication;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
public class SAPHeaderComponent extends AbstractCompositeComponent<SAPHeaderComponentSettings> {
|
||||
private SAPHeaderComponentSettings settings;
|
||||
private final SAPHeaderComponentLifecycle componentLifecycle;
|
||||
|
||||
private final SAPHeader sapHeader;
|
||||
private final SAPHeaderWithAuthentication sapHeader;
|
||||
|
||||
public SAPHeaderComponent(SAPHeaderComponentLifecycle componentLifecycle, SAPHeaderComponentSettings settings,
|
||||
public SAPHeaderComponent(SAPHeaderComponentLifecycle componentLifecycle, UserService userService, SAPHeaderComponentSettings settings,
|
||||
StringMessages stringMessages, boolean startInAutoScreenMode) {
|
||||
this.componentLifecycle = componentLifecycle;
|
||||
this.settings = settings;
|
||||
this.sapHeader = new SAPHeader(stringMessages.sapSailingAnalytics());
|
||||
|
||||
sapHeader.setHeaderTitle(settings.getTitle());
|
||||
this.sapHeader = new SAPHeaderWithAuthentication(stringMessages.sapSailingAnalytics(), settings.getTitle());
|
||||
new FixedSailingAuthentication(userService, sapHeader.getAuthenticationMenuView());
|
||||
|
||||
initWidget(sapHeader);
|
||||
|
||||
if (startInAutoScreenMode) {
|
||||
|
||||
+6
-6
@@ -30,6 +30,7 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
|
||||
import com.sap.sse.gwt.client.shared.perspective.AbstractPerspectiveComposite;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycleWithAllSettings;
|
||||
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
/**
|
||||
* A perspective managing a header with a single leaderboard filling the rest of the screen.
|
||||
@@ -40,14 +41,14 @@ import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||
public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposite<LeaderboardWithHeaderPerspectiveLifecycle,
|
||||
LeaderboardWithHeaderPerspectiveSettings> implements LeaderboardUpdateProvider {
|
||||
private final DockLayoutPanel dockPanel;
|
||||
private final static int SAP_HEADER_HEIGHT = 70;
|
||||
private final static int SAP_HEADER_HEIGHT = 75;
|
||||
private final Widget currentContentWidget;
|
||||
private final LeaderboardPanel leaderboardPanel;
|
||||
private final StringMessages stringMessages;
|
||||
private final PerspectiveLifecycleWithAllSettings<LeaderboardWithHeaderPerspectiveLifecycle, LeaderboardWithHeaderPerspectiveSettings> componentLifecyclesAndSettings;
|
||||
|
||||
public LeaderboardWithHeaderPerspective(PerspectiveLifecycleWithAllSettings<LeaderboardWithHeaderPerspectiveLifecycle, LeaderboardWithHeaderPerspectiveSettings> perspectiveLifecycleWithAllSettings,
|
||||
SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
SailingServiceAsync sailingService, UserService userService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
CompetitorSelectionProvider competitorSelectionProvider, Timer timer,
|
||||
String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
UserAgentDetails userAgent, boolean startInFullScreenMode) {
|
||||
@@ -62,9 +63,8 @@ public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposi
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SAPHeaderComponentLifecycle sapHeaderLifecycle = getPerspectiveLifecycle().getSapHeaderLifecycle();
|
||||
SAPHeaderComponent sapHeader = createSAPHeader(sapHeaderLifecycle,
|
||||
SAPHeaderComponent sapHeader = createSAPHeader(sapHeaderLifecycle, userService,
|
||||
perspectiveLifecycleWithAllSettings.findComponentSettingsByLifecycle(sapHeaderLifecycle),
|
||||
stringMessages, startInFullScreenMode);
|
||||
leaderboardPanel = createLeaderboardPanel(sailingService, asyncActionsExecutor,
|
||||
@@ -183,9 +183,9 @@ public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposi
|
||||
leaderboardPanel.removeLeaderboardUpdateListener(listener);
|
||||
}
|
||||
|
||||
private SAPHeaderComponent createSAPHeader(SAPHeaderComponentLifecycle componentLifecycle, SAPHeaderComponentSettings settings,
|
||||
private SAPHeaderComponent createSAPHeader(SAPHeaderComponentLifecycle componentLifecycle, UserService userService, SAPHeaderComponentSettings settings,
|
||||
final StringMessages stringMessages, boolean withFullscreenButton) {
|
||||
return new SAPHeaderComponent(componentLifecycle, settings, stringMessages, withFullscreenButton);
|
||||
return new SAPHeaderComponent(componentLifecycle, userService, settings, stringMessages, withFullscreenButton);
|
||||
}
|
||||
|
||||
private LeaderboardPanel createLeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
|
||||
+4
@@ -6,6 +6,10 @@ import com.sap.sse.security.ui.authentication.generic.sapheader.FixedFlyoutAuthe
|
||||
import com.sap.sse.security.ui.authentication.view.AuthenticationMenuView;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
/**
|
||||
* Sailing-specific extension of {@link GenericAuthentication} using a {@link FixedFlyoutAuthenticationView}, which is
|
||||
* suitable in cases where the header is fixed on top of the page.
|
||||
*/
|
||||
public class FixedSailingAuthentication extends GenericAuthentication {
|
||||
private static final CommonSharedResources res = CommonSharedResources.INSTANCE;
|
||||
|
||||
|
||||
+4
@@ -6,6 +6,10 @@ import com.sap.sse.security.ui.authentication.generic.sapheader.GenericFlyoutAut
|
||||
import com.sap.sse.security.ui.authentication.view.AuthenticationMenuView;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
/**
|
||||
* Sailing-specific extension of {@link GenericAuthentication} using a {@link GenericFlyoutAuthenticationView}, which is
|
||||
* suitable in cases where the header scrolls with the page.
|
||||
*/
|
||||
public class GenericSailingAuthentication extends GenericAuthentication {
|
||||
private static final CommonSharedResources res = CommonSharedResources.INSTANCE;
|
||||
|
||||
|
||||
+4
-11
@@ -2,9 +2,8 @@ package com.sap.sailing.gwt.common.authentication;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gwt.http.client.UrlBuilder;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.sap.sse.gwt.client.AbstractEntryPointLinkFactory;
|
||||
import com.sap.sse.gwt.settings.UrlBuilderUtil;
|
||||
import com.sap.sse.security.ui.authentication.generic.GenericAuthenticationLinkFactory;
|
||||
|
||||
public final class SailingAuthenticationEntryPointLinkFactory extends AbstractEntryPointLinkFactory implements
|
||||
@@ -17,7 +16,7 @@ public final class SailingAuthenticationEntryPointLinkFactory extends AbstractEn
|
||||
|
||||
@Override
|
||||
public final String createUserProfileLink() {
|
||||
return createEntryPointLink("/gwt/Home.html#/user/profile/:", new HashMap<String, String>());
|
||||
return createEntryPointLink("/gwt/Home.html", "/user/profile/:", new HashMap<String, String>());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,13 +29,7 @@ public final class SailingAuthenticationEntryPointLinkFactory extends AbstractEn
|
||||
return createFullQualifiedLink("/gwt/Home.html", "/user/passwordreset/:");
|
||||
}
|
||||
|
||||
private String createFullQualifiedLink(String baseUrl, String hash) {
|
||||
String path = createEntryPointLink(baseUrl, new HashMap<String, String>());
|
||||
UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
|
||||
for (String parameter : Window.Location.getParameterMap().keySet()) {
|
||||
urlBuilder.removeParameter(parameter);
|
||||
}
|
||||
return urlBuilder.setPath(path).setHash(hash).buildString();
|
||||
private String createFullQualifiedLink(String path, String hash) {
|
||||
return UrlBuilderUtil.createUrlBuilderFromCurrentLocationWithCleanParametersAndPathAndHash(path, hash).buildString();
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sailing.gwt.common.client.suggestion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sse.common.filter.AbstractListFilter;
|
||||
|
||||
public abstract class AbstractListSuggestOracle<C> extends AbstractSuggestOracle<C> {
|
||||
|
||||
private final List<C> suggestionObjectList = new ArrayList<>();
|
||||
private final AbstractListFilter<C> suggestionMatchingFilter = new AbstractListFilter<C>() {
|
||||
@Override
|
||||
public Iterable<String> getStrings(C value) {
|
||||
return getMatchingStrings(value);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected final void getSuggestions(Request request, Callback callback, Iterable<String> queryTokens) {
|
||||
Iterable<C> filteredList = suggestionMatchingFilter.applyFilter(queryTokens, suggestionObjectList);
|
||||
this.setSuggestions(request, callback, filteredList, queryTokens);
|
||||
}
|
||||
|
||||
public final AbstractListFilter<C> getSuggestionMatchingFilter() {
|
||||
return suggestionMatchingFilter;
|
||||
}
|
||||
|
||||
public void setSelectableValues(Collection<C> selectableValues) {
|
||||
suggestionObjectList.clear();
|
||||
suggestionObjectList.addAll(selectableValues);
|
||||
}
|
||||
|
||||
protected abstract Iterable<String> getMatchingStrings(C value);
|
||||
|
||||
protected abstract String createSuggestionKeyString(C value);
|
||||
|
||||
protected abstract String createSuggestionAdditionalDisplayString(C value);
|
||||
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package com.sap.sailing.gwt.common.client.suggestion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
|
||||
import com.google.gwt.user.client.ui.SuggestOracle;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public abstract class AbstractSuggestOracle<T> extends SuggestOracle {
|
||||
|
||||
public final void setSuggestions(Request request, Callback callback, Iterable<T> suggestionObjects,
|
||||
Iterable<String> queryTokens) {
|
||||
List<Suggestion> suggestions = new ArrayList<>();
|
||||
List<String> normalizedQueryTokens = new ArrayList<>();
|
||||
for (String token : queryTokens) {
|
||||
normalizedQueryTokens.add(token.toLowerCase());
|
||||
}
|
||||
int count = 0;
|
||||
for (T match : suggestionObjects) {
|
||||
suggestions.add(new SimpleSuggestion(match, normalizedQueryTokens));
|
||||
if (++count >= request.getLimit()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Response response = new Response(suggestions);
|
||||
response.setMoreSuggestionsCount(Util.size(suggestionObjects) - count);
|
||||
callback.onSuggestionsReady(request, response);
|
||||
}
|
||||
|
||||
protected abstract void getSuggestions(Request request, Callback callback, Iterable<String> queryTokens);
|
||||
|
||||
protected abstract String createSuggestionKeyString(T value);
|
||||
|
||||
protected abstract String createSuggestionAdditionalDisplayString(T value);
|
||||
|
||||
@Override
|
||||
public final void requestDefaultSuggestions(Request request, Callback callback) {
|
||||
getSuggestions(request, callback, Collections.<String> emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void requestSuggestions(Request request, Callback callback) {
|
||||
String normalizedQuery = request.getQuery().trim();
|
||||
if (normalizedQuery == null || normalizedQuery.isEmpty()) {
|
||||
requestDefaultSuggestions(request, callback);
|
||||
} else {
|
||||
Iterable<String> queryTokens = Collections.singleton(normalizedQuery);
|
||||
getSuggestions(request, callback, queryTokens);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDisplayStringHTML() {
|
||||
return true;
|
||||
}
|
||||
|
||||
class SimpleSuggestion implements Suggestion {
|
||||
|
||||
private static final String STRONG_TAG_OPEN = "<strong>", STRONG_TAG_CLOSE = "</strong>";
|
||||
private static final String ITALIC_TAG_OPEN = "<em>", ITALIC_TAG_CLOSE = "</em>";
|
||||
private final T suggestObject;
|
||||
private final Iterable<String> queryTokens;
|
||||
|
||||
private SimpleSuggestion(T suggestObject, Iterable<String> queryTokens) {
|
||||
this.suggestObject = suggestObject;
|
||||
this.queryTokens = queryTokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayString() {
|
||||
SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
appendHighlighted(builder, createSuggestionKeyString(suggestObject));
|
||||
String additionalString = createSuggestionAdditionalDisplayString(suggestObject);
|
||||
if (additionalString != null && !additionalString.isEmpty()) {
|
||||
builder.append(SafeHtmlUtils.fromTrustedString(ITALIC_TAG_OPEN));
|
||||
builder.append(SafeHtmlUtils.fromSafeConstant(" - "));
|
||||
appendHighlighted(builder, additionalString);
|
||||
builder.append(SafeHtmlUtils.fromTrustedString(ITALIC_TAG_CLOSE));
|
||||
}
|
||||
return builder.toSafeHtml().asString();
|
||||
}
|
||||
|
||||
private void appendHighlighted(SafeHtmlBuilder builder, String displayString) {
|
||||
String normalizedString = displayString.toLowerCase();
|
||||
int cursor = 0;
|
||||
while (true) {
|
||||
int index = displayString.length();
|
||||
String matchToken = null;
|
||||
for (String token : queryTokens) {
|
||||
int matchIndex = normalizedString.indexOf(token, cursor);
|
||||
if (matchIndex >= 0 && matchIndex < index) {
|
||||
index = matchIndex;
|
||||
matchToken = token;
|
||||
}
|
||||
}
|
||||
if (matchToken != null) {
|
||||
builder.appendEscaped(displayString.substring(cursor, index));
|
||||
builder.append(SafeHtmlUtils.fromTrustedString(STRONG_TAG_OPEN));
|
||||
builder.appendEscaped(displayString.substring(index, index + matchToken.length()));
|
||||
builder.append(SafeHtmlUtils.fromTrustedString(STRONG_TAG_CLOSE));
|
||||
cursor = index + matchToken.length();
|
||||
} else {
|
||||
builder.appendEscaped(displayString.substring(cursor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacementString() {
|
||||
return createSuggestionKeyString(suggestObject);
|
||||
}
|
||||
|
||||
public T getSuggestObject() {
|
||||
return suggestObject;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.sap.sailing.gwt.common.client.suggestion;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.sap.sailing.domain.common.BoatClassMasterdata;
|
||||
|
||||
public class BoatClassMasterdataSuggestOracle extends AbstractListSuggestOracle<BoatClassMasterdata> {
|
||||
|
||||
public BoatClassMasterdataSuggestOracle() {
|
||||
this.setSelectableValues(Arrays.asList(BoatClassMasterdata.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createSuggestionKeyString(BoatClassMasterdata value) {
|
||||
return value.getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createSuggestionAdditionalDisplayString(BoatClassMasterdata value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<String> getMatchingStrings(BoatClassMasterdata value) {
|
||||
return value.getBoatClassNames();
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.sap.sailing.gwt.common.client.suggestion;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.event.logical.shared.SelectionEvent;
|
||||
import com.google.gwt.event.logical.shared.SelectionHandler;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.user.client.ui.SuggestBox;
|
||||
import com.google.gwt.user.client.ui.SuggestOracle;
|
||||
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
|
||||
public class CustomSuggestBox<T> extends SuggestBox {
|
||||
|
||||
public CustomSuggestBox(AbstractSuggestOracle<T> suggestOracle) {
|
||||
this(suggestOracle, new CustomSuggestionDisplay());
|
||||
}
|
||||
|
||||
public CustomSuggestBox(AbstractSuggestOracle<T> suggestOracle, SuggestionDisplay suggestionDisplay) {
|
||||
super(suggestOracle, new TextBox(), suggestionDisplay);
|
||||
}
|
||||
|
||||
public final HandlerRegistration addSuggestionSelectionHandler(final SuggestionSelectionHandler<T> handler) {
|
||||
return this.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
public void onSelection(SelectionEvent<Suggestion> event) {
|
||||
handler.onSuggestionSelected(
|
||||
((AbstractSuggestOracle<T>.SimpleSuggestion) event.getSelectedItem()).getSuggestObject());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface SuggestionSelectionHandler<T> {
|
||||
void onSuggestionSelected(T suggestObject);
|
||||
}
|
||||
|
||||
private static class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
|
||||
@Override
|
||||
protected void showSuggestions(SuggestBox suggestBox, Collection<? extends Suggestion> suggestions,
|
||||
boolean isDisplayStringHTML, boolean isAutoSelectEnabled, SuggestionCallback callback) {
|
||||
super.showSuggestions(suggestBox, suggestions, isDisplayStringHTML, isAutoSelectEnabled, callback);
|
||||
getPopupPanel().getElement().getStyle().setProperty("maxWidth", suggestBox.getOffsetWidth(), Unit.PX);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-4
@@ -18,7 +18,6 @@ import com.sap.sailing.gwt.home.desktop.places.start.StartActivityProxy;
|
||||
import com.sap.sailing.gwt.home.desktop.places.user.profile.UserProfileActivityProxy;
|
||||
import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewActivityProxy;
|
||||
import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewPlace;
|
||||
import com.sap.sailing.gwt.home.shared.ExperimentalFeatures;
|
||||
import com.sap.sailing.gwt.home.shared.SwitchingEntryPoint;
|
||||
import com.sap.sailing.gwt.home.shared.app.ApplicationPlaceUpdater;
|
||||
import com.sap.sailing.gwt.home.shared.places.error.ErrorPlace;
|
||||
@@ -69,9 +68,6 @@ public class DesktopActivityMapper implements ActivityMapper {
|
||||
} else if (place instanceof EventsPlace) {
|
||||
return new EventsActivityProxy((EventsPlace) place, clientFactory, clientFactory.getHomePlacesNavigator());
|
||||
} else if (place instanceof AbstractUserProfilePlace) {
|
||||
if (!ExperimentalFeatures.SHOW_USER_MANAGEMENT_ON_DESKTOP) {
|
||||
return new StartActivityProxy(new StartPlace(), clientFactory);
|
||||
}
|
||||
return new UserProfileActivityProxy((AbstractUserProfilePlace) place, clientFactory, clientFactory.getHomePlacesNavigator());
|
||||
} else if (place instanceof StartPlace) {
|
||||
return new StartActivityProxy((StartPlace) place, clientFactory);
|
||||
|
||||
+2
-112
@@ -1,128 +1,18 @@
|
||||
package com.sap.sailing.gwt.home.desktop.app;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.place.shared.PlaceController;
|
||||
import com.sap.sailing.gwt.home.desktop.places.aboutus.AboutUsPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.contact.ContactPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.multiregatta.regattastab.MultiregattaRegattasPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.AbstractEventRegattaPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.analyticstab.RegattaCompetitorAnalyticsPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.leaderboardtab.RegattaLeaderboardPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.overviewtab.RegattaOverviewPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.sponsoring.SponsoringPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewPlace.WhatsNewNavigationTabs;
|
||||
import com.sap.sailing.gwt.home.shared.app.AbstractPlaceNavigator;
|
||||
import com.sap.sailing.gwt.home.shared.app.HomePlacesNavigator;
|
||||
import com.sap.sailing.gwt.home.shared.app.PlaceNavigation;
|
||||
import com.sap.sailing.gwt.home.shared.places.event.AbstractEventPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.event.EventDefaultPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.events.EventsPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.fakeseries.SeriesDefaultPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.imprint.ImprintPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace.SolutionsNavigationTabs;
|
||||
import com.sap.sailing.gwt.home.shared.places.start.StartPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.confirmation.ConfirmationPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.confirmation.ConfirmationPlace.Action;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.passwordreset.PasswordResetPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.AbstractUserProfilePlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.UserProfileDefaultPlace;
|
||||
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
|
||||
|
||||
public class DesktopPlacesNavigator extends AbstractPlaceNavigator {
|
||||
public class DesktopPlacesNavigator extends HomePlacesNavigator {
|
||||
|
||||
protected DesktopPlacesNavigator(PlaceController placeController, boolean isStandaloneServer) {
|
||||
super(placeController, isStandaloneServer);
|
||||
}
|
||||
|
||||
public PlaceNavigation<StartPlace> getHomeNavigation() {
|
||||
return createGlobalPlaceNavigation(new StartPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<EventsPlace> getEventsNavigation() {
|
||||
return createGlobalPlaceNavigation(new EventsPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<SolutionsPlace> getSolutionsNavigation(SolutionsNavigationTabs navigationTab) {
|
||||
return createLocalPlaceNavigation(new SolutionsPlace(navigationTab));
|
||||
}
|
||||
|
||||
public PlaceNavigation<WhatsNewPlace> getWhatsNewNavigation(WhatsNewNavigationTabs navigationTab) {
|
||||
return createLocalPlaceNavigation(new WhatsNewPlace(navigationTab));
|
||||
}
|
||||
|
||||
public String getSimulatorURL() {
|
||||
Map<String, String> parameters = new HashMap<String, String>();
|
||||
return EntryPointLinkFactory.createSimulatorLink(parameters);
|
||||
}
|
||||
|
||||
public PlaceNavigation<SponsoringPlace> getSponsoringNavigation() {
|
||||
return createGlobalPlaceNavigation(new SponsoringPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<AboutUsPlace> getAboutUsNavigation() {
|
||||
return createGlobalPlaceNavigation(new AboutUsPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<ContactPlace> getContactNavigation() {
|
||||
return createGlobalPlaceNavigation(new ContactPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<ImprintPlace> getImprintNavigation() {
|
||||
return createGlobalPlaceNavigation(new ImprintPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<ConfirmationPlace> getMailVerifiedConfirmationNavigation() {
|
||||
return createLocalPlaceNavigation(new ConfirmationPlace(Action.MAIL_VERIFIED));
|
||||
}
|
||||
|
||||
public PlaceNavigation<ConfirmationPlace> getPasswordResettedConfirmationNavigation(String username) {
|
||||
return createLocalPlaceNavigation(new ConfirmationPlace(Action.RESET_EXECUTED, username));
|
||||
}
|
||||
|
||||
public PlaceNavigation<PasswordResetPlace> getPasswordResetNavigation() {
|
||||
return createLocalPlaceNavigation(new PasswordResetPlace());
|
||||
}
|
||||
|
||||
public PlaceNavigation<MultiregattaRegattasPlace> getEventRegattasNavigation(String eventUuidAsString, String baseUrl, boolean isOnRemoteServer) {
|
||||
MultiregattaRegattasPlace eventPlace = new MultiregattaRegattasPlace(eventUuidAsString);
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, eventPlace);
|
||||
}
|
||||
|
||||
public PlaceNavigation<SeriesDefaultPlace> getEventSeriesNavigation(String seriesId, String baseUrl, boolean isOnRemoteServer) {
|
||||
SeriesDefaultPlace place = new SeriesDefaultPlace(seriesId);
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, place);
|
||||
}
|
||||
|
||||
public PlaceNavigation<EventDefaultPlace> getEventNavigation(String eventUuidAsString, String baseUrl, boolean isOnRemoteServer) {
|
||||
EventDefaultPlace eventPlace = new EventDefaultPlace(eventUuidAsString);
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, eventPlace);
|
||||
}
|
||||
|
||||
public <P extends AbstractEventPlace> PlaceNavigation<P> getEventNavigation(P place, String baseUrl,
|
||||
boolean isOnRemoteServer) {
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, place);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public PlaceNavigation<AbstractEventRegattaPlace> getRegattaNavigation(String eventUuidAsString, String leaderboardIdAsNameString, String baseUrl, boolean isOnRemoteServer) {
|
||||
RegattaOverviewPlace eventPlace = new RegattaOverviewPlace(eventUuidAsString, leaderboardIdAsNameString);
|
||||
return (PlaceNavigation) createPlaceNavigation(baseUrl, isOnRemoteServer, eventPlace);
|
||||
}
|
||||
|
||||
public PlaceNavigation<RegattaCompetitorAnalyticsPlace> getCompetitorAnalyticsNavigation(String eventUuidAsString, String regattaId, String baseUrl, boolean isOnRemoteServer) {
|
||||
RegattaCompetitorAnalyticsPlace regattaPlace = new RegattaCompetitorAnalyticsPlace(eventUuidAsString, regattaId);
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, regattaPlace);
|
||||
}
|
||||
|
||||
public PlaceNavigation<RegattaLeaderboardPlace> getLeaderboardNavigation(String eventUuidAsString, String regattaId, String baseUrl, boolean isOnRemoteServer) {
|
||||
RegattaLeaderboardPlace regattaPlace = new RegattaLeaderboardPlace(eventUuidAsString, regattaId);
|
||||
return createPlaceNavigation(baseUrl, isOnRemoteServer, regattaPlace);
|
||||
}
|
||||
|
||||
public PlaceNavigation<? extends AbstractUserProfilePlace> getUserProfileNavigation() {
|
||||
return createLocalPlaceNavigation(new UserProfileDefaultPlace());
|
||||
}
|
||||
}
|
||||
-4
@@ -24,7 +24,6 @@ import com.sap.sailing.gwt.common.client.i18n.TextMessages;
|
||||
import com.sap.sailing.gwt.home.client.place.event.legacy.EventPlace;
|
||||
import com.sap.sailing.gwt.home.client.place.event.legacy.RegattaPlace;
|
||||
import com.sap.sailing.gwt.home.desktop.app.DesktopPlacesNavigator;
|
||||
import com.sap.sailing.gwt.home.shared.ExperimentalFeatures;
|
||||
import com.sap.sailing.gwt.home.shared.app.PlaceNavigation;
|
||||
import com.sap.sailing.gwt.home.shared.places.event.AbstractEventPlace;
|
||||
import com.sap.sailing.gwt.home.shared.places.events.EventsPlace;
|
||||
@@ -97,9 +96,6 @@ public class Header extends Composite {
|
||||
});
|
||||
|
||||
authenticationMenuView = new AuthenticationMenuViewImpl(usermenu, HeaderResources.INSTANCE.css().loggedin(), HeaderResources.INSTANCE.css().open());
|
||||
if (!ExperimentalFeatures.SHOW_USER_MANAGEMENT_ON_DESKTOP) {
|
||||
usermenu.removeFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("startPageLink")
|
||||
|
||||
+1
@@ -46,5 +46,6 @@ public class MainMedia extends Composite {
|
||||
for (SailingImageDTO image : photos) {
|
||||
imageCarousel.addImage(image);
|
||||
}
|
||||
imageCarousel.setInfiniteScrolling(true);
|
||||
}
|
||||
}
|
||||
+8
-12
@@ -5,16 +5,14 @@ import java.util.Collections;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Style.FontWeight;
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.home.communication.regatta.RegattaWithProgressDTO;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.multiregatta.EventMultiregattaView.Presenter;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.RefreshableWidget;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -31,6 +29,7 @@ public class MultiRegattaList extends Composite implements RefreshableWidget<Sor
|
||||
private final Presenter currentPresenter;
|
||||
private final Collection<String> selectableBoatCategories = new TreeSet<>();
|
||||
private final boolean showMessageIfEmpty;
|
||||
private boolean hasData = false;
|
||||
|
||||
public MultiRegattaList(Presenter presenter, boolean showMessageIfEmpty) {
|
||||
this.currentPresenter = presenter;
|
||||
@@ -46,8 +45,9 @@ public class MultiRegattaList extends Composite implements RefreshableWidget<Sor
|
||||
public void setListData(Collection<RegattaWithProgressDTO> data) {
|
||||
regattasContainerUi.clear();
|
||||
selectableBoatCategories.clear();
|
||||
if (data.isEmpty() && showMessageIfEmpty) {
|
||||
regattasContainerUi.add(getNoDataInfoWidget());
|
||||
hasData = !data.isEmpty();
|
||||
if (!hasData && showMessageIfEmpty) {
|
||||
regattasContainerUi.add(new InfoPlaceholder(StringMessages.INSTANCE.noDataForEvent()));
|
||||
}
|
||||
for (RegattaWithProgressDTO regattaWithProgress : data) {
|
||||
regattasContainerUi.add(new MultiRegattaListItem(regattaWithProgress, currentPresenter));
|
||||
@@ -58,18 +58,14 @@ public class MultiRegattaList extends Composite implements RefreshableWidget<Sor
|
||||
}
|
||||
}
|
||||
|
||||
private Widget getNoDataInfoWidget() {
|
||||
Label label = new Label(StringMessages.INSTANCE.noDataForEvent());
|
||||
label.getElement().getStyle().setMarginTop(1, Unit.EM);
|
||||
label.getElement().getStyle().setFontWeight(FontWeight.BOLD);
|
||||
return label;
|
||||
}
|
||||
|
||||
public Collection<String> getSelectableBoatCategories() {
|
||||
return selectableBoatCategories;
|
||||
}
|
||||
|
||||
public void setVisibleLeaderboardGroup(String leaderboardGroupName) {
|
||||
if(!hasData) {
|
||||
return;
|
||||
}
|
||||
for (int i=0; i < regattasContainerUi.getWidgetCount(); i++) {
|
||||
MultiRegattaListItem item = (MultiRegattaListItem) regattasContainerUi.getWidget(i);
|
||||
item.setVisibilityDependingOnLeaderboardGroup(leaderboardGroupName);
|
||||
|
||||
+1
-1
@@ -225,7 +225,7 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
|
||||
@Override
|
||||
public void onBusyStateChange(boolean busyState) {
|
||||
busyIndicator.setBusy(busyState);
|
||||
if(delegate != null) {
|
||||
if (delegate != null) {
|
||||
delegate.setBusyState(busyState);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,8 +36,8 @@ public abstract class RaceviewerLaunchPad extends Composite {
|
||||
}
|
||||
if (data.isRunning() || data.isFinished()) {
|
||||
addItem(data, RaceviewerLaunchPadMenuItem.START_ANALYSIS);
|
||||
addItem(data, RaceviewerLaunchPadMenuItem.WINNING_LANES);
|
||||
}
|
||||
addItem(data, RaceviewerLaunchPadMenuItem.WINNING_LANES);
|
||||
sinkEvents(Event.ONCLICK);
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -23,10 +23,12 @@ import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView.Pr
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.RegattaAnalyticsDataManager;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.SharedLeaderboardRegattaTabView;
|
||||
import com.sap.sailing.gwt.home.shared.ExperimentalFeatures;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.Placeholder;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
@@ -63,6 +65,11 @@ public class RegattaCompetitorAnalyticsTabView extends SharedLeaderboardRegattaT
|
||||
|
||||
@Override
|
||||
public void start(RegattaCompetitorAnalyticsPlace myPlace, AcceptsOneWidget contentArea) {
|
||||
if(currentPresenter.getRegattaMetadata() == null) {
|
||||
contentArea.setWidget(new InfoPlaceholder(StringMessages.INSTANCE.noDataForEvent()));
|
||||
return;
|
||||
}
|
||||
|
||||
contentArea.setWidget(new Placeholder());
|
||||
String regattaId = currentPresenter.getRegattaId();
|
||||
|
||||
|
||||
+7
@@ -17,8 +17,10 @@ import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.RegattaAnalyticsDataManager;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.SharedLeaderboardRegattaTabView;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView.Presenter;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.Placeholder;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
|
||||
/**
|
||||
@@ -58,6 +60,11 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView<R
|
||||
|
||||
@Override
|
||||
public void start(final RegattaLeaderboardPlace myPlace, final AcceptsOneWidget contentArea) {
|
||||
if(currentPresenter.getRegattaMetadata() == null) {
|
||||
contentArea.setWidget(new InfoPlaceholder(StringMessages.INSTANCE.noDataForEvent()));
|
||||
return;
|
||||
}
|
||||
|
||||
contentArea.setWidget(new Placeholder());
|
||||
String regattaId = currentPresenter.getRegattaId();
|
||||
if (regattaId != null && !regattaId.isEmpty()) {
|
||||
|
||||
+24
-16
@@ -12,7 +12,9 @@ import com.sap.sailing.gwt.home.communication.event.GetLiveRacesForRegattaAction
|
||||
import com.sap.sailing.gwt.home.communication.event.GetRegattaStatisticsAction;
|
||||
import com.sap.sailing.gwt.home.communication.event.GetRegattaWithProgressAction;
|
||||
import com.sap.sailing.gwt.home.communication.event.minileaderboard.GetMiniLeaderbordAction;
|
||||
import com.sap.sailing.gwt.home.communication.event.statistics.GetEventStatisticsAction;
|
||||
import com.sap.sailing.gwt.home.communication.eventview.EventViewDTO.EventType;
|
||||
import com.sap.sailing.gwt.home.communication.eventview.HasRegattaMetadata;
|
||||
import com.sap.sailing.gwt.home.communication.eventview.HasRegattaMetadata.RegattaState;
|
||||
import com.sap.sailing.gwt.home.communication.regatta.RegattaWithProgressDTO;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.eventstage.EventOverviewStage;
|
||||
@@ -54,11 +56,7 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView<
|
||||
|
||||
@Override
|
||||
public TabView.State getState() {
|
||||
if (currentPresenter.getEventDTO().getType() == EventType.MULTI_REGATTA) {
|
||||
return State.VISIBLE;
|
||||
} else {
|
||||
return State.VISIBLE;
|
||||
}
|
||||
return State.VISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,27 +64,37 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView<
|
||||
liveRacesListUi = new LiveRacesList(currentPresenter, false);
|
||||
stageUi = new EventOverviewStage(currentPresenter);
|
||||
statisticsBoxUi = new StatisticsBox(false);
|
||||
standingsUi = new StandingsList(currentPresenter.getRegattaMetadata().getState() == RegattaState.FINISHED, currentPresenter.getRegattaLeaderboardNavigation(currentPresenter.getRegattaId()));
|
||||
final HasRegattaMetadata regattaMetadata = currentPresenter.getRegattaMetadata();
|
||||
standingsUi = new StandingsList(regattaMetadata != null && regattaMetadata.getState() == RegattaState.FINISHED, currentPresenter.getRegattaLeaderboardNavigation(currentPresenter.getRegattaId()));
|
||||
|
||||
initWidget(ourUiBinder.createAndBindUi(this));
|
||||
|
||||
RefreshManager refreshManager = new RefreshManagerWithErrorAndBusy(this, contentArea, currentPresenter.getDispatch(), currentPresenter.getErrorAndBusyClientFactory());
|
||||
refreshManager.add(new RefreshableWidget<RegattaWithProgressDTO>() {
|
||||
@Override
|
||||
public void setData(RegattaWithProgressDTO data) {
|
||||
regattaInfoContainerUi.setWidget(new MultiRegattaListItem(data, true));
|
||||
}
|
||||
}, new GetRegattaWithProgressAction(currentPresenter.getEventDTO().getId(), currentPresenter.getRegattaId()));
|
||||
if (currentPresenter.getRegattaMetadata() != null) {
|
||||
refreshManager.add(new RefreshableWidget<RegattaWithProgressDTO>() {
|
||||
@Override
|
||||
public void setData(RegattaWithProgressDTO data) {
|
||||
regattaInfoContainerUi.setWidget(new MultiRegattaListItem(data, true));
|
||||
}
|
||||
}, new GetRegattaWithProgressAction(currentPresenter.getEventDTO().getId(),
|
||||
currentPresenter.getRegattaId()));
|
||||
}
|
||||
|
||||
if (currentPresenter.getEventDTO().getType() == EventType.MULTI_REGATTA) {
|
||||
stageUi.removeFromParent();
|
||||
} else {
|
||||
stageUi.setupRefresh(refreshManager);
|
||||
}
|
||||
refreshManager.add(liveRacesListUi.getRefreshable(), new GetLiveRacesForRegattaAction(currentPresenter.getEventDTO()
|
||||
.getId(), currentPresenter.getRegattaId()));
|
||||
refreshManager.add(standingsUi, new GetMiniLeaderbordAction(currentPresenter.getEventDTO().getId(), currentPresenter.getRegattaId(), 5));
|
||||
refreshManager.add(statisticsBoxUi, new GetRegattaStatisticsAction(currentPresenter.getEventDTO().getId(), currentPresenter.getRegattaId()));
|
||||
if(currentPresenter.getRegattaMetadata() != null) {
|
||||
refreshManager.add(liveRacesListUi.getRefreshable(), new GetLiveRacesForRegattaAction(currentPresenter.getEventDTO()
|
||||
.getId(), currentPresenter.getRegattaId()));
|
||||
refreshManager.add(standingsUi, new GetMiniLeaderbordAction(currentPresenter.getEventDTO().getId(), currentPresenter.getRegattaId(), 5));
|
||||
}
|
||||
refreshManager.add(statisticsBoxUi,
|
||||
currentPresenter.getRegattaMetadata() != null
|
||||
? new GetRegattaStatisticsAction(currentPresenter.getEventDTO().getId(),
|
||||
currentPresenter.getRegattaId())
|
||||
: new GetEventStatisticsAction(currentPresenter.getEventDTO().getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
@@ -46,6 +46,7 @@ import com.sap.sailing.gwt.home.shared.partials.filter.FilterPresenter;
|
||||
import com.sap.sailing.gwt.home.shared.partials.filter.FilterValueChangeHandler;
|
||||
import com.sap.sailing.gwt.home.shared.partials.filter.FilterWidget;
|
||||
import com.sap.sailing.gwt.home.shared.partials.filter.RacesByCompetitorTextBoxFilter;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder;
|
||||
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionPresenter;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.ActionProvider.AbstractActionProvider;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.RefreshManager;
|
||||
@@ -105,6 +106,11 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView<Reg
|
||||
|
||||
@Override
|
||||
public void start(RegattaRacesPlace myPlace, final AcceptsOneWidget contentArea) {
|
||||
if(currentPresenter.getRegattaMetadata() == null) {
|
||||
contentArea.setWidget(new InfoPlaceholder(StringMessages.INSTANCE.noDataForEvent()));
|
||||
return;
|
||||
}
|
||||
|
||||
listNavigationPanelUi = new ListNavigationPanel<Navigation>(new RegattaRacesTabViewNavigationSelectionCallback());
|
||||
listNavigationPanelUi.setAdditionalWidget(competitorFilterUi);
|
||||
liveRacesListUi = new LiveRacesList(currentPresenter, false);
|
||||
|
||||
-2
@@ -3,8 +3,6 @@
|
||||
xmlns:um="urn:import:com.sap.sailing.gwt.home.shared.usermanagement.decorator">
|
||||
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
|
||||
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:style>
|
||||
</ui:style>
|
||||
<um:AuthorizedContentDecoratorDesktop ui:field="decoratorUi">
|
||||
<um:content>
|
||||
<ud:UserAccountDetails ui:field="accountDetailsUi"
|
||||
|
||||
+13
-103
@@ -1,133 +1,43 @@
|
||||
package com.sap.sailing.gwt.home.desktop.places.user.profile.preferencestab;
|
||||
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.gwt.home.communication.event.SimpleCompetitorWithIdDTO;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.CompetitorSuggestionResult;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.FavoriteBoatClassesDTO;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.FavoriteCompetitorsDTO;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.FavoritesResult;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.GetCompetitorSuggestionAction;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.GetFavoritesAction;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.SaveFavoriteBoatClassesAction;
|
||||
import com.sap.sailing.gwt.home.communication.user.profile.SaveFavoriteCompetitorsAction;
|
||||
import com.sap.sailing.gwt.home.desktop.places.user.profile.UserProfileClientFactory;
|
||||
import com.sap.sailing.gwt.home.desktop.places.user.profile.UserProfileView;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.AbstractSuggestedMultiSelectionBoatClassDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.AbstractSuggestedMultiSelectionCompetitorDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionBoatClassDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionCompetitorDataProvider;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.VoidResult;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.preferences.UserPreferencesPresenter;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.preferences.UserPreferencesView;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.preferences.UserPreferencesView.Presenter;
|
||||
import com.sap.sse.security.ui.authentication.app.AuthenticationContext;
|
||||
|
||||
public class UserProfilePreferencesPresenter implements UserProfilePreferencesView.Presenter {
|
||||
|
||||
private final UserProfilePreferencesView view;
|
||||
private final UserProfileView.Presenter userProfilePresenter;
|
||||
private final SuggestedMultiSelectionCompetitorDataProvider competitorDataProvider =
|
||||
new SuggestedMultiSelectionCompetitorDataProviderImpl();
|
||||
private final SuggestedMultiSelectionBoatClassDataProvider boatClassDataProvider =
|
||||
new SuggestedMultiSelectionBoatClassDataProviderImpl();
|
||||
private final UserPreferencesView.Presenter userPreferencesPresenter;
|
||||
|
||||
public UserProfilePreferencesPresenter(final UserProfilePreferencesView view,
|
||||
final UserProfileView.Presenter userProfilePresenter) {
|
||||
this.view = view;
|
||||
this.userProfilePresenter = userProfilePresenter;
|
||||
this.userPreferencesPresenter = new UserPreferencesPresenter<UserProfileClientFactory>(
|
||||
userProfilePresenter.getClientFactory());
|
||||
view.setPresenter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Presenter getUserPreferencesPresenter() {
|
||||
return userPreferencesPresenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticationContext(AuthenticationContext authenticationContext) {
|
||||
view.getDecorator().setAuthenticationContext(authenticationContext);
|
||||
if (authenticationContext.isLoggedIn()) {
|
||||
userProfilePresenter.getClientFactory().getDispatch().execute(new GetFavoritesAction(),
|
||||
new AsyncCallback<FavoritesResult>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
userProfilePresenter.getClientFactory()
|
||||
.createErrorView("Error while loading notification preferences!", caught);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(FavoritesResult result) {
|
||||
initFavoriteCompetitors(result.getFavoriteCompetitors());
|
||||
initFavoriteBoatClasses(result.getFavoriteBoatClasses());
|
||||
}
|
||||
});
|
||||
userPreferencesPresenter.loadPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void initFavoriteCompetitors(FavoriteCompetitorsDTO favoriteCompetitors) {
|
||||
competitorDataProvider.initNotifications(favoriteCompetitors.isNotifyAboutResults());
|
||||
competitorDataProvider.initSelectedItems(favoriteCompetitors.getSelectedCompetitors());
|
||||
}
|
||||
|
||||
private void initFavoriteBoatClasses(FavoriteBoatClassesDTO favoriteBoatClasses) {
|
||||
boatClassDataProvider.initNotifications(favoriteBoatClasses.isNotifyAboutUpcomingRaces(),
|
||||
favoriteBoatClasses.isNotifyAboutResults());
|
||||
boatClassDataProvider.initSelectedItems(favoriteBoatClasses.getSelectedBoatClasses());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTriggerLoginForm() {
|
||||
userProfilePresenter.doTriggerLoginForm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestedMultiSelectionBoatClassDataProvider getFavoriteBoatClassesDataProvider() {
|
||||
return boatClassDataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestedMultiSelectionCompetitorDataProvider getFavoriteCompetitorsDataProvider() {
|
||||
return competitorDataProvider;
|
||||
}
|
||||
|
||||
private class SuggestedMultiSelectionBoatClassDataProviderImpl
|
||||
extends AbstractSuggestedMultiSelectionBoatClassDataProvider {
|
||||
@Override
|
||||
protected void persist(FavoriteBoatClassesDTO favorites) {
|
||||
userProfilePresenter.getClientFactory().getDispatch().execute(
|
||||
new SaveFavoriteBoatClassesAction(favorites), new SaveAsyncCallback());
|
||||
}
|
||||
}
|
||||
|
||||
private class SuggestedMultiSelectionCompetitorDataProviderImpl
|
||||
extends AbstractSuggestedMultiSelectionCompetitorDataProvider {
|
||||
|
||||
@Override
|
||||
protected void getSuggestions(Iterable<String> queryTokens, int limit,
|
||||
final SuggestionItemsCallback<SimpleCompetitorWithIdDTO> callback) {
|
||||
userProfilePresenter.getClientFactory().getDispatch().execute(
|
||||
new GetCompetitorSuggestionAction(queryTokens, limit),
|
||||
new AsyncCallback<CompetitorSuggestionResult>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Window.alert("Error while loading competitor suggestion");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(CompetitorSuggestionResult result) {
|
||||
callback.setSuggestionItems(result.getValues());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void persist(FavoriteCompetitorsDTO favorites) {
|
||||
userProfilePresenter.getClientFactory().getDispatch().execute(new SaveFavoriteCompetitorsAction(favorites),
|
||||
new SaveAsyncCallback());
|
||||
}
|
||||
}
|
||||
|
||||
private class SaveAsyncCallback implements AsyncCallback<VoidResult> {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
userProfilePresenter.getClientFactory().createErrorView(
|
||||
"Error while saving notification preferences!", caught);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(VoidResult result) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -1,8 +1,7 @@
|
||||
package com.sap.sailing.gwt.home.desktop.places.user.profile.preferencestab;
|
||||
|
||||
import com.google.gwt.user.client.ui.IsWidget;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionBoatClassDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionCompetitorDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.preferences.UserPreferencesView;
|
||||
import com.sap.sse.security.ui.authentication.app.NeedsAuthenticationContext;
|
||||
import com.sap.sse.security.ui.authentication.decorator.NotLoggedInPresenter;
|
||||
|
||||
@@ -13,7 +12,6 @@ public interface UserProfilePreferencesView extends IsWidget {
|
||||
NeedsAuthenticationContext getDecorator();
|
||||
|
||||
public interface Presenter extends NotLoggedInPresenter, NeedsAuthenticationContext {
|
||||
SuggestedMultiSelectionBoatClassDataProvider getFavoriteBoatClassesDataProvider();
|
||||
SuggestedMultiSelectionCompetitorDataProvider getFavoriteCompetitorsDataProvider();
|
||||
UserPreferencesView.Presenter getUserPreferencesPresenter();
|
||||
}
|
||||
}
|
||||
+3
-83
@@ -1,22 +1,12 @@
|
||||
package com.sap.sailing.gwt.home.desktop.places.user.profile.preferencestab;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.DivElement;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.HasEnabled;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.dto.BoatClassDTO;
|
||||
import com.sap.sailing.gwt.home.communication.event.SimpleCompetitorWithIdDTO;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelection;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionBoatClassDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelectionCompetitorDataProvider;
|
||||
import com.sap.sailing.gwt.home.shared.partials.multiselection.SuggestedMultiSelection.NotificationCallback;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.preferences.UserPreferences;
|
||||
import com.sap.sailing.gwt.home.shared.usermanagement.decorator.AuthorizedContentDecoratorDesktop;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.security.ui.authentication.app.NeedsAuthenticationContext;
|
||||
|
||||
public class UserProfilePreferencesViewImpl extends Composite implements UserProfilePreferencesView {
|
||||
@@ -27,88 +17,18 @@ public class UserProfilePreferencesViewImpl extends Composite implements UserPro
|
||||
private static MyBinder uiBinder = GWT.create(MyBinder.class);
|
||||
|
||||
@UiField(provided = true) AuthorizedContentDecoratorDesktop decoratorUi;
|
||||
@UiField(provided = true) SuggestedMultiSelection<SimpleCompetitorWithIdDTO> favoriteCompetitorsSelctionUi;
|
||||
@UiField(provided = true) SuggestedMultiSelection<BoatClassDTO> favoriteBoatClassesSelctionUi;
|
||||
@UiField DivElement notificationsTextUi;
|
||||
@UiField(provided = true) UserPreferences userPreferencesUi;
|
||||
|
||||
@Override
|
||||
public void setPresenter(Presenter presenter) {
|
||||
decoratorUi = new AuthorizedContentDecoratorDesktop(presenter);
|
||||
favoriteCompetitorsSelctionUi = new CompetitorDisplayImpl(
|
||||
presenter.getFavoriteCompetitorsDataProvider()).selectionUi;
|
||||
favoriteBoatClassesSelctionUi = new BoatClassDisplayImpl(
|
||||
presenter.getFavoriteBoatClassesDataProvider()).selectionUi;
|
||||
userPreferencesUi = new UserPreferences(presenter.getUserPreferencesPresenter());
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
// TODO hide notificationsTextUi if the user's mail address is already verified
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeedsAuthenticationContext getDecorator() {
|
||||
return decoratorUi;
|
||||
}
|
||||
|
||||
private class CompetitorDisplayImpl implements SuggestedMultiSelectionCompetitorDataProvider.Display {
|
||||
private final SuggestedMultiSelection<SimpleCompetitorWithIdDTO> selectionUi;
|
||||
private final HasEnabled notifyAboutResultsUi;
|
||||
|
||||
private CompetitorDisplayImpl(final SuggestedMultiSelectionCompetitorDataProvider dataProvider) {
|
||||
selectionUi = SuggestedMultiSelection.forCompetitors(dataProvider, StringMessages.INSTANCE.favoriteCompetitors());
|
||||
notifyAboutResultsUi = selectionUi.addNotificationToggle(new NotificationCallback() {
|
||||
@Override
|
||||
public void onNotificationToggled(boolean enabled) {
|
||||
dataProvider.setNotifyAboutResults(enabled);
|
||||
}
|
||||
}, StringMessages.INSTANCE.notificationAboutNewResults());
|
||||
dataProvider.addDisplay(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedItems(Collection<SimpleCompetitorWithIdDTO> selectedItems) {
|
||||
selectionUi.setSelectedItems(selectedItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotifyAboutResults(boolean notifyAboutResults) {
|
||||
notifyAboutResultsUi.setEnabled(notifyAboutResults);
|
||||
}
|
||||
}
|
||||
|
||||
private class BoatClassDisplayImpl implements SuggestedMultiSelectionBoatClassDataProvider.Display {
|
||||
private final SuggestedMultiSelection<BoatClassDTO> selectionUi;
|
||||
private final HasEnabled notifyAboutUpcomingRacesUi;
|
||||
private final HasEnabled notifyAboutResultsUi;
|
||||
|
||||
private BoatClassDisplayImpl(final SuggestedMultiSelectionBoatClassDataProvider dataProvider) {
|
||||
selectionUi = SuggestedMultiSelection.forBoatClasses(dataProvider, StringMessages.INSTANCE.favoriteBoatClasses());
|
||||
notifyAboutUpcomingRacesUi = selectionUi.addNotificationToggle(new NotificationCallback() {
|
||||
@Override
|
||||
public void onNotificationToggled(boolean enabled) {
|
||||
dataProvider.setNotifyAboutUpcomingRaces(enabled);
|
||||
}
|
||||
}, StringMessages.INSTANCE.notificationAboutUpcomingRaces());
|
||||
notifyAboutResultsUi = selectionUi.addNotificationToggle(new NotificationCallback() {
|
||||
@Override
|
||||
public void onNotificationToggled(boolean enabled) {
|
||||
dataProvider.setNotifyAboutResults(enabled);
|
||||
}
|
||||
}, StringMessages.INSTANCE.notificationAboutNewResults());
|
||||
dataProvider.addDisplay(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedItems(Collection<BoatClassDTO> selectedItems) {
|
||||
selectionUi.setSelectedItems(selectedItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotifyAboutUpcomingRaces(boolean notifyAboutUpcomingRaces) {
|
||||
notifyAboutUpcomingRacesUi.setEnabled(notifyAboutUpcomingRaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotifyAboutResults(boolean notifyAboutResults) {
|
||||
notifyAboutResultsUi.setEnabled(notifyAboutResults);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 404 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user