mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-08 21:30:57 +00:00
Merge branch 'master' into bug1351
This commit is contained in:
commit
4fc4d4adee
7 files changed
+231
-51
No files matched your search
+2
@@ -685,4 +685,6 @@ public interface StringMessages extends Messages {
|
||||
String stopAllReplicas();
|
||||
String warningServerIsReplica();
|
||||
String dropReplicaConnection();
|
||||
String timePanelName();
|
||||
String competitorFilterExplanation();
|
||||
}
|
||||
+3
-1
@@ -677,4 +677,6 @@ raceAbandonedNoMoreRacingToday=Race abandoned - No more racing today
|
||||
raceAbandonedFurtherSignalsAshore=Race abandoned - Further signals ashore
|
||||
stopAllReplicas=Drop connection to all replicas (Dangerous!)
|
||||
warningServerIsReplica=ATTENTION: This server is configured to be a replica. Do NOT change anything or this will lead to strange effects!
|
||||
dropReplicaConnection=Drop connection
|
||||
dropReplicaConnection=Drop connection
|
||||
timePanelName=Time Control
|
||||
competitorFilterExplanation=The filter will show all competitors matching the criterias:
|
||||
+8
-5
@@ -14,7 +14,7 @@ dampeningInterval=Mittelungsintervall
|
||||
map=Karte
|
||||
race=Wettfahrt
|
||||
races=Wettfahrten
|
||||
tracked=verfolgt
|
||||
tracked=getracked
|
||||
time=Zeit
|
||||
timeTooltip=Zeit, die der Teilnehmer auf diesem Schenkel gesegelt ist.
|
||||
playSpeed=Abspielgeschwindigkeit
|
||||
@@ -75,7 +75,7 @@ rankGain=Platz +/-
|
||||
rankGainTooltip=Die Differenz zwischen der Platzierung im aktuellen und im vorangegangenen Schenkel
|
||||
sapSailingAnalytics=Sailing Analytics
|
||||
leaderboard=Ergebnisse
|
||||
leaderboards=Ranglistenlisten
|
||||
leaderboards=Ranglisten
|
||||
leaderboardSettings=Einstellungen Rangliste
|
||||
settings=Einstellungen
|
||||
selectAtLeastOneLegDetail=Mindestens ein Detail auswählen
|
||||
@@ -279,12 +279,12 @@ stepSizeMustBeGreaterThanNull=Die Schrittweite muss größer als 0 sein
|
||||
stepSizeInSeconds=Schrittweite in Sekunden
|
||||
refreshInterval=Aktualisierungs-Interval (s)
|
||||
refreshIntervalMustBeGreaterThanXSeconds=Aktualisierungs-Interval muss größer als {0} Sekunden sein
|
||||
competitorCharts=Competitor charts
|
||||
competitorCharts=Teilnehmer-Diagramme
|
||||
selectAtLeastOneCompetitor=Um die Teilnehmer-Diagramme anzeigen zu lassen, wähle mindestens einen Teilnehmer aus
|
||||
directionChange=Richtungsänderung
|
||||
speedChange=Geschwindigkeitsänderung
|
||||
degreesShort=deg
|
||||
untracked=Nicht verfolgt
|
||||
untracked=Nicht getracked
|
||||
delayForLiveMode=Verzögerung im Live-Modus:
|
||||
notAvailable=Nicht verfügbar
|
||||
details=Details
|
||||
@@ -663,4 +663,7 @@ raceAbandonedNoMoreRacingToday=Rennabbruch - heute keine Rennen mehr
|
||||
raceAbandonedFurtherSignalsAshore=Rennabbruch - weitere Signale an Land
|
||||
stoppAllReplicas=Verbindung zu allen Replicas abbrechen
|
||||
warningServerIsReplica=ACHTUNG: Dieser Server ist als Replica konfiguriert. Vermeiden Sie alle Operationen!
|
||||
dropReplicaConnection=Verbindung abbrechen
|
||||
dropReplicaConnection=Verbindung abbrechen
|
||||
timePanelName=Zeitregler
|
||||
competitorFilterExplanation=Der Filter zeigt alle Teilnehmer, welche folgende Bedingungen erfüllen:
|
||||
The filter will show all competitors matching the criterias:
|
||||
+1
-1
@@ -489,7 +489,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
|
||||
@Override
|
||||
public String getLocalizedShortName() {
|
||||
return "Time control";
|
||||
return stringMessages.timePanelName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -251,7 +251,7 @@ public abstract class AbstractCompetitorsFilterSetDialog extends DataEntryDialog
|
||||
competitorsFiltersGridHeadline.setVisible(showGridHeadline);
|
||||
competitorsFiltersGridFooter.setVisible(showGridFooter);
|
||||
if(showGridHeadline) {
|
||||
competitorsFiltersGridHeadline.setText("The filter will show all competitors matching the criterias:");
|
||||
competitorsFiltersGridHeadline.setText(stringMessages.competitorFilterExplanation());
|
||||
}
|
||||
if(showGridFooter) {
|
||||
competitorsFiltersGridFooter.setText("");
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package com.sap.sailing.server.gateway.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.stub;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.CombinedWindTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
|
||||
/**
|
||||
* After refactoring the {@link WindJsonGetServlet} to first copy the relevant fixes to hold the read lock only
|
||||
* for a short time, this test uses the old implementation to compute the expected result and compares the
|
||||
* output received from the servlet.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class WindJsonGetServletRefactoringTest {
|
||||
private WindJsonGetServlet servlet;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
servlet = new WindJsonGetServlet();
|
||||
}
|
||||
|
||||
private JSONObject getExpectedResult(String windSourceToRead, TrackedRace trackedRace, TimePoint from, TimePoint to) {
|
||||
JSONObject jsonWindTracks = new JSONObject();
|
||||
List<WindSource> windSources = servlet.getAvailableWindSources(trackedRace);
|
||||
for (WindSource windSource : windSources) {
|
||||
if("ALL".equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
|
||||
JSONArray jsonWindArray = new JSONArray();
|
||||
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
|
||||
while (windIter.hasNext()) {
|
||||
Wind wind = windIter.next();
|
||||
if (wind.getTimePoint().compareTo(to) > 0) {
|
||||
break;
|
||||
}
|
||||
JSONObject jsonWind = new JSONObject();
|
||||
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
|
||||
jsonWind.put("knotspeed", wind.getKnots());
|
||||
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
|
||||
if (wind.getTimePoint() != null) {
|
||||
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
|
||||
jsonWind.put("dampenedtruebearingdeg",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
|
||||
.getBearing().getDegrees());
|
||||
jsonWind.put("dampenedknotspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
|
||||
jsonWind.put("dampenedmeterspersecondspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
|
||||
.getMetersPerSecond());
|
||||
}
|
||||
if (wind.getPosition() != null) {
|
||||
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
|
||||
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
|
||||
}
|
||||
jsonWindArray.add(jsonWind);
|
||||
}
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
jsonWindTracks.put(windSource.toString(), jsonWindArray);
|
||||
}
|
||||
}
|
||||
return jsonWindTracks;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareOldAndNewImplementation() {
|
||||
TrackedRace trackedRace = mock(TrackedRace.class);
|
||||
final WindSourceImpl webWindSource = new WindSourceImpl(WindSourceType.WEB);
|
||||
when(trackedRace.getWindSources()).thenReturn(Arrays.asList(new WindSource[] { webWindSource }));
|
||||
RaceDefinition race = mock(RaceDefinition.class);
|
||||
when(race.getName()).thenReturn("Race Name");
|
||||
when(trackedRace.getRace()).thenReturn(race);
|
||||
Set<WindSource> noWindSourcesToExclude = Collections.emptySet();
|
||||
when(trackedRace.getWindSourcesToExclude()).thenReturn(noWindSourcesToExclude);
|
||||
WindTrackImpl webWindTrack = new WindTrackImpl(/* millisecondsOverWhichToAverage */ 30000, /* useSpeed */ true, /* nameForReadWriteLock */ "compareOldAndNewImplementation");
|
||||
when(trackedRace.getOrCreateWindTrack(webWindSource)).thenReturn(webWindTrack);
|
||||
WindSourceImpl combinedWindSource = new WindSourceImpl(WindSourceType.COMBINED);
|
||||
CombinedWindTrackImpl combinedWindTrack = new CombinedWindTrackImpl(trackedRace, /* baseConfidence */ 0.5);
|
||||
when(trackedRace.getOrCreateWindTrack(combinedWindSource)).thenReturn(combinedWindTrack);
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
final TimePoint earlier = now.minus(10000);
|
||||
final TimePoint later = now.plus(10000);
|
||||
final Matcher<TimePoint> isTooEarlyOrTooLate = new ArgumentMatcher<TimePoint>() {
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
return item instanceof TimePoint &&
|
||||
((TimePoint) item).before(earlier);
|
||||
}
|
||||
};
|
||||
final Matcher<TimePoint> isNeitherTooEarlyNorTooLate = new ArgumentMatcher<TimePoint>() {
|
||||
@Override
|
||||
public boolean matches(Object argument) {
|
||||
return !isTooEarlyOrTooLate.matches(argument);
|
||||
}
|
||||
};
|
||||
stub(trackedRace.getWind(isA(Position.class), argThat(isTooEarlyOrTooLate))).toReturn(null);
|
||||
Wind someCombinedWindFix = new WindImpl(new DegreePosition(50, 4), now,
|
||||
new KnotSpeedWithBearingImpl(15, new DegreeBearingImpl(234)));
|
||||
stub(trackedRace.getWind(isA(Position.class), argThat(isNeitherTooEarlyNorTooLate))).toReturn(someCombinedWindFix);
|
||||
webWindTrack.add(new WindImpl(new DegreePosition(49, 3), now,
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
assertEquals(getExpectedResult(WindJsonGetServlet.ALL, trackedRace, earlier, later),
|
||||
servlet.getResult(WindJsonGetServlet.ALL, trackedRace, earlier, later));
|
||||
}
|
||||
}
|
||||
+75
-43
@@ -2,8 +2,10 @@ package com.sap.sailing.server.gateway.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -26,6 +28,8 @@ import com.sap.sailing.server.gateway.AbstractJsonHttpServlet;
|
||||
import com.sap.sailing.util.InvalidDateException;
|
||||
|
||||
public class WindJsonGetServlet extends AbstractJsonHttpServlet {
|
||||
static final String ALL = "ALL";
|
||||
|
||||
private static final long serialVersionUID = -1408004464252437535L;
|
||||
|
||||
private static final String PARAM_NAME_WINDSOURCE = "windsource";
|
||||
@@ -45,7 +49,7 @@ public class WindJsonGetServlet extends AbstractJsonHttpServlet {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
String windSourceParam = req.getParameter(PARAM_NAME_WINDSOURCE);
|
||||
String windSourceToRead = windSourceParam != null ? windSourceParam : "ALL";
|
||||
String windSourceToRead = windSourceParam != null ? windSourceParam : ALL;
|
||||
|
||||
TrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
|
||||
|
||||
@@ -66,54 +70,82 @@ public class WindJsonGetServlet extends AbstractJsonHttpServlet {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject jsonWindTracks = new JSONObject();
|
||||
List<WindSource> windSources = getAvailableWindSources(trackedRace);
|
||||
for (WindSource windSource : windSources) {
|
||||
if("ALL".equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
|
||||
JSONArray jsonWindArray = new JSONArray();
|
||||
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
|
||||
while (windIter.hasNext()) {
|
||||
Wind wind = windIter.next();
|
||||
if (wind.getTimePoint().compareTo(to) > 0) {
|
||||
break;
|
||||
}
|
||||
JSONObject jsonWind = new JSONObject();
|
||||
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
|
||||
jsonWind.put("knotspeed", wind.getKnots());
|
||||
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
|
||||
if (wind.getTimePoint() != null) {
|
||||
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
|
||||
jsonWind.put("dampenedtruebearingdeg",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
|
||||
.getBearing().getDegrees());
|
||||
jsonWind.put("dampenedknotspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
|
||||
jsonWind.put("dampenedmeterspersecondspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
|
||||
.getMetersPerSecond());
|
||||
}
|
||||
if (wind.getPosition() != null) {
|
||||
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
|
||||
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
|
||||
}
|
||||
jsonWindArray.add(jsonWind);
|
||||
}
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
jsonWindTracks.put(windSource.toString(), jsonWindArray);
|
||||
}
|
||||
}
|
||||
JSONObject jsonWindTracks = getResult(windSourceToRead, trackedRace, from, to);
|
||||
setJsonResponseHeader(resp);
|
||||
jsonWindTracks.writeJSONString(resp.getWriter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<WindSource> getAvailableWindSources(TrackedRace trackedRace) {
|
||||
JSONObject getResult(String windSourceToRead, TrackedRace trackedRace, TimePoint from, TimePoint to) {
|
||||
Map<WindSource, List<Wind>> fixes = getRelevantFixes(windSourceToRead, trackedRace, from, to);
|
||||
JSONObject jsonWindTracks = getResultAsJsonObject(trackedRace, fixes);
|
||||
return jsonWindTracks;
|
||||
}
|
||||
|
||||
private Map<WindSource, List<Wind>> getRelevantFixes(String windSourceToRead, TrackedRace trackedRace, TimePoint from,
|
||||
TimePoint to) {
|
||||
// quickly extract relevant fixes to hold locks as shortly as possible; process later
|
||||
List<WindSource> windSources = getAvailableWindSources(trackedRace);
|
||||
Map<WindSource, List<Wind>> fixes = new HashMap<>();
|
||||
for (WindSource windSource : windSources) {
|
||||
if (ALL.equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
|
||||
ArrayList<Wind> fixesForWindSource = new ArrayList<>();
|
||||
fixes.put(windSource, fixesForWindSource);
|
||||
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
|
||||
while (windIter.hasNext()) {
|
||||
Wind wind = windIter.next();
|
||||
if (wind.getTimePoint().compareTo(to) > 0) {
|
||||
break;
|
||||
} else {
|
||||
fixesForWindSource.add(wind);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fixes;
|
||||
}
|
||||
|
||||
private JSONObject getResultAsJsonObject(TrackedRace trackedRace, Map<WindSource, List<Wind>> fixes) {
|
||||
JSONObject jsonWindTracks = new JSONObject();
|
||||
for (Map.Entry<WindSource, List<Wind>> e : fixes.entrySet()) {
|
||||
WindSource windSource = e.getKey();
|
||||
JSONArray jsonWindArray = new JSONArray();
|
||||
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
for (Wind wind : e.getValue()) {
|
||||
JSONObject jsonWind = new JSONObject();
|
||||
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
|
||||
jsonWind.put("knotspeed", wind.getKnots());
|
||||
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
|
||||
if (wind.getTimePoint() != null) {
|
||||
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
|
||||
jsonWind.put("dampenedtruebearingdeg",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getBearing()
|
||||
.getDegrees());
|
||||
jsonWind.put("dampenedknotspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
|
||||
jsonWind.put("dampenedmeterspersecondspeed",
|
||||
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
|
||||
.getMetersPerSecond());
|
||||
}
|
||||
if (wind.getPosition() != null) {
|
||||
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
|
||||
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
|
||||
}
|
||||
jsonWindArray.add(jsonWind);
|
||||
}
|
||||
jsonWindTracks.put(windSource.toString(), jsonWindArray);
|
||||
}
|
||||
return jsonWindTracks;
|
||||
}
|
||||
|
||||
List<WindSource> getAvailableWindSources(TrackedRace trackedRace) {
|
||||
List<WindSource> windSources = new ArrayList<WindSource>();
|
||||
for (WindSource windSource : trackedRace.getWindSources()) {
|
||||
windSources.add(windSource);
|
||||
|
||||
Reference in new issue
Block a user