From 499c751d65cd74378d6757add4c47c7c6ef613ea Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 23 May 2018 14:16:29 +0200 Subject: [PATCH 001/102] bug4584: Added Event ID Text Box in Swiss Timing Connector Tab. Upon changing (if not null) updates JSON Url Text Box with correct URL for the given Event ID. --- .../SwissTimingEventManagementPanel.java | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index b5885834af5..57428c1b1e0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -60,6 +60,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane private final CellTable raceTable; private final Map previousConfigurations; private final ListBox previousConfigurationsComboBox; + private final TextBox eventIdBox; private final TextBox jsonUrlBox; private final TextBox hostnameTextbox; private final IntegerBox portIntegerbox; @@ -83,7 +84,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane captionPanelConnections.setContentWidget(verticalPanel); captionPanelConnections.setStyleName("bold"); - Grid connectionsGrid = new Grid(5, 2); + Grid connectionsGrid = new Grid(6, 2); verticalPanel.add(connectionsGrid); previousConfigurations = new HashMap(); @@ -102,26 +103,42 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane } }); fillConfigurations(); - + jsonUrlBox = new TextBox(); jsonUrlBox.getElement().getStyle().setWidth(50, Unit.EM); connectionsGrid.setWidget(0, 0, new Label(stringMessages.swissTimingEvents() + ":")); connectionsGrid.setWidget(0, 1, previousConfigurationsComboBox); - connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-URL (json):")); - connectionsGrid.setWidget(1, 1, jsonUrlBox); + + eventIdBox = new TextBox(); + eventIdBox.getElement().getStyle().setWidth(20, Unit.EM); + connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-ID:")); + connectionsGrid.setWidget(1, 1, eventIdBox); + + eventIdBox.addChangeHandler(new ChangeHandler() { + @Override + public void onChange(ChangeEvent event) { + if (eventIdBox.getValue() != "") { + jsonUrlBox.setValue("http://manage2sail.com/api/public/links/event/" + eventIdBox.getValue() + + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json&includeRaces=true"); + } + } + }); + + connectionsGrid.setWidget(2, 0, new Label("Manage2Sail Event-URL (json):")); + connectionsGrid.setWidget(2, 1, jsonUrlBox); hostnameTextbox = new TextBox(); portIntegerbox = new IntegerBox(); - connectionsGrid.setWidget(2, 0, new Label(stringConstants.hostname() + ":")); - connectionsGrid.setWidget(2, 1, hostnameTextbox); + connectionsGrid.setWidget(3, 0, new Label(stringConstants.hostname() + ":")); + connectionsGrid.setWidget(3, 1, hostnameTextbox); - connectionsGrid.setWidget(3, 0, new Label(stringConstants.port() + ":")); - connectionsGrid.setWidget(3, 1, portIntegerbox); + connectionsGrid.setWidget(4, 0, new Label(stringConstants.port() + ":")); + connectionsGrid.setWidget(4, 1, portIntegerbox); Button btnListRaces = new Button(stringConstants.listRaces()); - connectionsGrid.setWidget(4, 1, btnListRaces); + connectionsGrid.setWidget(5, 1, btnListRaces); btnListRaces.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { From e332af4c91d07a82cfb8ed18553b8c84f5b244fc Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 23 May 2018 20:45:31 +0200 Subject: [PATCH 002/102] bug4584: Using lambda notation for ChangeHandler of eventIdBox in SwissTimingEventManagmentPanel --- .../SwissTimingEventManagementPanel.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index 57428c1b1e0..467cb0df9ce 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -114,17 +114,14 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane eventIdBox.getElement().getStyle().setWidth(20, Unit.EM); connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-ID:")); connectionsGrid.setWidget(1, 1, eventIdBox); - - eventIdBox.addChangeHandler(new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - if (eventIdBox.getValue() != "") { - jsonUrlBox.setValue("http://manage2sail.com/api/public/links/event/" + eventIdBox.getValue() - + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json&includeRaces=true"); - } + + eventIdBox.addChangeHandler(event -> { + if (eventIdBox.getValue() != "") { + jsonUrlBox.setValue("http://manage2sail.com/api/public/links/event/" + eventIdBox.getValue() + + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json&includeRaces=true"); } }); - + connectionsGrid.setWidget(2, 0, new Label("Manage2Sail Event-URL (json):")); connectionsGrid.setWidget(2, 1, jsonUrlBox); From d2d4abc272bbb87adfce12a49ae505cb998b6311 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Tue, 12 Jun 2018 21:55:02 +0200 Subject: [PATCH 003/102] Adjusted getSpeedWithBearingSteps() to return steps with time points which ALWAYS correspond to non-raw fixes --- .../sailing/domain/tracking/GPSFixTrack.java | 12 ++-- .../domain/tracking/impl/GPSFixTrackImpl.java | 59 +++++-------------- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java index a91c230a693..4e2674fd8ac 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFixTrack.java @@ -175,11 +175,13 @@ public interface GPSFixTrack extends MappedTra void resumeValidityCaching(); /** - * Gets a list of bearings between the provided time range (inclusive the boundaries). The bearings are retrieved by - * means of {@link GPSFixTrack#getEstimatedSpeed(TimePoint)}. The first and last bearing steps will be always - * sampled at provided {@code fromTimePoint} and {@code toTimePoint}, whereas the steps between are sampled at time - * points of non-raw GPS fixes. The idea of this concept is to produce at least two bearing steps as result, in - * order to provide the caller at least a {@code totalCourseChangeAngleInDegrees > 0} between the given time range. + * Gets a list of speed with bearing steps considering the provided time range. The bearings are retrieved by means + * of {@link GPSFixTrack#getEstimatedSpeed(TimePoint)}. The steps are sampled at time points of non-raw GPS fixes. + * When there is no non-raw fix contained at {@code fromTimePoint}, the time point of the first step will be the + * time point of the first non-raw fix before {@code fromTimePoint}. Analogously, when there is no non-raw fix + * contained at {@code toTimePoint}, the last step's time point will be the first non-raw fix after + * {@code toTimePoint}. The idea of this concept is to produce at least two steps as part of the result, in order to + * provide the caller at least a {@code |totalCourseChangeAngleInDegrees > 0|} between the given time range. * * @param fromTimePoint * The from time point (inclusive) for resulting bearing steps diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java index b50674511a1..ab0e6249971 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java @@ -1171,20 +1171,13 @@ public abstract class GPSFixTrackImpl extends Bearing lastCourse = null; TimePoint lastTimePoint = null; double lastCourseChangeAngleInDegrees = 0; - TimePoint fixTimePointAfterToTimePoint = null; try { lockForRead(); - TimePoint timePoint = fromTimePoint; - for (Iterator iterator = getFixesIterator(fromTimePoint, false); iterator - .hasNext(); timePoint = iterator.next().getTimePoint()) { - if (timePoint == null) { - continue; - } - if (timePoint.after(toTimePoint)) { - fixTimePointAfterToTimePoint = timePoint; - timePoint = toTimePoint; - } - SpeedWithBearing estimatedSpeed = getEstimatedSpeed(timePoint); + FixType firstFix = getLastFixAtOrBefore(fromTimePoint); + TimePoint currentTimePoint = firstFix == null ? fromTimePoint : firstFix.getTimePoint(); + for (Iterator iterator = getFixesIterator(currentTimePoint, true); iterator + .hasNext(); currentTimePoint = iterator.next().getTimePoint()) { + SpeedWithBearing estimatedSpeed = getEstimatedSpeed(currentTimePoint); if (estimatedSpeed != null) { Bearing course = estimatedSpeed.getBearing(); /* @@ -1195,44 +1188,20 @@ public abstract class GPSFixTrackImpl extends double courseChangeAngleInDegrees = lastCourse == null ? 0 : lastCourse.getDifferenceTo(course, new DegreeBearingImpl(lastCourseChangeAngleInDegrees)) .getDegrees(); + double turningRateInDegreesPerSecond = lastTimePoint == null ? 0 + : Math.abs(courseChangeAngleInDegrees + / lastTimePoint.until(currentTimePoint).asSeconds()); - // Fix distorted turning rate due to inappropriate interpolation of getEstimatedSpeed() at first - // and last step - double courseChangeInDegreesForTurningRateCalculation = courseChangeAngleInDegrees; - Duration durationBetweenStepsForTurningRateCalculation = lastTimePoint == null ? null - : lastTimePoint.until(timePoint); - if (fromTimePoint.equals(lastTimePoint)) { - FixType firstFix = getLastFixAtOrBefore(fromTimePoint); - if (firstFix != null && !firstFix.getTimePoint().equals(fromTimePoint)) { - SpeedWithBearing firstFixEstimatedSpeed = getEstimatedSpeed(firstFix.getTimePoint()); - if (firstFixEstimatedSpeed != null) { - durationBetweenStepsForTurningRateCalculation = firstFix.getTimePoint() - .until(timePoint); - courseChangeInDegreesForTurningRateCalculation = courseChangeAngleInDegrees - + firstFixEstimatedSpeed.getBearing().getDifferenceTo(lastCourse).getDegrees(); - } - } - } else if (fixTimePointAfterToTimePoint != null && lastCourse != null) { - SpeedWithBearing lastFixEstimatedSpeed = getEstimatedSpeed(fixTimePointAfterToTimePoint); - if (lastFixEstimatedSpeed != null) { - durationBetweenStepsForTurningRateCalculation = lastTimePoint == null ? null - : lastTimePoint.until(fixTimePointAfterToTimePoint); - courseChangeInDegreesForTurningRateCalculation = courseChangeAngleInDegrees + estimatedSpeed - .getBearing().getDifferenceTo(lastFixEstimatedSpeed.getBearing()).getDegrees(); - } - } - - double turningRateInDegreesPerSecond = durationBetweenStepsForTurningRateCalculation == null ? 0 - : Math.abs(courseChangeInDegreesForTurningRateCalculation - / durationBetweenStepsForTurningRateCalculation.asSeconds()); - - speedWithBearingSteps.add(new SpeedWithBearingStepImpl(timePoint, estimatedSpeed, + speedWithBearingSteps.add(new SpeedWithBearingStepImpl(currentTimePoint, estimatedSpeed, courseChangeAngleInDegrees, turningRateInDegreesPerSecond)); + if (currentTimePoint.after(toTimePoint)) { + break; + } lastCourse = course; lastCourseChangeAngleInDegrees = courseChangeAngleInDegrees; - lastTimePoint = timePoint; + lastTimePoint = currentTimePoint; } - if (!timePoint.before(toTimePoint)) { + if (!currentTimePoint.before(toTimePoint)) { break; } } From 7507ec39e9df3ad2c7ad0acbabd8756bff118235 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Tue, 12 Jun 2018 23:26:31 +0200 Subject: [PATCH 004/102] Improved maneuver splitting at mark passing time points --- .../impl/ManeuverDetectorImpl.java | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 0a32506111d..a7080e68424 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -990,19 +990,50 @@ public class ManeuverDetectorImpl implements ManeuverDetector { SpeedWithBearingStepsIterable speedWithBearingSteps, TimePoint timePoint) { List stepsBefore = new ArrayList<>(); List stepsAfter = new ArrayList<>(); + SpeedWithBearingStep lastEntry = null; for (SpeedWithBearingStep entry : speedWithBearingSteps) { if (!entry.getTimePoint().after(timePoint)) { - if (stepsBefore.isEmpty()) { - // First bearing step supposed to have 0 as course change as - // it does not have any previous steps with bearings to compute bearing difference. - // If the condition is not met, the existing code which uses ManeuverBearingStep class will break. - entry = new SpeedWithBearingStepImpl(entry.getTimePoint(), entry.getSpeedWithBearing(), 0.0, 0.0); - } stepsBefore.add(entry); } if (!entry.getTimePoint().before(timePoint)) { if (stepsAfter.isEmpty()) { - entry = new SpeedWithBearingStepImpl(entry.getTimePoint(), entry.getSpeedWithBearing(), 0.0, 0.0); + // First step supposed to have 0 as course change as it does not have any previous steps to compute + // bearing difference. If the condition is not met, the existing code which uses + // SpeedWithBearingStepsIterable class will break. + if (lastEntry != null && lastEntry.getTimePoint().before(timePoint)) { + // If there is not any step located at the splitting time point, we need to retrieve the + // interpolated speed with bearing at time point in order to produce boundary step for both step + // sets at time point. If we will not do it, the course change between the steps split by + // splitting time point will be lost. + SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(timePoint); + if (speedWithBearing != null) { + double courseChangeAngleInDegrees = lastEntry.getSpeedWithBearing().getBearing() + .getDifferenceTo(speedWithBearing.getBearing(), + new DegreeBearingImpl(lastEntry.getCourseChangeInDegrees())) + .getDegrees(); + double turningRateInDegreesPerSecond = Math.abs( + courseChangeAngleInDegrees / lastEntry.getTimePoint().until(timePoint).asSeconds()); + SpeedWithBearingStep lastStepBefore = new SpeedWithBearingStepImpl(timePoint, + speedWithBearing, courseChangeAngleInDegrees, turningRateInDegreesPerSecond); + stepsBefore.add(lastStepBefore); + SpeedWithBearingStep firstStepAfter = new SpeedWithBearingStepImpl(timePoint, + speedWithBearing, 0.0, 0.0); + stepsAfter.add(firstStepAfter); + courseChangeAngleInDegrees = firstStepAfter.getSpeedWithBearing().getBearing() + .getDifferenceTo(speedWithBearing.getBearing(), + new DegreeBearingImpl(firstStepAfter.getCourseChangeInDegrees())) + .getDegrees(); + turningRateInDegreesPerSecond = Math.abs( + courseChangeAngleInDegrees / timePoint.until(entry.getTimePoint()).asSeconds()); + entry = new SpeedWithBearingStepImpl(entry.getTimePoint(), entry.getSpeedWithBearing(), + courseChangeAngleInDegrees, turningRateInDegreesPerSecond); + } + + } + if (stepsAfter.isEmpty()) { + entry = new SpeedWithBearingStepImpl(entry.getTimePoint(), entry.getSpeedWithBearing(), 0.0, + 0.0); + } } stepsAfter.add(entry); } From 176afbaf7e9af1c52cbdfa1d67e1ea45bb74da88 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 15 Jun 2018 11:15:30 +0200 Subject: [PATCH 005/102] bug4614: created a sortable column that takes a signed value for speed change and maneuverloss in ManeuverTablePanel.java --- .../racemap/maneuver/ManeuverTablePanel.java | 90 +++++++++++++++++-- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index 35c94d9fa89..706446d3333 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -148,27 +148,30 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createSortableMinMaxColumn( +/** + * Creates a sortable column with the absolute value. + * Whereas {@link #createSortableMinMaxColumn()} creates a sortable column with signed values. + */ + private SortableColumn createSortableAbsMinMaxColumn( Function extractor, String title, String unit) { final SortableColumn col = new AbstractSortableColumnWithMinMax( new TextCell(), SortingOrder.ASCENDING) { @@ -236,6 +239,77 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createSortableMinMaxColumn( + Function extractor, String title, String unit) { + final SortableColumn col = new AbstractSortableColumnWithMinMax( + new TextCell(), SortingOrder.ASCENDING) { + final InvertibleComparator comparator = new InvertibleComparatorAdapter() { + @Override + public int compare(ManeuverTableData o1, ManeuverTableData o2) { + Double o1v = extractor.apply(o1); + Double o2v = extractor.apply(o2); + if (o1v == null && o2v == null) { + return 0; + } + if (o1v == null && o2v != null) { + return -1; + } + if (o1v != null && o2v == null) { + return 1; + } + return Double.compare(o1v, o2v); + } + }; + final HasStringAndDoubleValue dataProvider = new HasStringAndDoubleValue() { + @Override + public String getStringValueToRender(ManeuverTableData row) { + Double value = extractor.apply(row); + if (value == null) { + return null; + } + return towDigitAccuracy.format(value); + } + + @Override + public Double getDoubleValue(ManeuverTableData row) { + Double value = extractor.apply(row); + return value == null ? null : value; + } + }; + + final MinMaxRenderer renderer = new MinMaxRenderer(dataProvider, comparator); + + @Override + public InvertibleComparator getComparator() { + return comparator; + } + + @Override + public void render(Context context, ManeuverTableData object, SafeHtmlBuilder sb) { + renderer.render(context, object, title, sb); + } + + @Override + public Header getHeader() { + return new TextHeader(title + " [" + unit + "]"); + } + + @Override + public String getValue(ManeuverTableData object) { + return dataProvider.getStringValueToRender(object); + } + + @Override + public void updateMinMax() { + renderer.updateMinMax(maneuverCellTable.getDataProvider().getList()); + } + }; + col.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); + return col; + } private SortableColumn createManeuverTypeColumn() { return new SortableColumn(new TextCell(), SortingOrder.ASCENDING) { From 62b1eccc2b135a946cb88a4928aa490d6ea63c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Fri, 15 Jun 2018 11:22:06 +0200 Subject: [PATCH 006/102] bug3262 initial youtube v3 support --- .../sailing/gwt/ui/client/MediaService.java | 5 ++ .../gwt/ui/client/MediaServiceAsync.java | 2 + .../gwt/ui/client/media/NewMediaDialog.java | 81 ++++++------------- .../gwt/ui/server/MediaServiceImpl.java | 41 ++++++++++ 4 files changed, 71 insertions(+), 58 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java index c2c0300d06a..fbb4bc17f00 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java @@ -35,4 +35,9 @@ public interface MediaService extends RemoteService { * Obtains a MediaTrack for the given literal url, if one exists, {@code null} otherwise */ MediaTrack getMediaTrackByUrl(String url); + + /** + * Obtains metadata from the youtube api + */ + VideoMetadataDTO checkYoutubeMetadata(String url); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java index 2139416441e..4e6b6d7a6cb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaServiceAsync.java @@ -35,4 +35,6 @@ public interface MediaServiceAsync { void getMediaTrackByUrl(String url, AsyncCallback asyncCallback); + void checkYoutubeMetadata(String url, AsyncCallback asyncCallback); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 2db2a59776e..8df0b98a8e3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -92,7 +92,6 @@ public class NewMediaDialog extends DataEntryDialog { this.stringMessages = stringMessages; this.raceIdentifier = raceIdentifier; this.mediaService = mediaService; - registerNativeMethods(); } @Override @@ -131,6 +130,7 @@ public class NewMediaDialog extends DataEntryDialog { } } + //used for audio only tracks, using native mediaelement to determine time private void loadMediaDuration() { MediaBase mediaBase = Audio.createIfSupported(); if (mediaBase != null) { @@ -221,7 +221,7 @@ public class NewMediaDialog extends DataEntryDialog { if (youtubeId != null) { mediaTrack.url = youtubeId; mediaTrack.mimeType = MimeType.youtube; - loadYoutubeMetadata(youtubeId); + loadYoutubeMetadata(); } else { mediaTrack.url = url; loadMediaDuration(); @@ -249,6 +249,26 @@ public class NewMediaDialog extends DataEntryDialog { } } + private void loadYoutubeMetadata() { + mediaService.checkYoutubeMetadata(mediaTrack.url, new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + } + + @Override + public void onSuccess(VideoMetadataDTO result) { + if (result.isDownloadable()) { + mediaTrack.duration = result.getDuration(); + mediaTrack.title = result.getMessage(); + refreshUI(); + } else { + + } + } + }); + } + private String sliceBefore(String lastPathSegment, String slicer) { int paramSegment = lastPathSegment.indexOf(slicer); if (paramSegment > 0) { @@ -257,64 +277,10 @@ public class NewMediaDialog extends DataEntryDialog { return lastPathSegment; } - private native void registerNativeMethods() /*-{ - var that = this; - window.youtubeMetadataCallback = function(metadata) { - var title = metadata.entry.media$group.media$title.$t; - var duration = metadata.entry.media$group.yt$duration.seconds; - var description = metadata.entry.media$group.media$description.$t; - that.@com.sap.sailing.gwt.ui.client.media.NewMediaDialog::youtubeMetadataCallback(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)(title, duration, description); - } - }-*/; - - /** - * Inspired by https://developers.google.com/web-toolkit/doc/latest/tutorial/Xsite - * - * @param youtubeId - */ - public native void loadYoutubeMetadata(String youtubeId) /*-{ - var that = this; - //Create temporary script element. - window.youtubeMetadataCallbackScript = document.createElement("script"); - window.youtubeMetadataCallbackScript.src = "http://gdata.youtube.com/feeds/api/videos/" - + youtubeId - + "?alt=json&orderby=published&format=6&callback=youtubeMetadataCallback"; - document.body.appendChild(window.youtubeMetadataCallbackScript); - - // Cancel meta data capturing after has 2-seconds timeout. - setTimeout( - function() { - //Remove temporary script element. - if (window != null && window.youtubeMetadataCallbackScript != null) { - document.body.removeChild(window.youtubeMetadataCallbackScript); - delete window.youtubeMetadataCallbackScript; - } - that.@com.sap.sailing.gwt.ui.client.media.NewMediaDialog::setBusy(Z)(false); - }, 2000); - }-*/; - public void setBusy(boolean busy) { busyIndicator.setBusy(busy); } - public void youtubeMetadataCallback(String title, String durationInSeconds, String description) { - busyIndicator.setBusy(false); - mediaTrack.title = title; - try { - long duration = (long) Math.round(1000 * Double - .valueOf(durationInSeconds)); - if (duration > 0) { - mediaTrack.duration = new MillisecondsDurationImpl(duration); - } else { - mediaTrack.duration = null; - } - } catch (NumberFormatException ex) { - mediaTrack.duration = null; - } - mediaTrack.startTime = this.defaultStartTime; - refreshUI(); - } - private void refreshUI() { titleBox.setValue(mediaTrack.title, DONT_FIRE_EVENTS); if (mediaTrack.isYoutube()) { @@ -372,11 +338,10 @@ public class NewMediaDialog extends DataEntryDialog { /** * For a given url that points to an mp4 video, attempts are made to parse the header, to determine the actual - * starttime of the video and to check for a 360° flag. The video will be analyzed by the backendserver, either via + * starttime of the video and to check for a 360� flag. The video will be analyzed by the backendserver, either via * direct download, or proxied by the client, if a video is only available locally. If the video header cannot be * read, default values are used instead. */ - // TODO Eclipse doesn't find any calls to this private method; can it be removed? private void checkMetadata(String url, Label lbl, AsyncCallback asyncCallback) { // check on server first mediaService.checkMetadata(mediaTrack.url, new AsyncCallback() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 4895301c4de..8e13901d189 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.server; +import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; @@ -7,17 +8,21 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; +import java.net.URLConnection; import java.nio.channels.Channels; +import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -37,6 +42,8 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import com.google.gwt.thirdparty.json.JSONException; +import com.google.gwt.thirdparty.json.JSONObject; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.VideoMetadataDTO; @@ -49,6 +56,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.impl.MillisecondsDurationImpl; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { + private String YOUTUBE_V3_API_KEY = "AIzaSyDYwPzLevXauI-kTSVXTLroLyHEONuF9Rw"; private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); @@ -57,6 +65,7 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); private ServiceTracker racingEventServiceTracker; + private static final int REQUIRED_SIZE_IN_BYTES = 10000000; private static final long serialVersionUID = -8917349579281305977L; @@ -344,4 +353,36 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi } return result; } + + @Override + public VideoMetadataDTO checkYoutubeMetadata(String videoId) { + ensureUserCanManageMedia(); + boolean canDownload = false; + String message = ""; + Duration duration = null; + try { + URL apiURL = new URL( + "https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key=" + YOUTUBE_V3_API_KEY + + "&part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)"); + URLConnection connection = apiURL.openConnection(); + connection.setConnectTimeout(METADATA_CONNECTION_TIMEOUT); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { + String pageText = reader.lines().collect(Collectors.joining("\n")); + JSONObject jsonAnswer = new JSONObject(pageText); + final JSONObject item = jsonAnswer.getJSONArray("items").getJSONObject(0); + message = item.getJSONObject("snippet").getString("title"); + String rawDuration = item.getJSONObject("contentDetails").getString("duration"); + duration = new MillisecondsDurationImpl(java.time.Duration.parse(rawDuration).toMillis()); + canDownload = true; + } catch (JSONException e) { + message = e.getMessage(); + logger.log(Level.WARNING, "Error in youtube metadata call", e); + } + } catch (IOException e) { + message = e.getMessage(); + logger.log(Level.WARNING, "Error in youtube metadata call", e); + } + return new VideoMetadataDTO(canDownload, duration, false, null, message); + } } From ed4c8983975dfd2a8c0798cdf42536d6493a41a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Fri, 15 Jun 2018 12:57:12 +0200 Subject: [PATCH 007/102] bug3262 improved error handling --- .../com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 8df0b98a8e3..621b71450b8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -254,6 +254,7 @@ public class NewMediaDialog extends DataEntryDialog { @Override public void onFailure(Throwable caught) { + infoLabel.setWidget(new Label(caught.getMessage())); } @Override @@ -263,7 +264,7 @@ public class NewMediaDialog extends DataEntryDialog { mediaTrack.title = result.getMessage(); refreshUI(); } else { - + infoLabel.setWidget(new Label(result.getMessage())); } } }); From 9987a9f967d138b5b6000ca29cd685c0a5476c5e Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 15 Jun 2018 17:02:14 +0200 Subject: [PATCH 008/102] bug4584: The eventId textbox in SwissTimingEventManagmentPanel can detect a eventId out of a part of any manage2sail url that contains at least a complete eventId. It will update the url field. The Json Textbox extracts the eventId out of a valid url and write the Id in the EventId textbox. Also API access token and base url are now variables. --- .../SwissTimingEventManagementPanel.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index 467cb0df9ce..dbab80ffa00 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -5,7 +5,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; - import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ChangeEvent; @@ -65,6 +64,10 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane private final TextBox hostnameTextbox; private final IntegerBox portIntegerbox; private final List availableSwissTimingRaces = new ArrayList(); + private final String manage2sailBaseAPIUrl = "http://manage2sail.com/api/public/links/event/"; + private final String manage2sailAPIaccessToken = "?accesstoken=bDAv8CwsTM94ujZ"; + private final String manage2sailUrlAppendix = "&mediaType=json&includeRaces=true"; + private final String eventIdPattern = "[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"; public SwissTimingEventManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, @@ -111,19 +114,23 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane connectionsGrid.setWidget(0, 1, previousConfigurationsComboBox); eventIdBox = new TextBox(); - eventIdBox.getElement().getStyle().setWidth(20, Unit.EM); + eventIdBox.getElement().getStyle().setWidth(30, Unit.EM); + eventIdBox.setTitle("Event ID or a url that contains the event Id from Manage2Sail"); connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-ID:")); connectionsGrid.setWidget(1, 1, eventIdBox); - eventIdBox.addChangeHandler(event -> { if (eventIdBox.getValue() != "") { - jsonUrlBox.setValue("http://manage2sail.com/api/public/links/event/" + eventIdBox.getValue() - + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json&includeRaces=true"); + createUrlFromEventId(eventIdBox.getValue()); } }); connectionsGrid.setWidget(2, 0, new Label("Manage2Sail Event-URL (json):")); connectionsGrid.setWidget(2, 1, jsonUrlBox); + jsonUrlBox.addChangeHandler(event -> { + if (jsonUrlBox.getValue() != "") { + createEventIdFromUrl(jsonUrlBox.getValue()); + } + }); hostnameTextbox = new TextBox(); portIntegerbox = new IntegerBox(); @@ -332,6 +339,32 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane }); } + /** + * This function tries to create a valid JsonUrl for any input given that matches the pattern of an event Id from + * M2S. If there is an event id detected the Json Url gets updated and the event Id textbox is filled with the + * detected event Id. The ID pattern is defined in {@link eventIdPattern}. + * + */ + private void createUrlFromEventId(String eventIdTextbox) { + if (eventIdTextbox.matches(".*" + eventIdPattern + ".*")) { + final String inferredEventId = eventIdTextbox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); + jsonUrlBox.setValue( + manage2sailBaseAPIUrl + inferredEventId + manage2sailAPIaccessToken + manage2sailUrlAppendix); + eventIdBox.setValue(inferredEventId); + } + } + + /** + * Similar to {@link #createUrlFromEventId()} this function tries to extract a M2S event Id by looking at the given + * url in the Json Url Textbox. + */ + private void createEventIdFromUrl(String jsonUrlTextBox) { + if (jsonUrlTextBox.matches("http://manage2sail.com/.*" + eventIdPattern + ".*")) { + final String inferredEventId = jsonUrlTextBox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); + eventIdBox.setValue(inferredEventId); + } + } + private ListHandler getRaceTableColumnSortHandler(List raceRecords, Column regattaNameColumn, Column seriesNameColumn, Column nameColumn, Column trackingStartColumn, From c08ab551ac01798a9d0f1d9089be2f323683f2a9 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Sun, 17 Jun 2018 13:50:31 +0200 Subject: [PATCH 009/102] Refined stable course analysis --- .../impl/ManeuverDetectorImpl.java | 31 +++---------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index a7080e68424..a05eae5ec19 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -72,13 +72,6 @@ public class ManeuverDetectorImpl implements ManeuverDetector { */ private static final double MIN_ANGULAR_VELOCITY_FOR_MAIN_CURVE_BOUNDARIES_IN_DEGREES_PER_SECOND = 0.2; - /** - * Defines the course change limit toward opposite direction related to the direction of maneuver main curve. If - * speed maxima or stable bearing analysis produce a curve extension which exceeds this limit, the extension gets - * rejected. - */ - private static final double MAX_COURSE_CHANGE_TOWARD_MANEUVER_OPPOSITE_DIRECTION_FOR_CURVE_EXTENSION_IN_DEGREES = 15.0; - /** * Tracked race whose tracks are being processed for maneuver detection. */ @@ -1387,25 +1380,11 @@ public class ManeuverDetectorImpl implements ManeuverDetector { if (isCourseChangeLimitExceededForCurveExtension(maneuverMainCurveDetails, maneuverStart)) { maneuverStart = null; } - TimePoint stableBearingAnalysisUntil = maneuverStart == null ? maneuverMainCurveDetails.getTimePointBefore() - : maneuverStart.getExtensionTimePoint(); + // Stable course analysis is considered as not necessary for preparation phase of maneuver because no + // oversteering is usually performed before maneuver Speed lowestSpeed = maneuverStart == null ? null : maneuverStart.getLowestSpeedWithinExtensionArea(); double courseChangeSinceManeuverMainCurveInDegrees = maneuverStart == null ? 0 : maneuverStart.getCourseChangeInDegreesWithinExtensionArea(); - stepsToAnalyze = getSpeedWithBearingStepsWithinTimeRange(stepsToAnalyze, earliestTimePointForSpeedTrendAnalysis, - stableBearingAnalysisUntil); - ManeuverCurveBoundaryExtension stableBearingExtension = findStableBearingWithMaxAbsCourseChangeSpeed( - stepsToAnalyze, true, MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS); - if (stableBearingExtension != null - && !isCourseChangeLimitExceededForCurveExtension(maneuverMainCurveDetails, stableBearingExtension)) { - maneuverStart = stableBearingExtension; - courseChangeSinceManeuverMainCurveInDegrees += stableBearingExtension - .getCourseChangeInDegreesWithinExtensionArea(); - if (lowestSpeed == null - || lowestSpeed.compareTo(stableBearingExtension.getLowestSpeedWithinExtensionArea()) > 0) { - lowestSpeed = stableBearingExtension.getLowestSpeedWithinExtensionArea(); - } - } return maneuverStart != null ? new ManeuverCurveBoundaryExtension(maneuverStart.getExtensionTimePoint(), maneuverStart.getSpeedWithBearingAtExtensionTimePoint(), @@ -1422,10 +1401,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { if (curveBoundaryExtension == null) { return false; } - return curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea() - * maneuverMainCurveDetails.getDirectionChangeInDegrees() < 0 - && Math.abs(curveBoundaryExtension - .getCourseChangeInDegreesWithinExtensionArea()) > MAX_COURSE_CHANGE_TOWARD_MANEUVER_OPPOSITE_DIRECTION_FOR_CURVE_EXTENSION_IN_DEGREES; + return Math.abs(curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea()) > Math + .abs(curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea()) / 2.0; } /** From 8b3e7c40a2933dadebeea3d09ee41cc8a90564b6 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 18 Jun 2018 00:23:22 +0200 Subject: [PATCH 010/102] Fixed double occurence of the first speed with bearing step --- .../com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java index ab0e6249971..f4266d5e422 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java @@ -1175,7 +1175,7 @@ public abstract class GPSFixTrackImpl extends lockForRead(); FixType firstFix = getLastFixAtOrBefore(fromTimePoint); TimePoint currentTimePoint = firstFix == null ? fromTimePoint : firstFix.getTimePoint(); - for (Iterator iterator = getFixesIterator(currentTimePoint, true); iterator + for (Iterator iterator = getFixesIterator(currentTimePoint, false); iterator .hasNext(); currentTimePoint = iterator.next().getTimePoint()) { SpeedWithBearing estimatedSpeed = getEstimatedSpeed(currentTimePoint); if (estimatedSpeed != null) { From c4f4d5e8d969f93e5540a4bc3476555780bdc542 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 18 Jun 2018 00:24:27 +0200 Subject: [PATCH 011/102] Added handling for mark-passing set with finish-line passing only --- .../maneuverdetection/impl/ManeuverDetectorImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index a05eae5ec19..2d5f7d4a5ae 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -534,7 +534,13 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } } if (latestRawFixTimePoint != null) { - return new TrackTimeInfo(earliestTrackRecord, latestTrackRecord, latestRawFixTimePoint); + if(!earliestTrackRecord.equals(latestTrackRecord)) { + return new TrackTimeInfo(earliestTrackRecord, latestTrackRecord, latestRawFixTimePoint); + } + GPSFixMoving firstRawFix = track.getFirstRawFix(); + if(firstRawFix != null) { + return new TrackTimeInfo(firstRawFix.getTimePoint(), latestRawFixTimePoint, latestRawFixTimePoint); + } } } } From c203cc53765ed617614e9a8ebede526170f30fc3 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 18 Jun 2018 16:30:57 +0200 Subject: [PATCH 012/102] Changed max turning rate threshold for stable course analysis to 1 --- .../maneuverdetection/impl/ManeuverDetectorImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 2d5f7d4a5ae..f248d9dbeae 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -64,7 +64,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * Defines the maximal absolute course change velocity in degrees per second that shall be regarded as a stable * course. */ - private static final double MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS = 2; + private static final double MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS = 1; /** * Defines the absolute course change in degrees between bearing steps to ignore in order to shorten the @@ -1302,7 +1302,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * approximate the beginning time point of the maneuver, the speed maximum is determined throughout forward in time * iteration of speed steps starting from time point of main curve beginning. From the determined speed maximum, the * iteration continues until the point, when the bearing changes occur only with a maximum of - * {@value #MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS} degrees per second, which is + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is * regarded as a stable course. The exiting time point of maneuver is approximated analogously by speed maximum * determination throughout backward in time iteration of speed steps starting from time of main curve end, followed * by a search for a point with stable course. @@ -1354,7 +1354,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * determined, the course changes get analyzed starting from {@code t'} until {@code (t -} * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() approx. maneuver duration}{@code )} in order to * locate the point where the bearing starts to change with a rate of maximal - * {@value #MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS} degrees per second, which is + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is * regarded as a stable course. * * @param maneuverMainCurveDetails @@ -1423,7 +1423,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * determined, the course changes get analyzed starting from {@code t'} until {@code (t +} * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() approx. maneuver duration} {@code * 3)} in order * to locate the point where the bearing starts to change with a rate of maximal - * {@value #MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS} degrees per second, which is + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is * regarded as a stable course. * * @param maneuverMainCurveDetails @@ -1464,7 +1464,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { stepsToAnalyze = getSpeedWithBearingStepsWithinTimeRange(stepsToAnalyze, stableBearingAnalysisFrom, latestTimePointForSpeedTrendAnalysis); ManeuverCurveBoundaryExtension stableBearingExtension = findStableBearingWithMaxAbsCourseChangeSpeed( - stepsToAnalyze, false, MAX_ABS_COURSE_CHANGE_IN_DEGREES_PER_SECOND_FOR_STABLE_BEARING_ANALYSIS); + stepsToAnalyze, false, MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS); if (stableBearingExtension != null && !isCourseChangeLimitExceededForCurveExtension(maneuverMainCurveDetails, stableBearingExtension)) { maneuverEnd = stableBearingExtension; From 25244390df049ae5f850c0dbca6fa3617f4ee200 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 18 Jun 2018 17:56:22 +0200 Subject: [PATCH 013/102] Reduced course change limit for maneuver section extension --- .../domain/maneuverdetection/impl/ManeuverDetectorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index f248d9dbeae..6572f25f378 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -1408,7 +1408,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return false; } return Math.abs(curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea()) > Math - .abs(curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea()) / 2.0; + .abs(curveBoundaryExtension.getCourseChangeInDegreesWithinExtensionArea()) / 3.0; } /** From fd2bc834a61a0159dae44ce9596088393210ecae Mon Sep 17 00:00:00 2001 From: "service.tip.git" Date: Thu, 21 Jun 2018 16:06:18 +0000 Subject: [PATCH 014/102] [INTERNAL] Translation delivery: commit by SLS Change-Id: I0df2d7c2484a590811f2c919776e0f14210fa9f2 --- .../ui/client/StringMessages_es.properties | 111 ------------------ .../ui/client/StringMessages_fr.properties | 111 ------------------ .../ui/client/StringMessages_ja.properties | 111 ------------------ .../ui/client/StringMessages_pt.properties | 111 ------------------ .../ui/client/StringMessages_ru.properties | 111 ------------------ .../ui/client/StringMessages_zh.properties | 111 ------------------ 6 files changed, 666 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 4812828ca75..3e6086b8051 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -9,12 +9,10 @@ trackedBefore=Historial de eventos rastreados general=General listRaces=Listar carreras listRegattas=Listar regatas -numberPairResultsPresenter=Diagrama de dispersión wind=Viento maneuverType=Maniobra windPanelLabel=Este es el panel de vientos, y hasta el momento está totalmente vacío. refresh=Refrescar -remove=Eliminar removeNumber=Eliminar ({0}) windSource=Fuente de vientos dampeningInterval=Intervalo de amortiguación @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=Carrera rastreada conectada al nombre de linkToColumn=Enlace a columna unlink=Quitar enlace leaderboardName=Nombre de tabla de clasificación -cancel=Cancelar pleaseEnterAName=Indique un nombre pleaseEnterABoatClass=Indique una clase de embarcación discardRacesFromHowManyStartedRacesOn=Descartar un inicio de carrera más con cuántas carreras iniciadas @@ -65,7 +62,6 @@ startingFromNumberOfRaces=Empezando por cuántas carreras renameLeaderboard=Cambiar nombre de tabla de clasificación addColumnToLeaderboard=Añadir columna a tabla de clasificación pleaseEnterNameForNewRaceColumn=Indique un nombre para la nueva columna de carrera -ok=OK medalRace=Medal Race renameRace=Cambiar nombre de carrera openSelectedLeaderboard=Abrir tabla de clasificación seleccionada @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=Tabla de clasificación leaderboards=Tablas de clasificación leaderboardSettings=Opciones de tabla de clasificación -settings=Opciones selectAtLeastOneLegDetail=Seleccione al menos un detalle de tramo currentSpeedOverGroundInKnots=SOG (speed over ground) currentSpeedOverGroundInKnotsTooltip=Velocidad actual sobre el fondo. @@ -173,7 +168,6 @@ tacks=Bordadas jibes=Trasluchadas penaltyCircles=Círculos de penalización medalRaceIsNull=Valor de regata final no permitido -configuration=Configuración maneuverTypes=Maniobras chooseChart=Seleccionar gráfico distanceTraveled=Distancia viajada @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=s/NM metersUnit=m millimetersUnit=mm degreesUnit=° -close=Cerrar compareCompetitors=Comparar competidores description=Descripción sailNumber=Número de vela country=País no3LetterCodes=No es posible encontrar los códigos IOC de 3 letras. -add=Añadir delete=Borrar showCharts=Mostrar gráficos raceWithThisNameAlreadyExists=Ya existe una carrera con este nombre. @@ -269,7 +261,6 @@ printHint=Imprime la versión aplicada blockedApplyButton=Los competidores registrados difieren de los competidores de la lista de emparejamientos multiplierInfo=Multiplica los flights y los crea uno junto a otro de modo que sea posible una competición con menos modificaciones de embarcación. noPairingListAvailable=La función de impresión solo está disponible si ya se han aplicado las listas de emparejamientos a los registros de carrera de las clasificaciones seleccionadas. -settingsForComponent=Opciones para {0} noEventsFound=No se han encontrado eventos noEventSelected=Ningún evento seleccionado noLeaderboardsFound=No se han encontrado tablas de clasificación @@ -307,8 +298,6 @@ leaderboardGroup=Grupo de tablas de clasificación pleaseEnterNonEmptyDescription=Indique una descripción no vacía groupWithThisNameAlreadyExists=Ya existe un grupo de tablas de clasificación con este nombre. detailsOfLeaderboardGroup=Detalles del grupo de tablas de clasificación -edit=Editar -save=Grabar abort=Detener noLeaderboardGroupWithNameFound=No se ha encontrado ningún grupo de tablas de clasificación con el nombre {0} overview=Resumen @@ -343,7 +332,6 @@ degreesShort=grados untracked=No rastreado delayForLiveMode=Retraso para modo en directo notAvailable=No disponible -details=Detalles noGroupSelected=Ningún grupo seleccionado combinedWindSourceTypeName=Combinado legMiddleWindSourceTypeName=Mitad de tramo @@ -425,7 +413,6 @@ simulateAsLiveRace=Simular como carrera en directo simulateWithOffset=Offset antes de inicio en minutos: boatClassDoesNotMatchSelectedRegatta=Las carreras seleccionadas contienen clases de embarcación distintas de la clase ''''{0}'''' de la regata seleccionada. No se cargará ninguna carrera. regattaExistForSelectedBoatClass=Hay al menos una regata para las clases de embarcación seleccionadas. ¿Realmente desea crear regata(s) por defecto para esta(s) carrera(s)? -reload=Recargar addRegatta=Añadir regata... importRegattas=Importar regatas... exchangeName=Intercambiar nombre @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=El total de puntos netos de un competidor de la rega windData=Datos de vientos gpsData=Datos de GPS status=Status -noDataFound=No se han encontrado datos displayName=Visualizar nombre histogram=Histograma numberOfDataPoints=Número de puntos de datos @@ -895,12 +881,9 @@ legType=Tipo de tramo sailID=Número de vela seriesLeaderboard=Tabla de clasificación de serie regattaLeaderboards=Clasificaciones de regata -clearSelection=Borrar selección running=En ejecución -runAsSubstantive=Ejecutar done=Hecho lastFinished=Último que ha terminado -run=Ejecutar times=tiempos dataAmount=Cantidad de datos averageCleanedServerTime=∅ Tiempo de servidor limpio @@ -924,12 +907,8 @@ selectSheet=Seleccionar hoja cleanedServerTime=Tiempo de servidor limpio overallTime=Tiempo total cleanedOverallTime=Tiempo global limpio -dataMiningResult=Resultado de minería de datos -groupBy=Agrupar por statisticToCalculate=Calcular estadística -queryResultsChartSubtitle=Se ha trabajado en {0} entradas de datos en {1} segundos noQuerySelected=Ninguna consulta seleccionada -runAutomatically=Ejecutar automáticamente windImport_Upload=Cargar windImport_Title=Importar viento desde expedición windImport_BoatId=ID de embarcación: @@ -959,14 +938,9 @@ raceTimeTooltip=Tiempo total recorrido en esta carrera, se empieza a medir cuand raceTimeDownwindTooltip=Tiempo total de empopada recorrido en esta carrera raceTimeReachingTooltip=Tiempo total de alcance recorrido en esta carrera raceTimeUpwindTooltip=Tiempo total de ceñida recorrido en esta carrera -noStatisticSelectedError=No se ha seleccionado ninguna estadística para calcular noCustomGrouperScriptTextError=El script de grupo está vacío -noDimensionToGroupBySelectedError=No se ha seleccionado ninguna dimensión para agrupar noGrouperSelectedError=No se ha seleccionado ningún tipo de agrupador -noDataRetrieverChainDefinitonSelectedError=No se ha seleccionado ningún recuperador de datos -queryNotValidBecause=No se puede hacer ninguna consulta porque dataMining=Minería de datos -errorRunningDataMiningQuery=Se ha producido un error al ejecutar la consulta hideToolbar=Ocultar barra de herramientas showSeriesLeaderboards=Mostrar tablas de clasificación de las series showOverallLeaderboard=Mostrar tabla de clasificación general @@ -989,7 +963,6 @@ id=ID allowReload=Permitir recarga compress=Comprimir compressTooltip=Utilizar solamente si la instancia del servidor de exportación funciona al menos con la confirmación 0fbf6071dea125bec4a56dee55d61c99def4a62e. -queryRunner=Ejecutor de la consulta rerunQueryAfterRefresh=Volver a ejecutar la consulta después de actualizar refreshIntervalMustntBeEmpty=El intervalo de actualización no debe estar en blanco selectionTables=Tablas de selección @@ -1074,12 +1047,7 @@ TWATooltip=Ángulo entre la dirección de los competidores y el viento TWA=Ángulo real del viento showBoatClassChartsLabel=También puede visualizar el diagrama global para las clases de embarcación disponibles. showDiagram=Mostrar diagrama -runAutomaticallyTooltip=Ejecute la consulta automáticamente después de modificar, por ejemplo, la estadística o la agrupación. rerunQueryAfterRefreshTooltip=Vuelve a ejecutar la consulta después de que se hayan refrescado las tablas. -queryDefinitionProvider=Proveedor de definición de consultas -statisticProvider=Proveedor de estadísticas -calculateThe=Calcular -groupingProvider=Proveedor de agrupación releaseNotes=Historial de noticias y lanzamientos hasSplitFleetContiguousScoring=Dividir flotas puntuadas de forma contigua addRaceLogTracker=Añadir rastreador de registro de carrera @@ -1237,7 +1205,6 @@ showAll=Mostrar todo raceVisibilityColumn=Visibilidad enterCarryValueFor=Introducir puntos acumulados para el competidor {0} advanced=Avanzado -basedOn=Basado en retrieveWith=Recuperar con mappingDetails=Detalles de asignación deviceMappingQrCodeExplanation=Si utiliza la aplicación de rastreo, también puede añadir la asignación de dispositivos seleccionando un competidor/marca, definiendo las fechas de inicio y fin y escaneando este código QR @@ -1248,10 +1215,6 @@ enterImageURL=Indicar URL de imagen... enterVideoURL=Indicar URL de vídeo... enterSponsorImageURL=Indicar URL de imagen de patrocinador... enterRaceName=Indicar nombre de carrera... -serverError=Se ha producido un error al intentar contactar con el servidor. Compruebe la conexión de red e inténtelo de nuevo. -remoteProcedureCall=Llamada de procedimiento remoto -serverReplies=Servidor responde -errorCommunicatingWithServer=Error al comunicar con el servidor userManagement=Gestión de usuarios regattaStructureImport=Importación de la estructura de regata filteredBy=Filtrado por @@ -1279,7 +1242,6 @@ noFleetsDefined=Ninguna flota definida successfullyCreatedRegattas=Regatas creadas con éxito errorTryingToRegisterRacesForTracking=Error al intentar registrar carreras {0} para rastreo: {1}. Compruebe sintaxis URI en directo/grabada. errorDeterminingPolarAvailability=Error al determinar la disponibilidad de datos polares / VPP para la carrera {0}: {1} -error=Error fileStorage=Almacenamiento de archivos active=Activo scoringSchemeHighPointEssOverallDescription=Calificación en puntos. El ganador de una prueba obtiene 10 puntos; el segundo, 9 puntos, etc. Si hay un empate en la serie de Extreme Sailing, el empate se resuelve a favor del competidor que ha ganado más pruebas. Si aún persiste el empate, se utiliza el resultado de la última prueba. @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=Nombre completo del competidor alwaysShowCompetitorNationalityColumn=Siempre mostrar la nacionalidad del competidor alwaysShowCompetitorNationalityColumnTooltip=Muestra ambos, las banderas de país así como las imágenes de competidor, si están disponibles. loadingDimensionValues=Cargando valores de dimensión -runningQuery=Ejecutando consulta inviteBuoyTenders=Invitar a balizadores orMultipleEmails=o varios correos electrónicos separados por una coma courseOverGroundTrueDegreesTooltip=Regata sobre el fondo (Course Over Ground) en grados @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=COG distanceIncludingGateStartInMeters=Distancia (con inicio de puerta de salida) distanceTraveledIncludingGateStartTooltip=Distancia recorrida desde el inicio hasta el final del tramo\o hasta la fecha y hora actual si no se ha acabado el tramo.\nSi el tramo incluye un inicio de puerta de salida, la distancia desde el extremo hasta la posición inicial se incluye\n para que puedan compararse los competidores aunque hayan empezado en tiempos diferentes. raceDistanceTraveledIncludingGateStartTooltip=Distancia recorrida desde el inicio hasta el final de la carrera\o hasta la fecha y hora actual si no se ha acabado la carrera. Para el inicio de la puerta de salida, la distancia desde el extremo hasta la posición inicial se incluye\n para que puedan compararse los competidores aunque hayan empezado en tiempos diferentes. -results=Resultados -groupName=Nombre de grupo -valueAscending=Valor (ascendente) -valueDescending=Valor (descendente) -sortBy=Ordenado por dashboardHeader=Dashboard dashboardNoWindBotAvailableHeader=Wind Bot no está disponible dashboardNoWindBotAvailableMessage=Para obtener los datos de viento en directo a partir de unidades de medida de viento, asegúrese de que Wind Bot está activado y contectado con SAP Sailing Analytics. @@ -1370,16 +1326,12 @@ dashboardRCBoat=Barco RC fixedMarkPassing=(fijado) suppressedMarkPassing=(suprimido) windUp=Viento al norte (mostrar viento de la parte superior del mapa) -filterBy=Filtrar por -currentFilterSelection=Seleccion de filtro actual notCapableOfGeneratingACodeForIdentifier=No sé generar un código para este identificador. serverUrl=URL del servidor rotatedFromTrueNorth=Ha rotado {0} grados desde el norte real. clickToToggleWindUp=Golpe/clic para alternar entre visualización mapa viento al norte y norte arriba clickToToggleWindStreamlets=Golpe/clic para mostrar u ocultar corrientes de viento startLineToFirstMarkTriangle=Inicio a la primera marca ({0} m) -dataMiningComponentsHaveBeenUpdated=Los componentes de minería de datos se han actualizado -dataMiningComponentsNeedReloadDialogMessage=Pulse Recargar para volver a cargar los componentes ahora. De este modo, se descartarán los datos visualizados en ese momento y se ejecutará una consulta por defecto.\nHaga clic en «Cerrar» para no hacer nada. La minería de datos no funcionará correctamente hasta que se hayan vuelto a cargar los componentes. noDataForEvent=Todavía no hay datos para el evento. countriesCount={0,number} países countriesCount[one]={0,number} país @@ -1477,15 +1429,7 @@ noFinishedRaces=Todavía no ha finalizado ninguna carrera racesOverview=Resumen de carreras listFormatLabel=Formato de lista competitionFormatLabel=Formato de competición -empty=Vacío -runAQuery=Realice una consulta latestRegattaStandings=Últimas posiciones de la regata -plainText=Texto sin formato -columnChart=Gráfico de columnas -columnChartWithErrorBars=Gráfico de columnas con barras de error -choosePresentation=Seleccionar presentación -cantDisplayDataOfType=No se pueden mostrar datos del tipo {0} -shownDecimals=Decimales mostrados openFullscreenView=Abrir vista de pantalla completa closeFullscreenView=Cerrar vista de pantalla completa videosCount={0,number} vídeos @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} foto eventsHaveTakenPlace=Han tenido lugar {0} eventos eventsHaveTakenPlace[one]=Ha tenido lugar un evento raceOffice=Oficina de carrera -analyze=Analizar -dataMiningSettings=Opciones de minería de datos -multiResultsPresenter=Presentador de resultados múltiples -plainResultsPresenter=Presentador de resultados simples -resultsChart=Gráfico de resultados -tabbedResultsPresenter=Presentador de resultados por pestañas polarResultsPresenter=Presentador de resultados de coordenadas polares maneuverSpeedDetailsResultsPresenter=Presentador de resultados detallados de velocidad de maniobra -dataMiningRetrieval=Recuperación de datos actionWatch=Observar actionAnalyze=Analizar denoteAllRacesForRaceLogTrackingShorctut=Acceso directo para indicar todas las carreras para un rastreo de registro de carrera @@ -1519,24 +1456,8 @@ defaultName=Por defecto exampleTextForName=Su nombre aparece como: flightsCount={0,number} flights flightsCount[one]={0,number} flight -viewQueryDefinition=Ver definición de consulta -queryDefinitionViewer=Visor de definición de consulta -groupAverageAscending=Promedio de grupo (ascendente) -groupAverageDescending=Promedio de grupo (descendente) -groupMedianAscending=Mediana de grupo (ascendente) -groupMedianDescending=Mediana de grupo (descendente) resultsFoundForSearch={0,number} resultados encontrados para ''''{1}'''' resultsFoundForSearch[one]={0,number} resultado encontrado para ''''{1}'''' -runPredefinedQuery=Ejecutar consulta predefinida -selectPredefinedQuery=Seleccionar consulta predefinida -predefinedQueryRunner=Programa de ejecución de consulta predefinida -developerOptions=Opciones de desarrollador -copyToClipboard=Copiar a portapapeles -code=Código -useClassGetName=Utilizar Class.getName() para nombres de tipo -useClassGetNameTooltip=Más resistente frente a modificaciones en la base de código, pero el fragmento de código se puede utilizar solamente en el ámbito donde estén disponibles las clases. -useStringLiterals=Utilizar literales de cadena para los nombres de tipo -useStringLiteralsTooltip=El fragmento de código puede utilizarse en todas las ubicaciones, pero se romperá si se modifica la base de código. errorLoadingDataWithTryAgain=Error al cargar los datos. Inténtelo de nuevo más tarde. addGalleryPhoto=Añadir foto de galería addStageImage=Añadir imagen de etapa @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=Los siguientes competidores no se pueden registrar competitorToolTipMessage={0} ya ha sido asignado a la flota {2} en la carrera {3} y, por consiguiente, no se puede asignar a la flota {1} de la misma carrera addMarkToRegatta=Añadir marca a regata selectALeaderboardGroup=Seleccione un grupo de tablas de clasificación... -pleaseSelect=Seleccione requiresValidRegatta=Esta página requiere una regata, una columna de carreras y un nombre de flota válidos para identificar la regata que se desea mostrar. couldNotObtainRace=No se ha podido obtener la regata con el nombre {1} para la flota {2} para una regata con el nombre {0}: {3} errorTryingToCreateEmbeddedMap=Error al intentar crear el mapa incrustado: {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=No hay datos de rastreo eventRegattaHeaderLegendWindNo=No hay datos de viento eventRegattaHeaderLegendVideoNo=No hay transmisiones de vídeo eventRegattaHeaderLegendAudioNo=No hay transmisiones de audio -angleInDegree=Ángulo en grado -angleInRadian=Ángulo en radián -centralAngleInRadian=Ángulo central en radián -centralAngleInDegree=Ángulo central en grado -kilometers=Kilómetros -meters=Metros -nauticalMiles=Millas náuticas -seaMiles=Millas marinas -geographicalMiles=Millas geográficas -days=Días -hours=Horas -minutes=Minutos -seconds=Segundos -milliseconds=Milisegundos -floatNumber=Flotador -integer=Entero appendResult=Resultado de estructura append sampleColor=Muestra de color -sharedSettingsLink=Enlace con opciones leaderboardPage=Página de clasificación -makeDefault=Establecer como predeterminado -makeDefaultInProgress=En curso... -settingsSavedMessage=Sus opciones actuales se han definido correctamente como estándar -settingsSaveErrorMessage=Se ha producido un error durante la definición de sus opciones como estándar showLiveNow=Mostrar "En directo ahora" useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=Utilice solo una "Inferencia de hora de inicio" y "Control de rastreo de horas de inicio y de fin" unknownLeaderboardType=Tipo de tabla de clasificación {0} desconocido @@ -1878,10 +1777,6 @@ settingsId=ID de opciones documentSettingsId=ID de opciones de documento settingsForId=Opciones para ID ''''{0}'''' userProfileSettingsTabDescription=Las opciones del usuario se generan mediante los diálogos de opciones que se encuentran en varios puntos de la página. Esta vista muestra todas sus opciones agrupadas de una manera técnica para los usuarios expertos. Tenga en cuenta que las entradas eliminadas no se pueden restablecer, por lo que tenga precaución al utilizarlas. -resetToDefault=Reinicializar a valores por defecto -resetToDefaultInProgress=Reinicializando... -settingsRemoved=Opciones por defecto restablecidas -settingsRemovedError=No se han podido restablecer las opciones por defecto userSettingsFilter=Filtro de opciones requiresRegattaRaceAndLeaderboard=Esta página requiere un nombre de regata, un nombre de carrera y un nombre de tabla de clasificación válidos. couldNotFindRaceInRegatta=No se ha podido obtener la carrera con el nombre {0} para la regata con el nombre {1} @@ -1913,7 +1808,6 @@ errorFetchingDimensionData=Error al obtener los valores de dimensión de {0} : { errorFetchingStatistics=Error al obtener las estadísticas disponibles desde el servidor: {0} errorFetchingAggregators=Error al obtener los agregadores disponibles desde el servidor: {0} errorLoadingDataRetrieverChainDefinitions=Error al recuperar las DataRetrieverChainDefinitions disponibles: {0} -errorFetchingComponentsChangedTimepoint=Error al obtener la fecha modificada de los componentes a partir del servidor: {0} errorRunningQuery=Error al ejecutar la consulta: {0} errorReadingWindFixes=Error al leer las correcciones de viento {0} errorAddingWindFixForRace=Error al añadir una corrección de viento para la carrera {0}: {1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=La altura de marcha mínima en metros requerida minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=Duración mínima entre apéndice(s) de foil minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=Si no está en blanco y el tiempo entre dos apéndices de foil adyacentes es inferior, aquellos apéndices de foil adyacentes se fusionarán en uno solo. needToProvideValidMinimumRideHeight=Requiere proporcionar un valor de altura de marcha mínimo válido en metros. -dataMiningErrorMargins=Márgenes de error -elements={0} elementos -chooseDifferentDimensionTitle=Seleccione una dimensión diferente -chooseDifferentDimensionMessage=Seleccione una dimensión diferente para agrupar resultados -pleaseSelectADimension=Seleccione una dimensión currentPortDaggerboardRake=Inclinación de la orza de babor currentPortDaggerboardRakeTooltip=La inclinación actual de la orza de babor currentStbdDaggerboardRake=Inclinación de la orza de estribor diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index 6381633c88b..c343ba99d82 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -9,12 +9,10 @@ trackedBefore=Historique des manifestations suivies general=Généralités listRaces=Lister courses listRegattas=Lister régates -numberPairResultsPresenter=Concentration points mesure wind=Vent maneuverType=Manœuvre windPanelLabel=Ceci est le panneau des mesures du vent, vide pour l''instant. refresh=Actualiser -remove=Supprimer removeNumber=Supprimer ({0}) windSource=Source du vent dampeningInterval=Intervalle d''atténuation @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=Course suivie liée au nom de course sél linkToColumn=Lier à la colonne unlink=Annuler Lier leaderboardName=Nom du palmarès -cancel=Annuler pleaseEnterAName=Saisissez un nom. pleaseEnterABoatClass=Saisissez une catégorie de bateau. discardRacesFromHowManyStartedRacesOn=Éliminez une course de plus en commençant au nombre de courses déjà lancées. @@ -65,7 +62,6 @@ startingFromNumberOfRaces=Début au nombre de courses renameLeaderboard=Renommer palmarès addColumnToLeaderboard=Ajouter colonne à palmarès pleaseEnterNameForNewRaceColumn=Saisissez un nom pour la nouvelle colonne de course. -ok=OK medalRace=Course médaillée renameRace=Renommer course openSelectedLeaderboard=Ouvrir le palmarès sélectionné @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=Palmarès leaderboards=Palmarès leaderboardSettings=Options du palmarès -settings=Options selectAtLeastOneLegDetail=Sélectionnez au moins un détail de portion de parcours. currentSpeedOverGroundInKnots=Vf currentSpeedOverGroundInKnotsTooltip=La vitesse fond actuelle. @@ -173,7 +168,6 @@ tacks=Virements jibes=Changements d''amure penaltyCircles=Tours de pénalité medalRaceIsNull=Valeur de course à la médaille non autorisée -configuration=Configuration maneuverTypes=Manœuvres chooseChart=Sélectionner graphique distanceTraveled=Distance parcourue @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=s/NM metersUnit=m millimetersUnit=mm degreesUnit=° -close=Fermer compareCompetitors=Comparer concurrents description=Description sailNumber=Numéro de voile country=Pays no3LetterCodes=Codes CIO à trois lettres introuvables -add=Ajouter delete=Supprimer showCharts=Afficher graphiques raceWithThisNameAlreadyExists=Une course de ce nom existe déjà. @@ -269,7 +261,6 @@ printHint=Imprime la version appliquée blockedApplyButton=Le nombre de concurrents inscrits ne correspond pas au nombre de concurrents de la liste d''appariement. multiplierInfo=Multipliez les flights et créez-les les uns après les autres pour obtenir une compétition avec le moins de bateaux possible. noPairingListAvailable=La fonction d''impression est uniquement disponible lorsqu''une liste d''appariement a déjà été appliquée aux journaux de course des palmarès sélectionnés. -settingsForComponent=Options pour {0} noEventsFound=Aucune manifestation trouvée noEventSelected=Aucune manifestation sélectionnée noLeaderboardsFound=Aucun palmarès trouvé @@ -307,8 +298,6 @@ leaderboardGroup=Groupe de palmarès pleaseEnterNonEmptyDescription=Saisissez une description (ne pas laisser vide). groupWithThisNameAlreadyExists=Un groupe de palmarès de ce nom existe déjà. detailsOfLeaderboardGroup=Détails du groupe de palmarès -edit=Modifier -save=Enregistrer abort=Abandonner noLeaderboardGroupWithNameFound=Aucun groupe de palmarès ayant pour nom {0} n''a été trouvé. overview=Synthèse @@ -343,7 +332,6 @@ degreesShort=deg untracked=Non suivi delayForLiveMode=Décalage pour mode en direct notAvailable=Non disponible -details=Détails noGroupSelected=Aucun groupe sélectionné combinedWindSourceTypeName=Combiné legMiddleWindSourceTypeName=Milieu de la portion de parcours @@ -425,7 +413,6 @@ simulateAsLiveRace=Simulation de course en direct simulateWithOffset=Décalage avant le départ (en minutes) : boatClassDoesNotMatchSelectedRegatta=Les courses sélectionnées contiennent des catégories de bateaux qui ne sont pas identiques à la catégorie de bateau "{0}" de la régate sélectionnée. Aucune course ne sera chargée. regattaExistForSelectedBoatClass=Il existe au moins une régate pour les catégories de bateaux sélectionnées. Voulez-vous vraiment créer une (des) régate(s) pour cette (ces) course(s) ? -reload=Recharger addRegatta=Ajouter régate... importRegattas=Importer régates... exchangeName=Nom d''échange @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=Total net des points d''un concurrent dans la régat windData=Données de vent gpsData=Données GPS status=Statut -noDataFound=Aucune donnée trouvée displayName=Afficher nom histogram=Histogramme numberOfDataPoints=Nombre de points de données @@ -895,12 +881,9 @@ legType=Type de portion de parcours sailID=Numéro de voile seriesLeaderboard=Palmarès de la série regattaLeaderboards=Palmarès de la régate -clearSelection=Réinitialiser sélection running=En cours d''exécution -runAsSubstantive=Exécuter done=Terminé lastFinished=Fin de dernière exécution -run=Exécuter times=durées dataAmount=Volume de données averageCleanedServerTime=∅ Durée de nettoyage du serveur @@ -924,12 +907,8 @@ selectSheet=Sélectionner fiche cleanedServerTime=Durée de nettoyage du serveur overallTime=Durée globale cleanedOverallTime=Durée globale de nettoyage -dataMiningResult=Résultat d''exploration de données -groupBy=Regrouper par statisticToCalculate=Calculer statistiques -queryResultsChartSubtitle={0} entrées de données analysées en {1} secondes noQuerySelected=Aucune requête sélectionnée -runAutomatically=Exécuter automatiquement windImport_Upload=Charger windImport_Title=Importer vent à partir de Expedition windImport_BoatId=N° du bateau : @@ -959,14 +938,9 @@ raceTimeTooltip=Durée de navigation totale dans cette course, chronométrée à raceTimeDownwindTooltip=Durée totale de navigation vent arrière dans cette course raceTimeReachingTooltip=Durée totale de navigation jusqu''à l''arrivée dans cette course raceTimeUpwindTooltip=Durée totale de navigation dans le lit du vent dans cette course -noStatisticSelectedError=Aucune statistique à calculer sélectionnée noCustomGrouperScriptTextError=Script de regroupement vide -noDimensionToGroupBySelectedError=Aucune dimension sélectionnée pour le regroupement noGrouperSelectedError=Aucun type de regroupement sélectionné -noDataRetrieverChainDefinitonSelectedError=Aucun récupérateur de données sélectionné -queryNotValidBecause=Aucune requête possible dû à dataMining=Exploration de données -errorRunningDataMiningQuery=Une erreur s''est produite lors de l''exécution de la requête. hideToolbar=Masquer barre d''outils showSeriesLeaderboards=Afficher palmarès de la série showOverallLeaderboard=Afficher palmarès général @@ -989,7 +963,6 @@ id=ID allowReload=Autoriser rechargement compress=Comprimer compressTooltip=Utiliser seulement si l''instance de serveur d''exportation \ns''exécute au moins avec le commit 0fbf6071dea125bec4a56dee55d61c99def4a62e. -queryRunner=Outil d''exécution de requêtes rerunQueryAfterRefresh=Exécuter la requête de nouveau après l''actualisation refreshIntervalMustntBeEmpty=L''intervalle d''actualisation ne doit pas être vide. selectionTables=Tables de sélection @@ -1074,12 +1047,7 @@ TWATooltip=L''angle entre la direction du concurrent et le vent. TWA=Angle du vent réel showBoatClassChartsLabel=Vous pouvez également afficher le diagramme global pour les catégories de bateau disponibles. showDiagram=Afficher diagramme -runAutomaticallyTooltip=Exécutez la requête automatiquement, par exemple après avoir modifié les statistiques ou le regroupement. rerunQueryAfterRefreshTooltip=Réexécute la requête après l''actualisation des tables. -queryDefinitionProvider=Fournisseur de définitions de requêtes -statisticProvider=Fournisseur de statistiques -calculateThe=Calculer le/la -groupingProvider=Fournisseur de regroupements releaseNotes=Historique de versions et nouveautés hasSplitFleetContiguousScoring=Les flottes divisées ont obtenu des scores contigus. addRaceLogTracker=Ajouter tracker de journal de course @@ -1237,7 +1205,6 @@ showAll=Afficher tout raceVisibilityColumn=Visibilité enterCarryValueFor=Saisissez les points accumulés par le concurrent {0}. advanced=Avancé -basedOn=basé sur retrieveWith=Récupérer avec mappingDetails=Détails du mappage deviceMappingQrCodeExplanation=Si vous utilisez l''application de suivi, vous pouvez également ajouter le mappage de l''appareil en sélectionnant un concurrent/une marque, en définissant des heures de départ et d''arrivée et en scannant ce code QR. @@ -1248,10 +1215,6 @@ enterImageURL=Saisissez l''URL de l''image... enterVideoURL=Saisissez l''URL de la vidéo... enterSponsorImageURL=Saisissez l''URL de l''image du sponsor... enterRaceName=Saisissez le nom du parcours... -serverError=Une erreur s''est produite lors de la tentative de contact du serveur. Vérifiez la connexion réseau et réessayez. -remoteProcedureCall=Procédure d''appel distante -serverReplies=Réponses du serveur -errorCommunicatingWithServer=Erreur lors de la communication avec le serveur userManagement=Gestion des utilisateurs regattaStructureImport=Importation de la structure de la régate filteredBy=filtré par @@ -1279,7 +1242,6 @@ noFleetsDefined=Aucune flotte définie successfullyCreatedRegattas=Création des régates réussie errorTryingToRegisterRacesForTracking=Erreur lors de la tentative d''enregistrement des courses {0} pour le suivi : {1}. Vérifiez la syntaxe de l''URL stockée/du direct. errorDeterminingPolarAvailability=Erreur lors de la détermination de la disponibilité des données polaires/VPP pour la course {0} : {1} -error=Erreur fileStorage=Stockage du fichier active=Actif scoringSchemeHighPointEssOverallDescription=Score en points. Le vainqueur d''un Act totalise 10 points, le 2e - 9 points, etc. En cas d''égalité au classement général des Extreme Sailing Series, la victoire est accordée au concurrent ayant remporté le plus d''Act. Si l''égalité persiste, le résultat obtenu au dernier Act sera utilisé. @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=Nom complet du concurrent alwaysShowCompetitorNationalityColumn=Afficher toujours la nationalité du concurrent alwaysShowCompetitorNationalityColumnTooltip=Si disponibles, afficher les deux : le drapeau national ainsi que l''image du concurrent loadingDimensionValues=Chargement des valeurs de dimension -runningQuery=Requête en cours inviteBuoyTenders=Inviter navire baliseur orMultipleEmails=ou plusieurs adresses e-mails séparées par des virgules courseOverGroundTrueDegreesTooltip=Route fond vraie en degrés @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=Route fond distanceIncludingGateStartInMeters=Distance (si départ au lièvre) distanceTraveledIncludingGateStartTooltip=La distance naviguée du départ de la portion de parcours jusqu''à sa fin\nou jusqu''à l''heure actuelle (si la portion de parcours n''est pas terminée).\nSi la portion de parcours inclut un départ au lièvre, la distance de la bouée de ligne jusqu''à la position de départ est incluse \npour permettre de comparer les concurrents même si leur départ ne s''est pas fait à la même heure. raceDistanceTraveledIncludingGateStartTooltip=La distance naviguée du départ de la course jusqu''à sa fin\nou jusqu''à l''heure actuelle (si la course n''est pas terminée).\nEn cas de départ au lièvre, la distance de la bouée de ligne jusqu''à la position de départ est incluse \npour permettre de comparer les concurrents même si leur départ ne s''est pas fait à la même heure. -results=Résultats -groupName=Nom du groupe -valueAscending=Valeur (ascendant) -valueDescending=Valeur (descendant) -sortBy=Trier par dashboardHeader=Tableau de bord dashboardNoWindBotAvailableHeader=Le bot du vent n''est pas disponible. dashboardNoWindBotAvailableMessage=Pour pouvoir recevoir des données de vent depuis des unités de mesure du vent, assurez-vous que le bot du vent est allumé et connecté à SAP Sailing Analytics. @@ -1370,16 +1326,12 @@ dashboardRCBoat=Bateau du comité de course fixedMarkPassing=(fixé) suppressedMarkPassing=(supprimé) windUp=Orienté par rapport au sens du vent (le vent vient du haut de la carte) -filterBy=Filtrer par -currentFilterSelection=Sélection du filtre actuel notCapableOfGeneratingACodeForIdentifier=Je ne suis pas capable de générer un code pour cet identifiant. serverUrl=URL du serveur rotatedFromTrueNorth=Pivoté de {0} degrés à partir du vrai Nord. clickToToggleWindUp=Touchez/Cliquez pour basculer entre l''affichage orienté nord et l''affichage par rapport au sens du vent clickToToggleWindStreamlets=Touchez/cliquez pour afficher ou masquer les couloirs de vent. startLineToFirstMarkTriangle=Distance entre le départ et la première marque ({0} m) -dataMiningComponentsHaveBeenUpdated=Les composants d''exploration de données ont été mis à jour. -dataMiningComponentsNeedReloadDialogMessage=Cliquez sur Recharger pour recharger les composants maintenant. Les données actuellement affichées seront ignorées et une requête par défaut sera exécutée.n\Cliquez sur Fermer pour ne rien faire. L''exploration de données ne fonctionnera pas correctement tant que les composants n''auront pas été rechargés. noDataForEvent=Il n''y a pas encore de données pour cette manifestation. countriesCount={0,number} pays countriesCount[one]={0,number} pays @@ -1477,15 +1429,7 @@ noFinishedRaces=Aucune course terminée pour l''instant. racesOverview=Synthèse des courses listFormatLabel=Format de liste competitionFormatLabel=Format de compétition -empty=Vide -runAQuery=Exécuter une requête latestRegattaStandings=Classement de régate le plus récent -plainText=Texte brut -columnChart=Diagramme à colonnes -columnChartWithErrorBars=Diagramme à colonnes avec barres d''erreur -choosePresentation=Sélectionner la présentation -cantDisplayDataOfType=Affichage des données du type {0} impossible -shownDecimals=Nombre de décimales openFullscreenView=Ouvrir le mode Plein écran closeFullscreenView=Fermer le mode Plein écran videosCount={0,number} vidéos @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} photo eventsHaveTakenPlace={0} manifestations ont eu lieu. eventsHaveTakenPlace[one]=Une manifestation a eu lieu. raceOffice=Bureau de course -analyze=Analyser -dataMiningSettings=Options de l''exploration de données -multiResultsPresenter=Visualiseur de résultats multiples -plainResultsPresenter=Visualiseur de résultats bruts -resultsChart=Graphique des résultats -tabbedResultsPresenter=Visualiseur de résultats avec onglets polarResultsPresenter=Visualiseur de résultats polaires maneuverSpeedDetailsResultsPresenter=Visualiseur des résultats : détails sur la vitesse lors de la manœuvre -dataMiningRetrieval=Récupération de données actionWatch=Regarder actionAnalyze=Analyser denoteAllRacesForRaceLogTrackingShorctut=Raccourci pour marquer toutes les courses pour le suivi par journaux de course @@ -1519,24 +1456,8 @@ defaultName=Par défaut exampleTextForName=Votre nom apparaît de la manière suivante : flightsCount={0,number} départs de deux équipages flightsCount[one]={0,number} départ de deux équipages -viewQueryDefinition=Afficher la définition de la requête -queryDefinitionViewer=Visualiseur de définitions de requête -groupAverageAscending=Moyenne du groupe (ascendant) -groupAverageDescending=Moyenne du groupe (descendant) -groupMedianAscending=Médiane du groupe (ascendant) -groupMedianDescending=Médiane du groupe (descendant) resultsFoundForSearch={0,number} résultats trouvés pour "{1}" resultsFoundForSearch[one]={0,number} résultat trouvé pour "{1}" -runPredefinedQuery=Exécuter requête prédéfinie -selectPredefinedQuery=Sélectionner requête prédéfinie -predefinedQueryRunner=Outil d''exécution de requêtes prédéfinies -developerOptions=Options développeur -copyToClipboard=Copier dans le presse-papiers -code=Code -useClassGetName=Utilisez Class.getName() pour les noms des types. -useClassGetNameTooltip=Plus stable face aux modifications dans la base de code, mais le fichier de script peut uniquement être utilisé dans l''étendue où les classes sont disponibles. -useStringLiterals=Utiliser littéraux de chaîne pour les noms de types -useStringLiteralsTooltip=Le fichier de script peut être utilisé partout, mais sera corrompu si la base de code change. errorLoadingDataWithTryAgain=Erreur lors du chargement des données. Nouvel essai dans quelques minutes. addGalleryPhoto=Ajouter photo de la galerie addStageImage=Ajouter image d''étape @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=Les concurrents suivants ne peuvent pas être insc competitorToolTipMessage={0} a déjà été affecté à la flotte {2} dans la course {3} et ne peut donc pas être affecté à la flotte {1} dans la même course. addMarkToRegatta=Ajouter marque à la régate selectALeaderboardGroup=Sélectionner groupe de palmarès… -pleaseSelect=Sélectionnez requiresValidRegatta=Pour identifier la course à afficher, cette page nécessite une régate valide, une colonne de course et un nom de flotte. couldNotObtainRace=Impossible de trouver une course ayant le nom {1} pour la flotte {2} pour une régate ayant le nom {0} : {3} errorTryingToCreateEmbeddedMap=Erreur lors de la tentative de création de la carte intégrée : {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=Aucune donnée de suivi eventRegattaHeaderLegendWindNo=Aucune donnée de vent eventRegattaHeaderLegendVideoNo=Aucun flux vidéo eventRegattaHeaderLegendAudioNo=Aucun flux audio -angleInDegree=Angle en degrés -angleInRadian=Angle en radians -centralAngleInRadian=Angle au centre en radians -centralAngleInDegree=Angle au centre en degrés -kilometers=Kilomètres -meters=Mètres -nauticalMiles=Milles nautiques -seaMiles=Milles marins -geographicalMiles=Milles géographiques -days=Jours -hours=Heures -minutes=Minutes -seconds=Secondes -milliseconds=Millisecondes -floatNumber=Flottant -integer=Entier appendResult=Ajouter résultat sampleColor=Échantillon de couleur -sharedSettingsLink=Lier aux options leaderboardPage=Page du palmarès -makeDefault=Définir par défaut -makeDefaultInProgress=En cours... -settingsSavedMessage=Vos options actuelles ont correctement été définies comme options par défaut. -settingsSaveErrorMessage=Une erreur s''est produite lors de la définition de vos options comme options par défaut. showLiveNow=Afficher "En direct" useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=Utilisez "Interférence temporelle de départ" ou bien "Contrôler suivi à partir des heures de départ et d''arrivée", mais pas les deux. unknownLeaderboardType=Type de palmarès {0} inconnu @@ -1878,10 +1777,6 @@ settingsId=ID d''options documentSettingsId=ID d''options du document settingsForId=Options pour l''ID "{0}" userProfileSettingsTabDescription=Les options utilisateur sont générées dans des boîtes de dialogues pour options que vous trouverez à de nombreux endroits sur la page. Cette vue montre toutes vos options regroupées pour les mettre techniquement à disposition des utilisateurs de référence. Soyez prudent : les entrées supprimées ne peuvent pas être restaurées. -resetToDefault=Réinitialiser sur les valeurs par défaut -resetToDefaultInProgress=Réinitialisation en cours... -settingsRemoved=Options par défaut restaurées -settingsRemovedError=Impossible de restaurer les options par défaut userSettingsFilter=Filtre d''options requiresRegattaRaceAndLeaderboard=Cette page requiert un nom de régate, un nom de course et un nom de palmarès valides. couldNotFindRaceInRegatta=Impossible de trouver une course nommée {0} pour une régate nommée {1}. @@ -1913,7 +1808,6 @@ errorFetchingDimensionData=Erreur lors de l''accès aux valeurs de dimension de errorFetchingStatistics=Erreur lors de l''accès aux statistiques disponibles du serveur : {0} errorFetchingAggregators=Erreur lors de l''accès aux agrégateurs disponibles du serveur : {0} errorLoadingDataRetrieverChainDefinitions=Erreur lors de la récupération des définitions DataRetrieverChainDefinitions disponibles : {0} -errorFetchingComponentsChangedTimepoint=Erreur lors de la récupération de l''heure modifiée du composant du serveur : {0} errorRunningQuery=Erreur lors de l''exécution de la requête : {0} errorReadingWindFixes=Erreur lors de la lecture des points vent {0} errorAddingWindFixForRace=Erreur lors de l''ajout d''un point vent pour la course {0} : {1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=Hauteur de planing minimale requise en mètres minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=Durée minimum (s) entre deux tronçons en planing adjacents minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=Si cette valeur est renseignée et que la durée entre deux tronçons de planing adjacents est inférieure à celle-ci, ces tronçons de planing adjacents seront combinés en un seul. needToProvideValidMinimumRideHeight=Vous devez fournir une valeur de hauteur de planing minimale valide en mètres. -dataMiningErrorMargins=Marges d''erreur -elements={0} éléments -chooseDifferentDimensionTitle=Sélectionnez une dimension différente. -chooseDifferentDimensionMessage=Sélectionnez une dimension différente pour regrouper les résultats. -pleaseSelectADimension=Sélectionnez une dimension. currentPortDaggerboardRake=Inclinaison de la dérive bâbord currentPortDaggerboardRakeTooltip=Inclinaison actuelle de la dérive bâbord currentStbdDaggerboardRake=Inclinaison de la dérive tribord diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index b553cdf0a6d..96be70f2a10 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -9,12 +9,10 @@ trackedBefore=追跡イベントの履歴 general=一般 listRaces=レース一覧 listRegattas=レガッタ一覧 -numberPairResultsPresenter=散布図 wind=風 maneuverType=マニューバー windPanelLabel=これは風パネルであり、今のところ完全に空となっています。 refresh=リフレッシュ -remove=削除 removeNumber=削除 ({0}) windSource=風源 dampeningInterval=制動間隔 @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=選択されたレース名に結び付 linkToColumn=列にリンク unlink=リンク解除 leaderboardName=リーダーボード名 -cancel=取消 pleaseEnterAName=名称を入力してください pleaseEnterABoatClass=艇種を入力してください discardRacesFromHowManyStartedRacesOn=レース除外 (スタート済レースから) @@ -65,7 +62,6 @@ startingFromNumberOfRaces=レース数から開始 renameLeaderboard=リーダーボード名称変更 addColumnToLeaderboard=リーダーボードに列を追加 pleaseEnterNameForNewRaceColumn=新規レース列の名称を入力してください -ok=OK medalRace=メダルレース renameRace=レース名称変更 openSelectedLeaderboard=選択したリーダーボードを開く @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=リーダーボード leaderboards=リーダーボード leaderboardSettings=リーダーボード設定 -settings=設定 selectAtLeastOneLegDetail=レグ詳細を少なくとも 1 つ選択 currentSpeedOverGroundInKnots=SOG currentSpeedOverGroundInKnotsTooltip=現在の対地速力です。 @@ -173,7 +168,6 @@ tacks=タック jibes=ジャイブ penaltyCircles=ペナルティーサークル medalRaceIsNull=メダルレース値は不可 -configuration=設定 maneuverTypes=マニューバー chooseChart=チャートの選択 distanceTraveled=移動した距離 @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=s/NM metersUnit=m millimetersUnit=mm degreesUnit=° -close=閉じる compareCompetitors=競技者比較 description=内容説明 sailNumber=セールナンバー country=国 no3LetterCodes=IOC 3 文字コードを検索できませんせした。 -add=追加 delete=削除 showCharts=チャート表示 raceWithThisNameAlreadyExists=その名称のレースはすでに登録されています。 @@ -269,7 +261,6 @@ printHint=適用バージョンを印刷 blockedApplyButton=登録されている競技者数が対戦表にある競技者数と同じでありません multiplierInfo=複数のフライトを艇変更がより少なく競技が可能となるよう互いに隣り合わせに作成 noPairingListAvailable=印刷機能は選択したリーダーボードレースログに対戦表がすでに適用されている場合にのみ利用が可能です。 -settingsForComponent={0} の設定 noEventsFound=イベントが見つかりませんでした noEventSelected=イベントが選択されていません noLeaderboardsFound=リーダーボードが見つかりませんでした @@ -307,8 +298,6 @@ leaderboardGroup=リーダーボードグループ pleaseEnterNonEmptyDescription=空でない説明を入力してください groupWithThisNameAlreadyExists=この名称のリーダーボードグループはすでに登録されています。 detailsOfLeaderboardGroup=リーダーボードグループの詳細 -edit=編集 -save=保存 abort=中止 noLeaderboardGroupWithNameFound={0} という名称のリーダーボードグループは見つかりませんでした overview=概要 @@ -343,7 +332,6 @@ degreesShort=度 untracked=未追跡 delayForLiveMode=ライブモードの遅延: notAvailable=利用不可 -details=詳細 noGroupSelected=グループ未選択 combinedWindSourceTypeName=複合 legMiddleWindSourceTypeName=レグの中間 @@ -425,7 +413,6 @@ simulateAsLiveRace=ライブレースとしてシミュレート simulateWithOffset=スタート前のオフセット (分): boatClassDoesNotMatchSelectedRegatta=選択したレースに、選択したレガッタの艇種 ''{0}'' と同じでない艇種が含まれています。レースはロードされません。 regattaExistForSelectedBoatClass=選択した艇種に対して少なくとも 1 つのレガッタがあります。このレースに対してレガッタを初期登録しますか。 -reload=リロード addRegatta=レガッタ追加... importRegattas=レガッタのインポート... exchangeName=エクスチェンジ名 @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=レガッタにおける競技者の総合得点を windData=風データ gpsData=GPS データ status=ステータス -noDataFound=データが見つかりませんでした displayName=名称表示 histogram=ヒストグラム numberOfDataPoints=データポイント数 @@ -895,12 +881,9 @@ legType=レグタイプ sailID=セールナンバー seriesLeaderboard=シリーズリーダーボード regattaLeaderboards=レガッタリーダーボード -clearSelection=選択のクリア running=実行中 -runAsSubstantive=実行 done=実行済 lastFinished=最終フィニッシュ -run=実行 times=回 dataAmount=データ量 averageCleanedServerTime=∅ サーバ時間のクリア @@ -924,12 +907,8 @@ selectSheet=シート選択 cleanedServerTime=サーバ時間のクリア overallTime=全体時間 cleanedOverallTime=全体時間のクリア -dataMiningResult=データマイニング結果 -groupBy=グループキー statisticToCalculate=統計の計算 -queryResultsChartSubtitle={0} データエントリを {1} 秒で処理済 noQuerySelected=クエリ未選択 -runAutomatically=自動的に実行 windImport_Upload=アップロード windImport_Title=Expedition から風をインポート windImport_BoatId=艇 ID: @@ -959,14 +938,9 @@ raceTimeTooltip=競技者がスタート変針点を通過したときに計測 raceTimeDownwindTooltip=このレースでダウンウィンドで移動した合計時間 raceTimeReachingTooltip=このレースでリーチングで移動した合計時間 raceTimeUpwindTooltip=このレースでアップウィンドで移動した合計時間 -noStatisticSelectedError=計算する統計が選択されていません noCustomGrouperScriptTextError=分類スクリプトが空です -noDimensionToGroupBySelectedError=グループ別にする次元が選択されていません noGrouperSelectedError=分類タイプが選択されていません -noDataRetrieverChainDefinitonSelectedError=データリトリーバが選択されていません -queryNotValidBecause=クエリが可能でありません。理由: dataMining=データマイニング -errorRunningDataMiningQuery=クエリ実行でエラーが発生しました hideToolbar=ツールバー非表示 showSeriesLeaderboards=シリーズリーダーボード表示 showOverallLeaderboard=全体リーダーボード表示 @@ -989,7 +963,6 @@ id=ID allowReload=リロード許可 compress=圧縮 compressTooltip=エクスポートサーバインスタンスが少なくとも\nコミット 0fbf6071dea125bec4a56dee55d61c99def4a62e で実行中の場合にのみ使用してください。 -queryRunner=クエリランナー rerunQueryAfterRefresh=リフレッシュ後にクエリを再実行 refreshIntervalMustntBeEmpty=リフレッシュ間隔は空であってはなりません selectionTables=選択テーブル @@ -1074,12 +1047,7 @@ TWATooltip=競技者の方向と風の間のアングル TWA=真の風角度 showBoatClassChartsLabel=利用可能な艇種の全体図も表示することができます。 showDiagram=ダイアグラム表示 -runAutomaticallyTooltip=統計やグルーピングなどを変更した後にクエリを自動的に実行します。 rerunQueryAfterRefreshTooltip=テーブルがリフレッシュされた後にクエリを再実行します。 -queryDefinitionProvider=クエリ定義プロバイダ -statisticProvider=統計プロバイダ -calculateThe=計算: -groupingProvider=グルーピングプロバイダ releaseNotes=新規およびリリース履歴 hasSplitFleetContiguousScoring=連続的に得点するフリートの分割 addRaceLogTracker=RaceLog トラッカー追加 @@ -1237,7 +1205,6 @@ showAll=全表示 raceVisibilityColumn=可視性 enterCarryValueFor=競技者 {0} の持ち越し得点を入力 advanced=予選通過 -basedOn=基準 retrieveWith=検索キー mappingDetails=マッピング詳細 deviceMappingQrCodeExplanation=追跡アプリを使用している場合は、競技者/マークを選択し、開始時刻と終了時刻を設定してから、この QRCode をスキャンすることによってデバイスマッピングも追加することができます。 @@ -1248,10 +1215,6 @@ enterImageURL=画像 URL を入力... enterVideoURL=動画 URL を入力... enterSponsorImageURL=スポンサー画像 URL を入力... enterRaceName=レース名を入力... -serverError=サーバにアクセスしようとしてエラーが発生しました。ネットワーク接続を確認して再試行してください。 -remoteProcedureCall=リモートプロシージャコール -serverReplies=サーバ応答 -errorCommunicatingWithServer=サーバとの通信でエラー発生 userManagement=ユーザ管理 regattaStructureImport=レガッタ構成のインポート filteredBy=フィルタキー @@ -1279,7 +1242,6 @@ noFleetsDefined=フリートが定義されていません。 successfullyCreatedRegattas=レガッタが登録されました errorTryingToRegisterRacesForTracking=追跡するレース {0} を登録しようとしてエラーが発生: {1}。ライブ/格納 URI 構文をチェックしてください。 errorDeterminingPolarAvailability=レース {0} のポーラー/VPP データの利用可能性の決定でエラー発生: {1} -error=エラー fileStorage=ファイルストレージ active=有効 scoringSchemeHighPointEssOverallDescription=得点数によるスコアです。アクトの勝者が 10 点、2 位が 9 点などと得点します。Extreme Sailing シリーズ全体で同点となった場合は、より多くのアクトで勝者となった競技者を上位とします。それでも同じ場合には、最終アクトの結果を用います。 @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=競技者氏名 alwaysShowCompetitorNationalityColumn=競技者国籍を常に表示 alwaysShowCompetitorNationalityColumnTooltip=国旗と競技者画像 (利用可能な場合) の両方を表示 loadingDimensionValues=次元値ロード中 -runningQuery=クエリ実行中 inviteBuoyTenders=ブイ入札の招待 orMultipleEmails=または複数の電子メールをカンマで区切り courseOverGroundTrueDegreesTooltip=真の対地方位角度 (度) @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=COG distanceIncludingGateStartInMeters=距離 (ゲートスタートでの) distanceTraveledIncludingGateStartTooltip=レグ始点からレグ終点まで、またはレグをフィニッシュ\nしていない場合は現時点までに移動した距離です。\nレグにゲートスタートが含まれる場合は、異なる時刻にスタートしたときでも\n競技者を比較するためにピンエンドからスタート位置までの距離が含まれます。 raceDistanceTraveledIncludingGateStartTooltip=レース開始からレース終了まで、またはレースがフィニッシュ\nしていない場合は現時点までに移動した距離です。ゲートスタートの場合は、異なる時刻にスタートしたときでも\n競技者を比較するためにピンエンドからスタート位置までの距離が含まれます。 -results=結果 -groupName=グループ名 -valueAscending=値 (昇順) -valueDescending=値 (降順) -sortBy=ソートキー dashboardHeader=ダッシュボード dashboardNoWindBotAvailableHeader=ウィンドボットは利用できません。 dashboardNoWindBotAvailableMessage=風計測ユニットからライブ風データを受信するには、ウィンドボットがオンになっていて、SAP Sailing Analytics に接続されていることを確認してください。 @@ -1370,16 +1326,12 @@ dashboardRCBoat=RC 艇 fixedMarkPassing=(固定) suppressedMarkPassing=(非表示) windUp=ウィンドアップ (マップの最上部から風を表示) -filterBy=フィルタキー -currentFilterSelection=現在のフィルタ選択 notCapableOfGeneratingACodeForIdentifier=この ID のコードを生成することができません。 serverUrl=サーバ URL rotatedFromTrueNorth=真北から {0} 度回転しました。 clickToToggleWindUp=タップ/クリックして、風上が上のマップと北が上のマップとで表示を切り替えます clickToToggleWindStreamlets=タップ/クリックして風細流を表示または非表示にします startLineToFirstMarkTriangle=スタートから第一マークまで ({0}m) -dataMiningComponentsHaveBeenUpdated=データマイニングコンポーネントが更新されました -dataMiningComponentsNeedReloadDialogMessage=リロードをクリックして、コンポーネントをリロードします。これにより、現在表示されているデータが破棄され、デフォルトクエリが実行されます。\n何も行わない場合は閉じるをクリックします。データマイニングは、コンポーネントがリロードされるまでは正しく機能しません。 noDataForEvent=このイベントにまだデータが何もありません。 countriesCount={0,number} カ国 countriesCount[one]={0,number} カ国 @@ -1477,15 +1429,7 @@ noFinishedRaces=フィニッシュしたレースがまだありません。 racesOverview=レース概要 listFormatLabel=レース一覧 competitionFormatLabel=競技日 -empty=空 -runAQuery=クエリを実行 latestRegattaStandings=レガッタ最新順位表 -plainText=プレーンテキスト -columnChart=縦棒グラフ -columnChartWithErrorBars=縦棒グラフ (エラーバーあり) -choosePresentation=プレゼンテーション選択 -cantDisplayDataOfType={0} タイプのデータは表示できません。 -shownDecimals=小数点表示 openFullscreenView=全画面ビューを開く closeFullscreenView=全画面ビューを閉じる videosCount={0,number} 動画 @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} 写真 eventsHaveTakenPlace={0} イベントが行われました eventsHaveTakenPlace[one]=1 つのイベントが行われました raceOffice=レース事務所 -analyze=分析 -dataMiningSettings=データマイニング設定 -multiResultsPresenter=複数結果表示 -plainResultsPresenter=プレーン結果表示ツール -resultsChart=結果チャート -tabbedResultsPresenter=タブ結果表示ツール polarResultsPresenter=ポーラー結果表示ツール maneuverSpeedDetailsResultsPresenter=マニューバー速度詳細結果表示ツール -dataMiningRetrieval=データ取得 actionWatch=視聴 actionAnalyze=分析 denoteAllRacesForRaceLogTrackingShorctut=レースログ追跡の全レース表示へのショートカット @@ -1519,24 +1456,8 @@ defaultName=デフォルト exampleTextForName=名前は次のようになります: flightsCount={0,number} フライト flightsCount[one]={0,number} フライト -viewQueryDefinition=クエリ定義の表示 -queryDefinitionViewer=クエリ定義ビューア -groupAverageAscending=グループ平均 (昇順) -groupAverageDescending=グループ平均 (降順) -groupMedianAscending=グループ中央値 (昇順) -groupMedianDescending=グループ中央値 (降順) resultsFoundForSearch={0,number} 結果が ''{1}’ に見つかりました resultsFoundForSearch[one]={0,number} 結果が ''{1}’ に見つかりました -runPredefinedQuery=事前定義クエリを実行 -selectPredefinedQuery=事前定義クエリを選択 -predefinedQueryRunner=事前定義クエリランナー -developerOptions=開発者オプション -copyToClipboard=クリップボードにコピー -code=コード -useClassGetName=タイプ名に class.getName() を使用 -useClassGetNameTooltip=コードベースでの変更に対してより堅牢ですが、コードスニペットはクラスが利用できる範囲でのみ使用できます。 -useStringLiterals=タイプ名に文字列リテラルを使用 -useStringLiteralsTooltip=このコードスニペットはどこでも使用できますが、コードベースが変更される場合は中断します。 errorLoadingDataWithTryAgain=データのロードでエラーが発生しました。しばらくしてから再試行してください。 addGalleryPhoto=ギャラリーフォト追加 addStageImage=ステージ画像追加 @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=次の競技者はこのレースに登録する competitorToolTipMessage={0} はすでにレース {3} のフリート {2} に割り当てられており、そのため同一レース内でフリート {1} に割り当てることはできません addMarkToRegatta=マークをレガッタに追加 selectALeaderboardGroup=リーダーボードグループを選択... -pleaseSelect=選択: requiresValidRegatta=このページには、表示するレースを識別するために有効なレガッタ、レース列、およびフリート名が必要です。 couldNotObtainRace=名称 {0} のレガッタに対してフリート {2} の名称 {1} でレースが取得できませんでした: {3} errorTryingToCreateEmbeddedMap=埋込マップを登録しようとしてエラーが発生: {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=航跡データなし eventRegattaHeaderLegendWindNo=風向風速データなし eventRegattaHeaderLegendVideoNo=動画ストリームなし eventRegattaHeaderLegendAudioNo=音声ストリームなし -angleInDegree=角度 (度) -angleInRadian=角度 (ラジアン) -centralAngleInRadian=中心角 (度) -centralAngleInDegree=中心角 (ラジアン) -kilometers=キロメートル -meters=メートル -nauticalMiles=海里 -seaMiles=海里 -geographicalMiles=地理マイル -days=日 -hours=時間 -minutes=分 -seconds=秒 -milliseconds=ミリ秒 -floatNumber=浮動小数点型 -integer=整数 appendResult=結果を追加 sampleColor=色サンプル -sharedSettingsLink=設定にリンク leaderboardPage=リーダーボードページ -makeDefault=デフォルトを設定 -makeDefaultInProgress=実行中です... -settingsSavedMessage=現在の設定がデフォルトとして設定されました -settingsSaveErrorMessage=使用している設定をデフォルトとして設定する際にエラーが発生しました showLiveNow="実況中" を表示 useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes="推定スタート時刻" および "スタート/フィニッシュ時刻からのコントロール追跡" は 1 つだけ使用してください unknownLeaderboardType=未知のリーダーボードタイプ {0} @@ -1878,10 +1777,6 @@ settingsId=設定 ID documentSettingsId=文書設定 ID settingsForId=ID ''{0}'' の設定 userProfileSettingsTabDescription=ユーザ設定は、このページの多くの場所にあるダイアログを設定することによって生成されます。このビューには、収集したすべての設定がパワーユーザ向けの技術的な方法で表示されます。削除したエントリは復元できないため、注意して使用してください。 -resetToDefault=デフォルトに戻す -resetToDefaultInProgress=リセット中です... -settingsRemoved=デフォルト設定が復元されました -settingsRemovedError=デフォルト設定を復元できませんでした userSettingsFilter=設定フィルタ requiresRegattaRaceAndLeaderboard=このページには有効なレガッタ名、レース名、およびリーダーボード名が必要です。 couldNotFindRaceInRegatta=名前 {1} のレガッタに対して名前 {0} のレースが取得できませんでした @@ -1913,7 +1808,6 @@ errorFetchingDimensionData={0} の次元値のフェッチでエラーが発生: errorFetchingStatistics=サーバからの利用可能な統計のフェッチでエラーが発生: {0} errorFetchingAggregators=サーバからの利用可能な集計のフェッチでエラーが発生: {0} errorLoadingDataRetrieverChainDefinitions=利用可能な DataRetrieverChainDefinitions の取得でエラーが発生: {0} -errorFetchingComponentsChangedTimepoint=サーバからのコンポーネント変更済タイムポイントのフェッチでエラーが発生: {0} errorRunningQuery=クエリの実行でエラーが発生: {0} errorReadingWindFixes=風フィックス {0} の読込でエラーが発生しました errorAddingWindFixForRace=レース {0} に対する風フィックスの追加でエラーが発生: {1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=艇がフォイリング状態にあるとみ minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=隣り合ったフォイリングセグメント間の最小時間 (秒) minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=空白でなく、隣り合った 2 つのフォイリングセグメント間の時間がこの値より小さい場合、隣り合ったこれらのフォイリングセグメントは 1 つにマージされます。 needToProvideValidMinimumRideHeight=浮上高さの有効な値をメートル単位で指定する必要があります。 -dataMiningErrorMargins=エラーマージン -elements={0} 要素 -chooseDifferentDimensionTitle=異なる次元の選択 -chooseDifferentDimensionMessage=結果をグルーピングするための異なる次元を選択してください -pleaseSelectADimension=次元を選択してください currentPortDaggerboardRake=左舷ダガーボード傾斜角 currentPortDaggerboardRakeTooltip=現在の左舷ダガーボード傾斜角 currentStbdDaggerboardRake=右舷ダガーボード傾斜角 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index cc63e58185d..b13a076b6f7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -9,12 +9,10 @@ trackedBefore=Histórico de eventos rastreados general=Geral listRaces=Listar corridas listRegattas=Listar regatas -numberPairResultsPresenter=Diagrama de dispersão wind=Vento maneuverType=Manobra windPanelLabel=Este é o painel eólico que até o momento está completamente vazio. refresh=Atualizar -remove=Remover removeNumber=Remover ({0}) windSource=Origem do vento dampeningInterval=Intervalo de atenuação @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=Corrida rastreada conectada ao nome da co linkToColumn=Link a coluna unlink=Eliminar link leaderboardName=Nome do painel de classificação -cancel=Cancelar pleaseEnterAName=Insira um nome pleaseEnterABoatClass=Insira uma classe de barcos discardRacesFromHowManyStartedRacesOn=Descartar mais um corrida a partir de quantas corridas iniciadas @@ -65,7 +62,6 @@ startingFromNumberOfRaces=A partir de quantas corridas renameLeaderboard=Renomear painel de classificação addColumnToLeaderboard=Adicionar coluna ao painel de classificação pleaseEnterNameForNewRaceColumn=Insira um nome para a coluna da corrida nova -ok=OK medalRace=Corrida para medalha renameRace=Renomear corrida openSelectedLeaderboard=Abrir painel de classificação selecionado @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=Painel de classificação leaderboards=Painéis de classificação leaderboardSettings=Configurações do painel de classificação -settings=Configurações selectAtLeastOneLegDetail=Selecionar pelo menos um detalhe da perna currentSpeedOverGroundInKnots=SOG currentSpeedOverGroundInKnotsTooltip=A velocidade no fundo atual. @@ -173,7 +168,6 @@ tacks=Cambadas jibes=Jaibes penaltyCircles=Voltas de punição medalRaceIsNull=Valor de corrida para medalha não permitido -configuration=Configuração maneuverTypes=Manobras chooseChart=Selecionar diagrama distanceTraveled=Distância navegada @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=s/NM metersUnit=m millimetersUnit=mm degreesUnit=° -close=Encerrar compareCompetitors=Comparar competidores description=Descrição sailNumber=Número da vela country=País no3LetterCodes=Impossível encontrar os códigos de 3 letras do COI. -add=Adicionar delete=Eliminar showCharts=Visualizar diagramas raceWithThisNameAlreadyExists=Já existe uma corrida com esse nome. @@ -269,7 +261,6 @@ printHint=Imprime a versão aplicada blockedApplyButton=Os competidores registrados são diferentes dos competidores da lista de pares! multiplierInfo=Multiplica os voos e cria-os uns junto aos outros de modo que seja possível uma competição com menos mudanças de barco. noPairingListAvailable=A função de impressão só está disponível caso já tenha sido aplicada uma lista de pares aos registros de corridas dos painéis de classificação selecionados. -settingsForComponent=Configurações para {0} noEventsFound=Nenhum evento encontrado noEventSelected=Nenhum evento selecionado noLeaderboardsFound=Nenhum painel de classificação encontrado @@ -307,8 +298,6 @@ leaderboardGroup=Grupo de painéis de classificação pleaseEnterNonEmptyDescription=Insira uma descrição não vazia groupWithThisNameAlreadyExists=Já existe um grupo de painéis de classificação com este nome. detailsOfLeaderboardGroup=Detalhes do grupo de painéis de classificação -edit=Processar -save=Gravar abort=Cancelar noLeaderboardGroupWithNameFound=Não foi encontrado um grupo de painéis de classificação com o nome {0} overview=Síntese @@ -343,7 +332,6 @@ degreesShort=graus untracked=Não rastreado delayForLiveMode=Atraso para modo ao vivo: notAvailable=Não disponível -details=Detalhes noGroupSelected=Nenhum grupo selecionado combinedWindSourceTypeName=Combinado legMiddleWindSourceTypeName=Meio da perna @@ -425,7 +413,6 @@ simulateAsLiveRace=Simular como corrida ao vivo simulateWithOffset=Deslocamento antes da partida em minutos: boatClassDoesNotMatchSelectedRegatta=As corridas selecionadas contêm classes de barcos que não são as mesmas da classe de barcos ''{0}'' da regata selecionada. Não será carregada nenhuma corrida. regattaExistForSelectedBoatClass=Existe pelo menos uma regata para as classes de barcos selecionadas. Criar regatas padrão para essas corridas? -reload=Recarregar addRegatta=Adicionar regata... importRegattas=Importar regatas... exchangeName=Trocar nome @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=O total de pontos líquidos de um competidor na rega windData=Dados do vento gpsData=Dados do GPS status=Status -noDataFound=Não foram encontrados dados displayName=Nome de exibição histogram=Histograma numberOfDataPoints=Número de pontos de dados @@ -895,12 +881,9 @@ legType=Tipo de perna sailID=Número da vela seriesLeaderboard=Painel de classificação da série regattaLeaderboards=Painéis de classificação de regata -clearSelection=Anular seleção running=Em competição -runAsSubstantive=Competir done=Concluído lastFinished=Último a concluir -run=Competir times=vezes dataAmount=Quantidade de dados averageCleanedServerTime=∅ tempo de servidor anulado @@ -924,12 +907,8 @@ selectSheet=Selecionar folha cleanedServerTime=Tempo de servidor anulado overallTime=Tempo geral cleanedOverallTime=Tempo geral anulado -dataMiningResult=Resultado de data mining -groupBy=Agrupar por statisticToCalculate=Calcular estatística -queryResultsChartSubtitle=Percorreu {0} entradas de dados em {1} segundos noQuerySelected=Nenhuma consulta selecionada -runAutomatically=Executar automaticamente windImport_Upload=Carregar windImport_Title=Importar vento de Expedition windImport_BoatId=ID do barco: @@ -959,14 +938,9 @@ raceTimeTooltip=Tempo total navegado nesta corrida, começando a contagem quando raceTimeDownwindTooltip=Tempo total navegado a sotavento nesta corrida raceTimeReachingTooltip=Tempo total navegado a través nesta corrida raceTimeUpwindTooltip=Tempo total navegado a barlavento nesta corrida -noStatisticSelectedError=Não foi selecionada uma estatística para calcular noCustomGrouperScriptTextError=O script do sistema de agrupamento está vazio -noDimensionToGroupBySelectedError=Nenhuma dimensão pela qual efetuar o agrupamento selecionada noGrouperSelectedError=Nenhum tipo de sistema de agrupamento selecionado -noDataRetrieverChainDefinitonSelectedError=Nenhum recuperador de dados selecionado -queryNotValidBecause=Nenhuma consulta possível, pois dataMining=Data mining -errorRunningDataMiningQuery=Ocorreu um erro ao executar a consulta hideToolbar=Ocultar barra de ferramentas showSeriesLeaderboards=Visualizar painéis de classificação da série showOverallLeaderboard=Visualizar painel de classificação geral @@ -989,7 +963,6 @@ id=ID allowReload=Permitir recarregamento compress=Comprimir compressTooltip=Utilizar somente se a instância do servidor de exportação estiver sendo executada pelo menos\ncom o commit 0fbf6071dea125bec4a56dee55d61c99def4a62e. -queryRunner=Executor de consulta rerunQueryAfterRefresh=Executar consulta novamente após atualização refreshIntervalMustntBeEmpty=O intervalo de atualização não deve estar em branco selectionTables=Tabelas de seleção @@ -1074,12 +1047,7 @@ TWATooltip=O ângulo entre a direção dos competidores e o vento TWA=Ângulo do vento verdadeiro showBoatClassChartsLabel=Você também pode ver o diagrama geral para as classes de barcos disponíveis. showDiagram=Visualizar diagrama -runAutomaticallyTooltip=Execute a consulta automaticamente, após modificar, por exemplo, a estatística ou o agrupamento. rerunQueryAfterRefreshTooltip=Executa novamente a consulta após a atualização das tabelas. -queryDefinitionProvider=Fornecedor da definição da consulta -statisticProvider=Fornecedor da estatística -calculateThe=Calcular -groupingProvider=Fornecedor do agrupamento releaseNotes=Novidades e histórico de releases hasSplitFleetContiguousScoring=Flotilhas divididas com pontuação contínua addRaceLogTracker=Adicionar rastreador RaceLog @@ -1237,7 +1205,6 @@ showAll=Visualizar tudo raceVisibilityColumn=Visibilidade enterCarryValueFor=Inserir pontos transferidos para competidor {0} advanced=Avançado -basedOn=com base em retrieveWith=Recuperar com mappingDetails=Detalhes de mapeamento deviceMappingQrCodeExplanation=Se você estiver utilizando o app de rastreamento, você também pode adicionar o mapeamento do dispositivo selecionando um competidor/marca, definindo as horas de partida e chegada e digitalizando depois este código QR. @@ -1248,10 +1215,6 @@ enterImageURL=Inserir URL de imagem... enterVideoURL=Inserir URL de vídeo... enterSponsorImageURL=Inserir URL de imagem de patrocinador... enterRaceName=Inserir nome da corrida... -serverError=Ocorreu um erro ao tentar contatar o servidor. Verifique sua conexão à rede e tente novamente. -remoteProcedureCall=Chamada de procedimento remoto -serverReplies=Respostas do servidor -errorCommunicatingWithServer=Erro ao comunicar com o servidor userManagement=Administração de usuários regattaStructureImport=Importação de estrutura da regata filteredBy=filtrado por @@ -1279,7 +1242,6 @@ noFleetsDefined=Nenhuma flotilha definida. successfullyCreatedRegattas=Regatas criadas com êxito errorTryingToRegisterRacesForTracking=Erro ao tentar registrar corridas {0} para rastreamento: {1}. Verificar sintaxe de URI ao vivo/armazenado. errorDeterminingPolarAvailability=Erro ao determinar disponibilidade de dados de carta polar/VPP para corrida {0}: {1} -error=Erro fileStorage=Armazenamento de arquivos active=Ativo scoringSchemeHighPointEssOverallDescription=Pontuação em pontos. O vencedor de uma etapa pontua 10 pontos, o 2º - 9 pontos, .... O desempate na pontuação geral da Extreme Sailing Series é efetuado a favor do competidor com o maior número de vitórias em etapas. Se isso não efetuar o desempate, será utilizado o resultado na última etapa. @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=Nome completo do competidor alwaysShowCompetitorNationalityColumn=Exibir sempre a nacionalidade do competidor alwaysShowCompetitorNationalityColumnTooltip=Exibir ambos, bandeiras de nacionalidade e imagens do competidor, se disponíveis loadingDimensionValues=Carregando valores de dimensão -runningQuery=Executando consulta inviteBuoyTenders=Convidar navios-balizadores orMultipleEmails=ou vários e-mails separados por vírgula courseOverGroundTrueDegreesTooltip=Percurso verdadeiro no fundo em graus @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=COG distanceIncludingGateStartInMeters=Distância (com início do portão) distanceTraveledIncludingGateStartTooltip=A distância navegada desde o início até o fim da perna\nou até a data/hora atual, se a perna não estiver concluída.\nSe a perna incluir o início de um portão, a distância desde o fim da marcação até a posição inicial é incluída\npara ser possível comparar os competidores mesmo quando começam em horas diferentes. raceDistanceTraveledIncludingGateStartTooltip=A distância navegada desde o início até o fim da corrida\nou até a data/hora atual, se a corrida não estiver concluída.\nPara o início de um portão, a distância desde o fim da marcação até a posição inicial é incluída\npara ser possível comparar os competidores mesmo quando começam em horas diferentes. -results=Resultados -groupName=Nome do grupo -valueAscending=Valor (crescente) -valueDescending=Valor (decrescente) -sortBy=Ordenar por dashboardHeader=Painel dashboardNoWindBotAvailableHeader=O Wind Bot não está disponível. dashboardNoWindBotAvailableMessage=Para receber dados do vento em tempo real de unidades de medida do vento, certifique-se de que o Wind Bot está ativado e conectado ao SAP Sailing Analytics. @@ -1370,16 +1326,12 @@ dashboardRCBoat=Barco de rádio-controle fixedMarkPassing=(fixo) suppressedMarkPassing=(suprimido) windUp=Orientação pelo vento (visualizar vento no topo do mapa) -filterBy=Filtrar por -currentFilterSelection=Seleção de filtro atual notCapableOfGeneratingACodeForIdentifier=Não é possível gerar um código para este identificador. serverUrl=URL de servidor rotatedFromTrueNorth=Efetuou a rotação de {0} graus desde o norte verdadeiro. clickToToggleWindUp=Tocar/clicar para comutar entre exibição do mapa com orientação pelo vento e pelo norte clickToToggleWindStreamlets=Tocar/clicar para visualizar ou ocultar os cursos do vento startLineToFirstMarkTriangle=Partida para primeira marca ({0}m) -dataMiningComponentsHaveBeenUpdated=Os componentes de data mining foram atualizados -dataMiningComponentsNeedReloadDialogMessage=Clicar em Recarregar para recarregar os componentes agora. Isto irá descartar os dados exibidos atualmente e executar uma consulta padrão.\nClicar em Fechar para não efetuar nada. O data mining não irá funcionar corretamente até ser efetuado o recarregamento dos componentes. noDataForEvent=Ainda não existem dados para o evento. countriesCount={0,number} países countriesCount[one]={0,number} país @@ -1477,15 +1429,7 @@ noFinishedRaces=Ainda não existem corridas concluídas. racesOverview=Síntese de corridas listFormatLabel=Formato de lista competitionFormatLabel=Formato da competição -empty=Vazio -runAQuery=Executar uma consulta latestRegattaStandings=Posições da última regata -plainText=Texto simples -columnChart=Diagrama de colunas -columnChartWithErrorBars=Diagrama de colunas com barras de erros -choosePresentation=Selecionar apresentação -cantDisplayDataOfType=Não é possível exibir dados do tipo {0} -shownDecimals=Decimais exibidos openFullscreenView=Abrir visão de tela inteira closeFullscreenView=Fechar visão de tela inteira videosCount={0,number} vídeos @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} foto eventsHaveTakenPlace=Foram realizados {0} eventos eventsHaveTakenPlace[one]=Foi realizado um evento raceOffice=Secretaria do evento -analyze=Analisar -dataMiningSettings=Configurações de data mining -multiResultsPresenter=Apresentador de vários resultados -plainResultsPresenter=Apresentador de resultados simples -resultsChart=Diagrama de resultados -tabbedResultsPresenter=Apresentador de resultados por fichas polarResultsPresenter=Apresentador de resultados da carta polar maneuverSpeedDetailsResultsPresenter=Apresentador de resultados detalhados da velocidade da manobra -dataMiningRetrieval=Obtenção de dados actionWatch=Ver actionAnalyze=Analisar denoteAllRacesForRaceLogTrackingShorctut=Atalho para denotar todas as corridas para rastreamento do registro de corridas @@ -1519,24 +1456,8 @@ defaultName=Padrão exampleTextForName=Seu nome é parecido com: flightsCount={0,number} voos flightsCount[one]={0,number} voo -viewQueryDefinition=Ver definição da consulta -queryDefinitionViewer=Visualizador de definição da consulta -groupAverageAscending=Média do grupo (crescente) -groupAverageDescending=Média do grupo (decrescente) -groupMedianAscending=Mediana do grupo (crescente) -groupMedianDescending=Mediana do grupo (decrescente) resultsFoundForSearch={0,number} resultados encontrados para ''{1}'' resultsFoundForSearch[one]={0,number} resultado encontrado para ''{1}'' -runPredefinedQuery=Executar consulta predefinida -selectPredefinedQuery=Selecionar consulta predefinida -predefinedQueryRunner=Executor de consulta predefinida -developerOptions=Opções do desenvolvedor -copyToClipboard=Copiar para o clipboard -code=Código -useClassGetName=Utilizar Class.getName() para nomes de tipo -useClassGetNameTooltip=Mais robusto em relação às modificações na base do código, mas o trecho do código só pode ser utilizado no âmbito em que as classes estão disponíveis. -useStringLiterals=Utilizar literais de cadeia para nomes de tipo -useStringLiteralsTooltip=O trecho do código pode ser utilizado em qualquer local, mas será quebrado se a base do código for modificada. errorLoadingDataWithTryAgain=Erro ao carregar dados. Tentar novamente dentro de momentos. addGalleryPhoto=Adicionar foto da galeria addStageImage=Adicionar imagem da etapa @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=Os competidores seguintes não podem ser registrad competitorToolTipMessage={0} já foi atribuído à flotilha {2} na corrida {3} e por isso não pode ser atribuído à flotilha {1} na mesma corrida addMarkToRegatta=Adicionar marca à regata selectALeaderboardGroup=Selecionar um grupo de painéis de classificação... -pleaseSelect=Selecione requiresValidRegatta=Esta página requer uma regata válida, a coluna da corrida e o nome da flotilha para identificar a corrida a ser exibida. couldNotObtainRace=Não foi possível obter uma corrida com o nome {1} para a flotilha {2} para uma regata com o nome {0}: {3} errorTryingToCreateEmbeddedMap=Erro ao tentar criar o mapa integrado: {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=Sem dados de rastreamento eventRegattaHeaderLegendWindNo=Sem dados de vento eventRegattaHeaderLegendVideoNo=Sem fluxos de vídeo eventRegattaHeaderLegendAudioNo=Sem fluxos de áudio -angleInDegree=Ângulo em graus -angleInRadian=Ângulo em radianos -centralAngleInRadian=Ângulo central em radianos -centralAngleInDegree=Ângulo central em graus -kilometers=Quilômetros -meters=Metros -nauticalMiles=Milhas náuticas -seaMiles=Milhas marítimas -geographicalMiles=Milhas geográficas -days=Dias -hours=Horas -minutes=Minutos -seconds=Segundos -milliseconds=Milissegundos -floatNumber=Margem -integer=Número inteiro appendResult=Anexar resultado sampleColor=Amostra de cor -sharedSettingsLink=Link com configurações leaderboardPage=Página do painel de classificação -makeDefault=Definir como padrão -makeDefaultInProgress=Em andamento... -settingsSavedMessage=Suas configurações atuais foram definidas com êxito como padrão -settingsSaveErrorMessage=Ocorreu um erro ao definir suas configurações como padrão showLiveNow=Exibir "Ao vivo agora" useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=Utilizar só um de "Inferência da hora de partida" e "Rastreamento de controle das horas de partida e chegada" unknownLeaderboardType=Tipo de painel de classificação desconhecido {0} @@ -1878,10 +1777,6 @@ settingsId=ID de configurações documentSettingsId=ID de configuração do documento settingsForId=Configurações para ID ''{0}'' userProfileSettingsTabDescription=As configurações dos usuários são geradas por diálogos de configurações que podem ser encontrados em vários locais da página. Esta visão exibe todas as suas configurações coletadas de uma forma técnica para usuários avançados. Tenha em atenção que as entradas removidas não podem ser restauradas, por isso utilize-as com cuidado. -resetToDefault=Reinicializar para padrão -resetToDefaultInProgress=Na reinicialização... -settingsRemoved=Configurações padrão restauradas -settingsRemovedError=Não foi possível restaurar configurações padrão userSettingsFilter=Filtro de configurações requiresRegattaRaceAndLeaderboard=Esta página necessita de um nome de regata, um nome de corrida e um nome de painel de classificação válidos. couldNotFindRaceInRegatta=Não foi possível obter uma corrida com o nome {0} para uma regata com o nome {1} @@ -1913,7 +1808,6 @@ errorFetchingDimensionData=Erro ao chamar os valores de dimensão de {0}: {1} errorFetchingStatistics=Erro ao chamar as estatísticas disponíveis do servidor: {0} errorFetchingAggregators=Erro ao chamar os agregadores disponíveis do servidor: {0} errorLoadingDataRetrieverChainDefinitions=Erro ao recuperar as definições da cadeia do recuperador de dados disponíveis: {0} -errorFetchingComponentsChangedTimepoint=Erro ao chamar data/hora modificada de componentes do servidor: {0} errorRunningQuery=Erro ao executar consulta: {0} errorReadingWindFixes=Erro ao ler pontos fixos de vento {0} errorAddingWindFixForRace=Erro ao adicionar um ponto fixo de vento para corrida {0}: {1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=A altura mínima de flutuação em metros neces minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=Duração mínima entre segmentos adjacentes de navegação com hidrofólio (s) minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=Se não estiver em branco e o tempo entre dois segmentos adjacentes de navegação com hidrofólio for inferior a este, esses segmentos adjacentes de navegação com hidrofólio serão consolidados em um. needToProvideValidMinimumRideHeight=Você precisa fornecer um valor válido da altura mínima de flutuação em metros. -dataMiningErrorMargins=Margens de erro -elements={0} elementos -chooseDifferentDimensionTitle=Selecionar dimensão diferente -chooseDifferentDimensionMessage=Selecionar uma dimensão diferente para os resultados do agrupamento -pleaseSelectADimension=Selecione uma dimensão currentPortDaggerboardRake=Inclinação bolina bombordo currentPortDaggerboardRakeTooltip=A inclinação atual da bolina para bombordo currentStbdDaggerboardRake=Inclinação bolina boreste diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 152f6763935..ace2013fbf6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -9,12 +9,10 @@ trackedBefore=История отслеживаемых событий general=Общее listRaces=Список гонок listRegattas=Список регат -numberPairResultsPresenter=Множество точек wind=Ветер maneuverType=Маневр windPanelLabel=Это панель ветров, которая пока что совсем пуста. refresh=Обновить -remove=Удалить removeNumber=Удалить ({0}) windSource=Источник ветра dampeningInterval=Интервал смягчения @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=Отслеживаемая гонка с linkToColumn=Связать со столбцом unlink=Отменить связывание leaderboardName=Название таблицы лидеров -cancel=Отмена pleaseEnterAName=Введите название pleaseEnterABoatClass=Введите класс лодки discardRacesFromHowManyStartedRacesOn=Исключить еще одну гонку, начиная с числа стартов в гонках @@ -65,7 +62,6 @@ startingFromNumberOfRaces=Начиная с числа гонок renameLeaderboard=Переименовать таблицу лидеров addColumnToLeaderboard=Добавить столбец в таблицу лидеров pleaseEnterNameForNewRaceColumn=Введите название для столбца новой гонки -ok=ОК medalRace=Гонка на медали renameRace=Переименовать гонку openSelectedLeaderboard=Открыть выбранную таблицу лидеров @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=Таблица лидеров leaderboards=Таблицы лидеров leaderboardSettings=Параметры таблицы лидеров -settings=Параметры selectAtLeastOneLegDetail=Выберите подробности минимум одного отрезка currentSpeedOverGroundInKnots=SOG currentSpeedOverGroundInKnotsTooltip=Текущая скорость относительно грунта. @@ -173,7 +168,6 @@ tacks=Поворотов оверштаг jibes=Поворотов фордевинд penaltyCircles=Штрафных кругов medalRaceIsNull=Значение гонки на медали не разрешено -configuration=Конфигурация maneuverTypes=Маневры chooseChart=Выберите диаграмму distanceTraveled=Пройденная дистанция @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=с/NM metersUnit=м millimetersUnit=мм degreesUnit=° -close=Закрыть compareCompetitors=Сравнить участников description=Описание sailNumber=Номер на парусе country=Страна no3LetterCodes=Не удалось найти 3-буквенные коды IOC. -add=Добавить delete=Удалить showCharts=Показать диаграммы raceWithThisNameAlreadyExists=Гонка с таким названием уже существует. @@ -269,7 +261,6 @@ printHint=Печать примененной версии blockedApplyButton=Число зарегистрированных участников не равно числу участников из списка пар! multiplierInfo=Планируйте флайты таким образом, чтобы обеспечить соревнования с минимально возможной переменой лодок noPairingListAvailable=Функция печати доступна только после применения списка пар к журналам гонок выбранных таблиц лидеров. -settingsForComponent={0} — параметры noEventsFound=События не найдены noEventSelected=Не выбрано событие noLeaderboardsFound=Таблицы лидеров не найдены @@ -307,8 +298,6 @@ leaderboardGroup=Группа таблиц лидеров pleaseEnterNonEmptyDescription=Введите непустое описание groupWithThisNameAlreadyExists=Группа таблиц лидеров с таким названием уже существует. detailsOfLeaderboardGroup=Подробности группы таблиц лидеров -edit=Изменить -save=Сохранить abort=Прервать noLeaderboardGroupWithNameFound=Группа таблиц лидеров с названием {0} не найдена overview=Обзор @@ -343,7 +332,6 @@ degreesShort=град. untracked=Не отслеживается delayForLiveMode=Задержка для оперативного режима: notAvailable=Недоступно -details=Подробности noGroupSelected=Не выбрана группа combinedWindSourceTypeName=Объединено legMiddleWindSourceTypeName=Середина отрезка @@ -425,7 +413,6 @@ simulateAsLiveRace=Симулировать как текущую гонку simulateWithOffset=Сдвиг перед стартом в минутах: boatClassDoesNotMatchSelectedRegatta=Выбранные гонки содержат классы лодок, не совпадающие с классом лодок ''{0}'' выбранной регаты. Гонки не будут загружены. regattaExistForSelectedBoatClass=Для выбранных классов лодок имеется минимум одна регата. Создать регаты по умолчанию для соответствующих гонок? -reload=Перезагрузить addRegatta=Добавить регату... importRegattas=Импортировать регаты... exchangeName=Имя Exchange @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=Общая чистая сумма баллов уч windData=Данные о ветре gpsData=Данные GPS status=Статус -noDataFound=Данные не найдены displayName=Отображаемое имя histogram=Гистограмма numberOfDataPoints=Число точек данных @@ -895,12 +881,9 @@ legType=Тип отрезка sailID=Номер на парусе seriesLeaderboard=Таблица лидеров серии regattaLeaderboards=Таблицы лидеров регаты -clearSelection=Очистить выбор running=Выполнение -runAsSubstantive=Выполнить done=Готово lastFinished=Последний финиш -run=Выполнить times=раз dataAmount=Объем данных averageCleanedServerTime=∅ Очищенное время сервера @@ -924,12 +907,8 @@ selectSheet=Выбрать лист cleanedServerTime=Очищенное время сервера overallTime=Общее время cleanedOverallTime=Очищенное общее время -dataMiningResult=Результат добычи данных -groupBy=Группировать по statisticToCalculate=Рассчитать статистику -queryResultsChartSubtitle=Обработано {0} зап. данных за {1} с noQuerySelected=Не выбран запрос -runAutomatically=Выполнить автоматически windImport_Upload=Отправить windImport_Title=Импортировать ветер из Expedition windImport_BoatId=Ид. лодки: @@ -959,14 +938,9 @@ raceTimeTooltip=Общее время хода в данной гонке, от raceTimeDownwindTooltip=Общее время хода по ветру в данной гонке raceTimeReachingTooltip=Общее время хода полным ветром в данной гонке raceTimeUpwindTooltip=Общее время хода против ветра в данной гонке -noStatisticSelectedError=Не выбрана статистика для расчета noCustomGrouperScriptTextError=Скрипт группирования пуст -noDimensionToGroupBySelectedError=Не выбрано базовое измерение для группировки noGrouperSelectedError=Не выбран тип группирования -noDataRetrieverChainDefinitonSelectedError=Не выбрано средство извлечения данных -queryNotValidBecause=Запрос невозможен, так как dataMining=Добыча данных -errorRunningDataMiningQuery=При выполнении запроса возникла ошибка hideToolbar=Скрыть панель инструментов showSeriesLeaderboards=Показать таблицы лидеров серии showOverallLeaderboard=Показать итоговую таблицу лидеров @@ -989,7 +963,6 @@ id=Ид. allowReload=Разрешить перезагрузку compress=Сжать compressTooltip=Используйте только при выполнении экспортирующего экземпляра\nсервера с commit 0fbf6071dea125bec4a56dee55d61c99def4a62e. -queryRunner=Выполнение запроса rerunQueryAfterRefresh=Повторить запрос после обновления refreshIntervalMustntBeEmpty=Интервал обновления не должен быть пустым selectionTables=Таблица выбора @@ -1074,12 +1047,7 @@ TWATooltip=Угол между направлением участников и TWA=Угол истинного ветра showBoatClassChartsLabel=Также можно просмотреть общую диаграмму для доступных классов лодок. showDiagram=Показать диаграмму -runAutomaticallyTooltip=Автоматически выполнять запрос после изменения, например, статистики или группирования. rerunQueryAfterRefreshTooltip=Повторно выполняет запрос после обновления таблиц. -queryDefinitionProvider=Поставщик определения запроса -statisticProvider=Поставщик статистики -calculateThe=Рассчитать -groupingProvider=Поставщик группирования releaseNotes=Новости и история выпусков hasSplitFleetContiguousScoring=Разделенные флоты получили сходные оценки addRaceLogTracker=Добавить средство отслеживания журнала гонки @@ -1237,7 +1205,6 @@ showAll=Показать все raceVisibilityColumn=Видимость enterCarryValueFor=Введите перенесенные баллы для участника {0} advanced=Дополнительно -basedOn=на базе retrieveWith=Получить с помощью mappingDetails=Подробности сопоставления deviceMappingQrCodeExplanation=Если используется приложение отслеживания, можно добавить также сопоставление устройства, выбрав участника/отметку, установив интервал времени, а затем отсканировав этот QR-код. @@ -1248,10 +1215,6 @@ enterImageURL=Ввести URL-адрес изображения... enterVideoURL=Ввести URL-адрес видео... enterSponsorImageURL=Ввести URL-адрес изображения спонсора... enterRaceName=Ввести название гонки... -serverError=При попытке обратиться к серверу возникла ошибка. Проверьте подключение к сети и повторите попытку. -remoteProcedureCall=Удаленный вызов процедуры -serverReplies=Ответы сервера -errorCommunicatingWithServer=Ошибка связи с сервером userManagement=Управление пользователями regattaStructureImport=Импорт структуры регаты filteredBy=отфильтровано по @@ -1279,7 +1242,6 @@ noFleetsDefined=Не определены флоты. successfullyCreatedRegattas=Регаты успешно созданы errorTryingToRegisterRacesForTracking=Ошибка при попытке зарегистрировать гонки {0} для отслеживания: {1}. проверьте синтаксис URI оперативных/хранимых данных. errorDeterminingPolarAvailability=Ошибка при определении доступности полярных/VPP данных для гонки {0}: {1} -error=Ошибка fileStorage=Хранилище файлов active=Активно scoringSchemeHighPointEssOverallDescription=Оценка в баллах. Победитель в акте получает 10 баллов, 2-й — 9 баллов, .... Любое равенство в общей оценке Extreme Sailing Series разрешается в пользу участника с наибольшим числом побед в актах. В случае равенства числа побед для разрешения используется последний акт. @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=Полное имя участника alwaysShowCompetitorNationalityColumn=Всегда показывать государственную принадлежность участника alwaysShowCompetitorNationalityColumnTooltip=Показывать и флаги государств, и изображения участников (при наличии) loadingDimensionValues=Загрузка значений измерения -runningQuery=Выполнение запроса inviteBuoyTenders=Пригласить лоцманов orMultipleEmails=или несколько адресов эл. почты через запятую courseOverGroundTrueDegreesTooltip=Истинный курс относительно грунта в градусах @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=COG distanceIncludingGateStartInMeters=Дистанция (при старте через ворота) distanceTraveledIncludingGateStartTooltip=Дистанция, пройденная от начала отрезка до конца\nили до текущего момента времени, если отрезок не завершен.\nЕсли отрезок включает старт через ворота, дистанция от отметки до стартовой позиции включается,\nчтобы обеспечить сравнение участников даже при старте в разное время. raceDistanceTraveledIncludingGateStartTooltip=Дистанция, пройденная от начала гонки до конца\nили до текущего момента времени, если гонка не завершена.\nДля старта через ворота включается дистанция от привязки до стартовой позиции,\nчтобы обеспечить сравнение участников даже при старте в разное время. -results=Результаты -groupName=Название группы -valueAscending=Значение (по возрастанию) -valueDescending=Значение (по убыванию) -sortBy=Сортировать по dashboardHeader=Панель dashboardNoWindBotAvailableHeader=WindBot недоступен. dashboardNoWindBotAvailableMessage=Чтобы получать оперативные данные от устройств измерения ветра, убедитесь, что WindBot включен и подключен к SAP Sailing Analytics. @@ -1370,16 +1326,12 @@ dashboardRCBoat=Лодка ГК fixedMarkPassing=(фиксировано) suppressedMarkPassing=(подавляется) windUp=На ветер (показать ветер сверху карты) -filterBy=Фильтровать по -currentFilterSelection=Текущий выбор фильтра notCapableOfGeneratingACodeForIdentifier=Я не могу сгенерировать код для этого идентификатора. serverUrl=URL-адрес сервера rotatedFromTrueNorth=Повернуто на {0} град. от истинного севера. clickToToggleWindUp=Коснитесь/щелкните для переключения между отображением карты с ветром или севером вверху clickToToggleWindStreamlets=Коснитесь/щелкните для отображения или скрытия потоков ветра startLineToFirstMarkTriangle=От старта до первой отметки ({0} м) -dataMiningComponentsHaveBeenUpdated=Компоненты добычи данных обновлены. -dataMiningComponentsNeedReloadDialogMessage=Щелкните ''Перезагрузить'', чтобы перезагрузить компоненты сейчас. Текущее отображение данных будет сброшено с выполнением запроса по умолчанию. Добыча данных не будет работать правильно до перезагрузки компонентов. noDataForEvent=Данных для данного события еще нет. countriesCount={0,number} стран countriesCount[one]={0,number} страна @@ -1477,15 +1429,7 @@ noFinishedRaces=Завершенных гонок еще нет racesOverview=Обзор гонок listFormatLabel=Формат списка competitionFormatLabel=Формат соревнования -empty=Пусто -runAQuery=Выполнить запрос latestRegattaStandings=Позиции в последней регате -plainText=Простой текст -columnChart=Столбчатая диаграмма -columnChartWithErrorBars=Столбчатая диаграмма с планками погрешностей -choosePresentation=Выберите представление -cantDisplayDataOfType=Невозможно отобразить данные типа {0} -shownDecimals=Отображаемые десятичные разряды openFullscreenView=Открыть полноэкранный режим closeFullscreenView=Закрыть полноэкранный режим videosCount={0,number} видео @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} фото eventsHaveTakenPlace=Имело место {0} событий eventsHaveTakenPlace[one]=Имело место одно событие raceOffice=Служба гонки -analyze=Анализировать -dataMiningSettings=Параметры добычи данных -multiResultsPresenter=Презентатор множества результатов -plainResultsPresenter=Презентатор простых результатов -resultsChart=Диаграмма результатов -tabbedResultsPresenter=Презентатор результатов со вкладками polarResultsPresenter=Полярный презентатор результатов maneuverSpeedDetailsResultsPresenter=Демонстратор результатов детализации скорости маневра -dataMiningRetrieval=Извлечение данных actionWatch=Смотреть actionAnalyze=Анализировать denoteAllRacesForRaceLogTrackingShorctut=Ярлык для отмены отслеживания журналов всех гонок @@ -1519,24 +1456,8 @@ defaultName=По умолчанию exampleTextForName=Выше имя выглядит так: flightsCount={0,number} полетов flightsCount[one]={0,number} полет -viewQueryDefinition=Просмотреть определение запроса -queryDefinitionViewer=Средство просмотра определения запроса -groupAverageAscending=Среднее группы (по возрастанию) -groupAverageDescending=Среднее группы (по убыванию) -groupMedianAscending=Медиана группы (по возрастанию) -groupMedianDescending=Медиана группы (по убыванию) resultsFoundForSearch=Найдено {0,number} результатов для ''{1}'' resultsFoundForSearch[one]=Найден {0,number} результат для ''{1}'' -runPredefinedQuery=Выполнить готовый запрос -selectPredefinedQuery=Выбрать готовый запрос -predefinedQueryRunner=Средство выполнения готовых запросов -developerOptions=Параметры разработчика -copyToClipboard=Копировать в буфер обмена -code=Код -useClassGetName=Использовать Class.getName() для имен типов -useClassGetNameTooltip=Более надежно по сравнению с изменениями в основании кода, но фрагмент кода может быть использован только в области с доступными классами. -useStringLiterals=Использовать строковые литералы для имен типов -useStringLiteralsTooltip=Фрагмент кода может использоваться где угодно, но будет нарушен в случае изменения основания кода. errorLoadingDataWithTryAgain=Ошибка при загрузке данных. Повтор попытки через несколько секунд. addGalleryPhoto=Добавить фото галереи addStageImage=Добавить изображение этапа @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=Регистрация следующих учас competitorToolTipMessage={0} уже присвоен флоту {2} в гонке {3} и поэтому не может быть присвоен флоту {1} в той же самой гонке addMarkToRegatta=Добавить отметку к регате selectALeaderboardGroup=Выбрать группу таблиц лидеров... -pleaseSelect=Выберите requiresValidRegatta=Эта страница определяет гонку для отображения по допустимым значениям регаты, столбца гонки и названия флота. couldNotObtainRace=Не удалось получить гонку с названием {1} для флота {2} для регаты с названием {0}: {3} errorTryingToCreateEmbeddedMap=Ошибка при попытке создать внедренную карту: {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=Нет данных отслеживания eventRegattaHeaderLegendWindNo=Нет данных о ветре eventRegattaHeaderLegendVideoNo=Нет видеопотоков eventRegattaHeaderLegendAudioNo=Нет аудиопотоков -angleInDegree=Угол в градусах -angleInRadian=Угол в радианах -centralAngleInRadian=Центральный угол в градусах -centralAngleInDegree=Центральный угол в радианах -kilometers=Километры -meters=Метры -nauticalMiles=Морские мили -seaMiles=Морские мили -geographicalMiles=Географические мили -days=Дни -hours=Часы -minutes=Минуты -seconds=Секунды -milliseconds=Миллисекунды -floatNumber=Плавающее -integer=Целое appendResult=Добавить результат sampleColor=Образец цвета -sharedSettingsLink=Связать с настройками leaderboardPage=Страница таблицы лидеров -makeDefault=Использовать по умолчанию -makeDefaultInProgress=Выполняется... -settingsSavedMessage=Текущие настройки успешно заданы для использования по умолчанию -settingsSaveErrorMessage=При задании настроек для использования по умолчанию возникла ошибка showLiveNow=Показать "Оперативные данные" useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=Использовать только один из параметров "Вывод о времени старта" и "Контролировать отслеживание от времени старта и финиша" unknownLeaderboardType=Неизвестный тип таблицы лидеров {0} @@ -1878,10 +1777,6 @@ settingsId=Ид. настроек documentSettingsId=Ид. настроек документа settingsForId=Настройки для ид. ''{0}'' userProfileSettingsTabDescription=Пользовательские настройки генерируются диалогами настроек, расположенными во многих местах страницы. Здесь отображено техническое представление всех собранных настроек для ключевых пользователей. Соблюдайте осторожность, так как восстановить удаленные записи невозможно. -resetToDefault=Восстановить настройки по умолчанию -resetToDefaultInProgress=Выполняется сброс... -settingsRemoved=Восстановлены настройки по умолчанию -settingsRemovedError=Не удалось восстановить настройки по умолчанию userSettingsFilter=Фильтр настроек requiresRegattaRaceAndLeaderboard=Для этой страницы требуются действительные названия регаты, гонки и таблицы лидеров. couldNotFindRaceInRegatta=Не удалось получить гонку с названием {0} для регаты с названием {1} @@ -1913,7 +1808,6 @@ errorFetchingDimensionData=Ошибка при вызове значений и errorFetchingStatistics=Ошибка при вызове доступной статистики с сервера: {0} errorFetchingAggregators=Ошибка при вызове доступных агрегаторов с сервера: {0} errorLoadingDataRetrieverChainDefinitions=Ошибка при вызове доступных определений цепочек извлечения данных: {0} -errorFetchingComponentsChangedTimepoint=Ошибка при вызове отметки времени изменения компонентов с сервера: {0} errorRunningQuery=Ошибка при выполнении запроса: {0} errorReadingWindFixes=Ошибка при считывании замеров ветра {0} errorAddingWindFixForRace=Ошибка при добавлении замера ветра для гонки {0}: {1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=Минимальная высота просв minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=Минимальная продолжительность между двумя сегментами в крыльевом режиме (сек) minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=Если поле не является пустым, и время между двумя смежными сегментами в крыльевом режиме не превышает указанное значение, смежные сегменты в крыльевом режиме считаются одним сегментом. needToProvideValidMinimumRideHeight=Укажите действительное значение минимальной высоты просвета в метрах. -dataMiningErrorMargins=Пределы погрешности -elements={0} элем. -chooseDifferentDimensionTitle=Выберите другое измерение -chooseDifferentDimensionMessage=Выберите другое измерение для группирования результатов -pleaseSelectADimension=Выберите измерение currentPortDaggerboardRake=Скос киля на левый борт currentPortDaggerboardRakeTooltip=Текущий скос выдвижного киля на левый борт currentStbdDaggerboardRake=Скос киля на правый борт diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 0115aaad20f..a92935af942 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -9,12 +9,10 @@ trackedBefore=已跟踪活动的历史记录 general=常规 listRaces=比赛轮次清单 listRegattas=比赛清单 -numberPairResultsPresenter=散点图 wind=风力 maneuverType=操作 windPanelLabel=这是风力面板,目前完全空白。 refresh=刷新 -remove=移除 removeNumber=移除 ({0}) windSource=风源 dampeningInterval=阻尼间隔 @@ -53,7 +51,6 @@ trackedRaceConnectedToSelectedRaceName=跟踪的比赛轮次已连接到所选 linkToColumn=链接到列 unlink=取消链接 leaderboardName=积分榜名称 -cancel=取消 pleaseEnterAName=请输入名称 pleaseEnterABoatClass=请输入船只级别 discardRacesFromHowManyStartedRacesOn=从已开始的比赛轮次数开始再放弃一个比赛轮次 @@ -65,7 +62,6 @@ startingFromNumberOfRaces=从比赛轮次数开始 renameLeaderboard=重命名积分榜 addColumnToLeaderboard=向积分榜添加列 pleaseEnterNameForNewRaceColumn=请输入新比赛轮次列的名称 -ok=确定 medalRace=奖牌轮 renameRace=重命名比赛轮次 openSelectedLeaderboard=打开选择的积分榜 @@ -89,7 +85,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=积分榜 leaderboards=积分榜 leaderboardSettings=积分榜设置 -settings=设置 selectAtLeastOneLegDetail=至少选择一个航段详情 currentSpeedOverGroundInKnots=SOG currentSpeedOverGroundInKnotsTooltip=当前实际速度。 @@ -173,7 +168,6 @@ tacks=迎风转向 jibes=顺风转向 penaltyCircles=惩罚转圈 medalRaceIsNull=不允许奖牌轮值 -configuration=配置 maneuverTypes=操作 chooseChart=选择记录 distanceTraveled=航行距离 @@ -191,13 +185,11 @@ secondsPerNauticalMileUnit=s/NM metersUnit=m millimetersUnit=mm degreesUnit=° -close=关闭 compareCompetitors=比较参赛队 description=描述 sailNumber=帆号 country=国家/地区 no3LetterCodes=无法找到 IOC 3 个字母的代码。 -add=添加 delete=删除 showCharts=显示记录 raceWithThisNameAlreadyExists=具有此名称的比赛轮次已存在。 @@ -269,7 +261,6 @@ printHint=打印应用的版本 blockedApplyButton=注册的参赛队不等于配对清单中的参赛队! multiplierInfo=多个航程,彼此相邻,这样就可以减少船只更换情况进行比赛 noPairingListAvailable=打印功能仅在配对清单可应用于选择的积分榜比赛轮次日志时可用。 -settingsForComponent={0}设置 noEventsFound=未找到活动 noEventSelected=未选择活动 noLeaderboardsFound=未找到积分榜 @@ -307,8 +298,6 @@ leaderboardGroup=积分榜组 pleaseEnterNonEmptyDescription=请输入非空描述 groupWithThisNameAlreadyExists=具有此名称的积分榜组已存在。 detailsOfLeaderboardGroup=积分榜组详情 -edit=编辑 -save=保存 abort=中止 noLeaderboardGroupWithNameFound=未找到名称为{0}的积分榜组 overview=总览 @@ -343,7 +332,6 @@ degreesShort=deg untracked=取消跟踪 delayForLiveMode=实况模式延迟: notAvailable=不可用 -details=详情 noGroupSelected=未选择分组 combinedWindSourceTypeName=组合 legMiddleWindSourceTypeName=航段中部 @@ -425,7 +413,6 @@ simulateAsLiveRace=模拟实况比赛轮次 simulateWithOffset=起航前的偏移(分钟): boatClassDoesNotMatchSelectedRegatta=所选的比赛轮次包含不同于所选比赛船只级别 "{0}" 的船只级别。将不加载比赛轮次。 regattaExistForSelectedBoatClass=所选船只级别至少有一场比赛。是否确定为这些比赛轮次创建默认比赛? -reload=重新加载 addRegatta=添加比赛... importRegattas=导入比赛... exchangeName=交换机名称 @@ -647,7 +634,6 @@ totalNetPointsColumnTooltip=参赛队在比赛中的总净分。\n按名次( windData=风力数据 gpsData=GPS 数据 status=状态 -noDataFound=未找到数据 displayName=显示名称 histogram=直方图 numberOfDataPoints=数据点数 @@ -895,12 +881,9 @@ legType=航段形式 sailID=帆号 seriesLeaderboard=系列积分榜 regattaLeaderboards=比赛积分榜 -clearSelection=清除选择 running=运行 -runAsSubstantive=运行 done=完成 lastFinished=最后完成 -run=运行 times=次数 dataAmount=数据量 averageCleanedServerTime=∅ 清理服务器时间 @@ -924,12 +907,8 @@ selectSheet=选择工作表 cleanedServerTime=清理服务器时间 overallTime=总时间 cleanedOverallTime=清理总时间 -dataMiningResult=数据挖掘结果 -groupBy=分组方式 statisticToCalculate=计算统计 -queryResultsChartSubtitle={1}秒内处理{0}个数据条目 noQuerySelected=没有查询选择 -runAutomatically=自动运行 windImport_Upload=上传 windImport_Title=从导航图导入风力 windImport_BoatId=船只编号: @@ -959,14 +938,9 @@ raceTimeTooltip=参赛队在本比赛轮次中越过开始航路点\n(不是 raceTimeDownwindTooltip=本轮次比赛顺风航行的总时间 raceTimeReachingTooltip=本轮次比赛横风航行的总时间 raceTimeUpwindTooltip=本轮次比赛迎风航行的总时间 -noStatisticSelectedError=未选择待计算的统计 noCustomGrouperScriptTextError=群集器脚本为空 -noDimensionToGroupBySelectedError=未选择分组方式尺寸 noGrouperSelectedError=未选择群集器类型 -noDataRetrieverChainDefinitonSelectedError=未选择数据检索器 -queryNotValidBecause=无法查询,原因: dataMining=数据挖掘 -errorRunningDataMiningQuery=运行查询期间出错 hideToolbar=隐藏工具栏 showSeriesLeaderboards=显示系列积分榜 showOverallLeaderboard=显示总积分榜 @@ -989,7 +963,6 @@ id=编号 allowReload=允许重新加载 compress=压缩 compressTooltip=仅在导出服务器实例至少通过\n任务 0fbf6071dea125bec4a56dee55d61c99def4a62e 运行时使用。 -queryRunner=查询执行器 rerunQueryAfterRefresh=在刷新后重新运行查询 refreshIntervalMustntBeEmpty=刷新间隔不得为空 selectionTables=选择表 @@ -1074,12 +1047,7 @@ TWATooltip=赛队航行的方向和风的夹角 TWA=实际风向角 showBoatClassChartsLabel=还可以查看可用船只级别的总直直方图。 showDiagram=显示直方图 -runAutomaticallyTooltip=在更改统计或分组后,自动运行查询。 rerunQueryAfterRefreshTooltip=在刷新表后,重新运行查询。 -queryDefinitionProvider=查询定义提供方 -statisticProvider=统计提供方 -calculateThe=计算 -groupingProvider=分组提供方 releaseNotes=消息和版本历史 hasSplitFleetContiguousScoring=区分连续得分的船队 addRaceLogTracker=添加比赛轮次日志跟踪器 @@ -1237,7 +1205,6 @@ showAll=显示全部 raceVisibilityColumn=可见性 enterCarryValueFor=输入参赛队{0}的得分 advanced=高级 -basedOn=基于 retrieveWith=检索依据 mappingDetails=映射详情 deviceMappingQrCodeExplanation=如果使用跟踪应用,可以通过选择参赛队/标志、设置开始和结束时间,然后扫描此二维码添加设备映射。 @@ -1248,10 +1215,6 @@ enterImageURL=输入图像 URL... enterVideoURL=输入视频 URL... enterSponsorImageURL=输入赞助商图像 URL... enterRaceName=输入比赛轮次名称... -serverError=尝试联系服务器时出错。请检查网络连接,并重试。 -remoteProcedureCall=远程过程调用 -serverReplies=服务器应答 -errorCommunicatingWithServer=与服务器通信时发生错误 userManagement=用户管理 regattaStructureImport=比赛结构导入 filteredBy=筛选方式 @@ -1279,7 +1242,6 @@ noFleetsDefined=未定义船队。 successfullyCreatedRegattas=已成功创建比赛 errorTryingToRegisterRacesForTracking=尝试注册比赛轮次{0}用于跟踪出错: {1}。检查实况/存储的 URI 语法。 errorDeterminingPolarAvailability=确定比赛轮次{0}的极坐标/VPP 数据的可用性出错: {1} -error=错误 fileStorage=文件存储 active=已激活 scoringSchemeHighPointEssOverallDescription=得分。 分站赛获胜者得 10 分、第 2 名 9 分、.... 在总体极限帆船系列赛中如有平局,以获得最多分站赛名次的参赛队取得胜利。 如果仍有平局,就以最后一个分站赛的名次打破平局。 @@ -1322,7 +1284,6 @@ showCompetitorFullNameColumn=参赛队全名 alwaysShowCompetitorNationalityColumn=始终显示参赛队国籍 alwaysShowCompetitorNationalityColumnTooltip=如果可用,显示两者:国籍旗帜和参赛队图像 loadingDimensionValues=正在加载尺寸值 -runningQuery=运行查询 inviteBuoyTenders=邀请浮标供应船 orMultipleEmails=或使用逗号隔开的多个电子邮件 courseOverGroundTrueDegreesTooltip=实在对地航向角度 @@ -1330,11 +1291,6 @@ courseOverGroundTrueDegrees=对地航向 distanceIncludingGateStartInMeters=距离(起航门标) distanceTraveledIncludingGateStartTooltip=如果航段没有结束,从航段起点到终点\n或当前时间点航行的距离。\n如果航段包括起航门标,则包括尾销到起航位置的距离,\n这样,即便在不同时间起航,也可以比较参赛队。 raceDistanceTraveledIncludingGateStartTooltip=如果比赛轮次没有结束,从比赛轮次开始到结束\n或当前时间点航行的距离。对于起航门标,包括尾销到起航位置的距离,\n这样,即便在不同时间起航,也可以比较参赛队。 -results=结果 -groupName=分组名称 -valueAscending=值(递增) -valueDescending=值(递减) -sortBy=排序方式 dashboardHeader=仪表盘 dashboardNoWindBotAvailableHeader=风力机器人程序不可用。 dashboardNoWindBotAvailableMessage=要从风力测量装置接收实况风力数据,请确保开启风力机器人程序,并与 SAP Sailing Analytics 相连。 @@ -1370,16 +1326,12 @@ dashboardRCBoat=RC 船只 fixedMarkPassing=(固定) suppressedMarkPassing=(禁止) windUp=风向向上(从地图的顶部显示风力) -filterBy=筛选方式 -currentFilterSelection=当前筛选器选择 notCapableOfGeneratingACodeForIdentifier=无法为此标识符生成代码。 serverUrl=服务器 URL rotatedFromTrueNorth=自真北方向旋转{0}度。 clickToToggleWindUp=点按/点击在风向向上和北向上地图显示之间切换 clickToToggleWindStreamlets=点按/点击显示或隐藏风流 startLineToFirstMarkTriangle=开始经过第一个标志 ({0}m) -dataMiningComponentsHaveBeenUpdated=数据挖掘组件已更新 -dataMiningComponentsNeedReloadDialogMessage=点击“重新加载”,立即重新加载组件。这将放弃当前显示的数据,并运行默认查询。\n点击“关闭”不执行任何操作。在重新加载组件前,数据挖掘不能正常工作。 noDataForEvent=活动暂无任何数据。 countriesCount={0,number} 个国家/地区 countriesCount[one]={0,number} 个国家/地区 @@ -1477,15 +1429,7 @@ noFinishedRaces=没有结束的比赛轮次 racesOverview=比赛轮次总览 listFormatLabel=清单格式 competitionFormatLabel=赛制 -empty=空 -runAQuery=运行查询 latestRegattaStandings=最新比赛排名 -plainText=纯文本 -columnChart=柱状图 -columnChartWithErrorBars=含误差线的柱状图 -choosePresentation=选择介绍 -cantDisplayDataOfType=无法显示类型为{0}的数据 -shownDecimals=显示小数 openFullscreenView=打开全屏视图 closeFullscreenView=关闭全屏视图 videosCount={0,number} 个视频 @@ -1495,15 +1439,8 @@ photosCount[one]={0,number} 张照片 eventsHaveTakenPlace={0}个活动已举行 eventsHaveTakenPlace[one]=已举行一场活动 raceOffice=竞赛办公室 -analyze=分析 -dataMiningSettings=数据挖掘设置 -multiResultsPresenter=多结果展示区 -plainResultsPresenter=纯结果展示区 -resultsChart=结果记录 -tabbedResultsPresenter=选项卡式结果展示区 polarResultsPresenter=极坐标结果展示区 maneuverSpeedDetailsResultsPresenter=操作速度详细结果展示区 -dataMiningRetrieval=数据检索 actionWatch=观看 actionAnalyze=分析 denoteAllRacesForRaceLogTrackingShorctut=为比赛轮次日志跟踪描述所有比赛轮次的快捷方式 @@ -1519,24 +1456,8 @@ defaultName=默认值 exampleTextForName=名称如下所示: flightsCount={0,number} 个航程 flightsCount[one]={0,number} 个航程 -viewQueryDefinition=查看查询定义 -queryDefinitionViewer=查询定义查看器 -groupAverageAscending=分组平均值(递增) -groupAverageDescending=分组平均值(递减) -groupMedianAscending=分组中值(递增) -groupMedianDescending=分组中值(递减) resultsFoundForSearch=找到 "{1}" 的 {0,number} 个结果 resultsFoundForSearch[one]=找到 "{1}" 的 {0,number} 个结果 -runPredefinedQuery=运行预定义的查询 -selectPredefinedQuery=选择预定义的查询 -predefinedQueryRunner=预定义的查询执行器 -developerOptions=开发人员选项 -copyToClipboard=复制到剪贴板 -code=代码 -useClassGetName=将 Class.getName() 用于类型名称 -useClassGetNameTooltip=更灵活应对代码库的更改,但是代码片段只能在级别可用的范围内使用。 -useStringLiterals=将字符串文本用于类型名称 -useStringLiteralsTooltip=代码片段可在任意位置使用,当如果基本代码更改,其将中断。 errorLoadingDataWithTryAgain=加载数据出错。请稍后重试。 addGalleryPhoto=添加图库照片 addStageImage=添加阶段图像 @@ -1556,7 +1477,6 @@ warningForDisabledCompetitors=以下参赛队无法注册此比赛轮次:{0} competitorToolTipMessage={0}已分配到比赛轮次{3}中的船队{2},因此,无法分配到同一比赛轮次中的船队{1} addMarkToRegatta=向比赛添加标志 selectALeaderboardGroup=选择积分榜组... -pleaseSelect=请选择 requiresValidRegatta=此页面需要有效的比赛、比赛轮次列和船队名称,才能识别显示的比赛轮次。 couldNotObtainRace=无法为名称为{0}的比赛获取船队{2}名称为{1}的比赛轮次: {3} errorTryingToCreateEmbeddedMap=尝试创建嵌入式地图出错: {0} @@ -1836,30 +1756,9 @@ eventRegattaHeaderLegendGpsNo=无跟踪数据 eventRegattaHeaderLegendWindNo=无风力数据 eventRegattaHeaderLegendVideoNo=无视频流 eventRegattaHeaderLegendAudioNo=无音频流 -angleInDegree=角度(度) -angleInRadian=角度(弧度) -centralAngleInRadian=圆心角(弧度) -centralAngleInDegree=圆心角(度) -kilometers=千米 -meters=米 -nauticalMiles=海里 -seaMiles=海里 -geographicalMiles=地理英里 -days=天 -hours=小时 -minutes=分钟 -seconds=秒 -milliseconds=毫秒 -floatNumber=浮点数 -integer=整数 appendResult=附加结果 sampleColor=颜色样本 -sharedSettingsLink=链接与设置 leaderboardPage=积分榜页面 -makeDefault=设为默认值 -makeDefaultInProgress=正在进行... -settingsSavedMessage=当前设置已成功设为默认设置 -settingsSaveErrorMessage=将设置设为默认设置时发生错误 showLiveNow=显示“现在直播” useOnlyOneOfStartTimeInferenceAndControlTrackingFromStartAndFinishTimes=仅使用“开始时间推断”和“从开始和结束时间控制跟踪”中的其中一个 unknownLeaderboardType=积分榜类型{0}未知 @@ -1878,10 +1777,6 @@ settingsId=设置编号 documentSettingsId=文档设置编号 settingsForId=编号 ''''{0}'''' 的设置 userProfileSettingsTabDescription=用户设置由可在页面多个位置找到的设置对话生成。此视图以技术方式为高级用户显示收集的所有设置。请注意,移除的条目无法恢复,请谨慎使用。 -resetToDefault=重置为默认值 -resetToDefaultInProgress=正在重置... -settingsRemoved=默认设置已恢复 -settingsRemovedError=无法恢复默认设置 userSettingsFilter=设置筛选器 requiresRegattaRaceAndLeaderboard=此页面需要有效的比赛名称、比赛轮次名称和积分榜名称。 couldNotFindRaceInRegatta=无法为名称为{1}的比赛获取名称为{0}的比赛轮次 @@ -1913,7 +1808,6 @@ errorFetchingDimensionData=获取{0}的维度值出错:{1} errorFetchingStatistics=从服务器获取可用统计出错:{0} errorFetchingAggregators=从服务器获取可用聚合器出错:{0} errorLoadingDataRetrieverChainDefinitions=检索可用 DataRetrieverChainDefinitions 出错:{0} -errorFetchingComponentsChangedTimepoint=从服务器获取组件更改的时间点出错:{0} errorRunningQuery=运行查询出错:{0} errorReadingWindFixes=读取风力修复{0}出错 errorAddingWindFixForRace=添加比赛轮次{0}的风力修复出错:{1} @@ -1987,11 +1881,6 @@ minimumRideHeightInMetersTooltip=将船只视为处于水翼腾空状态所需 minimumDurationBetweenAdjacentFoilingSegmentsInSeconds=相邻水翼腾空段之间的最短持续时间 (s) minimumDurationBetweenAdjacentFoilingSegmentsInSecondsTooltip=如果不为空,并且两个相邻水翼腾空段之间的时间短于此值,会将这些相邻的水翼腾空段合并成一个。 needToProvideValidMinimumRideHeight=您需要提供有效的最小行驶高度值(米)。 -dataMiningErrorMargins=误差容限 -elements={0}元素 -chooseDifferentDimensionTitle=选择不同的维度 -chooseDifferentDimensionMessage=为分组结果选择不同的维度 -pleaseSelectADimension=请选择维度 currentPortDaggerboardRake=左舷活动披水板倾斜度 currentPortDaggerboardRakeTooltip=当前左舷活动披水板倾斜度 currentStbdDaggerboardRake=右舷活动披水板倾斜度 From 55ea3be373bbf1503cafd78021b3adf2f19d8fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Fri, 22 Jun 2018 17:10:42 +0200 Subject: [PATCH 015/102] bug4479 initial impl for wind put rest end point --- .../gateway/jaxrs/api/RestApiApplication.java | 1 + .../gateway/jaxrs/api/WindResource.java | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java index 8f89926dc16..bc9b3b15e35 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java @@ -28,6 +28,7 @@ public class RestApiApplication extends Application { classes.add(PingResource.class); classes.add(TrackedRaceListResource.class); classes.add(StatisticsResource.class); + classes.add(WindResource.class); // Exception Mappers classes.add(ShiroAuthorizationExceptionTo401ResponseMapper.class); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java new file mode 100644 index 00000000000..751a70861af --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -0,0 +1,60 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.ParseException; + +import com.sap.sailing.domain.common.RegattaNameAndRaceName; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.Helpers; +import com.sap.sailing.server.gateway.deserialization.impl.PositionJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.WindJsonDeserializer; +import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; + +@Path("/v1/wind") +public class WindResource extends AbstractSailingServerResource { + private final JsonDeserializer deserializer = new WindJsonDeserializer(new PositionJsonDeserializer()); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("putWind") + public Response putWind(String json) throws ParseException, JsonDeserializationException { + Object requestBody = JSONValue.parseWithException(json); + JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); + JSONArray windDatas = (JSONArray) requestObject.get("windData"); + + String regattaName = (String) requestObject.get("regattaName"); + String raceName = (String) requestObject.get("raceName"); + + WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); + String windSourceId = (String) requestObject.get("windSourceId"); + DynamicTrackedRace trackedRace = getService().getTrackedRace(new RegattaNameAndRaceName(regattaName, raceName)); + WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); + + JSONArray answer = new JSONArray(); + if (trackedRace != null) { + for (int i = 0; i < windDatas.size(); i++) { + JSONObject windData = Helpers.toJSONObjectSafe(windDatas.get(i)); + Wind data = deserializer.deserialize(windData); + boolean success = trackedRace.recordWind(data, windsource); + answer.add(i, success); + } + } else { + return Response.ok("{\"error\":\"Could not resolve race\"}").build(); + } + return Response.ok(answer.toJSONString()).build(); + } +} \ No newline at end of file From 1f1a4faedd89f9e580ae1b8ae9017be5b115d7c6 Mon Sep 17 00:00:00 2001 From: Benjamin Barth Date: Tue, 26 Jun 2018 15:17:51 +0200 Subject: [PATCH 016/102] bug4635 Adjusted SetStartTimeReceivedDialog validation and info text --- .../SetStartTimeReceivedDialog.java | 46 +++++++++---------- .../sailing/gwt/ui/client/StringMessages.java | 1 + .../gwt/ui/client/StringMessages.properties | 3 +- .../ui/client/StringMessages_de.properties | 4 +- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeReceivedDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeReceivedDialog.java index a5e6227d56b..12e4d1787e7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeReceivedDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeReceivedDialog.java @@ -2,6 +2,10 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.Date; +import com.google.gwt.dom.client.Style.FontStyle; +import com.google.gwt.dom.client.Style.FontWeight; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; @@ -14,44 +18,36 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class SetStartTimeReceivedDialog extends DataEntryDialogWithDateTimeBox { private final StringMessages stringMessages; - + private DateAndTimeInput timeBox; public SetStartTimeReceivedDialog(StringMessages stringMessages, DataEntryDialog.DialogCallback callback) { - super(stringMessages.setStartTimeReceived(), stringMessages.setStartTimeReceivedDescription(), stringMessages.ok(), stringMessages.cancel(), new ReceivedStartTimeDialog(stringMessages), callback); + super(stringMessages.setStartTimeReceived(), stringMessages.setStartTimeReceivedDescription(), + stringMessages.ok(), stringMessages.cancel(), valueToValidate -> null, callback); this.stringMessages = stringMessages; } - + @Override protected Widget getAdditionalWidget() { - Grid content = new Grid(1, 2); - - Label timeBoxLabel = new Label(stringMessages.startTime() + ":"); + final FlowPanel panel = new FlowPanel(); + final Label noticeLabel = new Label(stringMessages.setStartTimeReceivedNotice()); + noticeLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); + noticeLabel.getElement().getStyle().setFontStyle(FontStyle.ITALIC); + panel.add(noticeLabel); + final Grid content = new Grid(1, 3); + final Label timeBoxLabel = new Label(stringMessages.startTime() + ":"); content.setWidget(0, 0, timeBoxLabel); - timeBox = createDateTimeBox(new Date(), Accuracy.SECONDS); + timeBox = createDateTimeBox(null, Accuracy.SECONDS); content.setWidget(0, 1, timeBox); - - return content; + final Button setNowButton = new Button(stringMessages.now()); + setNowButton.addClickHandler(event -> timeBox.setValue(new Date(), true)); + content.setWidget(0, 2, setNowButton); + panel.add(content); + return panel; } @Override protected Date getResult() { return timeBox.getValue(); } - - private static class ReceivedStartTimeDialog implements Validator { - - private StringMessages stringMessages; - - public ReceivedStartTimeDialog(StringMessages stringMessages) { - this.stringMessages = stringMessages; - } - - @Override - public String getErrorMessage(Date valueToValidate) { - return valueToValidate == null ? stringMessages.pleaseEnterAValue() : null; - } - - } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 1ae7582ed35..8be8bc73e38 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1182,6 +1182,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String showUncorrectedTotalPoints(); String setStartTimeReceived(); String setStartTimeReceivedDescription(); + String setStartTimeReceivedNotice(); String lastScoreCorrectionsTime(); String lastScoreCorrectionsComment(); String setTimeToNow(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 5c021a4a6a9..6e5c795cca9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1168,7 +1168,8 @@ racesScoredTooltip=Number of races the competitor has completed or gotten a scor averageNumberOfOperationsPerMessage=Average number of operations per message showUncorrectedTotalPoints=Show uncorrected total points setStartTimeReceived=Set start time received -setStartTimeReceivedDescription=This sets the startTimeReceived of the selected TrackedRace that isn''t persistent. This means that the new value will be forgotten after the server has been restarted. +setStartTimeReceivedDescription=Sets the startTimeReceived of the selected TrackedRace to the provided value. Leave the value empty to remove the current start time. +setStartTimeReceivedNotice=This change is not persistent, i.e. the new value will be forgotten when the server has been restarted. lastScoreCorrectionsTime=Last score correction time lastScoreCorrectionsComment=Last score correction comment setTimeToNow=Set time to ''now'' diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 2ccdfe80364..09416b7ccb0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1155,8 +1155,8 @@ racesScoredTooltip=Anzahl von Rennen, die der Segler vollendet oder für die er averageNumberOfOperationsPerMessage=Durchschnittliche Anzahl Operationen pro Nachricht showUncorrectedTotalPoints=Unkorrigierte Punkte anzeigen setStartTimeReceived=Setze die erhaltene Startzeit -setStartTimeReceivedDescription=This sets the startTimeReceived of the selected TrackedRace, that isn''t persistent. This means, that the new value will be forgotten after the server has been restarted. -setStartTimeReceivedDescription=Dies setzt die startTimeReceived des selektierten TrackedRaces, welches nicht persistent ist. Das beudeutet, dass der neue Wert vergessen wird, sobald der Server neu gestartet wurde. +setStartTimeReceivedDescription=Setzt die startTimeReceived des selektierten TrackedRaces auf den angegebenen Wert. Den Wert leer lassen, um die aktuelle Startzeit zu entfernen. +setStartTimeReceivedNotice=Diese Änderung ist nicht persistent, d.h. der neue Wert wird beim Neustart des Servers vergessen. lastScoreCorrectionsTime=Letzter Zeitpunkt der Punktzahl-Korrektur lastScoreCorrectionsComment=Letzter Kommentar der Punktzahl-Korrektur setTimeToNow=Setze den Zeitpunkt auf ''Jetzt'' From 99ce17d784b44d7e388211ef11c856e45227b216 Mon Sep 17 00:00:00 2001 From: Benjamin Barth Date: Tue, 26 Jun 2018 15:20:47 +0200 Subject: [PATCH 017/102] bug4635 Adjusted server-side handling of null value inputs --- .../adminconsole/TrackedRacesManagementPanel.java | 8 +++----- .../sailing/gwt/ui/server/SailingServiceImpl.java | 15 ++++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java index dc42407db41..1432949153e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java @@ -47,11 +47,9 @@ public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel { } @Override public void onSuccess(RaceDTO result) { - if (result != null) { - selectedRaceDTO = result; - refreshSelectedRaceData(); - TrackedRacesManagementPanel.this.regattaRefresher.fillRegattas(); - } + selectedRaceDTO = result; + refreshSelectedRaceData(); + TrackedRacesManagementPanel.this.regattaRefresher.fillRegattas(); } }); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 278f1ba0298..27e382d8db5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -6438,15 +6438,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived) { - if (newStartTimeReceived != null) { - RegattaNameAndRaceName regattaAndRaceIdentifier = new RegattaNameAndRaceName( - raceIdentifier.getRegattaName(), raceIdentifier.getRaceName()); - DynamicTrackedRace trackedRace = getService().getTrackedRace(regattaAndRaceIdentifier); - trackedRace.setStartTimeReceived(new MillisecondsTimePoint(newStartTimeReceived)); - - return baseDomainFactory.createRaceDTO(getService(), false, regattaAndRaceIdentifier, trackedRace); - } - return null; + RegattaNameAndRaceName regattaAndRaceIdentifier = new RegattaNameAndRaceName(raceIdentifier.getRegattaName(), + raceIdentifier.getRaceName()); + DynamicTrackedRace trackedRace = getService().getTrackedRace(regattaAndRaceIdentifier); + trackedRace.setStartTimeReceived( + newStartTimeReceived == null ? null : new MillisecondsTimePoint(newStartTimeReceived)); + return baseDomainFactory.createRaceDTO(getService(), false, regattaAndRaceIdentifier, trackedRace); } @Override From 262c0fa4f3bb1dfb17e073738b724917bc8489f7 Mon Sep 17 00:00:00 2001 From: Benjamin Barth Date: Tue, 26 Jun 2018 16:19:03 +0200 Subject: [PATCH 018/102] bug4635 Slightly adjustments to SetStartTimeReceivedDialog's info text --- .../com/sap/sailing/gwt/ui/client/StringMessages.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_de.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 6e5c795cca9..49b7b9d6b35 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1168,7 +1168,7 @@ racesScoredTooltip=Number of races the competitor has completed or gotten a scor averageNumberOfOperationsPerMessage=Average number of operations per message showUncorrectedTotalPoints=Show uncorrected total points setStartTimeReceived=Set start time received -setStartTimeReceivedDescription=Sets the startTimeReceived of the selected TrackedRace to the provided value. Leave the value empty to remove the current start time. +setStartTimeReceivedDescription=Sets the startTimeReceived of the selected TrackedRace to the provided value. Leave the value empty to remove the currently set startTimeReceived. setStartTimeReceivedNotice=This change is not persistent, i.e. the new value will be forgotten when the server has been restarted. lastScoreCorrectionsTime=Last score correction time lastScoreCorrectionsComment=Last score correction comment diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 09416b7ccb0..6cbda32fbcd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1155,7 +1155,7 @@ racesScoredTooltip=Anzahl von Rennen, die der Segler vollendet oder für die er averageNumberOfOperationsPerMessage=Durchschnittliche Anzahl Operationen pro Nachricht showUncorrectedTotalPoints=Unkorrigierte Punkte anzeigen setStartTimeReceived=Setze die erhaltene Startzeit -setStartTimeReceivedDescription=Setzt die startTimeReceived des selektierten TrackedRaces auf den angegebenen Wert. Den Wert leer lassen, um die aktuelle Startzeit zu entfernen. +setStartTimeReceivedDescription=Setzt die startTimeReceived des selektierten TrackedRaces auf den angegebenen Wert. Den Wert leer lassen, um die aktuell gesetzte startTimeReceived zu entfernen. setStartTimeReceivedNotice=Diese Änderung ist nicht persistent, d.h. der neue Wert wird beim Neustart des Servers vergessen. lastScoreCorrectionsTime=Letzter Zeitpunkt der Punktzahl-Korrektur lastScoreCorrectionsComment=Letzter Kommentar der Punktzahl-Korrektur From e2dff991fcad817a10673759e016681e67b7a6f5 Mon Sep 17 00:00:00 2001 From: Benjamin Barth Date: Tue, 26 Jun 2018 16:19:56 +0200 Subject: [PATCH 019/102] bug4635 Added admin release notes regarding SetStartTimeReceivedDialog --- java/com.sap.sailing.www/release_notes_admin.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index eb33f2b4755..06ac2c45ea2 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -29,6 +29,9 @@ This can be done from the adminconsole->tracked races->audio & videos tab
  • In raceboard when you edit a video you do not need to explicitly press "Preview" before saving anymore. Saving now implicitly applies the changes.
  • +
  • In the "Tracked races" tab, within the eponymous section of the AdminConsole, the "Set start + time received" dialog can be used to remove the currently set start time received, by simply + leaving the value empty and confirming the dialog.
  • May 2018

    From e00dfd34b823b3344eff9f28dc479f11d5427b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 27 Jun 2018 09:50:24 +0200 Subject: [PATCH 020/102] bug3262 new api key and referer value --- .../java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 8e13901d189..85bbbb59e6c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -56,7 +56,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.impl.MillisecondsDurationImpl; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { - private String YOUTUBE_V3_API_KEY = "AIzaSyDYwPzLevXauI-kTSVXTLroLyHEONuF9Rw"; + private String YOUTUBE_V3_API_KEY = "AIzaSyD1Se4tIkt-wglccbco3S7twaHiG20hR9E"; private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); @@ -365,6 +365,7 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi "https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key=" + YOUTUBE_V3_API_KEY + "&part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)"); URLConnection connection = apiURL.openConnection(); + connection.setRequestProperty("Referer", "http://sapsailing.com/"); connection.setConnectTimeout(METADATA_CONNECTION_TIMEOUT); try (BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { From 7c4ae8b93a883a6f4485fdbdaeafe57a5aad42ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 27 Jun 2018 09:50:47 +0200 Subject: [PATCH 021/102] bug3262 cleanups new media dialog --- .../gwt/ui/client/media/NewMediaDialog.java | 73 +------------------ 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 19987b45b67..c2d1bb7f9e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -102,34 +102,16 @@ public class NewMediaDialog extends DataEntryDialog { @Override protected MediaTrack getResult() { mediaTrack.title = titleBox.getValue(); - updateStartTimeFromUi(); - updateDurationFromUi(); connectMediaWithRace(); return mediaTrack; } - private void updateDurationFromUi() { - String duration = durationBox.getValue(); - if (duration != null && !duration.trim().isEmpty()) { - mediaTrack.duration = TimeFormatUtil.hrsMinSecToMilliSeconds(duration); - } else { - mediaTrack.duration = null; - } - } - protected void connectMediaWithRace() { Set assignedRaces = new HashSet(); assignedRaces.add(this.raceIdentifier); mediaTrack.assignedRaces = assignedRaces; } - protected void updateStartTimeFromUi() { - Date startTime = startTimeBox.getValue(); - if (startTime != null && !startTime.equals("")) { - mediaTrack.startTime = new MillisecondsTimePoint(startTime); - } - } - //used for audio only tracks, using native mediaelement to determine time private void loadMediaDuration() { MediaBase mediaBase = Audio.createIfSupported(); @@ -240,7 +222,7 @@ public class NewMediaDialog extends DataEntryDialog { if (youtubeId != null) { mediaTrack.url = youtubeId; mediaTrack.mimeType = MimeType.youtube; - loadYoutubeMetadata(youtubeId); + loadYoutubeMetadata(); } else { mediaTrack.url = url; loadMediaDuration(); @@ -297,63 +279,10 @@ public class NewMediaDialog extends DataEntryDialog { return lastPathSegment; } - private native void registerNativeMethods() /*-{ - var that = this; - window.youtubeMetadataCallback = function(metadata) { - var title = metadata.entry.media$group.media$title.$t; - var duration = metadata.entry.media$group.yt$duration.seconds; - var description = metadata.entry.media$group.media$description.$t; - that.@com.sap.sailing.gwt.ui.client.media.NewMediaDialog::youtubeMetadataCallback(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)(title, duration, description); - } - }-*/; - - /** - * Inspired by https://developers.google.com/web-toolkit/doc/latest/tutorial/Xsite - * - * @param youtubeId - */ - public native void loadYoutubeMetadata(String youtubeId) /*-{ - var that = this; - //Create temporary script element. - window.youtubeMetadataCallbackScript = document.createElement("script"); - window.youtubeMetadataCallbackScript.src = "http://gdata.youtube.com/feeds/api/videos/" - + youtubeId - + "?alt=json&orderby=published&format=6&callback=youtubeMetadataCallback"; - document.body.appendChild(window.youtubeMetadataCallbackScript); - - // Cancel meta data capturing after has 2-seconds timeout. - setTimeout( - function() { - //Remove temporary script element. - if (window != null && window.youtubeMetadataCallbackScript != null) { - document.body.removeChild(window.youtubeMetadataCallbackScript); - delete window.youtubeMetadataCallbackScript; - } - that.@com.sap.sailing.gwt.ui.client.media.NewMediaDialog::setBusy(Z)(false); - }, 2000); - }-*/; - public void setBusy(boolean busy) { busyIndicator.setBusy(busy); } - public void youtubeMetadataCallback(String title, String durationInSeconds, String description) { - busyIndicator.setBusy(false); - mediaTrack.title = title; - try { - long duration = (long) Math.round(1000 * Double - .valueOf(durationInSeconds)); - if (duration > 0) { - mediaTrack.duration = new MillisecondsDurationImpl(duration); - } else { - mediaTrack.duration = null; - } - } catch (NumberFormatException ex) { - mediaTrack.duration = null; - } - refreshUI(); - } - protected void refreshUI() { titleBox.setValue(mediaTrack.title, DONT_FIRE_EVENTS); if (mediaTrack.isYoutube()) { From 54ce4741c3558869c2acbb74f5f7b4d010277e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 27 Jun 2018 12:42:08 +0200 Subject: [PATCH 022/102] bug3262 changed to even newer api key, added basic escaping for input from client --- .../sailing/gwt/ui/client/MediaService.java | 5 +- .../gwt/ui/client/media/NewMediaDialog.java | 36 +++++++------ .../gwt/ui/server/MediaServiceImpl.java | 54 +++++++++++-------- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java index fbb4bc17f00..f84ab2b1116 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/MediaService.java @@ -1,5 +1,7 @@ package com.sap.sailing.gwt.ui.client; +import java.io.UnsupportedEncodingException; + import com.google.gwt.user.client.rpc.RemoteService; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.VideoMetadataDTO; @@ -38,6 +40,7 @@ public interface MediaService extends RemoteService { /** * Obtains metadata from the youtube api + * @throws UnsupportedEncodingException */ - VideoMetadataDTO checkYoutubeMetadata(String url); + VideoMetadataDTO checkYoutubeMetadata(String url) throws UnsupportedEncodingException; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index c2d1bb7f9e6..f51e37256f4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -251,24 +251,26 @@ public class NewMediaDialog extends DataEntryDialog { } private void loadYoutubeMetadata() { - mediaService.checkYoutubeMetadata(mediaTrack.url, new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - infoLabel.setWidget(new Label(caught.getMessage())); - } - - @Override - public void onSuccess(VideoMetadataDTO result) { - if (result.isDownloadable()) { - mediaTrack.duration = result.getDuration(); - mediaTrack.title = result.getMessage(); - refreshUI(); - } else { - infoLabel.setWidget(new Label(result.getMessage())); + if(mediaTrack.url != null && !mediaTrack.url.isEmpty()) { + mediaService.checkYoutubeMetadata(mediaTrack.url, new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + infoLabel.setWidget(new Label(caught.getMessage())); } - } - }); + + @Override + public void onSuccess(VideoMetadataDTO result) { + if (result.isDownloadable()) { + mediaTrack.duration = result.getDuration(); + mediaTrack.title = result.getMessage(); + refreshUI(); + } else { + infoLabel.setWidget(new Label(result.getMessage())); + } + } + }); + } } private String sliceBefore(String lastPathSegment, String slicer) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 85bbbb59e6c..bc2f6336275 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -9,11 +9,13 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.net.URLConnection; +import java.net.URLEncoder; import java.nio.channels.Channels; import java.nio.charset.StandardCharsets; import java.text.DateFormat; @@ -56,7 +58,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.impl.MillisecondsDurationImpl; public class MediaServiceImpl extends RemoteServiceServlet implements MediaService { - private String YOUTUBE_V3_API_KEY = "AIzaSyD1Se4tIkt-wglccbco3S7twaHiG20hR9E"; + private String YOUTUBE_V3_API_KEY = "AIzaSyBzCJ9cxb9_PPzuYfrHIEdSRtR631b64Xs"; private static final Logger logger = Logger.getLogger(MediaServiceImpl.class.getName()); @@ -355,35 +357,43 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi } @Override - public VideoMetadataDTO checkYoutubeMetadata(String videoId) { + public VideoMetadataDTO checkYoutubeMetadata(String videoId) throws UnsupportedEncodingException { ensureUserCanManageMedia(); boolean canDownload = false; String message = ""; Duration duration = null; - try { - URL apiURL = new URL( - "https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key=" + YOUTUBE_V3_API_KEY - + "&part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)"); - URLConnection connection = apiURL.openConnection(); - connection.setRequestProperty("Referer", "http://sapsailing.com/"); - connection.setConnectTimeout(METADATA_CONNECTION_TIMEOUT); - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { - String pageText = reader.lines().collect(Collectors.joining("\n")); - JSONObject jsonAnswer = new JSONObject(pageText); - final JSONObject item = jsonAnswer.getJSONArray("items").getJSONObject(0); - message = item.getJSONObject("snippet").getString("title"); - String rawDuration = item.getJSONObject("contentDetails").getString("duration"); - duration = new MillisecondsDurationImpl(java.time.Duration.parse(rawDuration).toMillis()); - canDownload = true; - } catch (JSONException e) { + if (videoId.isEmpty()) { + message = "Empty id"; + } else { + videoId = URLEncoder.encode(videoId, StandardCharsets.UTF_8.name()); + try { + URL apiURL = new URL( + "https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key=" + YOUTUBE_V3_API_KEY + + "&part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)"); + URLConnection connection = apiURL.openConnection(); + connection.setRequestProperty("Referer", "http://mediaservice.sapsailing.com/"); + connection.setConnectTimeout(METADATA_CONNECTION_TIMEOUT); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { + String pageText = reader.lines().collect(Collectors.joining("\n")); + JSONObject jsonAnswer = new JSONObject(pageText); + final JSONObject item = jsonAnswer.getJSONArray("items").getJSONObject(0); + message = item.getJSONObject("snippet").getString("title"); + String rawDuration = item.getJSONObject("contentDetails").getString("duration"); + duration = new MillisecondsDurationImpl(java.time.Duration.parse(rawDuration).toMillis()); + canDownload = true; + } catch (JSONException e) { + message = e.getMessage(); + logger.log(Level.WARNING, "Error in youtube metadata call", e); + } + } catch (IOException e) { message = e.getMessage(); logger.log(Level.WARNING, "Error in youtube metadata call", e); } - } catch (IOException e) { - message = e.getMessage(); - logger.log(Level.WARNING, "Error in youtube metadata call", e); } + //sanitize, as we inject it into an url with our api key! + + return new VideoMetadataDTO(canDownload, duration, false, null, message); } } From 1cc2a9aee5cf4deb235eefa142effdb816586fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 27 Jun 2018 13:46:53 +0200 Subject: [PATCH 023/102] bug4640 added reset zoom button in timeslider in raceboard --- .../sap/sailing/gwt/ui/client/StringMessages.java | 1 + .../sailing/gwt/ui/client/StringMessages.properties | 3 ++- .../gwt/ui/client/StringMessages_de.properties | 3 ++- .../com/sap/sailing/gwt/ui/client/TimePanel.java | 13 +++++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 1ae7582ed35..17dd380d85c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2076,4 +2076,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiUrlChangeNewURL(); String multiUrlNoPrefixWarning(); String multiUrlChangeExplain(); + String resetZoom(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 5c021a4a6a9..d4cee06f66e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -2069,4 +2069,5 @@ multiUrlChangeCannotSave=An error occured while saving multiUrlChangeSave=Save url changes multiUrlChangeNewURL=new url multiUrlNoPrefixWarning=No common prefix was found, this usually means that not all selected videos are hosted at the same location currently. Proceed at own risk! -multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! \ No newline at end of file +multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! +resetZoom=Reset zoom \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 2ccdfe80364..4229dacca53 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -2065,4 +2065,5 @@ multiUrlChangeCannotSave=Es ist ein Fehler beim Speichern aufgetreten multiUrlChangeSave=Änderungen der URLs speichern multiUrlChangeNewURL=Neue Url multiUrlNoPrefixWarning=Es wurde kein gemeinsamer Präfix gefunden, dies deutet darauf hin, dass nicht alle ausgewählten Videos vom gleichen Host kommen. Auf eigene Gefahr fortsetzen! -multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! \ No newline at end of file +multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! +resetZoom=Reset zoom \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java index 62e6ef26328..3bba073d7c7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java @@ -78,6 +78,7 @@ public class TimePanel extends AbstractCompositeCom private final Button slowDownButton; private final Button speedUpButton; private final Button toggleAdvancedModeButton; + private final Button resetZoomButton; private final FlowPanel controlsPanel; private final SimplePanel timePanelSlider; @@ -309,6 +310,16 @@ public class TimePanel extends AbstractCompositeCom controlsPanel.add(timeControlPanel); controlsPanel.add(timeToStartControlPanel); + resetZoomButton = new Button(stringMessages.resetZoom()); + resetZoomButton.setEnabled(false); + resetZoomButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + timeRangeProvider.resetTimeZoom(); + } + }); + controlsPanel.add(resetZoomButton); + hideControlsPanel(); } @@ -536,10 +547,12 @@ public class TimePanel extends AbstractCompositeCom @Override public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) { + resetZoomButton.setEnabled(true); } @Override public void onTimeZoomReset() { + resetZoomButton.setEnabled(false); } @Override From b999cfe0b3ec3dd39138dbd0851f3d1693300642 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 27 Jun 2018 14:10:08 +0200 Subject: [PATCH 024/102] bug4584: added i18n for swiss timing eventmanagment panel. Updated function names to update URL and Event ID Box. --- .../SwissTimingEventManagementPanel.java | 17 ++++++++--------- .../sailing/gwt/ui/client/StringMessages.java | 4 ++++ .../gwt/ui/client/StringMessages.properties | 6 +++++- .../gwt/ui/client/StringMessages_de.properties | 6 +++++- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index dbab80ffa00..19be5e17e65 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -115,20 +115,20 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane eventIdBox = new TextBox(); eventIdBox.getElement().getStyle().setWidth(30, Unit.EM); - eventIdBox.setTitle("Event ID or a url that contains the event Id from Manage2Sail"); - connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-ID:")); + eventIdBox.setTitle(stringMessages.manage2SailEventIdBoxTooltip()); + connectionsGrid.setWidget(1, 0, new Label(stringMessages.manage2SailEventIdBox() + ":")); connectionsGrid.setWidget(1, 1, eventIdBox); eventIdBox.addChangeHandler(event -> { if (eventIdBox.getValue() != "") { - createUrlFromEventId(eventIdBox.getValue()); + updateUrlFromEventId(eventIdBox.getValue()); } }); - connectionsGrid.setWidget(2, 0, new Label("Manage2Sail Event-URL (json):")); + connectionsGrid.setWidget(2, 0, new Label(stringMessages.manage2SailEventURLBox() + ":")); connectionsGrid.setWidget(2, 1, jsonUrlBox); jsonUrlBox.addChangeHandler(event -> { if (jsonUrlBox.getValue() != "") { - createEventIdFromUrl(jsonUrlBox.getValue()); + updateEventIdFromUrl(jsonUrlBox.getValue()); } }); @@ -138,7 +138,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane connectionsGrid.setWidget(3, 0, new Label(stringConstants.hostname() + ":")); connectionsGrid.setWidget(3, 1, hostnameTextbox); - connectionsGrid.setWidget(4, 0, new Label(stringConstants.port() + ":")); + connectionsGrid.setWidget(4, 0, new Label(stringMessages.manage2SailPort() + ":")); connectionsGrid.setWidget(4, 1, portIntegerbox); Button btnListRaces = new Button(stringConstants.listRaces()); @@ -343,9 +343,8 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane * This function tries to create a valid JsonUrl for any input given that matches the pattern of an event Id from * M2S. If there is an event id detected the Json Url gets updated and the event Id textbox is filled with the * detected event Id. The ID pattern is defined in {@link eventIdPattern}. - * */ - private void createUrlFromEventId(String eventIdTextbox) { + private void updateUrlFromEventId(String eventIdTextbox) { if (eventIdTextbox.matches(".*" + eventIdPattern + ".*")) { final String inferredEventId = eventIdTextbox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); jsonUrlBox.setValue( @@ -358,7 +357,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane * Similar to {@link #createUrlFromEventId()} this function tries to extract a M2S event Id by looking at the given * url in the Json Url Textbox. */ - private void createEventIdFromUrl(String jsonUrlTextBox) { + private void updateEventIdFromUrl(String jsonUrlTextBox) { if (jsonUrlTextBox.matches("http://manage2sail.com/.*" + eventIdPattern + ".*")) { final String inferredEventId = jsonUrlTextBox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); eventIdBox.setValue(inferredEventId); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 1ae7582ed35..f98e78e25ef 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2076,4 +2076,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiUrlChangeNewURL(); String multiUrlNoPrefixWarning(); String multiUrlChangeExplain(); + String manage2SailEventIdBox(); + String manage2SailEventURLBox(); + String manage2SailEventIdBoxTooltip(); + String manage2SailPort(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 276103ac8f5..dc0371cf72f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -2069,4 +2069,8 @@ multiUrlChangeCannotSave=An error occured while saving multiUrlChangeSave=Save url changes multiUrlChangeNewURL=new url multiUrlNoPrefixWarning=No common prefix was found, this usually means that not all selected videos are hosted at the same location currently. Proceed at own risk! -multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! \ No newline at end of file +multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! +manage2SailEventIdBox=Manage2Sail Event ID +manage2SailEventURLBox=Manage2Sail EventURL (json) +manage2SailEventIdBoxTooltip=Event ID or a URL that contains the event ID from Manage2Sail +manage2SailPort=Port \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index cebfe425beb..fd04c869e65 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -2065,4 +2065,8 @@ multiUrlChangeCannotSave=Es ist ein Fehler beim Speichern aufgetreten multiUrlChangeSave=Änderungen der URLs speichern multiUrlChangeNewURL=Neue Url multiUrlNoPrefixWarning=Es wurde kein gemeinsamer Präfix gefunden, dies deutet darauf hin, dass nicht alle ausgewählten Videos vom gleichen Host kommen. Auf eigene Gefahr fortsetzen! -multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! \ No newline at end of file +multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! +manage2SailEventId=Manage2Sail Event ID +manage2SailEventURL=Manage2Sail Event URL (json) +manage2SailEventIdBoxTooltip=Event ID von Manage 2 Sail oder URL die die Event ID beinhaltet +manage2SailPort=Port \ No newline at end of file From c690cbc06321af8b1dc886220527057b933e6427 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 27 Jun 2018 15:58:11 +0200 Subject: [PATCH 025/102] bug4584: removed unused port() from StringMessages. Added manage2SailPort() instead. --- .../main/java/com/sap/sailing/gwt/ui/client/StringMessages.java | 1 - .../java/com/sap/sailing/gwt/ui/client/StringMessages.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_de.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_es.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_fr.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_ja.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_pt.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 1 - .../com/sap/sailing/gwt/ui/client/StringMessages_zh.properties | 1 - 9 files changed, 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index f98e78e25ef..3c194706ec7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -153,7 +153,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String legDetailsToShow(); String columnMoveUp(); String columnMoveDown(); - String port(); String raceStartTimeColumn(); String showOnlySelectedCompetitors(); String showSelectedCompetitorsInfo(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 0d5f556716a..9ed12362a96 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -153,7 +153,6 @@ overallDetailsToShow=Overall Details legDetailsToShow=Leg Details columnMoveUp=Up columnMoveDown=Down -port=Port raceStartTimeColumn=Race Started showOnlySelectedCompetitors=Show only selected competitors showSelectedCompetitorsInfo=Show info box for selected competitors diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 77dd211e7f5..4c876dea4c7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -155,7 +155,6 @@ overallDetailsToShow=Regatta-Details legDetailsToShow=Schenkeldetails columnMoveUp=Hoch columnMoveDown=Runter -port=Backbord raceStartTimeColumn=Rennstart showOnlySelectedCompetitors=Nur ausgewählte Teilnehmer anzeigen showSelectedCompetitorsInfo=Infobox für ausgewählte Teilnehmer anzeigen diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 4812828ca75..eb527a6c840 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Detalles generales legDetailsToShow=Detalles de tramo columnMoveUp=Arriba columnMoveDown=Abajo -port=Babor raceStartTimeColumn=Carrera iniciada showOnlySelectedCompetitors=Mostrar solo competidores seleccionados showSelectedCompetitorsInfo=Mostrar cuadro de información para competidores seleccionados diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index 6381633c88b..fa770dc2b88 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Détails généraux legDetailsToShow=Détails de la portion de parcours columnMoveUp=Dans le lit columnMoveDown=Arrière -port=À bâbord raceStartTimeColumn=Course lancée showOnlySelectedCompetitors=Afficher uniquement concurrents sélectionnés showSelectedCompetitorsInfo=Afficher carré d''infos pour concurrents sélectionnés diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index b553cdf0a6d..fc84b874550 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -158,7 +158,6 @@ overallDetailsToShow=全体詳細 legDetailsToShow=レグ詳細 columnMoveUp=上 columnMoveDown=下 -port=ポート raceStartTimeColumn=レーススタート済 showOnlySelectedCompetitors=選択した競技者のみ表示 showSelectedCompetitorsInfo=選択した競技者の情報ボックスを表示 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index cc63e58185d..e4a167d5b5f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Detalhes gerais legDetailsToShow=Detalhes da perna columnMoveUp=Para cima columnMoveDown=Para baixo -port=Bombordo raceStartTimeColumn=Corrida iniciada showOnlySelectedCompetitors=Visualizar somente os competidores selecionados showSelectedCompetitorsInfo=Visualizar caixa de informações para competidores selecionados diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 152f6763935..ab370e8dc0d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Общие подробности legDetailsToShow=Подробности columnMoveUp=Вверх columnMoveDown=Вниз -port=Порт raceStartTimeColumn=Гонка начата showOnlySelectedCompetitors=Показать только выбранных участников showSelectedCompetitorsInfo=Показать окно сведений для выбранных участников diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 0115aaad20f..936062c31c8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -158,7 +158,6 @@ overallDetailsToShow=总体详情 legDetailsToShow=航段详情 columnMoveUp=向上 columnMoveDown=向下 -port=端口 raceStartTimeColumn=比赛轮次开始时间 showOnlySelectedCompetitors=仅显示选择的参赛队 showSelectedCompetitorsInfo=显示所选参赛队的信息框 From 3cc0b9c73acda4dfd4a4b1e42d4a52cdfada8777 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 28 Jun 2018 10:01:18 +0200 Subject: [PATCH 026/102] bug4584: Updated javadoc for SwissTimingEventManagmentPanel.java. --- .../ui/adminconsole/SwissTimingEventManagementPanel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index 19be5e17e65..d3ccd11814b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -340,7 +340,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane } /** - * This function tries to create a valid JsonUrl for any input given that matches the pattern of an event Id from + * This function tries to infer a valid JsonUrl for any input given that matches the pattern of an event Id from * M2S. If there is an event id detected the Json Url gets updated and the event Id textbox is filled with the * detected event Id. The ID pattern is defined in {@link eventIdPattern}. */ @@ -354,8 +354,8 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane } /** - * Similar to {@link #createUrlFromEventId()} this function tries to extract a M2S event Id by looking at the given - * url in the Json Url Textbox. + * Similar to {@link #updateUrlFromEventId} this function tries to extract a M2S event Id by looking at the given + * url in the Json Url Textbox. The value of {@link eventIdBox} is then set to the event ID inferred from the Json Url. */ private void updateEventIdFromUrl(String jsonUrlTextBox) { if (jsonUrlTextBox.matches("http://manage2sail.com/.*" + eventIdPattern + ".*")) { From 5c62f03c62ddbee0e5a223f8e4cac849a86c3790 Mon Sep 17 00:00:00 2001 From: "service.tip.git" Date: Thu, 28 Jun 2018 16:06:54 +0000 Subject: [PATCH 027/102] [INTERNAL] Translation delivery: commit by SLS Change-Id: I44379c39d5c9e498a0e3ee6f5b233b41b3b1d8f8 --- .../sailing/gwt/ui/client/StringMessages_es.properties | 10 +++++++++- .../sailing/gwt/ui/client/StringMessages_fr.properties | 8 ++++++++ .../sailing/gwt/ui/client/StringMessages_ja.properties | 8 ++++++++ .../sailing/gwt/ui/client/StringMessages_pt.properties | 8 ++++++++ .../sailing/gwt/ui/client/StringMessages_ru.properties | 8 ++++++++ .../sailing/gwt/ui/client/StringMessages_zh.properties | 8 ++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 3e6086b8051..c0699845849 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -2046,7 +2046,7 @@ multiVideoURLOfIndex=Inserte el URL de índice del servidor web multiVideoScan=Escanear índice multiVideoLinking=Añadir varios vídeos multiVideoNotAnalyzed=Vídeo aún por analizar -multiVideoAlreadyKnown=Vídeo ya tiene pistas multimedia existentes +multiVideoAlreadyKnown=Vídeo ya tiene rastreo de medios existente multiVideoClientIsUploading=Vídeo analizado mediante proxy de cliente multiVideoFinishedLinking=Vídeo enlazado multiVideoErrorInAnalyzingFile=Error al analizar el fichero @@ -2058,3 +2058,11 @@ multiVideoIdle=Cola de trabajo inactiva multiVideoDoNoAdd=No añadir multiVideoOffsetInput=Offset de vídeo global en milisegundos: multiVideoDescription=El servidor web debe proporcionar una lista de índices (subcarpetas compatibles si están indexadas) para permitir la detección de ficheros. Tras un análisis inicial de los metadatos contenidos en los ficheros mp4, los vídeos que deben añadirse, deben seleccionarse mediante la columna de la casilla de selección a la izquierda. El botón "Añadir audio/vídeo" creará pistas multimedia para todos los ficheros seleccionados y los añadirá a todas las carreras seleccionadas en la columna de la derecha. +multiUrlChangeMediaTrack=Ajustar URLs de rastreo múltiple de medios +multiUrlChangeReplace=Reemplazar por +multiUrlChangeFind=Buscar +multiUrlChangeCannotSave=Se ha producido un error al grabar +multiUrlChangeSave=Grabar modificaciones de URL +multiUrlChangeNewURL=URL nuevo +multiUrlNoPrefixWarning=No existe ningún prefijo común, esto significa que normalmente no todos los vídeos seleccionados se alojan actualmente en la misma ubicación. Proceda bajo su propio riesgo. +multiUrlChangeExplain=Este diálogo reemplazará en masa las partes comunes al inicio de los URLs del rastreo de medios. Asegúrate de que todas las URL empiezan con el mismo prefijo. Además, eche un vistazo a la nueva columna del URL, y pruebe los URLs resultantes antes de pulsar Grabar. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index c343ba99d82..a2f012fd0e5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -2058,3 +2058,11 @@ multiVideoIdle=La réserve de travail est inactive. multiVideoDoNoAdd=Ne pas ajouter multiVideoOffsetInput=Décalage vidéo global en millisecondes : multiVideoDescription=Le serveur Web doit fournir une liste des index (les sous-dossiers sont pris en charge s''ils sont répertoriés) pour permettre la détection de fichier. Après une analyse initiale des métadonnées contenues dans les fichiers MP4, les vidéos à ajouter doivent être sélectionnées en cochant les cases correspondantes dans la colonne de gauche. Le bouton "Ajouter audio/vidéo" va créer des pistes médias pour tous les fichiers sélectionnés, et ajouter ces fichiers à toutes les courses sélectionnées dans la colonne de droite. +multiUrlChangeMediaTrack=Ajuster plusieurs URL de piste média +multiUrlChangeReplace=Remplacer par +multiUrlChangeFind=Rechercher +multiUrlChangeCannotSave=Une erreur s''est produite lors de la sauvegarde. +multiUrlChangeSave=Sauvegarder modifications apportées aux URL +multiUrlChangeNewURL=Nouvelle URL +multiUrlNoPrefixWarning=Aucun préfixe commun n''a été trouvé. Cela signifie généralement que les vidéos sélectionnées ne sont actuellement pas toutes hébergées au même endroit. Vous assumez les risques liés à leur utilisation. +multiUrlChangeExplain=Cette boîte de dialogue remplacera en masse les éléments communs au début des URL de piste média. Assurez-vous que toutes les URL commencent par le même préfixe. Veuillez également prendre le temps d''observer la nouvelle colonne d''URL, et de tester les URL résultantes avant de sauvegarder. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 96be70f2a10..fcb938dd4f3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -2058,3 +2058,11 @@ multiVideoIdle=ワークキューが無効です multiVideoDoNoAdd=追加しないでください multiVideoOffsetInput=グローバル動画オフセット (msec): multiVideoDescription=Web サーバは、ファイルディスカバリを可能にするため、インデックスリスト (インデックスが作成されている場合はサブフォルダがサポート済) を用意していることが必要です。MP4 ファイルに含まれているメタデータの初期解析後に、追加する動画を左側のチェックボックス列で選択する必要があります。"音声/動画追加" ボタンにより、選択されているすべてのファイルに対してメディアトラックが作成され、右側の列で選択されているすべてのレースに対してそれらのファイルが追加されます。 +multiUrlChangeMediaTrack=複数メディアトラック URL の調整 +multiUrlChangeReplace=置換後の文字列 +multiUrlChangeFind=検索 +multiUrlChangeCannotSave=保存中にエラーが発生 +multiUrlChangeSave=URL 変更の保存 +multiUrlChangeNewURL=新規 URL +multiUrlNoPrefixWarning=共通の接頭辞が見つかりませんでした。これは通常、選択した動画のうち現在同じ場所に置かれていないものがあることを意味します。自身の責任で進めてください。 +multiUrlChangeExplain=このダイアログはメディアトラック URL 先頭の共通部分をまとめて置換します。すべての URL が同一の接頭辞で始まっていることを確認してください。また、新規 URL 列を参照し、保存を選択する前に結果として得られる URL を吟味してください。 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index b13a076b6f7..6333aea7517 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -2058,3 +2058,11 @@ multiVideoIdle=A lista de trabalho está inativa multiVideoDoNoAdd=Não adicionar multiVideoOffsetInput=Deslocamento de vídeo global em milissegundos: multiVideoDescription=O servidor da web é necessário para fornecer a lista de índices (subpastas suportadas se indexadas) de modo a permitir a descoberta de arquivos. Após uma análise inicial dos metadados contidos nos arquivos mp4, os vídeos que deviam ser adicionados precisam ser selecionados mediante a coluna do campo de seleção à esquerda. O botão "Adicionar áudio/vídeo" irá criar faixas de mídia para todos os arquivos selecionados e adicionar esses arquivos a todas as corridas selecionadas na coluna à direita. +multiUrlChangeMediaTrack=Ajustar vários URLs de faixa de mídia +multiUrlChangeReplace=Substituir por +multiUrlChangeFind=Procurar +multiUrlChangeCannotSave=Ocorreu um erro ao gravar +multiUrlChangeSave=Gravar modificações do URL +multiUrlChangeNewURL=novo URL +multiUrlNoPrefixWarning=Não foi encontrado um prefixo comum. Isso normalmente significa que nem todos os vídeos selecionados estão hospedados atualmente no mesmo local. Continue por sua própria conta e risco! +multiUrlChangeExplain=Este diálogo irá substituir em massa as partes comuns no início dos URLs da faixa de mídia. Assegure que todos os URLs começam com o mesmo prefixo! Veja também a nova coluna do URL e teste os URLs resultantes antes de pressionar Gravar! diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index ace2013fbf6..42efd3365a1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -2058,3 +2058,11 @@ multiVideoIdle=Рабочий список неактивен multiVideoDoNoAdd=Не добавлять multiVideoOffsetInput=Смещение глобального видео в мс: multiVideoDescription=Веб-сервер необходим для предоставления списка индекса (подпапки поддерживаются, если проиндексированы) для поиска файлов. После первичного анализа метаданных, содержащихся в файлах mp4, видео, которые должны быть добавлены, необходимо выбрать в столбце с флажками слева. При нажатии кнопки "Добавить аудио/видео" будут созданы медиаканалы для всех выбранных файлов, а файлы будут добавлены ко всем гонкам, выбранным в правом столбце. +multiUrlChangeMediaTrack=Адаптировать URL объектов мультимедиа +multiUrlChangeReplace=Заменить на +multiUrlChangeFind=Найти +multiUrlChangeCannotSave=Ошибка при сохранении +multiUrlChangeSave=Сохранить изменения URL +multiUrlChangeNewURL=Новый URL +multiUrlNoPrefixWarning=Общий префикс не найден, это означает, что не все выбранные видео хранятся в одном и том же месте. Можете продолжить на свой риск! +multiUrlChangeExplain=В этом диалоговом окне можно выполнить массовую замену общих частей в начале URL объектов мультимедиа. Убедитесь, что все URL имеют одинаковый префикс! Проверьте также столбец с новым URL и протестируйте новые URL перед сохранением! diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index a92935af942..5af00f85ac1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -2058,3 +2058,11 @@ multiVideoIdle=工作队列空闲 multiVideoDoNoAdd=请勿添加 multiVideoOffsetInput=全球视频偏移量(毫秒): multiVideoDescription=需要 Web 服务器提供索引清单(如果已编入索引,则支持的子文件夹),以允许发现文件。初始分析 mp4 文件中包含的元数据后,需要通过左侧的复选框列选择应添加的视频。“添加音频/视频”按钮将为所有选择的文件创建媒体轨道,并将这些文件添加到右列中选择的所有比赛轮次中。 +multiUrlChangeMediaTrack=调整多个媒体轨道 URL +multiUrlChangeReplace=替换为 +multiUrlChangeFind=查找 +multiUrlChangeCannotSave=保存时出错 +multiUrlChangeSave=保存 URL 更改 +multiUrlChangeNewURL=新 URL +multiUrlNoPrefixWarning=未找到通用前缀,这通常意味着并非所有选定的视频都被托管在当前的相同位置。如继续操作,后果自负! +multiUrlChangeExplain=此对话框将在媒体轨道 URL 的起始处批量替换通用部分。确保所有 URL 都以相同的前缀开头!也请注意新的 URL 列,然后在保存之前测试生成的 URL! From 7b5645b6949062ab5c25da91bf119c018afb50f2 Mon Sep 17 00:00:00 2001 From: Leon Radeck Date: Thu, 28 Jun 2018 10:51:05 +1000 Subject: [PATCH 028/102] Add option to show data labels to result chart --- .../ui/client/presentation/ResultsChart.java | 29 ++++++++++++++++++- .../sap/sse/gwt/client/StringMessages.java | 1 + .../sse/gwt/client/StringMessages.properties | 1 + .../gwt/client/StringMessages_de.properties | 1 + 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java index a814a6ce7ff..b765ec0d725 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java @@ -1,5 +1,6 @@ package com.sap.sse.datamining.ui.client.presentation; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -25,11 +26,15 @@ import org.moxieapps.gwt.highcharts.client.events.SeriesClickEvent; import org.moxieapps.gwt.highcharts.client.events.SeriesClickEventHandler; import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData; import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter; +import org.moxieapps.gwt.highcharts.client.labels.DataLabels; +import org.moxieapps.gwt.highcharts.client.labels.DataLabelsData; +import org.moxieapps.gwt.highcharts.client.labels.DataLabelsFormatter; import org.moxieapps.gwt.highcharts.client.labels.YAxisLabels; import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions; import com.google.gwt.text.shared.AbstractRenderer; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimpleLayoutPanel; @@ -136,6 +141,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { private final HorizontalPanel sortByPanel; private final ValueListBox> keyComparatorListBox; private final ValueListBox decimalsListBox; + private final CheckBox showDataLabels; private final SimpleLayoutPanel chartPanel; private final Chart chart; @@ -207,6 +213,19 @@ public class ResultsChart extends AbstractNumericResultsPresenter { showResultData(); }); addControl(decimalsPanel); + + HorizontalPanel showDataLabelsPanel = new HorizontalPanel(); + decimalsPanel.setSpacing(5); + decimalsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); + showDataLabels = new CheckBox(); + showDataLabelsPanel.add(showDataLabels); + showDataLabels.setValue(true); + showDataLabels.addValueChangeHandler(e -> { + resetChartSeries(); + showResultData(); + }); + addControl(showDataLabelsPanel); + StringMessages stringMessages = getDataMiningStringMessages(); ChartToCsvExporter csvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); @@ -430,7 +449,15 @@ public class ResultsChart extends AbstractNumericResultsPresenter { } } })); - chart.setSeriesPlotOptions(new SeriesPlotOptions().setSeriesClickEventHandler(new SeriesClickHandler())); + chart.setSeriesPlotOptions(new SeriesPlotOptions() + .setDataLabels(new DataLabels().setEnabled(true).setFormatter(new DataLabelsFormatter() { + @Override + public String format(DataLabelsData dataLabelsData) { + String dataLabel = String.valueOf(BigDecimal.valueOf(dataLabelsData.getYAsDouble()) + .setScale(decimalsListBox.getValue(), BigDecimal.ROUND_HALF_UP).doubleValue()); + return showDataLabels.getValue() ? dataLabel : null; + } + })).setSeriesClickEventHandler(new SeriesClickHandler())); return chart; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java index 38d274df3ac..c4daf0ca04f 100755 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java @@ -74,6 +74,7 @@ public interface StringMessages extends Messages { String groupMedianDescending(); String choosePresentation(); String shownDecimals(); + String showDataLabels(); String elements(long count); String resultsChart(); String cantDisplayDataOfType(String resultType); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties index 348b7a83769..4f973609c3a 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties @@ -56,6 +56,7 @@ groupMedianAscending=Group Median (Ascending) groupMedianDescending=Group Median (Descending) choosePresentation=Choose Presentation shownDecimals=Shown Decimals +showDataLabels=Show labels elements={0} elements resultsChart=Results Chart cantDisplayDataOfType=Can''t display data of type {0} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties index 1635e689dcf..94d4788b09f 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties @@ -55,6 +55,7 @@ groupMedianAscending=Gruppe Median (Aufsteigend) groupMedianDescending=Gruppe Median (Absteigend) choosePresentation=Wähle Präsentation shownDecimals=Angezeigte Nachkommastellen +showDataLabels=Zahlen anzeigen elements={0} Elemente resultsChart=Ergebnis-Diagramm cantDisplayDataOfType=Daten des Typs {0} können nicht dargestellt werden From 00bb64eed5b779500ce8b0eb47526bc9379b4020 Mon Sep 17 00:00:00 2001 From: Leon Radeck Date: Fri, 29 Jun 2018 09:36:31 +1000 Subject: [PATCH 029/102] Added label to showDataLabelsPanel --- .../sse/datamining/ui/client/presentation/ResultsChart.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java index b765ec0d725..7dd572b581c 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java @@ -215,8 +215,8 @@ public class ResultsChart extends AbstractNumericResultsPresenter { addControl(decimalsPanel); HorizontalPanel showDataLabelsPanel = new HorizontalPanel(); - decimalsPanel.setSpacing(5); - decimalsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); + showDataLabelsPanel.setSpacing(5); + showDataLabelsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); showDataLabels = new CheckBox(); showDataLabelsPanel.add(showDataLabels); showDataLabels.setValue(true); From 5bdf6ce153b25e6c5293cd3198e8f1b967b762dd Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 10:29:12 +0200 Subject: [PATCH 030/102] bug4643: Added existing WindFinder spot collections to AvailableWindFinderSpotCollections.java. WindFinder Tab in Event Dialog has a suggestion box where they are used. Event Detail panel has a field with the used windfinder collections. Added StringMessages. --- .../AvailableWindFinderSpotCollections.java | 41 +++++++++++++++++++ .../META-INF/MANIFEST.MF | 3 +- .../adminconsole/EventDetailsComposite.java | 11 ++++- .../gwt/ui/adminconsole/EventDialog.java | 14 ++++++- .../sailing/gwt/ui/client/StringMessages.java | 3 +- .../gwt/ui/client/StringMessages.properties | 1 + .../ui/client/StringMessages_de.properties | 1 + .../release_notes_admin.html | 1 + .../GenericStringListEditorComposite.java | 24 ++++++++--- ...enericStringListInlineEditorComposite.java | 3 +- .../ui/client/component/UserDetailsView.java | 3 +- 11 files changed, 91 insertions(+), 14 deletions(-) create mode 100755 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java new file mode 100755 index 00000000000..df9c8448488 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java @@ -0,0 +1,41 @@ +package com.sap.sailing.domain.common.windfinder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public enum AvailableWindFinderSpotCollections { + KIELERFOERDE("kielerfoerde"), + CHIEMSEE("chiemsee"), + STARNBERGERSEE("starnbergersee"), + WANNSEE("wannsee"), + TRAVEMUENDE("travemuende"), + BALTIC_OFFSHORE("baltic_offshore"), + LITAUEN("litauen"), + HAMBURG_ALSTER("hamburg_alster"), + GDANSK("gdansk"), + BREST("brest"), + SZCZECIN("szczecin"), + SANKT_PETERSBURG("sankt_petersburg"), + SANKT_MORITZ("sankt_moritz"), + PORTO_CERVO("porto_cervo"); + + private final String name; + + private AvailableWindFinderSpotCollections(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static List getAllAvailableWindFinderSpotCollectionsInAlphabeticalOrder() { + List result = new ArrayList<>(); + for (AvailableWindFinderSpotCollections awsc : values()) { + result.add(awsc.getName()); + } + Collections.sort(result); + return result; + } +} diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 06fcd8d9c08..6287e654bdf 100644 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -57,8 +57,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.expeditionconnector, com.sap.sailing.expeditionconnector.common, org.mp4parser.isoparser;bundle-version="1.9.31", - com.sap.sailing.domain.windfinderadapter, - com.sap.sse.datamining.ui;bundle-version="1.0.0" + com.sap.sse.datamining.ui Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy Import-Package: javax.servlet;version="3.1.0", diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java index 9c663ec8e86..d5bbc1c769b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java @@ -40,6 +40,7 @@ public class EventDetailsComposite extends Composite { private final SimpleAnchorListComposite imageURLList; private final SimpleAnchorListComposite videoURLList; private final SimpleStringListComposite leaderboardGroupList; + private final SimpleStringListComposite windfinderSpotCollectionsList; private final CaptionPanel mainPanel; @@ -71,7 +72,10 @@ public class EventDetailsComposite extends Composite { courseAreaNamesList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.courseAreas(), "CourseAreaValueList"); imageURLList = createLabelAndAnchorListWidget(grid, currentRow++, stringMessages.images(), "ImageURLValueList"); videoURLList = createLabelAndAnchorListWidget(grid, currentRow++, stringMessages.videos(), "VideoURLValueList"); - leaderboardGroupList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.leaderboardGroups(), "LeaderboardGroupValueList"); + leaderboardGroupList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.leaderboardGroups(), + "LeaderboardGroupValueList"); + windfinderSpotCollectionsList = createLabelAndValueListWidget(grid, currentRow++, + stringMessages.windFinderSpotCollectionsList(), "WindFinderSpotCollectionsList"); for(int i=0; i < rows; i++) { grid.getCellFormatter().setVerticalAlignment(i, 0, HasVerticalAlignment.ALIGN_TOP); @@ -166,6 +170,11 @@ public class EventDetailsComposite extends Composite { leaderboardGroupNamesAsList.add(leaderboardGroupDTO.getName()); } leaderboardGroupList.setValues(leaderboardGroupNamesAsList); + List windfinderSpotCollectionsNamesAsList = new ArrayList<>(); + for (String windfinderSpotCollection : event.getWindFinderReviewedSpotsCollectionIds()) { + windfinderSpotCollectionsNamesAsList.add(windfinderSpotCollection); + } + windfinderSpotCollectionsList.setValues(windfinderSpotCollectionsNamesAsList); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java index ece46747371..77a0afb9941 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java @@ -22,6 +22,7 @@ import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.windfinder.AvailableWindFinderSpotCollections; import com.sap.sailing.gwt.ui.client.DataEntryDialogWithDateTimeBox; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -32,6 +33,7 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.VenueDTO; import com.sap.sse.gwt.client.IconResources; import com.sap.sse.gwt.client.controls.datetime.DateAndTimeInput; +import com.sap.sse.gwt.client.controls.listedit.GenericStringListEditorComposite; import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite; import com.sap.sse.gwt.client.controls.listedit.StringConstantsListEditorComposite; import com.sap.sse.gwt.client.controls.listedit.StringListInlineEditorComposite; @@ -162,9 +164,17 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox suggestedWindFinderSpotCollections = AvailableWindFinderSpotCollections + .getAllAvailableWindFinderSpotCollectionsInAlphabeticalOrder() == null ? Collections.emptyList() + : AvailableWindFinderSpotCollections + .getAllAvailableWindFinderSpotCollectionsInAlphabeticalOrder(); + windFinderSpotCollectionIdsComposite = new StringListInlineEditorComposite(Collections. emptyList(), - new GenericStringListInlineEditorComposite.ExpandedUi(stringMessages, IconResources.INSTANCE.removeIcon(), /* suggestValues */ - Collections.emptyList(), stringMessages.enterIdOfWindfinderReviewedSpotCollection(), 80)); + new GenericStringListEditorComposite.ExpandedUi(stringMessages, + IconResources.INSTANCE.removeIcon(), /* suggestValues */ + suggestedWindFinderSpotCollections, stringMessages.enterIdOfWindFinderReviewedSpotCollection(), + 35)); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index f98e78e25ef..4228317eb1a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1972,7 +1972,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String windFinderWindSourceTypeTooltip(); String windFinder(); String enterTagsForTheVideo(); - String enterIdOfWindfinderReviewedSpotCollection(); + String enterIdOfWindFinderReviewedSpotCollection(); String enterTagsForTheImage(); String unableToResolveWindFinderSpotId(String id, String message); String windFinderWeatherData(); @@ -2080,4 +2080,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String manage2SailEventURLBox(); String manage2SailEventIdBoxTooltip(); String manage2SailPort(); + String windFinderSpotCollectionsList(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 0d5f556716a..1ac045aa314 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1978,6 +1978,7 @@ windFinderWindSourceTypeTooltip=Measured wind from one or more spots of www.wind windFinder=WindFinder enterTagsForTheVideo=Enter tags for the video enterIdOfWindfinderReviewedSpotCollection=Enter ID of a reviewed WindFinder spot collection, e.g., "schilksee" +windFinderSpotCollectionsList=WindFinder spot collections enterTagsForTheImage=Enter tags for the image unableToResolveWindFinderSpotId=Unable to resolve WindFinder spot with ID {0}: {1} windFinderWeatherData=Weather Data diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 77dd211e7f5..fad66ff8e12 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1973,6 +1973,7 @@ errorWhileSlicingARace=Ein Fehler ist beim Schneiden eines Rennens aufgetreten windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Gemessener Wind von einer oder mehreren Stationen von www.windfinder.com windFinder=WindFinder +windFinderSpotCollectionsList=WindFinder Messstellen-Sammlungen enterTagsForTheVideo=Tags zum Video erfassen enterIdOfWindfinderReviewedSpotCollection=ID einer geprüften WindFinder Messstellen-Sammlung eingeben, z.B. "schilksee" enterTagsForTheImage=Tags zum Bild erfassen diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 28259db3f04..808598d4faf 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -24,6 +24,7 @@

    June 2018

      +
    • Added the available WindFinder Spot Collections. The desired ones can be selected from the WindFinder Tab in the Event Dialog.
    • Added the ability to change the url of multiple Mediatracks with a common prefix at once. This is useful e.g. for video migration scenarios when movin videaos from one server to another. This can be done from the adminconsole->tracked races->audio & videos tab
    • diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java index 02bfdddb31d..84d6e79c394 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java @@ -3,6 +3,7 @@ package com.sap.sse.gwt.client.controls.listedit; import java.util.ArrayList; import java.util.List; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyUpEvent; @@ -85,11 +86,9 @@ public abstract class GenericStringListEditorComposite extends ListEd } public static class ExpandedUi extends ExpandedListEditorUi { - protected final MultiWordSuggestOracle inputOracle; protected final String placeholderTextForAddTextbox; - - + protected final Integer inputBoxSize; public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable suggestValues) { this(stringMessages, removeImage, suggestValues, /* placeholderTextForAddTextbox */ null); } @@ -98,8 +97,17 @@ public abstract class GenericStringListEditorComposite extends ListEd * @param suggestValues must not be null but may be empty * @param placeholderTextForAddTextbox may be null */ - public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable suggestValues, String placeholderTextForAddTextbox) { - super(stringMessages, removeImage, /*canRemoveItems*/true); + public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable suggestValues, + String placeholderTextForAddTextbox) { + this(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox, null); + + } + /** + * @param inputBoxSize The size of the input box in EM Unit. + */ + public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable suggestValues, + String placeholderTextForAddTextbox, Integer inputBoxSize) { + super(stringMessages, removeImage, /* canRemoveItems */true); this.placeholderTextForAddTextbox = placeholderTextForAddTextbox; this.inputOracle = new MultiWordSuggestOracle(); for (String suggestValue : suggestValues) { @@ -108,8 +116,9 @@ public abstract class GenericStringListEditorComposite extends ListEd List defaultSuggestions = new ArrayList<>(); Util.addAll(suggestValues, defaultSuggestions); this.inputOracle.setDefaultSuggestionsFromText(defaultSuggestions); + this.inputBoxSize = inputBoxSize; } - + protected GenericStringListEditorComposite getContext() { return (GenericStringListEditorComposite) context; } @@ -134,6 +143,9 @@ public abstract class GenericStringListEditorComposite extends ListEd protected Widget createAddWidget() { final SuggestBox inputBox = createSuggestBox(); inputBox.ensureDebugId("InputSuggestBox"); + if (inputBoxSize != null) { + inputBox.setWidth(Integer.toString(inputBoxSize) + Unit.EM); + } final Button addButton = new Button(getStringMessages().add()); addButton.ensureDebugId("AddButton"); addButton.setEnabled(false); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListInlineEditorComposite.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListInlineEditorComposite.java index bc9e6cf73b9..302003fb4e8 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListInlineEditorComposite.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListInlineEditorComposite.java @@ -36,7 +36,8 @@ public abstract class GenericStringListInlineEditorComposite extends this(stringMessages, removeImage, suggestValues, /* placeholderTextForAddTextbox */ null, textBoxSize); } - public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, List suggestValues, String placeholderTextForAddTextbox, int textBoxSize) { + public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, List suggestValues, + String placeholderTextForAddTextbox, int textBoxSize) { super(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox); this.textBoxSize = textBoxSize; } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserDetailsView.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserDetailsView.java index 6528d99f4af..b273db71b48 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserDetailsView.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserDetailsView.java @@ -82,7 +82,8 @@ public class UserDetailsView extends FlowPanel { for (Permission permission : additionalPermissions) { defaultPermissionNames.add(permission.getStringPermission()); } - rolesEditor = new StringListEditorComposite(user==null?Collections.emptySet():user.getRoles(), stringMessages, com.sap.sse.gwt.client.IconResources.INSTANCE.removeIcon(), defaultRoleNames, + rolesEditor = new StringListEditorComposite(user == null ? Collections. emptySet() : user.getRoles(), + stringMessages, com.sap.sse.gwt.client.IconResources.INSTANCE.removeIcon(), defaultRoleNames, stringMessages.enterRoleName()); rolesEditor.addValueChangeHandler(new ValueChangeHandler>() { @Override From 67eea0fd4dd7f9086d3e5ba20ee4504e1d24295a Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 2 Jul 2018 13:26:26 +0200 Subject: [PATCH 031/102] Bug 4640: Added release notes --- .../places/whatsnew/resources/SailingAnalyticsNotes.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html index 954412dc7fa..a4f323b99b4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html @@ -4,6 +4,11 @@

      What's New - SAP Sailing Analytics

      +
      July 2018
      +
        +
      • In RaceBoard.html when zooming into a specific time range, the time slider will provide a "reset zoom" button in expanded mode.
      • +
      +
      June 2018
      • In RaceBoard.html the availability of media is now visualized under the time slider via a bar overlay. When hovering the bar, the title of the video is shown.
      • From d298287677d4fc83e5efb233dbb6b42c76618b0f Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 2 Jul 2018 14:39:04 +0200 Subject: [PATCH 032/102] Bug 3262: Added release notes --- java/com.sap.sailing.www/release_notes_admin.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 28259db3f04..d6f5376609a 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -22,6 +22,13 @@

        Release Notes - Administration Console

        +

        July 2018

        +
          +
        • When adding a YouTube video for tracked races ("Manage Media" in RaceBoard.html or "Tracked Races > Audio & Video" in AdminConsole.html) the respective video metadata is now read using YouTube API v3. + This functionality used to work some years ago using API v2 but was broken since this API version was discontinued some time ago. + Due to limitations of the new API we can't read the start timepoint of videos by now. You still need to provide this value manually.
        • +
        +

        June 2018

        • Added the ability to change the url of multiple Mediatracks with a common prefix at once. From 618a02ad9ada775ae94e192e8d1e782ecf1b2c99 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 2 Jul 2018 14:39:38 +0200 Subject: [PATCH 033/102] Bug 3262: Reverted unintended diff to master --- .../com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index f51e37256f4..704f7c2d51e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -405,7 +405,7 @@ public class NewMediaDialog extends DataEntryDialog { } else { mediaTrack.duration = result.getDuration(); mediaTrack.mimeType = result.isSpherical() ? MimeType.mp4panorama : MimeType.mp4; - if (result.getRecordStartedTime() != null) { + if (result.getRecordStartedTime() != null && !manuallyEditedStartTime) { mediaTrack.startTime = new MillisecondsTimePoint(result.getRecordStartedTime()); } refreshUI(); From 3f4e67ed47195eda2148b5108050c2886d9c50b2 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 2 Jul 2018 14:41:34 +0200 Subject: [PATCH 034/102] Bug 3262: Fixed Unicode char in JavaDoc --- .../com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 704f7c2d51e..ef40c4c0988 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -339,7 +339,7 @@ public class NewMediaDialog extends DataEntryDialog { /** * For a given url that points to an mp4 video, attempts are made to parse the header, to determine the actual - * starttime of the video and to check for a 360\B0 flag. The video will be analyzed by the backendserver, either via + * starttime of the video and to check for a 360° flag. The video will be analyzed by the backendserver, either via * direct download, or proxied by the client, if a video is only available locally. If the video header cannot be * read, default values are used instead. */ From e8bcede1c9faaec2683755210c073f9fcb6aa47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 10:38:30 +0200 Subject: [PATCH 035/102] bug4479 allow multiple races to be specified --- .../gateway/jaxrs/api/WindResource.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 751a70861af..5bd49178c9f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -37,23 +37,29 @@ public class WindResource extends AbstractSailingServerResource { JSONArray windDatas = (JSONArray) requestObject.get("windData"); String regattaName = (String) requestObject.get("regattaName"); - String raceName = (String) requestObject.get("raceName"); + JSONArray raceNames = (JSONArray) requestObject.get("raceName"); WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); String windSourceId = (String) requestObject.get("windSourceId"); - DynamicTrackedRace trackedRace = getService().getTrackedRace(new RegattaNameAndRaceName(regattaName, raceName)); - WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); - JSONArray answer = new JSONArray(); - if (trackedRace != null) { - for (int i = 0; i < windDatas.size(); i++) { - JSONObject windData = Helpers.toJSONObjectSafe(windDatas.get(i)); - Wind data = deserializer.deserialize(windData); - boolean success = trackedRace.recordWind(data, windsource); - answer.add(i, success); + JSONObject answer = new JSONObject(); + for(Object raceName:raceNames) { + RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, (String) raceName); + DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier); + WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); + + if (trackedRace != null) { + JSONArray subAnswer = new JSONArray(); + for (int i = 0; i < windDatas.size(); i++) { + JSONObject windData = Helpers.toJSONObjectSafe(windDatas.get(i)); + Wind data = deserializer.deserialize(windData); + boolean success = trackedRace.recordWind(data, windsource); + subAnswer.add(i, success); + } + answer.put(identifier.getRaceName(), subAnswer); + } else { + answer.put(identifier.getRaceName(),"Could not resolve traced race"); } - } else { - return Response.ok("{\"error\":\"Could not resolve race\"}").build(); } return Response.ok(answer.toJSONString()).build(); } From 62f23b15f8b04732d8aa36cc3cb81e5148efe7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 10:46:38 +0200 Subject: [PATCH 036/102] bug4479 added permissions check --- .../sap/sailing/server/gateway/jaxrs/api/WindResource.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 5bd49178c9f..9a0ec2bb869 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -6,6 +6,7 @@ import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -16,6 +17,8 @@ import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; +import com.sap.sailing.domain.common.security.Permission; +import com.sap.sailing.domain.common.security.Permission.Mode; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; @@ -32,6 +35,8 @@ public class WindResource extends AbstractSailingServerResource { @Consumes(MediaType.APPLICATION_JSON) @Path("putWind") public Response putWind(String json) throws ParseException, JsonDeserializationException { + SecurityUtils.getSubject().checkPermission(Permission.EVENT.getStringPermission(Mode.UPDATE)); + Object requestBody = JSONValue.parseWithException(json); JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); JSONArray windDatas = (JSONArray) requestObject.get("windData"); From b4697f98e3e58858f2eb8943f55896b4fdbec967 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 3 Jul 2018 12:05:22 +0200 Subject: [PATCH 037/102] code formatting Change-Id: If53e49429cbabba1c0a452b3cb409f1a34b48862 --- .../racemap/maneuver/ManeuverTablePanel.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index 706446d3333..ff330995cea 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -167,10 +167,11 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createSortableAbsMinMaxColumn( Function extractor, String title, String unit) { final SortableColumn col = new AbstractSortableColumnWithMinMax( @@ -239,6 +240,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createManeuverTypeColumn() { return new SortableColumn(new TextCell(), SortingOrder.ASCENDING) { - @Override public InvertibleComparator getComparator() { return new InvertibleComparatorAdapter() { @@ -343,7 +344,6 @@ public class ManeuverTablePanel extends AbstractCompositeComponent col = new SortableColumn( new DateCell(DateTimeFormat.getFormat(PredefinedFormat.TIME_LONG)), SortingOrder.ASCENDING) { @Override @@ -371,7 +371,6 @@ public class ManeuverTablePanel extends AbstractCompositeComponent column = new SortableColumn( new AbstractCell() { @Override @@ -406,9 +405,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent(new TextCell(), SortingOrder.ASCENDING) { - @Override public InvertibleComparator getComparator() { return comparator; From 1d61ab61ed94600296125297d3f9e37179c8da5f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 3 Jul 2018 12:35:36 +0200 Subject: [PATCH 038/102] bug4614: introduced SortableMinMaxColumn to factor out the commonalities of columns sorted by absolute and real value Change-Id: I10f0efbb1e63e694e4575b1322124cd8041e82db --- .../racemap/maneuver/ManeuverTablePanel.java | 139 +----------------- .../maneuver/SortableMinMaxColumn.java | 94 ++++++++++++ 2 files changed, 96 insertions(+), 137 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/SortableMinMaxColumn.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index ff330995cea..2d233399dbf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -9,11 +9,9 @@ import java.util.function.Function; import java.util.function.Supplier; import com.google.gwt.cell.client.AbstractCell; -import com.google.gwt.cell.client.Cell.Context; import com.google.gwt.cell.client.DateCell; import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.shared.GWT; -import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.i18n.shared.DateTimeFormat; import com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; @@ -42,14 +40,11 @@ import com.sap.sailing.gwt.ui.actions.GetManeuversForCompetitorsAction; import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.ManeuverTypeFormatter; -import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithMinMax; import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn; -import com.sap.sailing.gwt.ui.leaderboard.HasStringAndDoubleValue; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel.LeaderBoardStyle; -import com.sap.sailing.gwt.ui.leaderboard.MinMaxRenderer; import com.sap.sailing.gwt.ui.leaderboard.SortedCellTableWithStylableHeaders; import com.sap.sailing.gwt.ui.shared.ManeuverDTO; import com.sap.sse.common.TimeRange; @@ -82,8 +77,6 @@ public class ManeuverTablePanel extends AbstractCompositeComponent maneuverCellTable; @@ -174,71 +167,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createSortableAbsMinMaxColumn( Function extractor, String title, String unit) { - final SortableColumn col = new AbstractSortableColumnWithMinMax( - new TextCell(), SortingOrder.ASCENDING) { - final InvertibleComparator comparatorWithAbs = new InvertibleComparatorAdapter() { - @Override - public int compare(ManeuverTableData o1, ManeuverTableData o2) { - Double o1v = extractor.apply(o1); - Double o2v = extractor.apply(o2); - if (o1v == null && o2v == null) { - return 0; - } - if (o1v == null && o2v != null) { - return -1; - } - if (o1v != null && o2v == null) { - return 1; - } - return Double.compare(Math.abs(o1v), Math.abs(o2v)); - } - }; - final HasStringAndDoubleValue dataProvider = new HasStringAndDoubleValue() { - @Override - public String getStringValueToRender(ManeuverTableData row) { - Double value = extractor.apply(row); - if (value == null) { - return null; - } - return towDigitAccuracy.format(value); - } - - @Override - public Double getDoubleValue(ManeuverTableData row) { - Double value = extractor.apply(row); - return value == null ? null : Math.abs(value); - } - }; - - final MinMaxRenderer renderer = new MinMaxRenderer(dataProvider, comparatorWithAbs); - - @Override - public InvertibleComparator getComparator() { - return comparatorWithAbs; - } - - @Override - public void render(Context context, ManeuverTableData object, SafeHtmlBuilder sb) { - renderer.render(context, object, title, sb); - } - - @Override - public Header getHeader() { - return new TextHeader(title + " [" + unit + "]"); - } - - @Override - public String getValue(ManeuverTableData object) { - return dataProvider.getStringValueToRender(object); - } - - @Override - public void updateMinMax() { - renderer.updateMinMax(maneuverCellTable.getDataProvider().getList()); - } - }; - col.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); - return col; + return new SortableMinMaxColumn(extractor, title, unit, maneuverCellTable.getDataProvider(), /* absolute */ true); } /** @@ -246,71 +175,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent createSortableMinMaxColumn( Function extractor, String title, String unit) { - final SortableColumn col = new AbstractSortableColumnWithMinMax( - new TextCell(), SortingOrder.ASCENDING) { - final InvertibleComparator comparator = new InvertibleComparatorAdapter() { - @Override - public int compare(ManeuverTableData o1, ManeuverTableData o2) { - Double o1v = extractor.apply(o1); - Double o2v = extractor.apply(o2); - if (o1v == null && o2v == null) { - return 0; - } - if (o1v == null && o2v != null) { - return -1; - } - if (o1v != null && o2v == null) { - return 1; - } - return Double.compare(o1v, o2v); - } - }; - final HasStringAndDoubleValue dataProvider = new HasStringAndDoubleValue() { - @Override - public String getStringValueToRender(ManeuverTableData row) { - Double value = extractor.apply(row); - if (value == null) { - return null; - } - return towDigitAccuracy.format(value); - } - - @Override - public Double getDoubleValue(ManeuverTableData row) { - Double value = extractor.apply(row); - return value == null ? null : value; - } - }; - - final MinMaxRenderer renderer = new MinMaxRenderer(dataProvider, comparator); - - @Override - public InvertibleComparator getComparator() { - return comparator; - } - - @Override - public void render(Context context, ManeuverTableData object, SafeHtmlBuilder sb) { - renderer.render(context, object, title, sb); - } - - @Override - public Header getHeader() { - return new TextHeader(title + " [" + unit + "]"); - } - - @Override - public String getValue(ManeuverTableData object) { - return dataProvider.getStringValueToRender(object); - } - - @Override - public void updateMinMax() { - renderer.updateMinMax(maneuverCellTable.getDataProvider().getList()); - } - }; - col.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); - return col; + return new SortableMinMaxColumn(extractor, title, unit, maneuverCellTable.getDataProvider(), /* absolute */ false); } private SortableColumn createManeuverTypeColumn() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/SortableMinMaxColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/SortableMinMaxColumn.java new file mode 100755 index 00000000000..b6c5d146cff --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/SortableMinMaxColumn.java @@ -0,0 +1,94 @@ +package com.sap.sailing.gwt.ui.client.shared.racemap.maneuver; + +import java.util.Comparator; +import java.util.function.Function; + +import com.google.gwt.cell.client.Cell.Context; +import com.google.gwt.cell.client.TextCell; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.user.cellview.client.Header; +import com.google.gwt.user.cellview.client.TextHeader; +import com.google.gwt.user.client.ui.HasHorizontalAlignment; +import com.google.gwt.view.client.ListDataProvider; +import com.sap.sailing.domain.common.InvertibleComparator; +import com.sap.sailing.domain.common.SortingOrder; +import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter; +import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; +import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithMinMax; +import com.sap.sailing.gwt.ui.leaderboard.HasStringAndDoubleValue; +import com.sap.sailing.gwt.ui.leaderboard.MinMaxRenderer; + +public class SortableMinMaxColumn extends AbstractSortableColumnWithMinMax { + private final static NumberFormat TWO_DIGIT_ACCURACY = NumberFormatterFactory.getDecimalFormat(2); + private final String title; + private final String unit; + + final InvertibleComparator comparator; + + final HasStringAndDoubleValue dataProvider; + + final MinMaxRenderer renderer; + + final ListDataProvider maneuverTableListDataProvider; + + public SortableMinMaxColumn(final Function extractor, String title, String unit, + ListDataProvider maneuverTableListDataProvider, boolean absolute) { + super(new TextCell(), SortingOrder.ASCENDING); + this.title = title; + this.unit = unit; + this.maneuverTableListDataProvider = maneuverTableListDataProvider; + this.comparator = new InvertibleComparatorAdapter() { + @Override + public int compare(ManeuverTableData o1, ManeuverTableData o2) { + Double o1v = extractor.apply(o1); + Double o2v = extractor.apply(o2); + return Comparator.nullsFirst((Double v1, Double v2)->Double.compare(absolute?Math.abs(v1):v1, absolute?Math.abs(v2):v2)).compare(o1v, o2v); + } + }; + this.dataProvider = new HasStringAndDoubleValue() { + @Override + public String getStringValueToRender(ManeuverTableData row) { + Double value = extractor.apply(row); + if (value == null) { + return null; + } + return TWO_DIGIT_ACCURACY.format(value); + } + + @Override + public Double getDoubleValue(ManeuverTableData row) { + Double value = extractor.apply(row); + return value == null ? null : value; + } + }; + + this.renderer = new MinMaxRenderer(dataProvider, comparator); + this.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); + } + + @Override + public InvertibleComparator getComparator() { + return comparator; + } + + @Override + public void render(Context context, ManeuverTableData object, SafeHtmlBuilder sb) { + renderer.render(context, object, title, sb); + } + + @Override + public Header getHeader() { + return new TextHeader(title + " [" + unit + "]"); + } + + @Override + public String getValue(ManeuverTableData object) { + return dataProvider.getStringValueToRender(object); + } + + @Override + public void updateMinMax() { + renderer.updateMinMax(maneuverTableListDataProvider.getList()); + } +} \ No newline at end of file From 9873cf679ed411d33029676c1ff8d545c6f6e2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 13:46:55 +0200 Subject: [PATCH 039/102] bug4654 try to get the extension from the filename, if not determined by mime type --- .../sailing/server/gateway/impl/FileUploadServlet.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java index 309a8f81972..1b03f2db42a 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java @@ -55,7 +55,13 @@ public class FileUploadServlet extends AbstractFileUploadServlet { } else if (fileType.equals("image/png")) { fileExtension = ".png"; } else { - fileExtension = ""; + String fileName = fileItem.getName(); + int lastDot = fileName.lastIndexOf("."); + if (lastDot > 0) { + fileExtension = fileName.substring(lastDot); + } else { + fileExtension = ""; + } } try { if (fileItem.getSize() > 1024 * 1024 * MAX_SIZE_IN_MB) { From 18b01f4d1aacae2d83848d324ba54edca344ca77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 14:59:32 +0200 Subject: [PATCH 040/102] bug4479 added filter for windsourcetypes to web and expedition --- .../gateway/jaxrs/api/WindResource.java | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 9a0ec2bb869..d3f24cbdd1e 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -36,7 +36,7 @@ public class WindResource extends AbstractSailingServerResource { @Path("putWind") public Response putWind(String json) throws ParseException, JsonDeserializationException { SecurityUtils.getSubject().checkPermission(Permission.EVENT.getStringPermission(Mode.UPDATE)); - + Object requestBody = JSONValue.parseWithException(json); JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); JSONArray windDatas = (JSONArray) requestObject.get("windData"); @@ -45,25 +45,28 @@ public class WindResource extends AbstractSailingServerResource { JSONArray raceNames = (JSONArray) requestObject.get("raceName"); WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); - String windSourceId = (String) requestObject.get("windSourceId"); - JSONObject answer = new JSONObject(); - for(Object raceName:raceNames) { + String windSourceId = (String) requestObject.get("windSourceId"); + for (Object raceName : raceNames) { RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, (String) raceName); - DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier); - WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); - - if (trackedRace != null) { - JSONArray subAnswer = new JSONArray(); - for (int i = 0; i < windDatas.size(); i++) { - JSONObject windData = Helpers.toJSONObjectSafe(windDatas.get(i)); - Wind data = deserializer.deserialize(windData); - boolean success = trackedRace.recordWind(data, windsource); - subAnswer.add(i, success); + if (windSourceType == WindSourceType.EXPEDITION || windSourceType == WindSourceType.WEB) { + DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier); + WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); + + if (trackedRace != null) { + JSONArray subAnswer = new JSONArray(); + for (int i = 0; i < windDatas.size(); i++) { + JSONObject windData = Helpers.toJSONObjectSafe(windDatas.get(i)); + Wind data = deserializer.deserialize(windData); + boolean success = trackedRace.recordWind(data, windsource); + subAnswer.add(i, success); + } + answer.put(identifier.getRaceName(), subAnswer); + } else { + answer.put(identifier.getRaceName(), "Could not resolve traced race"); } - answer.put(identifier.getRaceName(), subAnswer); } else { - answer.put(identifier.getRaceName(),"Could not resolve traced race"); + answer.put(identifier.getRaceName(), "Only Windsourcetypes expedition or web are allowed"); } } return Response.ok(answer.toJSONString()).build(); From 37b75222b5db8c61450285b726aa849d528f04ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 15:21:54 +0200 Subject: [PATCH 041/102] bug4479 added documentation to new putWind endpoint --- .../webservices/api/v1/index.html | 5 +++ .../webservices/api/v1/putWind.html | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html index b8fea5fafba..b2b31e72379 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html @@ -249,6 +249,11 @@ name to make sure you get to the event you're interested in. /api/v1/boats Obtains information about a single boat + + + /api/v1/boats + Allows to add wind values to 1-x races, similar to AdminConsole +
          diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html new file mode 100644 index 00000000000..cacd245b228 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html @@ -0,0 +1,44 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

          SAP Sailing Analytics Webservices API Version 1.0

          +

          URL: /api/v1/wind/putWind

          + +Description: +

          Gets the details of a competitor

          +
          + + + + + + + + + + + + + + + + + + + + + +
          Webservice Type:REST
          Output format:Json
          Mandatory parameters:windDataregattaNameraceNamewindSourceTypewindSourceId
          Examples: + {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120}],"regattaName":"Cardiff","raceName":"R1","windSourceType":"WEB","windSourceId":"rest tracker id"} +
          +
          +Back to Web Service Overview + + From 288570483cde70547e9dcee73fd3692efa095f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Tue, 3 Jul 2018 15:32:49 +0200 Subject: [PATCH 042/102] bug4479 added documentation to new putWind endpoint --- .../webservices/api/v1/index.html | 2 +- .../webservices/api/v1/putWind.html | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html index b2b31e72379..2b8218015f4 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html @@ -251,7 +251,7 @@ name to make sure you get to the event you're interested in. - /api/v1/boats + /api/v1/wind/putWind Allows to add wind values to 1-x races, similar to AdminConsole diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html index cacd245b228..b0167d4d1ce 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html @@ -25,16 +25,14 @@ Mandatory parameters: - windData - regattaName - raceName - windSourceType - windSourceId + windData, regattaName, raceName, windSourceType, windSourceId Examples: - {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120}],"regattaName":"Cardiff","raceName":"R1","windSourceType":"WEB","windSourceId":"rest tracker id"} + Request:
          {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120}],"regattaName":"Cardiff","raceName":"R1","windSourceType":"WEB","windSourceId":"rest tracker id"} +
          + Answer:
          {"R1":[true]} From 5bd2b5754c991d811b08b746e3386eb4957c67b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 4 Jul 2018 09:19:50 +0200 Subject: [PATCH 043/102] bug4479 added documentation to new putWind endpoint --- .../sap/sailing/server/gateway/jaxrs/api/WindResource.java | 7 ++----- .../webservices/api/v1/putWind.html | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index d3f24cbdd1e..25a11d984f0 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -6,7 +6,6 @@ import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -17,8 +16,6 @@ import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; -import com.sap.sailing.domain.common.security.Permission; -import com.sap.sailing.domain.common.security.Permission.Mode; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; @@ -35,14 +32,14 @@ public class WindResource extends AbstractSailingServerResource { @Consumes(MediaType.APPLICATION_JSON) @Path("putWind") public Response putWind(String json) throws ParseException, JsonDeserializationException { - SecurityUtils.getSubject().checkPermission(Permission.EVENT.getStringPermission(Mode.UPDATE)); +// SecurityUtils.getSubject().checkPermission(Permission.TRACKED_RACE.getStringPermission(Mode.UPDATE)); Object requestBody = JSONValue.parseWithException(json); JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); JSONArray windDatas = (JSONArray) requestObject.get("windData"); String regattaName = (String) requestObject.get("regattaName"); - JSONArray raceNames = (JSONArray) requestObject.get("raceName"); + JSONArray raceNames = (JSONArray) requestObject.get("raceNames"); WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); JSONObject answer = new JSONObject(); diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html index b0167d4d1ce..eab62f406bf 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html @@ -30,7 +30,7 @@ Examples: - Request:
          {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120}],"regattaName":"Cardiff","raceName":"R1","windSourceType":"WEB","windSourceId":"rest tracker id"} + Request:
          {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120,"speedinknots":60}],"regattaName":"ESS 2016 Cardiff","raceNames":["Race 1"],"windSourceType":"WEB","windSourceId":"tracker02"}
          Answer:
          {"R1":[true]} From 06247ee1d1c363d89ed0ba4f40aa237695be12c1 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 09:26:33 +0200 Subject: [PATCH 044/102] Bug 4657: ShardingContext is set in LeaderboardsResource --- .../jaxrs/api/LeaderboardsResource.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 9ff9bcc874b..b401f6817ec 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -86,6 +86,7 @@ import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTracki import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; import com.sap.sailing.domain.common.security.Permission; import com.sap.sailing.domain.common.security.Permission.Mode; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; @@ -96,6 +97,7 @@ import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.RaceHandle; @@ -148,29 +150,35 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { public Response getLeaderboard(@PathParam("name") String leaderboardName, @DefaultValue("Live") @QueryParam("resultState") ResultStates resultState, @QueryParam("maxCompetitorsCount") Integer maxCompetitorsCount) { - Response response; - TimePoint requestTimePoint = MillisecondsTimePoint.now(); - Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); - if (leaderboard == null) { - response = Response.status(Status.NOT_FOUND) - .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") - .type(MediaType.TEXT_PLAIN).build(); - } else { - try { - TimePoint timePoint = calculateTimePointForResultState(leaderboard, resultState); - JSONObject jsonLeaderboard; - jsonLeaderboard = getLeaderboardJson(resultState, maxCompetitorsCount, requestTimePoint, leaderboard, timePoint, - /* race column names */ null, /* race detail names */ null); - StringWriter sw = new StringWriter(); - jsonLeaderboard.writeJSONString(sw); - String json = sw.getBuffer().toString(); - response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); - } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { - response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) + ShardingContext.setShardingConstraint(ShardingType.LEADERBOARDNAME, leaderboardName); + + try { + Response response; + TimePoint requestTimePoint = MillisecondsTimePoint.now(); + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") .type(MediaType.TEXT_PLAIN).build(); + } else { + try { + TimePoint timePoint = calculateTimePointForResultState(leaderboard, resultState); + JSONObject jsonLeaderboard; + jsonLeaderboard = getLeaderboardJson(resultState, maxCompetitorsCount, requestTimePoint, leaderboard, timePoint, + /* race column names */ null, /* race detail names */ null); + StringWriter sw = new StringWriter(); + jsonLeaderboard.writeJSONString(sw); + String json = sw.getBuffer().toString(); + response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) + .type(MediaType.TEXT_PLAIN).build(); + } } + return response; + } finally { + ShardingContext.clearShardingConstraint(ShardingType.LEADERBOARDNAME); } - return response; } @Override From 51f8b2da66739676ae994a672c6da8ae8b941a4a Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 09:40:43 +0200 Subject: [PATCH 045/102] Bug 4657: ShardingContext isn't needed to be checked in SailingServiceImpl.createStrippedLeaderboardDTO due to the fact that it does not depend on LeaderboardDTO creation --- .../gwt/ui/server/SailingServiceImpl.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 441c554b494..19180b1b143 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2545,20 +2545,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } return results; } - - private void checkLeaderboardRouting(String leaderboardName) { - final String currentRequestUrl = getThreadLocalRequest().getRequestURL().toString(); - if (!currentRequestUrl.contains("/leaderboard/")) { - logger.log(Level.WARNING, "Leaderboard routing stacktrace", new RuntimeException("Request without leaderboard routing information")); - } else { - if (currentRequestUrl.contains(leaderboardName)) { - logger.info("leaderboard access matches leaderboard url"); - } else { - logger.info("leaderboard access to " + leaderboardName + " does not match request url " + currentRequestUrl); - logger.log(Level.SEVERE, "Leaderboard routing stacktrace", new RuntimeException("Request without leaderboard routing information")); - } - } - } /** * Creates a {@link LeaderboardDTO} for leaderboard and fills in the name, race master data @@ -2569,8 +2555,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S * If withGeoLocationData is true the geographical location of all races will be determined. */ private StrippedLeaderboardDTO createStrippedLeaderboardDTO(Leaderboard leaderboard, boolean withGeoLocationData, boolean withStatisticalData) { - checkLeaderboardRouting(leaderboard.getName()); - StrippedLeaderboardDTO leaderboardDTO = new StrippedLeaderboardDTO(convertToBoatClassDTO(leaderboard.getBoatClass())); TimePoint startOfLatestRace = null; Long delayToLiveInMillisForLatestRace = null; From 98fb6a0cb2cdae671c53026d766c8eaef2ac508b Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 09:51:11 +0200 Subject: [PATCH 046/102] Bug 4657: Lowering priority of log entries regarding ShardingContext from SEVERE to WARNING --- .../src/com/sap/sailing/domain/sharding/ShardingContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java index 1819152b7f4..e9429afbbac 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java @@ -77,12 +77,12 @@ public class ShardingContext { */ public static void checkConstraint(final ShardingType type, final String shardingInfo) { if (shardingInfo == null || shardingInfo.isEmpty()) { - logger.severe("Empty sharding constraint"); + logger.warning("Empty sharding constraint"); return; } final ThreadLocal shardingHolder = shardingMap.get(type); if (shardingHolder == null) { - logger.log(Level.SEVERE, "No current sharding context set for " + type.name(), new RuntimeException()); + logger.log(Level.WARNING, "No current sharding context set for " + type.name(), new RuntimeException()); return; } String currentShardingInfo = shardingHolder.get(); From bd5cabf78ac4d00adc0abbfc0457f377c154d213 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 10:17:59 +0200 Subject: [PATCH 047/102] Bug 4657: Fixed clearing the ShardingContext to not remove the ThreadLocal potentially being used by other Threads --- .../src/com/sap/sailing/domain/sharding/ShardingContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java index e9429afbbac..221d4ec164c 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java @@ -99,7 +99,7 @@ public class ShardingContext { } public static void clearShardingConstraint(ShardingType type) { - ThreadLocal shardingHolder = shardingMap.remove(type); + ThreadLocal shardingHolder = shardingMap.get(type); if (shardingHolder != null) { shardingHolder.remove(); } @@ -113,7 +113,7 @@ public class ShardingContext { } String currentShardingInfo = shardingHolder.get(); if (currentShardingInfo == null) { - shardingHolder.set(encodedShardingInfo); + shardingHolder.set(encodedShardingInfo); } else if (!encodedShardingInfo.equals(currentShardingInfo)) { logger.log(Level.SEVERE, "Switching shard constraint for " + shardingType.name()+". Got <<" + encodedShardingInfo +">>, expeted <<"+ currentShardingInfo+">>", new RuntimeException()); } From ea56355330fd00209257ef4af793a324ab81e388 Mon Sep 17 00:00:00 2001 From: Alessandro Stolten Date: Wed, 4 Jul 2018 09:02:09 +0000 Subject: [PATCH 048/102] Updated onboarding (markdown) --- wiki/howto/onboarding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/howto/onboarding.md b/wiki/howto/onboarding.md index 2188c073a94..46f6a2d7040 100644 --- a/wiki/howto/onboarding.md +++ b/wiki/howto/onboarding.md @@ -133,7 +133,7 @@ To ensure that all components of the Analysis Suite are working, you should also - In Eclipse click Help -> Install New Software -> Add and enter [https://dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/) - Select the Developer Tools and install - After restarting Eclipse the "Welcome to Android Development"-window should help you with installing the Android SDK - - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: [https://dl.google.com/android/installer_r24.4.1-windows.exe](https://dl.google.com/android/installer_r24.4.1-windows.exe) + - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: [https://dl.google.com/android/installer_r24.4.1-windows.exe](https://dl.google.com/android/installer_r24.4.1-windows.exe) MacOS: [https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip](https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip) 2. Setup the Android SDK * In Eclipse press Window -> Android SDK Manager * Install everything of "Tools" (hint: watchout not to update Android SDK Tools, see note below) From 19b0f462bbd3c87a77d6ba87ab5b4ef8a95ee35c Mon Sep 17 00:00:00 2001 From: Alessandro Stolten Date: Wed, 4 Jul 2018 09:06:31 +0000 Subject: [PATCH 049/102] Updated onboarding (markdown) --- wiki/howto/onboarding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/howto/onboarding.md b/wiki/howto/onboarding.md index 46f6a2d7040..b5978be5196 100644 --- a/wiki/howto/onboarding.md +++ b/wiki/howto/onboarding.md @@ -133,7 +133,7 @@ To ensure that all components of the Analysis Suite are working, you should also - In Eclipse click Help -> Install New Software -> Add and enter [https://dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/) - Select the Developer Tools and install - After restarting Eclipse the "Welcome to Android Development"-window should help you with installing the Android SDK - - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: [https://dl.google.com/android/installer_r24.4.1-windows.exe](https://dl.google.com/android/installer_r24.4.1-windows.exe) MacOS: [https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip](https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip) + - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: Windows: [https://dl.google.com/android/installer_r24.4.1-windows.exe](https://dl.google.com/android/installer_r24.4.1-windows.exe) MacOS: [https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip](https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip) Linux: [https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz](https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz) 2. Setup the Android SDK * In Eclipse press Window -> Android SDK Manager * Install everything of "Tools" (hint: watchout not to update Android SDK Tools, see note below) From 57f6143f6e8421009728360df720cc2855473141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Wed, 4 Jul 2018 11:25:45 +0200 Subject: [PATCH 050/102] bug4343 related, initial setup documentation for sharding setup --- .../landscape/amazon-ec2-sharding-setup.md | 62 ++++++++++++++++++ wiki/info/landscape/sharding_ALB.PNG | Bin 0 -> 28153 bytes wiki/info/landscape/sharding_instances.PNG | Bin 0 -> 13006 bytes .../info/landscape/sharding_target_groups.PNG | Bin 0 -> 13389 bytes 4 files changed, 62 insertions(+) create mode 100644 wiki/info/landscape/amazon-ec2-sharding-setup.md create mode 100644 wiki/info/landscape/sharding_ALB.PNG create mode 100644 wiki/info/landscape/sharding_instances.PNG create mode 100644 wiki/info/landscape/sharding_target_groups.PNG diff --git a/wiki/info/landscape/amazon-ec2-sharding-setup.md b/wiki/info/landscape/amazon-ec2-sharding-setup.md new file mode 100644 index 00000000000..c554aaf2cce --- /dev/null +++ b/wiki/info/landscape/amazon-ec2-sharding-setup.md @@ -0,0 +1,62 @@ +# Amazon EC2 for SAP Sailing Analytics + +[[_TOC_]] + +## Sharding + +#### Servers + +- For a minimum Setup at least 2 Servers are required. +- One server is required to be the master server as in usual replication situations +- All other servers are required to be replicas + + +e.g. a simple example: + + +![](sharding_instances.PNG) + +#### Routing Setup + +Multiple target groups must be defined per routing target. + +Interesting here are the groups named after leaderboards (or other discriminators). + +The names of the target groups can be defined however fitting, e.g. multiple leaderboards can be routed to each, in this example the leaderboard name is used for simplicity. + +![](sharding_target_groups.PNG) + +The interesting part is the configuration of the load balancer: + +It is recommended to have a route with a dedicated hostname going specifically to the master!, as else it might not be possible to reach the non replicated AdminConsole! + +It is required to have a default route, that can go to any server, because there are some requests that are not specific to leaderboards, and still need to be processed. + +For each Leaderboard that should not go to the default group, a entry is required. To discriminate between leaderboards, they are encoded into (mostly) all requests. + +This allows to specify a path based routing. The path are "/gwt/service/sailing/" and "/gwt/service/dispatch/" and the routing relevant suffix is leaderboard/underscore\_escaped\_leaderboardname. + + +All non a-z,A-Z,0-9 characters are replaced with \_. + +This is because limitations in the ALB do not allow the unmodified URL encoded leaderboard name. + + +In case it is unclear how the name of a leaderboard is, the REST api of the master server can be asked for this. (after creating the leaderboards) + +NAME\_OF\_LEADERBOARD\_YOU\_NEED\_TO\_KNOW\_THE\_PATH\_OF is the same name as in the admin console, the browser will do all required url encoding for this + +https://www.sharding-master.sapsailing.com/sailingserver/api/v1/leaderboards/NAME\_OF\_LEADERBOARD\_YOU\_NEED\_TO\_KNOW\_THE\_PATH\_OF/ + +eg: +https://www.sharding-master.sapsailing.com/sailingserver/api/v1/leaderboards/ESS 2016 Cardiff + +This will return a JSON document with the leaderboard name encoded near at the end: + +"ShardingLeaderboardName":"\/leaderboard\/ESS\_2016\_Cardiff", please note that the response is encoded, to get the proper name this needs to be decoded first e.g using some json parser. + +![](sharding_ALB.PNG) + +Please note that the example image is missing the dispatch related rules! + +If you need to use the REST api for other applications, we suggest to use the master server directly, as else the requests would need to be replicated to the master server anyway. \ No newline at end of file diff --git a/wiki/info/landscape/sharding_ALB.PNG b/wiki/info/landscape/sharding_ALB.PNG new file mode 100644 index 0000000000000000000000000000000000000000..60dd3a856c96e814ee1502bee43a7ba9870ac6e9 GIT binary patch literal 28153 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 FUjb8pxbOe~ literal 0 HcmV?d00001 diff --git a/wiki/info/landscape/sharding_instances.PNG b/wiki/info/landscape/sharding_instances.PNG new file mode 100644 index 0000000000000000000000000000000000000000..7be720fdd026dfdb6e3884c351f471a84806e2a4 GIT binary patch literal 13006 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 literal 0 HcmV?d00001 diff --git a/wiki/info/landscape/sharding_target_groups.PNG b/wiki/info/landscape/sharding_target_groups.PNG new file mode 100644 index 0000000000000000000000000000000000000000..4836c0f1468b91d042f2690dc672558353deac69 GIT binary patch literal 13389 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 Date: Wed, 4 Jul 2018 11:40:53 +0200 Subject: [PATCH 051/102] Bug 4657: ShardingContext is set in LeaderboardsResourceV2 --- .../jaxrs/api/LeaderboardsResourceV2.java | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index 2cdbb1647de..d2f47046748 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -37,7 +37,9 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; import com.sap.sailing.domain.common.dto.LegEntryDTO; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sse.InvalidDateException; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; @@ -52,40 +54,46 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { public Response getLeaderboard(@PathParam("name") String leaderboardName, @DefaultValue("Live") @QueryParam("resultState") ResultStates resultState, @QueryParam("columnNames") final List raceColumnNames, - @QueryParam("raceDetails") final List raceDetails, + @QueryParam("raceDetails") final List raceDetails, @QueryParam("time") String time, @QueryParam("timeasmillis") Long timeasmillis, @QueryParam("maxCompetitorsCount") Integer maxCompetitorsCount) { - Response response; - Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); - if (leaderboard == null) { - response = Response.status(Status.NOT_FOUND) - .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") - .type(MediaType.TEXT_PLAIN).build(); - } else { - try { - TimePoint timePoint; + ShardingContext.setShardingConstraint(ShardingType.LEADERBOARDNAME, leaderboardName); + + try { + Response response; + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { try { - timePoint = parseTimePoint(time, timeasmillis, calculateTimePointForResultState(leaderboard, resultState)); - } catch (InvalidDateException e1) { - return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the time.") + TimePoint timePoint; + try { + timePoint = parseTimePoint(time, timeasmillis, calculateTimePointForResultState(leaderboard, resultState)); + } catch (InvalidDateException e1) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the time.") + .type(MediaType.TEXT_PLAIN).build(); + } + JSONObject jsonLeaderboard; + if (timePoint != null || resultState == ResultStates.Live) { + jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount, raceColumnNames, raceDetails); + } else { + jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount); + } + StringWriter sw = new StringWriter(); + jsonLeaderboard.writeJSONString(sw); + String json = sw.getBuffer().toString(); + response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) .type(MediaType.TEXT_PLAIN).build(); } - JSONObject jsonLeaderboard; - if (timePoint != null || resultState == ResultStates.Live) { - jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount, raceColumnNames, raceDetails); - } else { - jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount); - } - StringWriter sw = new StringWriter(); - jsonLeaderboard.writeJSONString(sw); - String json = sw.getBuffer().toString(); - response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); - } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { - response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) - .type(MediaType.TEXT_PLAIN).build(); } + return response; + } finally { + ShardingContext.clearShardingConstraint(ShardingType.LEADERBOARDNAME); } - return response; } @Override From 425d4b2724a7584a656b790d286b12a961d01c67 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 13:14:24 +0200 Subject: [PATCH 052/102] Bug 4657: Fixed leaderboard context given to SailingServiceAsync from MultiLeaderboardProxyPanel to adjust when switching the selected leaderboard --- .../EventSeriesAnalyticsDataManager.java | 8 +++++--- .../gwt/ui/leaderboard/LeaderboardEntryPoint.java | 9 +++++++-- .../gwt/ui/leaderboard/MetaLeaderboardViewer.java | 15 +++++++++------ .../leaderboard/MultiLeaderboardProxyPanel.java | 10 +++++++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java index 9337197fa4c..0ffae739c78 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.home.desktop.places.fakeseries; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.function.Function; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.DetailType; @@ -103,9 +104,10 @@ public class EventSeriesAnalyticsDataManager { MultiRaceLeaderboardSettings leaderboardSettings, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn, Iterable availableDetailTypes) { - if(multiLeaderboardPanel == null) { - SailingServiceAsync sailingService = sailingCF.getSailingService(()-> metaLeaderboardName); - multiLeaderboardPanel = new MultiLeaderboardProxyPanel(parent, context, sailingService, metaLeaderboardName, + if (multiLeaderboardPanel == null) { + final Function sailingServiceFactory = leaderBoardName -> sailingCF + .getSailingService(() -> leaderBoardName); + multiLeaderboardPanel = new MultiLeaderboardProxyPanel(parent, context, sailingServiceFactory, metaLeaderboardName, asyncActionsExecutor, timer, true /* isEmbedded */, preselectedLeaderboardName, errorReporter, StringMessages.INSTANCE, showRaceDetails, autoExpandLastRaceColumn, leaderboardSettings, flagImageResolver, availableDetailTypes); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index aca96b8a694..9c437bdd4be 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.leaderboard; import java.util.UUID; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; @@ -26,6 +27,8 @@ import com.sap.sailing.gwt.settings.client.leaderboard.MultiCompetitorLeaderboar import com.sap.sailing.gwt.settings.client.leaderboard.MultiCompetitorLeaderboardChartSettings; import com.sap.sailing.gwt.settings.client.utils.StoredSettingsLocationFactory; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.SailingServiceHelper; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; @@ -123,7 +126,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements // make a single live request as the default but don't continue to play by default final StoredSettingsLocation storageDefinition = StoredSettingsLocationFactory - .createStoredSettingsLocatorForLeaderboard(leaderboardContextDefinition); + .createStoredSettingsLocatorForLeaderboard(leaderboardContextDefinition); getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, null, new AsyncCallback>() { @Override @@ -146,9 +149,11 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements PerspectiveCompositeSettings defaultSettings) { configureWithSettings(defaultSettings, timer); + Function sailingServiceFactory = leaderboardName -> SailingServiceHelper + .createSailingServiceInstance(() -> leaderboardName); final MetaLeaderboardViewer leaderboardViewer = new MetaLeaderboardViewer( null, context, rootComponentLifeCycle, defaultSettings, - getSailingService(), new AsyncActionsExecutor(), timer, null, + sailingServiceFactory, new AsyncActionsExecutor(), timer, null, leaderboardGroupName, leaderboardName, LeaderboardEntryPoint.this, getStringMessages(), getActualChartDetailType(defaultSettings), result); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java index 7a14b373bec..0d43b6594fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java @@ -1,5 +1,7 @@ package com.sap.sailing.gwt.ui.leaderboard; +import java.util.function.Function; + import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; @@ -37,12 +39,12 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer> componentContext, MetaLeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + Function sailingServiceFactory, AsyncActionsExecutor asyncActionsExecutor, Timer timer, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { this(parent, componentContext, lifecycle, settings, new CompetitorSelectionModel(/* hasMultiSelection */true), - sailingService, asyncActionsExecutor, timer, + sailingServiceFactory, asyncActionsExecutor, timer, preselectedLeaderboardName, leaderboardGroupName, metaLeaderboardName, errorReporter, stringMessages, chartDetailType, availableDetailTypes); } @@ -51,7 +53,7 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer> componentContext, MetaLeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - CompetitorSelectionModel competitorSelectionModel, SailingServiceAsync sailingService, + CompetitorSelectionModel competitorSelectionModel, Function sailingServiceFactory, AsyncActionsExecutor asyncActionsExecutor, Timer timer, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, @@ -59,7 +61,8 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer availableDetailTypes; + private Function sailingServiceFactory; public MultiLeaderboardProxyPanel(Component parent, ComponentContext context, - SailingServiceAsync sailingService, String metaLeaderboardName, + Function sailingServiceFactory, String metaLeaderboardName, AsyncActionsExecutor asyncActionsExecutor, Timer timer, boolean isEmbedded, String preselectedLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, boolean showRaceDetails, boolean autoExpandLastRaceColumn, MultiRaceLeaderboardSettings settings, FlagImageResolver flagImageResolver, Iterable availableDetailTypes) { super(parent, context); + this.sailingServiceFactory = sailingServiceFactory; loadedSettings = settings; this.availableDetailTypes = availableDetailTypes; this.stringMessages = stringMessages; this.errorReporter = errorReporter; - this.sailingService = sailingService; + this.sailingService = sailingServiceFactory.apply(metaLeaderboardName); this.metaLeaderboardName = metaLeaderboardName; this.asyncActionsExecutor = asyncActionsExecutor; this.showRaceDetails = showRaceDetails; @@ -237,8 +240,9 @@ public class MultiLeaderboardProxyPanel extends AbstractLazyComponent Date: Wed, 4 Jul 2018 13:23:56 +0200 Subject: [PATCH 053/102] Bug 4657: Fixed leaderboard context in LeaderboardEntryPoint for additionally showable leaderboard panels --- .../ui/leaderboard/LeaderboardEntryPoint.java | 7 +++-- .../MultiRaceLeaderboardViewer.java | 26 +++++++++++-------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index 9c437bdd4be..c2e50bea7ce 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -136,6 +136,8 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements @Override public void onSuccess(Iterable result) { + final Function sailingServiceFactory = leaderboardName -> SailingServiceHelper + .createSailingServiceInstance(() -> leaderboardName); if (leaderboardDTO.type.isMetaLeaderboard()) { // overall MetaLeaderboardPerspectiveLifecycle rootComponentLifeCycle = new MetaLeaderboardPerspectiveLifecycle( @@ -148,9 +150,6 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements public void onSuccess( PerspectiveCompositeSettings defaultSettings) { configureWithSettings(defaultSettings, timer); - - Function sailingServiceFactory = leaderboardName -> SailingServiceHelper - .createSailingServiceInstance(() -> leaderboardName); final MetaLeaderboardViewer leaderboardViewer = new MetaLeaderboardViewer( null, context, rootComponentLifeCycle, defaultSettings, sailingServiceFactory, new AsyncActionsExecutor(), timer, null, @@ -195,7 +194,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements configureWithSettings(defaultSettings, timer); final MultiRaceLeaderboardViewer leaderboardViewer = new MultiRaceLeaderboardViewer( null, context, rootComponentLifeCycle, - defaultSettings, getSailingService(), + defaultSettings, sailingServiceFactory, new AsyncActionsExecutor(), timer, leaderboardGroupName, leaderboardName, LeaderboardEntryPoint.this, getStringMessages(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardViewer.java index 94f27ef61df..12398a44c0a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiRaceLeaderboardViewer.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.leaderboard; import java.util.List; +import java.util.function.Function; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -36,12 +37,12 @@ public class MultiRaceLeaderboardViewer extends AbstractLeaderboardViewer> componentContext, LeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - final SailingServiceAsync sailingService, final AsyncActionsExecutor asyncActionsExecutor, + final Function sailingServiceFactory, final AsyncActionsExecutor asyncActionsExecutor, final Timer timer, final String leaderboardGroupName, String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { this(parent, componentContext, lifecycle, settings, new CompetitorSelectionModel(/* hasMultiSelection */true), - sailingService, asyncActionsExecutor, timer, + sailingServiceFactory, asyncActionsExecutor, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, chartDetailType, availableDetailTypes); } @@ -51,13 +52,16 @@ public class MultiRaceLeaderboardViewer extends AbstractLeaderboardViewer settings, CompetitorSelectionModel competitorSelectionModel, - final SailingServiceAsync sailingService, final AsyncActionsExecutor asyncActionsExecutor, + final Function sailingServiceFactory, final AsyncActionsExecutor asyncActionsExecutor, final Timer timer, final String leaderboardGroupName, String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { super(parent, componentContext, lifecycle, settings, competitorSelectionModel, asyncActionsExecutor, timer, stringMessages); - init(new MultiRaceLeaderboardPanel(this, getComponentContext(), sailingService, asyncActionsExecutor, + + final SailingServiceAsync sailingServiceForMainLeaderboard = sailingServiceFactory.apply(leaderboardName); + + init(new MultiRaceLeaderboardPanel(this, getComponentContext(), sailingServiceForMainLeaderboard, asyncActionsExecutor, settings.findSettingsByComponentId(LeaderboardPanelLifecycle.ID), false, competitorSelectionModel, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, settings.getPerspectiveOwnSettings().isShowRaceDetails(), @@ -71,9 +75,8 @@ public class MultiRaceLeaderboardViewer extends AbstractLeaderboardViewer>( + if (perspectiveSettings.isShowOverallLeaderboard()) { + sailingServiceForMainLeaderboard.getOverallLeaderboardNamesContaining(leaderboardName, new MarkedAsyncCallback>( new AsyncCallback>() { @Override public void onSuccess(List result) { - if(result.size() == 1) { + if (result.size() == 1) { String overallLeaderboardName = result.get(0); + final SailingServiceAsync sailingServiceForOverallLeaderboard = sailingServiceFactory.apply(overallLeaderboardName); overallLeaderboardPanel = new OverallLeaderboardPanel(MultiRaceLeaderboardViewer.this, - getComponentContext(), sailingService, + getComponentContext(), sailingServiceForOverallLeaderboard, asyncActionsExecutor, settings.findSettingsByComponentId(OverallLeaderboardPanelLifecycle.ID), false, competitorSelectionProvider, timer, From 6c98c9fdd0c00ae1489412bbc9b23d887f87182a Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 4 Jul 2018 13:37:24 +0200 Subject: [PATCH 054/102] Bug 4657: Fixed creation of SailingServiceAsync instances --- .../sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index c2e50bea7ce..9133d11f089 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -137,7 +137,12 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements @Override public void onSuccess(Iterable result) { final Function sailingServiceFactory = leaderboardName -> SailingServiceHelper - .createSailingServiceInstance(() -> leaderboardName); + .createSailingServiceInstance(new ProvidesLeaderboardRouting() { + @Override + public String getLeaderboardName() { + return leaderboardName; + } + }); if (leaderboardDTO.type.isMetaLeaderboard()) { // overall MetaLeaderboardPerspectiveLifecycle rootComponentLifeCycle = new MetaLeaderboardPerspectiveLifecycle( From 2deeeafafdd12c2d752397241c403685a28429ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Thu, 28 Jun 2018 10:16:21 +0200 Subject: [PATCH 055/102] bug4649 Added sharding leaderboard name to RestRessource --- .../sailing/server/gateway/jaxrs/api/LeaderboardsResource.java | 2 ++ .../server/gateway/jaxrs/api/LeaderboardsResourceV2.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 9ff9bcc874b..f2648aeca01 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -86,6 +86,7 @@ import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTracki import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; import com.sap.sailing.domain.common.security.Permission; import com.sap.sailing.domain.common.security.Permission.Mode; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; @@ -186,6 +187,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { writeCommonLeaderboardData(jsonLeaderboard, leaderboard, resultState, leaderboardDTO.getTimePoint(), maxCompetitorsCount); JSONArray jsonCompetitorEntries = new JSONArray(); jsonLeaderboard.put("competitors", jsonCompetitorEntries); + jsonLeaderboard.put("ShardingLeaderboardName", ShardingType.LEADERBOARDNAME.encodeIfNeeded(leaderboard.getName())); int counter = 1; for (CompetitorDTO competitor : leaderboardDTO.competitors) { LeaderboardRowDTO leaderboardRowDTO = leaderboardDTO.rows.get(competitor); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index 2cdbb1647de..e819ee36a3e 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -37,6 +37,7 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; import com.sap.sailing.domain.common.dto.LegEntryDTO; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sse.InvalidDateException; import com.sap.sse.common.Distance; @@ -122,6 +123,7 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { } JSONArray jsonCompetitorEntries = new JSONArray(); jsonLeaderboard.put("competitors", jsonCompetitorEntries); + jsonLeaderboard.put("ShardingLeaderboardName", ShardingType.LEADERBOARDNAME.encodeIfNeeded(leaderboard.getName())); int competitorCounter = 1; // Remark: leaderboardDTO.competitors are ordered by total rank for (CompetitorDTO competitor : leaderboardDTO.competitors) { From 9222ff1b4eec37c4ba99b344779cd136c25d7745 Mon Sep 17 00:00:00 2001 From: Leon Radeck Date: Thu, 5 Jul 2018 10:41:28 +1000 Subject: [PATCH 056/102] Changed variable name --- .../ui/client/presentation/ResultsChart.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java index 7dd572b581c..88d13fc2d8a 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java @@ -141,7 +141,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { private final HorizontalPanel sortByPanel; private final ValueListBox> keyComparatorListBox; private final ValueListBox decimalsListBox; - private final CheckBox showDataLabels; + private final CheckBox showDataLabelsCheckBox; private final SimpleLayoutPanel chartPanel; private final Chart chart; @@ -217,10 +217,10 @@ public class ResultsChart extends AbstractNumericResultsPresenter { HorizontalPanel showDataLabelsPanel = new HorizontalPanel(); showDataLabelsPanel.setSpacing(5); showDataLabelsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); - showDataLabels = new CheckBox(); - showDataLabelsPanel.add(showDataLabels); - showDataLabels.setValue(true); - showDataLabels.addValueChangeHandler(e -> { + showDataLabelsCheckBox = new CheckBox(); + showDataLabelsPanel.add(showDataLabelsCheckBox); + showDataLabelsCheckBox.setValue(true); + showDataLabelsCheckBox.addValueChangeHandler(e -> { resetChartSeries(); showResultData(); }); @@ -455,7 +455,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { public String format(DataLabelsData dataLabelsData) { String dataLabel = String.valueOf(BigDecimal.valueOf(dataLabelsData.getYAsDouble()) .setScale(decimalsListBox.getValue(), BigDecimal.ROUND_HALF_UP).doubleValue()); - return showDataLabels.getValue() ? dataLabel : null; + return showDataLabelsCheckBox.getValue() ? dataLabel : null; } })).setSeriesClickEventHandler(new SeriesClickHandler())); return chart; From 70c3a037eaaf1f8502241996e364f02487f0c509 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Thu, 5 Jul 2018 06:45:30 +0200 Subject: [PATCH 057/102] Added maneuver detection implementation for extremely low GPS sampling rates --- ...eWithEstimationDataRetrievalProcessor.java | 6 +- .../maneuverdetection/ManeuverDetector.java | 46 -- ...uverDetectorWithEstimationDataSupport.java | 61 ++ .../impl/AbstractManeuverDetectorImpl.java | 155 ++++++ ...owGPSSamplingRateManeuverDetectorImpl.java | 156 ++++++ .../impl/ManeuverDetectorImpl.java | 526 ++---------------- ...ithEstimationDataSupportDecoratorImpl.java | 404 ++++++++++++++ .../sap/sailing/domain/tracking/Maneuver.java | 2 +- ...neuverWithCoarseGrainedBoundariesImpl.java | 37 ++ .../domain/tracking/impl/TrackedRaceImpl.java | 20 +- ...urvesWithEstimationDataJsonSerializer.java | 6 +- 11 files changed, 873 insertions(+), 546 deletions(-) create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetectorWithEstimationDataSupport.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/AbstractManeuverDetectorImpl.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java index f11191a86e7..f05bdf58be0 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java @@ -11,8 +11,9 @@ import com.sap.sailing.datamining.impl.data.CompleteManeuverCurveWithEstimationD import com.sap.sailing.datamining.shared.ManeuverSettings; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; -import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorWithEstimationDataSupportDecoratorImpl; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; @@ -45,7 +46,8 @@ public class CompleteManeuverCurveWithEstimationDataRetrievalProcessor extends List result = new ArrayList<>(); TrackedRace trackedRace = element.getTrackedRaceContext().getTrackedRace(); Competitor competitor = element.getCompetitor(); - ManeuverDetector maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + ManeuverDetectorWithEstimationDataSupport maneuverDetector = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + new ManeuverDetectorImpl(trackedRace, competitor)); Iterable maneuvers = trackedRace.getManeuvers(competitor, false); Iterable maneuverCurves = maneuverDetector.getCompleteManeuverCurves(maneuvers); Iterable maneuversWithEstimationData = maneuverDetector diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetector.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetector.java index b31f723b038..dc7a9118502 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetector.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetector.java @@ -36,50 +36,4 @@ public interface ManeuverDetector { */ List detectManeuvers(); - /** - * Derives maneuvers from the provided {@code maneuverCurves}. Since the provided complete maneuver curves already - * include the calculated boundaries of each complete maneuvering spot, this method operates in a very - * performance-efficient manner. - * - * @param maneuverCurves - * The maneuver curves from which the maneuvers shall be derived - * @return The maneuvers derived from the provided maneuver curves. The list gets empty, if the provided maneuver - * curves list is also empty. - */ - List detectManeuvers(Iterable maneuverCurves); - - /** - * Detects the complete maneuver curves performed within a GPS-track of the competitor associated with this - * {@link ManeuverDetector}-instance. In contrast to maneuvers determined by {@link #detectManeuvers()}, the - * complete maneuver curves are not subject to any splitting logic for maneuvers with multiple "tacking" and - * "jibing". See {@link ManeuverDetector} description for more info regarding the detection strategy. - * - * @return an empty list if no maneuver spots were detected, otherwise the list with detected maneuver curves. - * @see CompleteManeuverCurve - * @see ManeuverDetector - */ - List detectCompleteManeuverCurves(); - - /** - * Parses {@link CompleteManeuverCurve}-instances from provided {@link Maneuver}-instances. This method performs - * significantly faster than {@link #detectCompleteManeuverCurves()}. - * - * @param maneuvers - * The maneuvers to parse into complete maneuver curves - * @return an empty list if provided maneuvers list is empty, otherwise the list with complete maneuver curves - * derived from provided maneuvers. - * @see CompleteManeuverCurve - * @see Maneuver - */ - List getCompleteManeuverCurves(Iterable maneuvers); - - /** - * Converts provided {@link CompleteManeuverCurve}-instances into - * {@link CompleteManeuverCurveWithEstimationData}-instances. For this, additional information to - * {@code maneuverCurves} is computed. This computation is regarded as complex as the computation within - * {@link #detectManeuvers()}. - */ - List getCompleteManeuverCurvesWithEstimationData( - Iterable maneuverCurves); - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetectorWithEstimationDataSupport.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetectorWithEstimationDataSupport.java new file mode 100644 index 00000000000..3724d130b25 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/ManeuverDetectorWithEstimationDataSupport.java @@ -0,0 +1,61 @@ +package com.sap.sailing.domain.maneuverdetection; + +import java.util.List; + +import com.sap.sailing.domain.tracking.CompleteManeuverCurve; +import com.sap.sailing.domain.tracking.Maneuver; + +/** + * A maneuver detector which additional support for management of estimation data for wind estimation. + * + * @author Vladislav Chumak (D069712) + * @see ManeuverDetector + * + */ +public interface ManeuverDetectorWithEstimationDataSupport extends ManeuverDetector { + /** + * Derives maneuvers from the provided {@code maneuverCurves}. Since the provided complete maneuver curves already + * include the calculated boundaries of each complete maneuvering spot, this method operates in a very + * performance-efficient manner. + * + * @param maneuverCurves + * The maneuver curves from which the maneuvers shall be derived + * @return The maneuvers derived from the provided maneuver curves. The list gets empty, if the provided maneuver + * curves list is also empty. + */ + List detectManeuvers(Iterable maneuverCurves); + + /** + * Detects the complete maneuver curves performed within a GPS-track of the competitor associated with this + * {@link ManeuverDetector}-instance. In contrast to maneuvers determined by {@link #detectManeuvers()}, the + * complete maneuver curves are not subject to any splitting logic for maneuvers with multiple "tacking" and + * "jibing". See {@link ManeuverDetector} description for more info regarding the detection strategy. + * + * @return an empty list if no maneuver spots were detected, otherwise the list with detected maneuver curves. + * @see CompleteManeuverCurve + * @see ManeuverDetector + */ + List detectCompleteManeuverCurves(); + + /** + * Parses {@link CompleteManeuverCurve}-instances from provided {@link Maneuver}-instances. This method performs + * significantly faster than {@link #detectCompleteManeuverCurves()}. + * + * @param maneuvers + * The maneuvers to parse into complete maneuver curves + * @return an empty list if provided maneuvers list is empty, otherwise the list with complete maneuver curves + * derived from provided maneuvers. + * @see CompleteManeuverCurve + * @see Maneuver + */ + List getCompleteManeuverCurves(Iterable maneuvers); + + /** + * Converts provided {@link CompleteManeuverCurve}-instances into + * {@link CompleteManeuverCurveWithEstimationData}-instances. For this, additional information to + * {@code maneuverCurves} is computed. This computation is regarded as complex as the computation within + * {@link #detectManeuvers()}. + */ + List getCompleteManeuverCurvesWithEstimationData( + Iterable maneuverCurves); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/AbstractManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/AbstractManeuverDetectorImpl.java new file mode 100644 index 00000000000..a5d63b7e908 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/AbstractManeuverDetectorImpl.java @@ -0,0 +1,155 @@ +package com.sap.sailing.domain.maneuverdetection.impl; + +import java.util.NavigableSet; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.BearingChangeAnalyzer; +import com.sap.sailing.domain.common.NauticalSide; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; +import com.sap.sailing.domain.tracking.GPSFixTrack; +import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; + +public abstract class AbstractManeuverDetectorImpl implements ManeuverDetector { + + /** + * Tracked race whose tracks are being processed for maneuver detection. + */ + protected final TrackedRace trackedRace; + + /** + * The competitor, whose maneuvers are being discovered + */ + protected final Competitor competitor; + + /** + * The track of competitor + */ + protected final GPSFixTrack track; + + /** + * Constructs maneuver detector which is supposed to be used for maneuver detection within the provided tracked race + * for provided competitor. + * + * @param trackedRace + * The tracked race whose maneuvers are supposed to be detected + * @param competitor + * The competitor, whose maneuvers shall be discovered + */ + public AbstractManeuverDetectorImpl(TrackedRace trackedRace, Competitor competitor) { + this.trackedRace = trackedRace; + this.competitor = competitor; + this.track = trackedRace != null ? trackedRace.getTrack(competitor) : null; + } + + /** + * Gets track's start time point, end time point and the time point of last raw fix. + * + * @return {@code null} when there are no appropriate fixes contained within the analyzed track + */ + public TrackTimeInfo getTrackTimeInfo() { + NavigableSet markPassings = trackedRace.getMarkPassings(competitor); + TimePoint earliestTrackRecord = null; + TimePoint latestRawFixTimePoint = null; + MarkPassing crossedFinishLine = null; + // getLastWaypoint() will wait for a read lock on the course; do this outside the synchronized block to avoid + // deadlocks + final Waypoint lastWaypoint = trackedRace.getRace().getCourse().getLastWaypoint(); + if (lastWaypoint != null) { + trackedRace.lockForRead(markPassings); + try { + if (markPassings != null && !markPassings.isEmpty()) { + earliestTrackRecord = markPassings.iterator().next().getTimePoint(); + crossedFinishLine = trackedRace.getMarkPassing(competitor, lastWaypoint); + } + } finally { + trackedRace.unlockAfterRead(markPassings); + } + } + if (earliestTrackRecord == null) { + GPSFixMoving firstRawFix = track.getFirstRawFix(); + if (firstRawFix != null) { + earliestTrackRecord = firstRawFix.getTimePoint(); + } + } + if (earliestTrackRecord != null) { + TimePoint latestTrackRecord; + if (crossedFinishLine != null) { + latestTrackRecord = crossedFinishLine.getTimePoint(); + } else { + final GPSFixMoving lastRawFix = track.getLastRawFix(); + if (lastRawFix != null) { + latestTrackRecord = lastRawFix.getTimePoint(); + latestRawFixTimePoint = latestTrackRecord; + } else { + latestTrackRecord = null; + } + } + if (latestTrackRecord != null) { + if (latestRawFixTimePoint == null) { + final GPSFixMoving lastRawFix = track.getLastRawFix(); + if (lastRawFix != null) { + latestRawFixTimePoint = lastRawFix.getTimePoint(); + } + } + if (latestRawFixTimePoint != null) { + if (!earliestTrackRecord.equals(latestTrackRecord)) { + return new TrackTimeInfo(earliestTrackRecord, latestTrackRecord, latestRawFixTimePoint); + } + GPSFixMoving firstRawFix = track.getFirstRawFix(); + if (firstRawFix != null) { + return new TrackTimeInfo(firstRawFix.getTimePoint(), latestRawFixTimePoint, + latestRawFixTimePoint); + } + } + } + } + return null; + } + + /** + * Gets the number of cases, when the boats bow was headed through the wind coming from behind. + */ + protected int getNumberOfJibes(ManeuverCurveBoundaries maneuverBoundaries, Wind wind) { + BearingChangeAnalyzer bearingChangeAnalyzer = BearingChangeAnalyzer.INSTANCE; + int numberOfJibes = wind == null ? 0 + : bearingChangeAnalyzer.didPass(maneuverBoundaries.getSpeedWithBearingBefore().getBearing(), + maneuverBoundaries.getDirectionChangeInDegrees(), + maneuverBoundaries.getSpeedWithBearingAfter().getBearing(), wind.getBearing()); + return numberOfJibes; + } + + /** + * Gets the number of cases, when the boats bow was headed through the wind coming from the front. + */ + protected int getNumberOfTacks(ManeuverCurveBoundaries maneuverBoundaries, Wind wind) { + BearingChangeAnalyzer bearingChangeAnalyzer = BearingChangeAnalyzer.INSTANCE; + int numberOfTacks = wind == null ? 0 + : bearingChangeAnalyzer.didPass(maneuverBoundaries.getSpeedWithBearingBefore().getBearing(), + maneuverBoundaries.getDirectionChangeInDegrees(), + maneuverBoundaries.getSpeedWithBearingAfter().getBearing(), wind.getFrom()); + return numberOfTacks; + } + + /** + * Maps the provided {@code courseChangeInDegrees} from {@link Bearing} to {@link NauticalSide}. + */ + protected NauticalSide getDirectionOfCourseChange(double courseChangeInDegrees) { + return courseChangeInDegrees < 0 ? NauticalSide.PORT : NauticalSide.STARBOARD; + } + + /** + * Gets the approximated duration of the maneuver main curve considering the boat class of the competitor. + */ + protected Duration getApproximateManeuverDuration() { + return trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass().getApproximateManeuverDuration(); + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java new file mode 100644 index 00000000000..7396d284474 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java @@ -0,0 +1,156 @@ +package com.sap.sailing.domain.maneuverdetection.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.CourseChange; +import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.maneuverdetection.ApproximatedFixesCalculator; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; +import com.sap.sailing.domain.tracking.Maneuver; +import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.impl.ManeuverCurveBoundariesImpl; +import com.sap.sailing.domain.tracking.impl.ManeuverWithCoarseGrainedBoundariesImpl; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; + +/** + * Maneuver detector implementation for GPS tracks with extremely low sampling rate such as 1 fix per 30 seconds. + * + * @author Vladislav Chumak (D069712) + * + */ +public class LowGPSSamplingRateManeuverDetectorImpl extends AbstractManeuverDetectorImpl implements ManeuverDetector { + + public LowGPSSamplingRateManeuverDetectorImpl(TrackedRace trackedRace, Competitor competitor) { + super(trackedRace, competitor); + } + + /** + * Tries to detect maneuvers on the competitor's track based on a number of approximating fixes. The + * fixes contain bearing information, but this is not the bearing leading to the next approximation fix but the + * bearing the boat had at the time of the approximating fix which is taken from the original track. + *

          + * + * The time period assumed for a maneuver duration is taken from the + * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() boat class}. If no maneuver is detected, an empty + * list is returned. Maneuvers can only be expected to be detected if at least three fixes are provided in + * approximatedFixesToAnalyze. For the inner approximating fixes (all except the first and the last + * approximating fix), their course changes according to the approximated path (and not the underlying actual + * tracked fixes) are computed. Subsequent course changes to the same direction are then grouped. Those in closer + * timely distance than {@link #getApproximateManeuverDurationInMilliseconds()} (including single course changes + * that have no surrounding other course changes to group) are grouped into one {@link Maneuver}. + * + * @param ignoreMarkPassings + * When true, no {@link ManeuverType#MARK_PASSING} maneuvers will be identified, and the + * fact that a mark passing would split up what else may be a penalty circle is ignored. This is helpful + * for recursive calls, e.g., after identifying a tack and a jibe around a mark passing and trying to + * identify for the time before and after the mark passing which maneuvers exist on which side of the + * passing. + * @param earliestManeuverStart + * maneuver start will not be before this time point; if a maneuver is found whose time point is at or + * after this time point, no matter how close it is, its start regarding speed and course into the + * maneuver and the leg before the maneuver is not taken from an earlier time point, even if half the + * maneuver duration before the maneuver time point were before this time point. + * @param latestManeuverEnd + * maneuver end will not be after this time point; if a maneuver is found whose time point is at or + * before this time point, no matter how close it is, its end regarding speed and course out of the + * maneuver and the leg after the maneuver is not taken from a later time point, even if half the + * maneuver duration after the maneuver time point were after this time point. + * + * @return an empty list if no maneuver is detected for competitor between from and + * to, or else the list of maneuvers detected. + */ + @Override + public List detectManeuvers() { + List result = new ArrayList<>(); + TrackTimeInfo startAndEndTimePoints = getTrackTimeInfo(); + if (startAndEndTimePoints != null) { + ApproximatedFixesCalculator approximatedFixesCalculator = new ApproximatedFixesCalculatorImpl(trackedRace, + competitor); + Iterable approximatedFixes = approximatedFixesCalculator.approximate( + startAndEndTimePoints.getTrackStartTimePoint(), startAndEndTimePoints.getTrackEndTimePoint()); + if (Util.size(approximatedFixes) > 2) { + Iterator approximationPointsIter = approximatedFixes.iterator(); + GPSFixMoving previous = approximationPointsIter.next(); + GPSFixMoving current = approximationPointsIter.next(); + // the bearings in these variables are between approximation points + do { + GPSFixMoving next = approximationPointsIter.next(); + SpeedWithBearing speedWithBearingOnApproximationFromPreviousToCurrent = previous + .getSpeedAndBearingRequiredToReach(current); + SpeedWithBearing speedWithBearingOnApproximationFromCurrentToNext = current + .getSpeedAndBearingRequiredToReach(next); + CourseChange courseChange = speedWithBearingOnApproximationFromPreviousToCurrent + .getCourseChangeRequiredToReach(speedWithBearingOnApproximationFromCurrentToNext); + speedWithBearingOnApproximationFromPreviousToCurrent = speedWithBearingOnApproximationFromCurrentToNext; + Maneuver maneuver = createManeuverFromGroupOfCourseChanges(competitor, + speedWithBearingOnApproximationFromPreviousToCurrent, current, + speedWithBearingOnApproximationFromCurrentToNext, courseChange.getCourseChangeInDegrees()); + result.add(maneuver); + previous = current; + current = next; + } while (approximationPointsIter.hasNext()); + } + } + return result; + } + + private Maneuver createManeuverFromGroupOfCourseChanges(Competitor competitor, + SpeedWithBearing speedWithBearingOnApproximationAtBeginning, GPSFixMoving currentFix, + SpeedWithBearing speedWithBearingOnApproximationAtEnd, double totalCourseChangeInDegrees) { + TimePoint maneuverTimePoint = currentFix.getTimePoint(); + Position maneuverPosition = currentFix.getPosition(); + final Wind wind = trackedRace.getWind(maneuverPosition, maneuverTimePoint); + Tack tackAfterManeuver = null; + try { + tackAfterManeuver = wind == null ? null + : trackedRace.getTack(maneuverPosition, maneuverTimePoint, + speedWithBearingOnApproximationAtEnd.getBearing()); + } catch (NoWindException e) { + } + ManeuverType maneuverType; + ManeuverCurveBoundaries maneuverCurve = new ManeuverCurveBoundariesImpl( + maneuverTimePoint.minus(getApproximateManeuverDuration().divide(2)), + maneuverTimePoint.plus(getApproximateManeuverDuration().times(3.0)), + speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd, + totalCourseChangeInDegrees, + speedWithBearingOnApproximationAtBeginning.compareTo(speedWithBearingOnApproximationAtBeginning) < 0 + ? speedWithBearingOnApproximationAtBeginning : speedWithBearingOnApproximationAtEnd); + + if (wind != null) { + if (getNumberOfTacks(maneuverCurve, wind) > 0) { + maneuverType = ManeuverType.TACK; + } else if (getNumberOfJibes(maneuverCurve, wind) > 0) { + maneuverType = ManeuverType.JIBE; + } else { + // heading up or bearing away + Bearing windBearing = wind.getBearing(); + Bearing toWindBeforeManeuver = windBearing + .getDifferenceTo(speedWithBearingOnApproximationAtBeginning.getBearing()); + Bearing toWindAfterManeuver = windBearing + .getDifferenceTo(speedWithBearingOnApproximationAtEnd.getBearing()); + maneuverType = Math.abs(toWindBeforeManeuver.getDegrees()) < Math.abs(toWindAfterManeuver.getDegrees()) + ? ManeuverType.HEAD_UP : ManeuverType.BEAR_AWAY; + } + } else { + // no wind information; marking as UNKNOWN + maneuverType = ManeuverType.UNKNOWN; + } + Maneuver maneuver = new ManeuverWithCoarseGrainedBoundariesImpl(maneuverType, tackAfterManeuver, + maneuverPosition, maneuverTimePoint, maneuverCurve); + return maneuver; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 6572f25f378..e7d32f9abb1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -5,10 +5,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.NavigableSet; import java.util.function.Predicate; import java.util.logging.Logger; -import java.util.stream.Collectors; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; @@ -21,12 +19,9 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Wind; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; -import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData; +import com.sap.sailing.domain.maneuverdetection.ApproximatedFixesCalculator; import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; -import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; @@ -56,7 +51,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; * @see ManeuverDetector * */ -public class ManeuverDetectorImpl implements ManeuverDetector { +public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { private static final Logger logger = Logger.getLogger(ManeuverDetectorImpl.class.getName()); @@ -72,28 +67,11 @@ public class ManeuverDetectorImpl implements ManeuverDetector { */ private static final double MIN_ANGULAR_VELOCITY_FOR_MAIN_CURVE_BOUNDARIES_IN_DEGREES_PER_SECOND = 0.2; - /** - * Tracked race whose tracks are being processed for maneuver detection. - */ - protected final TrackedRace trackedRace; - - /** - * The competitor, whose maneuvers are being discovered - */ - protected final Competitor competitor; - - /** - * The track of competitor - */ - protected final GPSFixTrack track; - /** * Constructor for unit tests only. */ public ManeuverDetectorImpl() { - trackedRace = null; - competitor = null; - track = null; + super(null, null); } /** @@ -106,9 +84,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * The competitor, whose maneuvers shall be discovered */ public ManeuverDetectorImpl(TrackedRace trackedRace, Competitor competitor) { - this.trackedRace = trackedRace; - this.competitor = competitor; - this.track = trackedRace.getTrack(competitor); + super(trackedRace, competitor); } @Override @@ -116,355 +92,6 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return getAllManeuversFromManeuverSpots(detectManeuverSpots()); } - @Override - public List detectManeuvers(Iterable maneuverCurves) { - List maneuvers = new ArrayList<>(); - for (CompleteManeuverCurve maneuverCurve : maneuverCurves) { - TimePoint maneuverTimePoint = maneuverCurve.getMainCurveBoundaries().getTimePoint(); - Position maneuverPosition = track.getEstimatedPosition(maneuverTimePoint, /* extrapolate */false); - Wind wind = trackedRace.getWind(maneuverPosition, maneuverTimePoint); - maneuvers.addAll(determineManeuversFromManeuverCurve(maneuverCurve.getMainCurveBoundaries(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries(), wind, - maneuverCurve.getMarkPassing())); - } - return maneuvers; - } - - @Override - public List detectCompleteManeuverCurves() { - List maneuverSpots = detectManeuverSpots(); - return maneuverSpots.stream().filter(maneuverSpot -> maneuverSpot.getManeuverCurve() != null) - .map(maneuverSpot -> maneuverSpot.getManeuverCurve()).collect(Collectors.toList()); - } - - @Override - public List getCompleteManeuverCurves(Iterable maneuvers) { - List result = new ArrayList<>(); - CompleteManeuverCurve curveToAdd = null; - boolean previousManeuverCouldBelongToSameCurve = false; - Maneuver previousManeuver = null; - for (Maneuver maneuver : maneuvers) { - boolean maneuverCouldBelongToSameCurve = maneuver.getType() == ManeuverType.PENALTY_CIRCLE - || maneuver.isMarkPassing() - && (maneuver.getType() == ManeuverType.TACK || maneuver.getType() == ManeuverType.JIBE); - if (previousManeuverCouldBelongToSameCurve && maneuverCouldBelongToSameCurve - && previousManeuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter() - .equals(maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()) - && previousManeuver.getToSide() == maneuver.getToSide()) { - curveToAdd = extendCompleteManeuverCurveWithManeuver(curveToAdd, maneuver); - } else { - if (curveToAdd != null) { - result.add(curveToAdd); - } - curveToAdd = convertManeuverToCompleteManeuverCurve(maneuver); - } - previousManeuver = maneuver; - previousManeuverCouldBelongToSameCurve = maneuverCouldBelongToSameCurve; - } - if (curveToAdd != null) { - result.add(curveToAdd); - } - return result; - } - - /** - * Converts the provided maneuver into {@link CompleteManeuverCurve}. The boundaries of provided maneuver are reused - * for the resulting complete maneuver curve. - * - * @see CompleteManeuverCurve - * @see Maneuver - */ - private CompleteManeuverCurve convertManeuverToCompleteManeuverCurve(Maneuver maneuver) { - ManeuverMainCurveDetailsWithBearingSteps mainCurveBoundaries = new ManeuverMainCurveDetailsWithBearingSteps( - maneuver.getMainCurveBoundaries().getTimePointBefore(), - maneuver.getMainCurveBoundaries().getTimePointAfter(), maneuver.getTimePoint(), - maneuver.getMainCurveBoundaries().getSpeedWithBearingBefore(), - maneuver.getMainCurveBoundaries().getSpeedWithBearingAfter(), - maneuver.getMainCurveBoundaries().getDirectionChangeInDegrees(), - maneuver.getMaxTurningRateInDegreesPerSecond(), maneuver.getMainCurveBoundaries().getLowestSpeed(), - getSpeedWithBearingSteps(maneuver.getMainCurveBoundaries().getTimePointBefore(), - maneuver.getMainCurveBoundaries().getTimePointAfter())); - return new CompleteManeuverCurveImpl(mainCurveBoundaries, - maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries(), maneuver.getMarkPassing()); - } - - /** - * Extends the end of provided maneuver curve with the end of provided maneuver. For this, the curve boundaries with - * unstable course and speed are merged by appending, whereas the maneuver main curve gets recalculated completely - * from scratch. The additional attributes such as, direction change and lowest speed get adjusted accordingly. - */ - private CompleteManeuverCurve extendCompleteManeuverCurveWithManeuver(CompleteManeuverCurve maneuverCurve, - Maneuver maneuver) { - ManeuverMainCurveDetailsWithBearingSteps mainCurveDetails = computeManeuverMainCurveDetails( - maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), - maneuver.getMainCurveBoundaries().getTimePointAfter(), maneuver.getToSide()); - if (mainCurveDetails == null) { - return maneuverCurve; - } - ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries = new ManeuverCurveBoundariesImpl( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), - maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore(), - maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees() - + maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed() - .compareTo(maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed()) > 0 - ? maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed() - : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed()); - return new CompleteManeuverCurveImpl(mainCurveDetails, maneuverCurveWithStableSpeedAndCourseBoundaries, - maneuverCurve.getMarkPassing() == null ? maneuver.getMarkPassing() : maneuverCurve.getMarkPassing()); - } - - @Override - public List getCompleteManeuverCurvesWithEstimationData( - Iterable maneuverCurves) { - List result = new ArrayList<>(); - - CompleteManeuverCurve previousManeuverCurve = null; - CompleteManeuverCurve currentManeuverCurve = null; - for (CompleteManeuverCurve nextManeuverCurve : maneuverCurves) { - if (currentManeuverCurve != null) { - CompleteManeuverCurveWithEstimationData maneuverCurveWithEstimationData = calculateCompleteManeuverCurveWithEstimationData( - currentManeuverCurve, previousManeuverCurve, nextManeuverCurve); - result.add(maneuverCurveWithEstimationData); - } - previousManeuverCurve = currentManeuverCurve; - currentManeuverCurve = nextManeuverCurve; - } - if (currentManeuverCurve != null) { - CompleteManeuverCurveWithEstimationData maneuverCurveWithEstimationData = calculateCompleteManeuverCurveWithEstimationData( - currentManeuverCurve, previousManeuverCurve, null); - result.add(maneuverCurveWithEstimationData); - } - return result; - } - - /** - * Calculates a {@link CompleteManeuverCurveWithEstimationData}-instance for the provided {@code maneuverCurve}. The - * computation of additional information required by {@link CompleteManeuverCurveWithEstimationData} is regarded as - * computationally-intensive. - */ - private CompleteManeuverCurveWithEstimationData calculateCompleteManeuverCurveWithEstimationData( - CompleteManeuverCurve maneuverCurve, CompleteManeuverCurve previousManeuverCurve, - CompleteManeuverCurve nextManeuverCurve) { - Bearing courseAtMaxTurningRate = null; - SpeedWithBearingStep stepWithLowestSpeed = null; - SpeedWithBearingStep stepWithHighestSpeed = null; - SpeedWithBearingStep stepWithMaxTurningRate = null; - SpeedWithBearingStep previousStep = null; - for (SpeedWithBearingStep step : maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingSteps()) { - if (stepWithLowestSpeed == null - || stepWithLowestSpeed.getSpeedWithBearing().compareTo(step.getSpeedWithBearing()) > 0) { - stepWithLowestSpeed = step; - } - if (stepWithHighestSpeed == null - || stepWithHighestSpeed.getSpeedWithBearing().compareTo(step.getSpeedWithBearing()) < 0) { - stepWithHighestSpeed = step; - } - if (previousStep != null && (stepWithMaxTurningRate == null || stepWithMaxTurningRate - .getTurningRateInDegreesPerSecond() < step.getTurningRateInDegreesPerSecond())) { - stepWithMaxTurningRate = step; - courseAtMaxTurningRate = previousStep.getSpeedWithBearing().getBearing() - .add(new DegreeBearingImpl(step.getCourseChangeInDegrees() / 2)); - } - previousStep = step; - } - int gpsFixCountWithinMainCurve = 0; - int gpsFixCountWithinWholeCurve = 0; - int gpsFixesCountFromPreviousManeuver = 0; - int gpsFixesCountToNextManeuver = 0; - try { - track.lockForRead(); - boolean considerPreviousManeuver = previousManeuverCurve != null && previousManeuverCurve - .getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter() - .before(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); - boolean considerNextManeuver = nextManeuverCurve != null && nextManeuverCurve - .getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore() - .after(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); - for (GPSFixMoving fix : track.getFixes( - considerPreviousManeuver - ? previousManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() - .getTimePointAfter() - : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), - !considerPreviousManeuver, - considerNextManeuver - ? nextManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() - .getTimePointBefore() - : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), - !considerNextManeuver)) { - if (fix.getTimePoint().before( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore())) { - ++gpsFixesCountFromPreviousManeuver; - } else if (fix.getTimePoint().after( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter())) { - ++gpsFixesCountToNextManeuver; - } else { - if (!fix.getTimePoint().before(maneuverCurve.getMainCurveBoundaries().getTimePointBefore()) - && !fix.getTimePoint().after(maneuverCurve.getMainCurveBoundaries().getTimePointAfter())) { - ++gpsFixCountWithinMainCurve; - } - ++gpsFixCountWithinWholeCurve; - } - } - } finally { - track.unlockAfterRead(); - } - - ManeuverLoss projectedManeuverLoss = getManeuverLoss(maneuverCurve.getMainCurveBoundaries()); - Distance distanceSailedIfNotManeuvering = maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingBefore() - .travel(maneuverCurve.getMainCurveBoundaries().getDuration()); - Distance distanceSailedWithinManeuver = track.getDistanceTraveled( - maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), - maneuverCurve.getMainCurveBoundaries().getTimePointAfter()); - Duration longestGpsFixIntervalBetweenTwoFixes = track.getLongestIntervalBetweenTwoFixes( - maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), - maneuverCurve.getMainCurveBoundaries().getTimePointAfter()); - ManeuverMainCurveWithEstimationData mainCurve = new ManeuverMainCurveWithEstimationDataImpl( - maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), - maneuverCurve.getMainCurveBoundaries().getTimePointAfter(), - maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingBefore(), - maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingAfter(), - maneuverCurve.getMainCurveBoundaries().getDirectionChangeInDegrees(), - stepWithLowestSpeed.getSpeedWithBearing(), stepWithLowestSpeed.getTimePoint(), - stepWithHighestSpeed.getSpeedWithBearing(), stepWithHighestSpeed.getTimePoint(), - maneuverCurve.getMainCurveBoundaries().getTimePoint(), - maneuverCurve.getMainCurveBoundaries().getMaxTurningRateInDegreesPerSecond(), courseAtMaxTurningRate, - distanceSailedWithinManeuver, projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, - projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), - Math.abs(maneuverCurve.getMainCurveBoundaries().getDirectionChangeInDegrees()) - / maneuverCurve.getMainCurveBoundaries().getDuration().asSeconds(), - gpsFixCountWithinMainCurve, longestGpsFixIntervalBetweenTwoFixes); - projectedManeuverLoss = getManeuverLoss(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries()); - distanceSailedIfNotManeuvering = maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() - .getSpeedWithBearingBefore() - .travel(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDuration()); - distanceSailedWithinManeuver = track.getDistanceTraveled( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); - longestGpsFixIntervalBetweenTwoFixes = track.getLongestIntervalBetweenTwoFixes( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); - TrackTimeInfo trackTimeInfo = previousManeuverCurve == null || nextManeuverCurve == null ? getTrackTimeInfo() - : null; - Pair durationAndAvgSpeedWithBearingBefore = calculateDurationAndAvgSpeedWithBearingBetweenTimePoints( - previousManeuverCurve == null ? trackTimeInfo.getTrackStartTimePoint() - : previousManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() - .getTimePointAfter(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); - Pair durationAndAvgSpeedWithBearingAfter = calculateDurationAndAvgSpeedWithBearingBetweenTimePoints( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), - nextManeuverCurve == null ? trackTimeInfo.getTrackEndTimePoint() - : nextManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); - Duration intervalBetweenLastFixOfCurveAndNextFix = Duration.NULL; - GPSFixMoving lastManeuverFix = track.getLastFixAtOrBefore( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); - if (lastManeuverFix != null) { - GPSFixMoving firstFixAfterLastManeuverFix = track.getFirstFixAfter(lastManeuverFix.getTimePoint()); - if (firstFixAfterLastManeuverFix != null) { - intervalBetweenLastFixOfCurveAndNextFix = lastManeuverFix.getTimePoint() - .until(firstFixAfterLastManeuverFix.getTimePoint()); - } - } - Duration intervalBetweenFirstFixOfCurveAndPreviousFix = Duration.NULL; - GPSFixMoving firstManeuverFix = track.getFirstFixAtOrAfter( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); - if (firstManeuverFix != null) { - GPSFixMoving lastFixBeforeFirstManeuverFix = track.getLastFixBefore(firstManeuverFix.getTimePoint()); - if (lastFixBeforeFirstManeuverFix != null) { - intervalBetweenFirstFixOfCurveAndPreviousFix = lastFixBeforeFirstManeuverFix.getTimePoint() - .until(firstManeuverFix.getTimePoint()); - } - } - ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData curveWithUnstableCourseAndSpeed = new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataImpl( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees(), - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed(), - durationAndAvgSpeedWithBearingBefore.getB(), durationAndAvgSpeedWithBearingBefore.getA(), - gpsFixesCountFromPreviousManeuver, durationAndAvgSpeedWithBearingAfter.getB(), - durationAndAvgSpeedWithBearingAfter.getA(), gpsFixesCountToNextManeuver, distanceSailedWithinManeuver, - projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, - projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), gpsFixCountWithinWholeCurve, - longestGpsFixIntervalBetweenTwoFixes, intervalBetweenLastFixOfCurveAndNextFix, - intervalBetweenFirstFixOfCurveAndPreviousFix); - TimePoint maneuverTimePoint = maneuverCurve.getMainCurveBoundaries().getTimePoint(); - Position maneuverPosition = track.getEstimatedPosition(maneuverTimePoint, /* extrapolate */false); - Wind wind = trackedRace.getWind(maneuverPosition, maneuverTimePoint); - int numberOfJibes = getNumberOfJibes(mainCurve, wind); - int numberOfTacks = getNumberOfTacks(mainCurve, wind); - boolean maneuverStartsByRunningAwayFromWind = (mainCurve.getSpeedWithBearingBefore().getBearing().getDegrees() - - 180) * mainCurve.getDirectionChangeInDegrees() < 0; - Bearing relativeBearingToNextMarkPassingBeforeManeuver = getRelativeBearingToNextMark( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), maneuverCurve - .getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore().getBearing()); - Bearing relativeBearingToNextMarkPassingAfterManeuver = getRelativeBearingToNextMark( - maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), maneuverCurve - .getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter().getBearing()); - return new CompleteManeuverCurveWithEstimationDataImpl(maneuverPosition, mainCurve, - curveWithUnstableCourseAndSpeed, wind, numberOfTacks, numberOfJibes, - maneuverStartsByRunningAwayFromWind, relativeBearingToNextMarkPassingBeforeManeuver, - relativeBearingToNextMarkPassingAfterManeuver, maneuverCurve.isMarkPassing()); - } - - /** - * Calculates the duration and avg speed with avg course based on the competitor's track within the provided time - * range. - */ - private Pair calculateDurationAndAvgSpeedWithBearingBetweenTimePoints(TimePoint from, - TimePoint to) { - Duration duration = from.until(to); - Position fromPosition = track.getEstimatedPosition(from, false); - Position toPosition = track.getEstimatedPosition(to, false); - Distance distance = fromPosition.getDistance(toPosition); - Bearing bearing = fromPosition.getBearingGreatCircle(toPosition); - Speed speed = distance.inTime(Math.abs(duration.asMillis())); - SpeedWithBearing avgSpeedWithBearing = new KnotSpeedWithBearingImpl(speed.getKnots(), bearing); - return new Pair<>(duration, avgSpeedWithBearing); - } - - /** - * Gets the relative bearing of the next mark from the boat's position and course at {@code timePoint}. The relative - * bearing is calculated by absolute bearing of next mark from the boat's position minus the boat's course. - */ - private Bearing getRelativeBearingToNextMark(TimePoint timePoint, Bearing boatCourse) { - Bearing result = null; - TrackedLegOfCompetitor legAfter = trackedRace.getTrackedLeg(competitor, timePoint); - if (legAfter != null && legAfter.getLeg().getTo() != null) { - Position nextMarkPosition = trackedRace.getApproximatePosition(legAfter.getLeg().getTo(), timePoint); - Position maneuverEndPosition = track.getEstimatedPosition(timePoint, false); - Bearing absoluteBearing = maneuverEndPosition.getBearingGreatCircle(nextMarkPosition); - result = absoluteBearing.getDifferenceTo(boatCourse); - } - return result; - } - - /** - * Gets the number of cases, when the boats bow was headed through the wind coming from behind. - */ - private int getNumberOfJibes(ManeuverCurveBoundaries maneuverBoundaries, Wind wind) { - BearingChangeAnalyzer bearingChangeAnalyzer = BearingChangeAnalyzer.INSTANCE; - int numberOfJibes = wind == null ? 0 - : bearingChangeAnalyzer.didPass(maneuverBoundaries.getSpeedWithBearingBefore().getBearing(), - maneuverBoundaries.getDirectionChangeInDegrees(), - maneuverBoundaries.getSpeedWithBearingAfter().getBearing(), wind.getBearing()); - return numberOfJibes; - } - - /** - * Gets the number of cases, when the boats bow was headed through the wind coming from the front. - */ - private int getNumberOfTacks(ManeuverCurveBoundaries maneuverBoundaries, Wind wind) { - BearingChangeAnalyzer bearingChangeAnalyzer = BearingChangeAnalyzer.INSTANCE; - int numberOfTacks = wind == null ? 0 - : bearingChangeAnalyzer.didPass(maneuverBoundaries.getSpeedWithBearingBefore().getBearing(), - maneuverBoundaries.getDirectionChangeInDegrees(), - maneuverBoundaries.getSpeedWithBearingAfter().getBearing(), wind.getFrom()); - return numberOfTacks; - } - /** * Detects maneuver spots performed within a GPS-track of the competitor associated with this * {@link ManeuverDetector}-instance. See {@link ManeuverDetector} description for more info regarding the detection @@ -483,70 +110,6 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return Collections.emptyList(); } - /** - * Gets track's start time point, end time point and the time point of last raw fix. - * - * @return {@code null} when there are no appropriate fixes contained within the analyzed track - */ - public TrackTimeInfo getTrackTimeInfo() { - NavigableSet markPassings = trackedRace.getMarkPassings(competitor); - TimePoint earliestTrackRecord = null; - TimePoint latestRawFixTimePoint = null; - MarkPassing crossedFinishLine = null; - // getLastWaypoint() will wait for a read lock on the course; do this outside the synchronized block to avoid - // deadlocks - final Waypoint lastWaypoint = trackedRace.getRace().getCourse().getLastWaypoint(); - if (lastWaypoint != null) { - trackedRace.lockForRead(markPassings); - try { - if (markPassings != null && !markPassings.isEmpty()) { - earliestTrackRecord = markPassings.iterator().next().getTimePoint(); - crossedFinishLine = trackedRace.getMarkPassing(competitor, lastWaypoint); - } - } finally { - trackedRace.unlockAfterRead(markPassings); - } - } - if (earliestTrackRecord == null) { - GPSFixMoving firstRawFix = track.getFirstRawFix(); - if (firstRawFix != null) { - earliestTrackRecord = firstRawFix.getTimePoint(); - } - } - if (earliestTrackRecord != null) { - TimePoint latestTrackRecord; - if (crossedFinishLine != null) { - latestTrackRecord = crossedFinishLine.getTimePoint(); - } else { - final GPSFixMoving lastRawFix = track.getLastRawFix(); - if (lastRawFix != null) { - latestTrackRecord = lastRawFix.getTimePoint(); - latestRawFixTimePoint = latestTrackRecord; - } else { - latestTrackRecord = null; - } - } - if (latestTrackRecord != null) { - if (latestRawFixTimePoint == null) { - final GPSFixMoving lastRawFix = track.getLastRawFix(); - if (lastRawFix != null) { - latestRawFixTimePoint = lastRawFix.getTimePoint(); - } - } - if (latestRawFixTimePoint != null) { - if(!earliestTrackRecord.equals(latestTrackRecord)) { - return new TrackTimeInfo(earliestTrackRecord, latestTrackRecord, latestRawFixTimePoint); - } - GPSFixMoving firstRawFix = track.getFirstRawFix(); - if(firstRawFix != null) { - return new TrackTimeInfo(firstRawFix.getTimePoint(), latestRawFixTimePoint, latestRawFixTimePoint); - } - } - } - } - return null; - } - /** * Detects the maneuver spots with corresponding maneuvers within provided time frame. See step 1ff. in * {@link ManeuverDetector} description. @@ -566,12 +129,11 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * to, or else the list of maneuver spots with corresponding maneuvers detected. */ protected List detectManeuvers(TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) { - return detectManeuvers( - trackedRace.approximate(competitor, - trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass() - .getMaximumDistanceForCourseApproximation(), - earliestManeuverStart, latestManeuverEnd), - earliestManeuverStart, latestManeuverEnd); + ApproximatedFixesCalculator approximatedFixesCalculator = new ApproximatedFixesCalculatorImpl(trackedRace, + competitor); + Iterable approximatedFixes = approximatedFixesCalculator.approximate(earliestManeuverStart, + latestManeuverEnd); + return detectManeuvers(approximatedFixes, earliestManeuverStart, latestManeuverEnd); } /** @@ -621,13 +183,6 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } - /** - * Maps the provided {@code courseChangeInDegrees} from {@link Bearing} to {@link NauticalSide}. - */ - protected NauticalSide getDirectionOfCourseChange(double courseChangeInDegrees) { - return courseChangeInDegrees < 0 ? NauticalSide.PORT : NauticalSide.STARBOARD; - } - /** * Checks whether {@code currentFix} can be grouped together with the previous fixes in order to be regarded as a * single maneuver spot. For this, the {@code newCourseChangeDirection must match the direction of provided @@ -660,6 +215,16 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return true; } + protected List getAllManeuversFromManeuverSpots(List maneuverSpots) { + List maneuvers = new ArrayList<>(); + for (ManeuverSpot maneuverSpot : maneuverSpots) { + for (Maneuver maneuver : maneuverSpot.getManeuvers()) { + maneuvers.add(maneuver); + } + } + return maneuvers; + } + /** * Determines course change direction around the provided {@code fix} by means of * {@link #getSpeedWithBearingSteps(TimePoint, TimePoint)}. The course change analysis considers fixes within @@ -894,14 +459,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } else { Pair mainCurves = splitManeuverMainCurveByTimePoint( maneuverMainCurveDetails, firstPenaltyCircleCompletedAt); - if (mainCurves.getA() == null || mainCurves.getB() == null) { - // This should really not happen! - logger.warning( - "Maneuver detection has failed to process penalty circle maneuver correctly, because refinedPenaltyMainCurveDetails computation returned null. Race-Id: " - + trackedRace.getRace().getId() + ", Competitor: " + competitor.getName() - + ", Time point before maneuver: " - + maneuverUnstableCourseAndSpeedBoundaries.getTimePointBefore()); - } else { + if (mainCurves.getA() != null && mainCurves.getB() != null) { maneuversAlreadyAdded = true; Pair maneuverUnstableCourseAndSpeedBoundariesPair = splitManeuverCurveWithStableSpeedAndCourseByTimePoint( maneuverUnstableCourseAndSpeedBoundaries, mainCurves.getA(), mainCurves.getB(), @@ -1155,7 +713,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * distance" is compared to the competitor's actual position at that time. This distance is returned as the result * of this method. */ - private ManeuverLoss getManeuverLoss(ManeuverCurveBoundaries maneuverBoundaries) { + protected ManeuverLoss getManeuverLoss(ManeuverCurveBoundaries maneuverBoundaries) { final GPSFixTrack track = trackedRace.getTrack(competitor); SpeedWithBearing speedWhenSpeedStartedToDrop = maneuverBoundaries.getSpeedWithBearingBefore(); SpeedWithBearing speedAfterManeuver = maneuverBoundaries.getSpeedWithBearingAfter(); @@ -1192,16 +750,6 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return approximateManeuverDuration.divide(2); } - protected List getAllManeuversFromManeuverSpots(List maneuverSpots) { - List maneuvers = new ArrayList<>(); - for (ManeuverSpot maneuverSpot : maneuverSpots) { - for (Maneuver maneuver : maneuverSpot.getManeuvers()) { - maneuvers.add(maneuver); - } - } - return maneuvers; - } - /** * Starting at timePointBeforeManeuver, and assuming that the group of * approximatedFixesAndCourseChanges contains at least a tack and a jibe, finds the approximated fix's @@ -1252,6 +800,9 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } } } + if(result == null) { + System.out.println("a"); + } return result; } @@ -1271,8 +822,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * The target course change direction for the main curve to determine * @return The details of the maneuver main curve */ - private ManeuverMainCurveDetailsWithBearingSteps computeManeuverMainCurveDetails(TimePoint timePointBeforeManeuver, - TimePoint timePointAfterManeuver, NauticalSide maneuverDirection) { + protected ManeuverMainCurveDetailsWithBearingSteps computeManeuverMainCurveDetails( + TimePoint timePointBeforeManeuver, TimePoint timePointAfterManeuver, NauticalSide maneuverDirection) { SpeedWithBearingStepsIterable stepsToAnalyze = getSpeedWithBearingSteps(timePointBeforeManeuver, timePointAfterManeuver); ManeuverMainCurveDetailsWithBearingSteps maneuverMainCurveDetails = computeManeuverMainCurve(stepsToAnalyze, @@ -1285,7 +836,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * the steps, performance costy call to {@link GPSFixTrack#getSpeedWithBearingSteps(TimePoint, TimePoint, Duration)} * is made. */ - private SpeedWithBearingStepsIterable getSpeedWithBearingSteps(TimePoint timePointBeforeManeuver, + protected SpeedWithBearingStepsIterable getSpeedWithBearingSteps(TimePoint timePointBeforeManeuver, TimePoint timePointAfterManeuver) { SpeedWithBearingStepsIterable stepsToAnalyze = track.getSpeedWithBearingSteps(timePointBeforeManeuver, timePointAfterManeuver); @@ -1302,10 +853,10 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * approximate the beginning time point of the maneuver, the speed maximum is determined throughout forward in time * iteration of speed steps starting from time point of main curve beginning. From the determined speed maximum, the * iteration continues until the point, when the bearing changes occur only with a maximum of - * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is - * regarded as a stable course. The exiting time point of maneuver is approximated analogously by speed maximum - * determination throughout backward in time iteration of speed steps starting from time of main curve end, followed - * by a search for a point with stable course. + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is regarded as + * a stable course. The exiting time point of maneuver is approximated analogously by speed maximum determination + * throughout backward in time iteration of speed steps starting from time of main curve end, followed by a search + * for a point with stable course. * * @param maneuverMainCurveDetails * The details of the main curve, ideally computed by @@ -1354,8 +905,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * determined, the course changes get analyzed starting from {@code t'} until {@code (t -} * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() approx. maneuver duration}{@code )} in order to * locate the point where the bearing starts to change with a rate of maximal - * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is - * regarded as a stable course. + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is regarded as + * a stable course. * * @param maneuverMainCurveDetails * The details of the main curve, ideally computed by @@ -1423,8 +974,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { * determined, the course changes get analyzed starting from {@code t'} until {@code (t +} * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() approx. maneuver duration} {@code * 3)} in order * to locate the point where the bearing starts to change with a rate of maximal - * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is - * regarded as a stable course. + * {@value #MAX_TURNING_RATE_IN_DEG_PER_SECOND_FOR_STABLE_COURSE_ANALYSIS} degrees per second, which is regarded as + * a stable course. * * @param maneuverMainCurveDetails * The details of the main curve, ideally computed by @@ -1776,11 +1327,4 @@ public class ManeuverDetectorImpl implements ManeuverDetector { return new SpeedWithBearingStepsIterable(maneuverBearingSteps); } - /** - * Gets the approximated duration of the maneuver main curve considering the boat class of the competitor. - */ - protected Duration getApproximateManeuverDuration() { - return trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass().getApproximateManeuverDuration(); - } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java new file mode 100644 index 00000000000..689df4a4736 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java @@ -0,0 +1,404 @@ +package com.sap.sailing.domain.maneuverdetection.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +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.SpeedWithBearing; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; +import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; +import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; +import com.sap.sailing.domain.tracking.CompleteManeuverCurve; +import com.sap.sailing.domain.tracking.Maneuver; +import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.SpeedWithBearingStep; +import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; +import com.sap.sailing.domain.tracking.impl.CompleteManeuverCurveImpl; +import com.sap.sailing.domain.tracking.impl.ManeuverCurveBoundariesImpl; +import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; +import com.sap.sse.common.Duration; +import com.sap.sse.common.Speed; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util.Pair; +import com.sap.sse.common.impl.DegreeBearingImpl; + +/** + * A decorator which adds support for management of estimation data for wind estimation to an existing maneuver detector + * implementation. + * + * @author Vladislav Chumak (D069712) + * @see ManeuverDetector + * + */ +public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl + implements ManeuverDetectorWithEstimationDataSupport { + + private ManeuverDetectorImpl maneuverDetector; + + public ManeuverDetectorWithEstimationDataSupportDecoratorImpl(ManeuverDetectorImpl maneuverDetector) { + this.maneuverDetector = maneuverDetector; + } + + @Override + public List detectManeuvers() { + return maneuverDetector.detectManeuvers(); + } + + @Override + public List detectManeuvers(Iterable maneuverCurves) { + List maneuvers = new ArrayList<>(); + for (CompleteManeuverCurve maneuverCurve : maneuverCurves) { + TimePoint maneuverTimePoint = maneuverCurve.getMainCurveBoundaries().getTimePoint(); + Position maneuverPosition = maneuverDetector.track.getEstimatedPosition(maneuverTimePoint, + /* extrapolate */false); + Wind wind = maneuverDetector.trackedRace.getWind(maneuverPosition, maneuverTimePoint); + maneuvers + .addAll(maneuverDetector.determineManeuversFromManeuverCurve(maneuverCurve.getMainCurveBoundaries(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries(), wind, + maneuverCurve.getMarkPassing())); + } + return maneuvers; + } + + @Override + public List detectCompleteManeuverCurves() { + List maneuverSpots = maneuverDetector.detectManeuverSpots(); + return maneuverSpots.stream().filter(maneuverSpot -> maneuverSpot.getManeuverCurve() != null) + .map(maneuverSpot -> maneuverSpot.getManeuverCurve()).collect(Collectors.toList()); + } + + @Override + public List getCompleteManeuverCurves(Iterable maneuvers) { + List result = new ArrayList<>(); + CompleteManeuverCurve curveToAdd = null; + boolean previousManeuverCouldBelongToSameCurve = false; + Maneuver previousManeuver = null; + for (Maneuver maneuver : maneuvers) { + boolean maneuverCouldBelongToSameCurve = maneuver.getType() == ManeuverType.PENALTY_CIRCLE + || maneuver.isMarkPassing() + && (maneuver.getType() == ManeuverType.TACK || maneuver.getType() == ManeuverType.JIBE); + if (previousManeuverCouldBelongToSameCurve && maneuverCouldBelongToSameCurve + && previousManeuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter() + .equals(maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()) + && previousManeuver.getToSide() == maneuver.getToSide()) { + curveToAdd = extendCompleteManeuverCurveWithManeuver(curveToAdd, maneuver); + } else { + if (curveToAdd != null) { + result.add(curveToAdd); + } + curveToAdd = convertManeuverToCompleteManeuverCurve(maneuver); + } + previousManeuver = maneuver; + previousManeuverCouldBelongToSameCurve = maneuverCouldBelongToSameCurve; + } + if (curveToAdd != null) { + result.add(curveToAdd); + } + return result; + } + + /** + * Converts the provided maneuver into {@link CompleteManeuverCurve}. The boundaries of provided maneuver are reused + * for the resulting complete maneuver curve. + * + * @see CompleteManeuverCurve + * @see Maneuver + */ + private CompleteManeuverCurve convertManeuverToCompleteManeuverCurve(Maneuver maneuver) { + ManeuverMainCurveDetailsWithBearingSteps mainCurveBoundaries = new ManeuverMainCurveDetailsWithBearingSteps( + maneuver.getMainCurveBoundaries().getTimePointBefore(), + maneuver.getMainCurveBoundaries().getTimePointAfter(), maneuver.getTimePoint(), + maneuver.getMainCurveBoundaries().getSpeedWithBearingBefore(), + maneuver.getMainCurveBoundaries().getSpeedWithBearingAfter(), + maneuver.getMainCurveBoundaries().getDirectionChangeInDegrees(), + maneuver.getMaxTurningRateInDegreesPerSecond(), maneuver.getMainCurveBoundaries().getLowestSpeed(), + maneuverDetector.getSpeedWithBearingSteps(maneuver.getMainCurveBoundaries().getTimePointBefore(), + maneuver.getMainCurveBoundaries().getTimePointAfter())); + return new CompleteManeuverCurveImpl(mainCurveBoundaries, + maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries(), maneuver.getMarkPassing()); + } + + /** + * Extends the end of provided maneuver curve with the end of provided maneuver. For this, the curve boundaries with + * unstable course and speed are merged by appending, whereas the maneuver main curve gets recalculated completely + * from scratch. The additional attributes such as, direction change and lowest speed get adjusted accordingly. + */ + private CompleteManeuverCurve extendCompleteManeuverCurveWithManeuver(CompleteManeuverCurve maneuverCurve, + Maneuver maneuver) { + ManeuverMainCurveDetailsWithBearingSteps mainCurveDetails = maneuverDetector.computeManeuverMainCurveDetails( + maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), + maneuver.getMainCurveBoundaries().getTimePointAfter(), maneuver.getToSide()); + if (mainCurveDetails == null) { + return maneuverCurve; + } + ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries = new ManeuverCurveBoundariesImpl( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), + maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore(), + maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees() + + maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed() + .compareTo(maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed()) > 0 + ? maneuver.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed() + : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed()); + return new CompleteManeuverCurveImpl(mainCurveDetails, maneuverCurveWithStableSpeedAndCourseBoundaries, + maneuverCurve.getMarkPassing() == null ? maneuver.getMarkPassing() : maneuverCurve.getMarkPassing()); + } + + @Override + public List getCompleteManeuverCurvesWithEstimationData( + Iterable maneuverCurves) { + List result = new ArrayList<>(); + + CompleteManeuverCurve previousManeuverCurve = null; + CompleteManeuverCurve currentManeuverCurve = null; + for (CompleteManeuverCurve nextManeuverCurve : maneuverCurves) { + if (currentManeuverCurve != null) { + CompleteManeuverCurveWithEstimationData maneuverCurveWithEstimationData = calculateCompleteManeuverCurveWithEstimationData( + currentManeuverCurve, previousManeuverCurve, nextManeuverCurve); + result.add(maneuverCurveWithEstimationData); + } + previousManeuverCurve = currentManeuverCurve; + currentManeuverCurve = nextManeuverCurve; + } + if (currentManeuverCurve != null) { + CompleteManeuverCurveWithEstimationData maneuverCurveWithEstimationData = calculateCompleteManeuverCurveWithEstimationData( + currentManeuverCurve, previousManeuverCurve, null); + result.add(maneuverCurveWithEstimationData); + } + return result; + } + + /** + * Calculates a {@link CompleteManeuverCurveWithEstimationData}-instance for the provided {@code maneuverCurve}. The + * computation of additional information required by {@link CompleteManeuverCurveWithEstimationData} is regarded as + * computationally-intensive. + */ + private CompleteManeuverCurveWithEstimationData calculateCompleteManeuverCurveWithEstimationData( + CompleteManeuverCurve maneuverCurve, CompleteManeuverCurve previousManeuverCurve, + CompleteManeuverCurve nextManeuverCurve) { + Bearing courseAtMaxTurningRate = null; + SpeedWithBearingStep stepWithLowestSpeed = null; + SpeedWithBearingStep stepWithHighestSpeed = null; + SpeedWithBearingStep stepWithMaxTurningRate = null; + SpeedWithBearingStep previousStep = null; + for (SpeedWithBearingStep step : maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingSteps()) { + if (stepWithLowestSpeed == null + || stepWithLowestSpeed.getSpeedWithBearing().compareTo(step.getSpeedWithBearing()) > 0) { + stepWithLowestSpeed = step; + } + if (stepWithHighestSpeed == null + || stepWithHighestSpeed.getSpeedWithBearing().compareTo(step.getSpeedWithBearing()) < 0) { + stepWithHighestSpeed = step; + } + if (previousStep != null && (stepWithMaxTurningRate == null || stepWithMaxTurningRate + .getTurningRateInDegreesPerSecond() < step.getTurningRateInDegreesPerSecond())) { + stepWithMaxTurningRate = step; + courseAtMaxTurningRate = previousStep.getSpeedWithBearing().getBearing() + .add(new DegreeBearingImpl(step.getCourseChangeInDegrees() / 2)); + } + previousStep = step; + } + int gpsFixCountWithinMainCurve = 0; + int gpsFixCountWithinWholeCurve = 0; + int gpsFixesCountFromPreviousManeuver = 0; + int gpsFixesCountToNextManeuver = 0; + try { + maneuverDetector.track.lockForRead(); + boolean considerPreviousManeuver = previousManeuverCurve != null && previousManeuverCurve + .getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter() + .before(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); + boolean considerNextManeuver = nextManeuverCurve != null && nextManeuverCurve + .getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore() + .after(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); + for (GPSFixMoving fix : maneuverDetector.track.getFixes( + considerPreviousManeuver + ? previousManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() + .getTimePointAfter() + : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), + !considerPreviousManeuver, + considerNextManeuver + ? nextManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() + .getTimePointBefore() + : maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), + !considerNextManeuver)) { + if (fix.getTimePoint().before( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore())) { + ++gpsFixesCountFromPreviousManeuver; + } else if (fix.getTimePoint().after( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter())) { + ++gpsFixesCountToNextManeuver; + } else { + if (!fix.getTimePoint().before(maneuverCurve.getMainCurveBoundaries().getTimePointBefore()) + && !fix.getTimePoint().after(maneuverCurve.getMainCurveBoundaries().getTimePointAfter())) { + ++gpsFixCountWithinMainCurve; + } + ++gpsFixCountWithinWholeCurve; + } + } + } finally { + maneuverDetector.track.unlockAfterRead(); + } + + ManeuverLoss projectedManeuverLoss = maneuverDetector.getManeuverLoss(maneuverCurve.getMainCurveBoundaries()); + Distance distanceSailedIfNotManeuvering = maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingBefore() + .travel(maneuverCurve.getMainCurveBoundaries().getDuration()); + Distance distanceSailedWithinManeuver = maneuverDetector.track.getDistanceTraveled( + maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), + maneuverCurve.getMainCurveBoundaries().getTimePointAfter()); + Duration longestGpsFixIntervalBetweenTwoFixes = maneuverDetector.track.getLongestIntervalBetweenTwoFixes( + maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), + maneuverCurve.getMainCurveBoundaries().getTimePointAfter()); + ManeuverMainCurveWithEstimationData mainCurve = new ManeuverMainCurveWithEstimationDataImpl( + maneuverCurve.getMainCurveBoundaries().getTimePointBefore(), + maneuverCurve.getMainCurveBoundaries().getTimePointAfter(), + maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingBefore(), + maneuverCurve.getMainCurveBoundaries().getSpeedWithBearingAfter(), + maneuverCurve.getMainCurveBoundaries().getDirectionChangeInDegrees(), + stepWithLowestSpeed.getSpeedWithBearing(), stepWithLowestSpeed.getTimePoint(), + stepWithHighestSpeed.getSpeedWithBearing(), stepWithHighestSpeed.getTimePoint(), + maneuverCurve.getMainCurveBoundaries().getTimePoint(), + maneuverCurve.getMainCurveBoundaries().getMaxTurningRateInDegreesPerSecond(), courseAtMaxTurningRate, + distanceSailedWithinManeuver, projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, + projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), + Math.abs(maneuverCurve.getMainCurveBoundaries().getDirectionChangeInDegrees()) + / maneuverCurve.getMainCurveBoundaries().getDuration().asSeconds(), + gpsFixCountWithinMainCurve, longestGpsFixIntervalBetweenTwoFixes); + projectedManeuverLoss = maneuverDetector + .getManeuverLoss(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries()); + distanceSailedIfNotManeuvering = maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() + .getSpeedWithBearingBefore() + .travel(maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDuration()); + distanceSailedWithinManeuver = maneuverDetector.track.getDistanceTraveled( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); + longestGpsFixIntervalBetweenTwoFixes = maneuverDetector.track.getLongestIntervalBetweenTwoFixes( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); + TrackTimeInfo trackTimeInfo = previousManeuverCurve == null || nextManeuverCurve == null + ? maneuverDetector.getTrackTimeInfo() : null; + Pair durationAndAvgSpeedWithBearingBefore = calculateDurationAndAvgSpeedWithBearingBetweenTimePoints( + previousManeuverCurve == null ? trackTimeInfo.getTrackStartTimePoint() + : previousManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries() + .getTimePointAfter(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); + Pair durationAndAvgSpeedWithBearingAfter = calculateDurationAndAvgSpeedWithBearingBetweenTimePoints( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), + nextManeuverCurve == null ? trackTimeInfo.getTrackEndTimePoint() + : nextManeuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); + Duration intervalBetweenLastFixOfCurveAndNextFix = Duration.NULL; + GPSFixMoving lastManeuverFix = maneuverDetector.track.getLastFixAtOrBefore( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter()); + if (lastManeuverFix != null) { + GPSFixMoving firstFixAfterLastManeuverFix = maneuverDetector.track + .getFirstFixAfter(lastManeuverFix.getTimePoint()); + if (firstFixAfterLastManeuverFix != null) { + intervalBetweenLastFixOfCurveAndNextFix = lastManeuverFix.getTimePoint() + .until(firstFixAfterLastManeuverFix.getTimePoint()); + } + } + Duration intervalBetweenFirstFixOfCurveAndPreviousFix = Duration.NULL; + GPSFixMoving firstManeuverFix = maneuverDetector.track.getFirstFixAtOrAfter( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore()); + if (firstManeuverFix != null) { + GPSFixMoving lastFixBeforeFirstManeuverFix = maneuverDetector.track + .getLastFixBefore(firstManeuverFix.getTimePoint()); + if (lastFixBeforeFirstManeuverFix != null) { + intervalBetweenFirstFixOfCurveAndPreviousFix = lastFixBeforeFirstManeuverFix.getTimePoint() + .until(firstManeuverFix.getTimePoint()); + } + } + ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData curveWithUnstableCourseAndSpeed = new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataImpl( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getDirectionChangeInDegrees(), + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getLowestSpeed(), + durationAndAvgSpeedWithBearingBefore.getB(), durationAndAvgSpeedWithBearingBefore.getA(), + gpsFixesCountFromPreviousManeuver, durationAndAvgSpeedWithBearingAfter.getB(), + durationAndAvgSpeedWithBearingAfter.getA(), gpsFixesCountToNextManeuver, distanceSailedWithinManeuver, + projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, + projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), gpsFixCountWithinWholeCurve, + longestGpsFixIntervalBetweenTwoFixes, intervalBetweenLastFixOfCurveAndNextFix, + intervalBetweenFirstFixOfCurveAndPreviousFix); + TimePoint maneuverTimePoint = maneuverCurve.getMainCurveBoundaries().getTimePoint(); + Position maneuverPosition = maneuverDetector.track.getEstimatedPosition(maneuverTimePoint, + /* extrapolate */false); + Wind wind = maneuverDetector.trackedRace.getWind(maneuverPosition, maneuverTimePoint); + int numberOfJibes = maneuverDetector.getNumberOfJibes(mainCurve, wind); + int numberOfTacks = maneuverDetector.getNumberOfTacks(mainCurve, wind); + boolean maneuverStartsByRunningAwayFromWind = (mainCurve.getSpeedWithBearingBefore().getBearing().getDegrees() + - 180) * mainCurve.getDirectionChangeInDegrees() < 0; + Bearing relativeBearingToNextMarkPassingBeforeManeuver = getRelativeBearingToNextMark( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointBefore(), maneuverCurve + .getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingBefore().getBearing()); + Bearing relativeBearingToNextMarkPassingAfterManeuver = getRelativeBearingToNextMark( + maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), maneuverCurve + .getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter().getBearing()); + return new CompleteManeuverCurveWithEstimationDataImpl(maneuverPosition, mainCurve, + curveWithUnstableCourseAndSpeed, wind, numberOfTacks, numberOfJibes, + maneuverStartsByRunningAwayFromWind, relativeBearingToNextMarkPassingBeforeManeuver, + relativeBearingToNextMarkPassingAfterManeuver, maneuverCurve.isMarkPassing()); + } + + /** + * Calculates the duration and avg speed with avg course based on the competitor's track within the provided time + * range. + */ + private Pair calculateDurationAndAvgSpeedWithBearingBetweenTimePoints(TimePoint from, + TimePoint to) { + Duration duration = from.until(to); + Position fromPosition = maneuverDetector.track.getEstimatedPosition(from, false); + Position toPosition = maneuverDetector.track.getEstimatedPosition(to, false); + Distance distance = fromPosition.getDistance(toPosition); + Bearing bearing = fromPosition.getBearingGreatCircle(toPosition); + Speed speed = distance.inTime(Math.abs(duration.asMillis())); + SpeedWithBearing avgSpeedWithBearing = new KnotSpeedWithBearingImpl(speed.getKnots(), bearing); + return new Pair<>(duration, avgSpeedWithBearing); + } + + /** + * Gets the relative bearing of the next mark from the boat's position and course at {@code timePoint}. The relative + * bearing is calculated by absolute bearing of next mark from the boat's position minus the boat's course. + */ + private Bearing getRelativeBearingToNextMark(TimePoint timePoint, Bearing boatCourse) { + NonCachingMarkPositionAtTimePointCache markPositionAtTimePointCache = new NonCachingMarkPositionAtTimePointCache( + maneuverDetector.trackedRace, timePoint); + Bearing result = null; + TrackedLegOfCompetitor legAfter = maneuverDetector.trackedRace.getTrackedLeg(maneuverDetector.competitor, + timePoint); + if (legAfter != null && legAfter.getLeg().getTo() != null) { + Waypoint nextWaypoint = legAfter.getLeg().getTo(); + for (Mark mark : nextWaypoint.getMarks()) { + Position nextMarkPosition = markPositionAtTimePointCache.getEstimatedPosition(mark); + Position maneuverEndPosition = maneuverDetector.track.getEstimatedPosition(timePoint, false); + Bearing absoluteBearing = maneuverEndPosition.getBearingGreatCircle(nextMarkPosition); + Bearing resultCandidate = absoluteBearing.getDifferenceTo(boatCourse); + if (result == null) { + result = resultCandidate; + } else if (Math.signum(result.getDegrees()) != Math.signum(resultCandidate.getDegrees())) { + result = new DegreeBearingImpl(0); + break; + } else if (Math.abs(result.getDegrees()) > Math.abs(resultCandidate.getDegrees())) { + result = resultCandidate; + } + + } + } + return result; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java index 7011612e984..0bc4bdb5b2c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java @@ -174,5 +174,5 @@ public interface Maneuver extends GPSFix { */ @Statistic(messageKey = "AvgTurningRateInDegreesPerSecond", resultDecimals = 4) double getAvgTurningRateInDegreesPerSecond(); - + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java new file mode 100644 index 00000000000..328e073e972 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java @@ -0,0 +1,37 @@ +package com.sap.sailing.domain.tracking.impl; + +import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sse.common.TimePoint; + +/** + * Maneuver implementation which were detected on tracks with extremely low GPS-sampling rate. The implementation + * suggests to ignore the following attributes: + *

            + *
          • Maneuver loss
          • + *
          • Max. and Avg. Turning rate
          • + *
          • Time point before and after of all maneuver boundaries
          • + *
          • Lowest speed within all maneuver boundaries
          • + *
          + * However, to provide capability with existing code, the attributes are filled with values. + * + * @author Vladislav Chumak (D069712) + * + */ +public class ManeuverWithCoarseGrainedBoundariesImpl extends ManeuverImpl { + + private static final long serialVersionUID = -381990329349665889L; + + public ManeuverWithCoarseGrainedBoundariesImpl(ManeuverType type, Tack newTack, Position position, + TimePoint timePoint, ManeuverCurveBoundaries maneuverBoundaries) { + super(type, newTack, position, null, timePoint, maneuverBoundaries, maneuverBoundaries, Math.abs(maneuverBoundaries.getDirectionChangeInDegrees()), null); + } + + @Override + public ManeuverCurveBoundaries getManeuverBoundaries() { + return getMainCurveBoundaries(); + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index d80b0c45e6a..2760bf877ca 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -113,6 +113,7 @@ import com.sap.sailing.domain.maneuverdetection.IncrementalManeuverDetector; import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; import com.sap.sailing.domain.maneuverdetection.ShortTimeAfterLastHitCache; import com.sap.sailing.domain.maneuverdetection.impl.IncrementalManeuverDetectorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.LowGPSSamplingRateManeuverDetectorImpl; import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; @@ -701,10 +702,21 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl @Override public List computeCacheUpdate(Competitor competitor, EmptyUpdateInterval updateInterval) throws NoWindException { - IncrementalManeuverDetector maneuverDetector = maneuverDetectorPerCompetitorCache - .getValue(competitor); - List maneuvers = computeManeuvers(competitor, maneuverDetector); - return maneuvers; + Duration averageIntervalBetweenRawFixes = getTrack(competitor) + .getAverageIntervalBetweenRawFixes(); + if (averageIntervalBetweenRawFixes != null) { + ManeuverDetector maneuverDetector; + if (averageIntervalBetweenRawFixes.asSeconds() >= 30) { + maneuverDetector = new LowGPSSamplingRateManeuverDetectorImpl(TrackedRaceImpl.this, + competitor); + } else { + maneuverDetector = maneuverDetectorPerCompetitorCache.getValue(competitor); + } + List maneuvers = computeManeuvers(competitor, maneuverDetector); + return maneuvers; + } else { + return Collections.emptyList(); + } } }, /* nameForLocks */"Maneuver cache for race " + getRace().getName()); } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java index 9a0def79beb..b155633069e 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java @@ -6,8 +6,9 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; -import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; +import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorWithEstimationDataSupportDecoratorImpl; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.GPSFixTrack; @@ -82,7 +83,8 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst private Iterable getCompleteManeuverCurvesWithEstimationData( TrackedRace trackedRace, Competitor competitor) { Iterable maneuvers = trackedRace.getManeuvers(competitor, false); - ManeuverDetector maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + ManeuverDetectorWithEstimationDataSupport maneuverDetector = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + new ManeuverDetectorImpl(trackedRace, competitor)); Iterable maneuversWithEstimationData = null; try { Iterable maneuverCurves = maneuverDetector.getCompleteManeuverCurves(maneuvers); From 5da71b27b43f686acce8eecc2ee83ccb1382536f Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Thu, 5 Jul 2018 06:57:30 +0200 Subject: [PATCH 058/102] Removed outdated javadoc --- ...owGPSSamplingRateManeuverDetectorImpl.java | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java index 7396d284474..26de57c6b24 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/LowGPSSamplingRateManeuverDetectorImpl.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.CourseChange; import com.sap.sailing.domain.common.ManeuverType; @@ -37,41 +36,6 @@ public class LowGPSSamplingRateManeuverDetectorImpl extends AbstractManeuverDete super(trackedRace, competitor); } - /** - * Tries to detect maneuvers on the competitor's track based on a number of approximating fixes. The - * fixes contain bearing information, but this is not the bearing leading to the next approximation fix but the - * bearing the boat had at the time of the approximating fix which is taken from the original track. - *

          - * - * The time period assumed for a maneuver duration is taken from the - * {@link BoatClass#getApproximateManeuverDurationInMilliseconds() boat class}. If no maneuver is detected, an empty - * list is returned. Maneuvers can only be expected to be detected if at least three fixes are provided in - * approximatedFixesToAnalyze. For the inner approximating fixes (all except the first and the last - * approximating fix), their course changes according to the approximated path (and not the underlying actual - * tracked fixes) are computed. Subsequent course changes to the same direction are then grouped. Those in closer - * timely distance than {@link #getApproximateManeuverDurationInMilliseconds()} (including single course changes - * that have no surrounding other course changes to group) are grouped into one {@link Maneuver}. - * - * @param ignoreMarkPassings - * When true, no {@link ManeuverType#MARK_PASSING} maneuvers will be identified, and the - * fact that a mark passing would split up what else may be a penalty circle is ignored. This is helpful - * for recursive calls, e.g., after identifying a tack and a jibe around a mark passing and trying to - * identify for the time before and after the mark passing which maneuvers exist on which side of the - * passing. - * @param earliestManeuverStart - * maneuver start will not be before this time point; if a maneuver is found whose time point is at or - * after this time point, no matter how close it is, its start regarding speed and course into the - * maneuver and the leg before the maneuver is not taken from an earlier time point, even if half the - * maneuver duration before the maneuver time point were before this time point. - * @param latestManeuverEnd - * maneuver end will not be after this time point; if a maneuver is found whose time point is at or - * before this time point, no matter how close it is, its end regarding speed and course out of the - * maneuver and the leg after the maneuver is not taken from a later time point, even if half the - * maneuver duration after the maneuver time point were after this time point. - * - * @return an empty list if no maneuver is detected for competitor between from and - * to, or else the list of maneuvers detected. - */ @Override public List detectManeuvers() { List result = new ArrayList<>(); From 316ccc3b438136561905d74262a70f8c764927b1 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Thu, 5 Jul 2018 10:52:05 +0200 Subject: [PATCH 059/102] Added polar feature to exported estimation data + corrected polars bugs --- ...eWithEstimationDataRetrievalProcessor.java | 3 +- ...mpleteManeuverCurveWithEstimationData.java | 7 ++ ...teManeuverCurveWithEstimationDataImpl.java | 26 ++++++- ...ithEstimationDataSupportDecoratorImpl.java | 76 ++++++++++++++++++- .../domain/polars/PolarDataService.java | 8 ++ .../jaxrs/api/test/PolarDataResourceTest.java | 2 +- .../polars/impl/PolarDataServiceImpl.java | 23 ++++-- .../mining/AngleAndSpeedRegression.java | 3 +- ...nDataSerializationDeserializationTest.java | 22 +++++- ...rveWithEstimationDataJsonDeserializer.java | 16 +++- ...CurveWithEstimationDataJsonSerializer.java | 9 +++ ...urvesWithEstimationDataJsonSerializer.java | 17 ++++- .../gateway/jaxrs/api/RegattasResource.java | 2 +- 13 files changed, 193 insertions(+), 21 deletions(-) diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java index f05bdf58be0..eff334d61f5 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/CompleteManeuverCurveWithEstimationDataRetrievalProcessor.java @@ -47,7 +47,8 @@ public class CompleteManeuverCurveWithEstimationDataRetrievalProcessor extends TrackedRace trackedRace = element.getTrackedRaceContext().getTrackedRace(); Competitor competitor = element.getCompetitor(); ManeuverDetectorWithEstimationDataSupport maneuverDetector = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( - new ManeuverDetectorImpl(trackedRace, competitor)); + new ManeuverDetectorImpl(trackedRace, competitor), + element.getTrackedRaceContext().getLeaderboardContext().getPolarDataService()); Iterable maneuvers = trackedRace.getManeuvers(competitor, false); Iterable maneuverCurves = maneuverDetector.getCompleteManeuverCurves(maneuvers); Iterable maneuversWithEstimationData = maneuverDetector diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/CompleteManeuverCurveWithEstimationData.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/CompleteManeuverCurveWithEstimationData.java index bd41ca3d81a..9738718cca4 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/CompleteManeuverCurveWithEstimationData.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/CompleteManeuverCurveWithEstimationData.java @@ -5,6 +5,7 @@ import com.sap.sailing.domain.common.Positioned; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Timed; import com.sap.sse.datamining.annotations.Connector; @@ -96,6 +97,12 @@ public interface CompleteManeuverCurveWithEstimationData extends Timed, Position * boundaries, the boundaries of the curve with unstable course and speed are used. */ Bearing getRelativeBearingToNextMarkAfterManeuver(); + + Distance getDistanceToClosestMark(); + + Double getDeviationOfManeuverAngleFromTargetTackAngleInDegrees(); + + Double getDeviationOfManeuverAngleFromTargetJibeAngleInDegrees(); /** * Gets whether a mark was crossed within the maneuver curve. diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/CompleteManeuverCurveWithEstimationDataImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/CompleteManeuverCurveWithEstimationDataImpl.java index 38ac9040bbc..89c43715903 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/CompleteManeuverCurveWithEstimationDataImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/CompleteManeuverCurveWithEstimationDataImpl.java @@ -6,6 +6,7 @@ import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimat import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; /** * @@ -24,13 +25,16 @@ public class CompleteManeuverCurveWithEstimationDataImpl implements CompleteMane private final Bearing relativeBearingToNextMarkBeforeManeuver; private final Bearing relativeBearingToNextMarkAfterManeuver; private final boolean markPassing; - private Position position; + private final Position position; + private final Distance distanceToClosestMark; + private final Double deviationOfManeuverAngleFromTargetTackAngleInDegrees; + private final Double deviationOfManeuverAngleFromTargetJibeAngleInDegrees; public CompleteManeuverCurveWithEstimationDataImpl(Position position, ManeuverMainCurveWithEstimationData mainCurve, ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData curveWithUnstableCourseAndSpeed, Wind wind, int tackingCount, int jibingCount, boolean maneuverStartsByRunningAwayFromWind, Bearing relativeBearingToNextMarkBeforeManeuver, Bearing relativeBearingToNextMarkAfterManeuver, - boolean markPassing) { + boolean markPassing, Distance distanceToClosestMark, Double deviationOfManeuverAngleFromTargetTackAngleInDegrees, Double deviationOfManeuverAngleFromTargetJibeAngleInDegrees) { this.position = position; this.mainCurve = mainCurve; this.curveWithUnstableCourseAndSpeed = curveWithUnstableCourseAndSpeed; @@ -41,6 +45,9 @@ public class CompleteManeuverCurveWithEstimationDataImpl implements CompleteMane this.relativeBearingToNextMarkBeforeManeuver = relativeBearingToNextMarkBeforeManeuver; this.relativeBearingToNextMarkAfterManeuver = relativeBearingToNextMarkAfterManeuver; this.markPassing = markPassing; + this.distanceToClosestMark = distanceToClosestMark; + this.deviationOfManeuverAngleFromTargetTackAngleInDegrees = deviationOfManeuverAngleFromTargetTackAngleInDegrees; + this.deviationOfManeuverAngleFromTargetJibeAngleInDegrees = deviationOfManeuverAngleFromTargetJibeAngleInDegrees; } @Override @@ -92,5 +99,20 @@ public class CompleteManeuverCurveWithEstimationDataImpl implements CompleteMane public Position getPosition() { return position; } + + @Override + public Distance getDistanceToClosestMark() { + return distanceToClosestMark; + } + + @Override + public Double getDeviationOfManeuverAngleFromTargetTackAngleInDegrees() { + return deviationOfManeuverAngleFromTargetTackAngleInDegrees; + } + + @Override + public Double getDeviationOfManeuverAngleFromTargetJibeAngleInDegrees() { + return deviationOfManeuverAngleFromTargetJibeAngleInDegrees; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java index 689df4a4736..1281b2f14f3 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java @@ -4,7 +4,9 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Mark; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.Position; @@ -17,6 +19,7 @@ import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseA import com.sap.sailing.domain.maneuverdetection.ManeuverDetector; import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; +import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; @@ -44,10 +47,13 @@ import com.sap.sse.common.impl.DegreeBearingImpl; public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl implements ManeuverDetectorWithEstimationDataSupport { - private ManeuverDetectorImpl maneuverDetector; + private final ManeuverDetectorImpl maneuverDetector; + private final PolarDataService polarDataService; - public ManeuverDetectorWithEstimationDataSupportDecoratorImpl(ManeuverDetectorImpl maneuverDetector) { + public ManeuverDetectorWithEstimationDataSupportDecoratorImpl(ManeuverDetectorImpl maneuverDetector, + PolarDataService polarDataService) { this.maneuverDetector = maneuverDetector; + this.polarDataService = polarDataService; } @Override @@ -348,10 +354,72 @@ public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl Bearing relativeBearingToNextMarkPassingAfterManeuver = getRelativeBearingToNextMark( maneuverCurve.getManeuverCurveWithStableSpeedAndCourseBoundaries().getTimePointAfter(), maneuverCurve .getManeuverCurveWithStableSpeedAndCourseBoundaries().getSpeedWithBearingAfter().getBearing()); + BoatClass boatClass = maneuverDetector.trackedRace.getRace().getBoatOfCompetitor(maneuverDetector.competitor) + .getBoatClass(); + Double deviationFromTackAngle = null; + Double deviationFromJibeAngle = null; + Speed boatSpeed = curveWithUnstableCourseAndSpeed.getSpeedWithBearingBefore() + .compareTo(curveWithUnstableCourseAndSpeed.getSpeedWithBearingAfter()) < 0 + ? curveWithUnstableCourseAndSpeed.getSpeedWithBearingBefore() + : curveWithUnstableCourseAndSpeed.getSpeedWithBearingAfter(); + if (polarDataService.getAllBoatClassesWithPolarSheetsAvailable().contains(boatClass)) { + SpeedWithBearingWithConfidence closestTackTwa = polarDataService.getClosestTwaTws(ManeuverType.TACK, + boatSpeed, curveWithUnstableCourseAndSpeed.getDirectionChangeInDegrees(), boatClass); + SpeedWithBearingWithConfidence closestJibeTwa = polarDataService.getClosestTwaTws(ManeuverType.JIBE, + boatSpeed, curveWithUnstableCourseAndSpeed.getDirectionChangeInDegrees(), boatClass); + if (closestTackTwa != null) { + deviationFromTackAngle = polarDataService.getManeuverAngleInDegreesFromTwa( + closestTackTwa.getObject().getBearing().getDegrees(), ManeuverType.TACK); + } + if (closestJibeTwa != null) { + deviationFromJibeAngle = polarDataService.getManeuverAngleInDegreesFromTwa( + closestJibeTwa.getObject().getBearing().getDegrees(), ManeuverType.JIBE); + } + } + Distance closestDistanceToMark = getClosestDistanceToMark(mainCurve.getTimePointOfMaxTurningRate()); + return new CompleteManeuverCurveWithEstimationDataImpl(maneuverPosition, mainCurve, curveWithUnstableCourseAndSpeed, wind, numberOfTacks, numberOfJibes, maneuverStartsByRunningAwayFromWind, relativeBearingToNextMarkPassingBeforeManeuver, - relativeBearingToNextMarkPassingAfterManeuver, maneuverCurve.isMarkPassing()); + relativeBearingToNextMarkPassingAfterManeuver, maneuverCurve.isMarkPassing(), closestDistanceToMark, + deviationFromTackAngle, deviationFromJibeAngle); + } + + private Distance getClosestDistanceToMark(TimePoint timePoint) { + NonCachingMarkPositionAtTimePointCache markPositionAtTimePointCache = new NonCachingMarkPositionAtTimePointCache( + maneuverDetector.trackedRace, timePoint); + Distance result = null; + TrackedLegOfCompetitor legAfter = maneuverDetector.trackedRace.getTrackedLeg(maneuverDetector.competitor, + timePoint); + if (legAfter != null) { + Position maneuverPosition = maneuverDetector.track.getEstimatedPosition(timePoint, false); + if (legAfter.getLeg().getTo() != null) { + result = getClosestDistanceToMarkInternal(markPositionAtTimePointCache, legAfter.getLeg().getTo(), + maneuverPosition); + } + if (legAfter.getLeg().getFrom() != null) { + Distance distance = getClosestDistanceToMarkInternal(markPositionAtTimePointCache, + legAfter.getLeg().getFrom(), maneuverPosition); + if (result == null || distance != null && distance.compareTo(result) < 0) { + result = distance; + } + } + } + return result; + } + + private Distance getClosestDistanceToMarkInternal( + NonCachingMarkPositionAtTimePointCache markPositionAtTimePointCache, Waypoint waypoint, + Position maneuverPosition) { + Distance result = null; + for (Mark mark : waypoint.getMarks()) { + Position markPosition = markPositionAtTimePointCache.getEstimatedPosition(mark); + Distance distance = markPosition.getDistance(maneuverPosition); + if (result == null || distance.compareTo(result) < 0) { + result = distance; + } + } + return result; } /** @@ -381,10 +449,10 @@ public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl TrackedLegOfCompetitor legAfter = maneuverDetector.trackedRace.getTrackedLeg(maneuverDetector.competitor, timePoint); if (legAfter != null && legAfter.getLeg().getTo() != null) { + Position maneuverEndPosition = maneuverDetector.track.getEstimatedPosition(timePoint, false); Waypoint nextWaypoint = legAfter.getLeg().getTo(); for (Mark mark : nextWaypoint.getMarks()) { Position nextMarkPosition = markPositionAtTimePointCache.getEstimatedPosition(mark); - Position maneuverEndPosition = maneuverDetector.track.getEstimatedPosition(timePoint, false); Bearing absoluteBearing = maneuverEndPosition.getBearingGreatCircle(nextMarkPosition); Bearing resultCandidate = absoluteBearing.getDifferenceTo(boatCourse); if (result == null) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java index 0e1c1ce4e70..231fd9b68ce 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java @@ -1,5 +1,6 @@ package com.sap.sailing.domain.polars; +import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -181,4 +182,11 @@ public interface PolarDataService { * with this service, then lets the {@code consumer} accept that domain factory. */ void runWithDomainFactory(Consumer consumer) throws InterruptedException; + + Map getFixCountPerBoatClass(); + + SpeedWithBearingWithConfidence getClosestTwaTws(ManeuverType type, Speed speedAtManeuverStart, + double courseChangeDeg, BoatClass boatClass); + + double getManeuverAngleInDegreesFromTwa(double twa, ManeuverType maneuverType); } diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/jaxrs/api/test/PolarDataResourceTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/jaxrs/api/test/PolarDataResourceTest.java index 0ebfeff56d2..5ec38c3b121 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/jaxrs/api/test/PolarDataResourceTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/jaxrs/api/test/PolarDataResourceTest.java @@ -61,7 +61,7 @@ public class PolarDataResourceTest { assertThat(polarService.getSpeedRegressionsPerAngle().size(), is(68)); assertThat(polarService.getCubicRegressionsPerCourse().size(), is(4)); - assertThat(polarService.getFixCointPerBoatClass().get(boatClass), is(9330L)); + assertThat(polarService.getFixCountPerBoatClass().get(boatClass), is(9330L)); // presuming that if downwind functions & regression collections' size are correct then any other thing is // imported correctly assertThat(polarService.getAngleRegressionFunction(boatClass, LegType.DOWNWIND), is(angleDownwindFunction)); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 56b91be5abc..a6b97c92076 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -177,7 +177,8 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT return result; } - private SpeedWithBearingWithConfidence getClosestTwaTws(ManeuverType type, Speed speedAtManeuverStart, + @Override + public SpeedWithBearingWithConfidence getClosestTwaTws(ManeuverType type, Speed speedAtManeuverStart, double courseChangeDeg, BoatClass boatClass) { assert type == ManeuverType.TACK || type == ManeuverType.JIBE; double minDiff = Double.MAX_VALUE; @@ -186,8 +187,9 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT boatClass, speedAtManeuverStart, type == ManeuverType.TACK ? LegType.UPWIND : LegType.DOWNWIND, type == ManeuverType.TACK ? courseChangeDeg >= 0 ? Tack.PORT : Tack.STARBOARD : courseChangeDeg >= 0 ? Tack.STARBOARD : Tack.PORT)) { - double diff = Math.abs(trueWindSpeedAndAngle.getObject().getBearing().getDegrees() * 2) - - Math.abs(courseChangeDeg); + double targetManeuverAngle = getManeuverAngleInDegreesFromTwa( + trueWindSpeedAndAngle.getObject().getBearing().getDegrees(), type); + double diff = Math.abs(targetManeuverAngle) - Math.abs(courseChangeDeg); if (diff < minDiff) { minDiff = diff; closestTwsTwa = trueWindSpeedAndAngle; @@ -235,11 +237,21 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT } SpeedWithBearingWithConfidence speed = polarDataMiner.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType); - Bearing bearing = new DegreeBearingImpl(speed.getObject().getBearing().getDegrees() * 2); + Bearing bearing = new DegreeBearingImpl(getManeuverAngleInDegreesFromTwa(speed.getObject().getBearing().getDegrees(), maneuverType)); BearingWithConfidence bearingWithConfidence = new BearingWithConfidenceImpl(bearing, speed.getConfidence(), null); return bearingWithConfidence; } + + public double getManeuverAngleInDegreesFromTwa(double twa, ManeuverType maneuverType) { + if (maneuverType == ManeuverType.TACK) { + return Math.abs(twa) * 2; + } + if (maneuverType == ManeuverType.JIBE) { + return (180 - Math.abs(twa)) * 2; + } + throw new IllegalArgumentException("ManeuverType needs to be tack or jibe."); + } @Override public void insertExistingFixes(TrackedRace trackedRace) { @@ -388,7 +400,8 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT return polarDataMiner.getSpeedRegressionPerAngleClusterProcessor().getRegressionsImpl(); } - public Map getFixCointPerBoatClass() { + @Override + public Map getFixCountPerBoatClass() { return polarDataMiner.getSpeedRegressionPerAngleClusterProcessor().getFixCountPerBoatClass(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java index 4aec93bf628..258bc9a4ef9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java @@ -107,8 +107,7 @@ public class AngleAndSpeedRegression implements Serializable { boolean angleFound; try { angle = angleRegression.getOrCreatePolynomialFunction().value(windSpeedCandidateInKnots); - if ((tack == Tack.PORT && legType == LegType.UPWIND) - || (tack == Tack.STARBOARD && legType == LegType.DOWNWIND)) { + if (tack == Tack.PORT) { angle = -angle; } angleFound = true; diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java index 779585de9d7..b4c77ee8dd9 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java @@ -132,11 +132,15 @@ public class EstimationDataSerializationDeserializationTest { Bearing relativeBearingToNextMarkBeforeManeuver = new DegreeBearingImpl(202.23); Bearing relativeBearingToNextMarkAfterManeuver = new DegreeBearingImpl(10.01); boolean markPassing = true; + Distance closestDistanceToMark = new MeterDistance(3.0); + Double deviationFromTargetTackAngle = 23.30; + Double deviationFromTargetJibeAngle = 22.30; CompleteManeuverCurveWithEstimationData toSerialize = new CompleteManeuverCurveWithEstimationDataImpl( maneuverPosition, mainCurve, curve, wind, tackingCount, jibingCount, maneuverStartsByRunningAwayFromWind, relativeBearingToNextMarkBeforeManeuver, - relativeBearingToNextMarkAfterManeuver, markPassing); + relativeBearingToNextMarkAfterManeuver, markPassing, closestDistanceToMark, + deviationFromTargetTackAngle, deviationFromTargetJibeAngle); CompleteManeuverCurveWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), @@ -148,6 +152,11 @@ public class EstimationDataSerializationDeserializationTest { new WindJsonDeserializer(new PositionJsonDeserializer()), new PositionJsonDeserializer()); CompleteManeuverCurveWithEstimationData deserialized = deserializer.deserialize(json); + assertEquals(deviationFromTargetJibeAngle, + deserialized.getDeviationOfManeuverAngleFromTargetJibeAngleInDegrees()); + assertEquals(deviationFromTargetTackAngle, + deserialized.getDeviationOfManeuverAngleFromTargetTackAngleInDegrees()); + assertEquals(closestDistanceToMark, deserialized.getDistanceToClosestMark()); assertEquals(maneuverPosition, deserialized.getPosition()); assertEquals(tackingCount, deserialized.getTackingCount()); assertEquals(jibingCount, deserialized.getJibingCount()); @@ -303,11 +312,15 @@ public class EstimationDataSerializationDeserializationTest { Bearing relativeBearingToNextMarkAfterManeuver = null; boolean markPassing = false; DegreePosition maneuverPosition = new DegreePosition(50.325246, 11.148556); + Distance closestDistanceToMark = null; + Double deviationFromTargetTackAngle = null; + Double deviationFromTargetJibeAngle = null; CompleteManeuverCurveWithEstimationData toSerialize = new CompleteManeuverCurveWithEstimationDataImpl( maneuverPosition, mainCurve, curve, wind, tackingCount, jibingCount, maneuverStartsByRunningAwayFromWind, relativeBearingToNextMarkBeforeManeuver, - relativeBearingToNextMarkAfterManeuver, markPassing); + relativeBearingToNextMarkAfterManeuver, markPassing, closestDistanceToMark, + deviationFromTargetTackAngle, deviationFromTargetJibeAngle); CompleteManeuverCurveWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), @@ -319,6 +332,11 @@ public class EstimationDataSerializationDeserializationTest { new WindJsonDeserializer(new PositionJsonDeserializer()), new PositionJsonDeserializer()); CompleteManeuverCurveWithEstimationData deserialized = deserializer.deserialize(json); + assertEquals(deviationFromTargetJibeAngle, + deserialized.getDeviationOfManeuverAngleFromTargetJibeAngleInDegrees()); + assertEquals(deviationFromTargetTackAngle, + deserialized.getDeviationOfManeuverAngleFromTargetTackAngleInDegrees()); + assertEquals(closestDistanceToMark, deserialized.getDistanceToClosestMark()); assertEquals(maneuverPosition, deserialized.getPosition()); assertEquals(tackingCount, deserialized.getTackingCount()); assertEquals(jibingCount, deserialized.getJibingCount()); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java index 6c7dfe3c4b0..0408e37fd08 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java @@ -4,6 +4,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; @@ -12,6 +13,7 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializationExcepti import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurveWithEstimationDataJsonSerializer; import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; import com.sap.sse.common.impl.DegreeBearingImpl; /** @@ -59,16 +61,28 @@ public class CompleteManeuverCurveWithEstimationDataJsonDeserializer CompleteManeuverCurveWithEstimationDataJsonSerializer.RELATIVE_BEARING_TO_NEXT_MARK_BEFORE_MANEUVER); Double relativeBearingToNextMarkAfterManeuver = (Double) object.get( CompleteManeuverCurveWithEstimationDataJsonSerializer.RELATIVE_BEARING_TO_NEXT_MARK_AFTER_MANEUVER); + Double closestDistanceToMarkInMeters = (Double) object + .get(CompleteManeuverCurveWithEstimationDataJsonSerializer.CLOSEST_DISTANCE_TO_MARK); + Double deviationFromTargetTackAngle = (Double) object + .get(CompleteManeuverCurveWithEstimationDataJsonSerializer.DEVIATION_FROM_TARGET_TACK_ANGLE); + Double deviationFromTargetJibeAngle = (Double) object + .get(CompleteManeuverCurveWithEstimationDataJsonSerializer.DEVIATION_FROM_TARGET_JIBE_ANGLE); return new CompleteManeuverCurveWithEstimationDataImpl(position, mainCurve, curveWithUnstableCourseAndSpeed, wind, tackingCount, jibingCount, maneuverStartsByRunningAwayFromWind, convertBearing(relativeBearingToNextMarkBeforeManeuver), - convertBearing(relativeBearingToNextMarkAfterManeuver), markPassing); + convertBearing(relativeBearingToNextMarkAfterManeuver), markPassing, + convertDistance(closestDistanceToMarkInMeters), deviationFromTargetTackAngle, + deviationFromTargetJibeAngle); } private Bearing convertBearing(Double degrees) { return degrees == null ? null : new DegreeBearingImpl(degrees); } + private Distance convertDistance(Double meters) { + return meters == null ? null : new MeterDistance(meters); + } + public static Integer getInteger(Object object) { if (object == null) { return null; diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java index c904b298a51..5c3ae7b119d 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java @@ -23,6 +23,9 @@ public class CompleteManeuverCurveWithEstimationDataJsonSerializer public static final String MANEUVER_STARTS_BY_RUNNING_AWAY_FROM_WIND = "maneuverStartsByRunningAwayFromWind"; public static final String RELATIVE_BEARING_TO_NEXT_MARK_BEFORE_MANEUVER = "relativeBearingToNextMarkBeforeManeuver"; public static final String RELATIVE_BEARING_TO_NEXT_MARK_AFTER_MANEUVER = "relativeBearingToNextMarkAfterManeuver"; + public static final String CLOSEST_DISTANCE_TO_MARK = "closestDistanceToMarkInMeters"; + public static final String DEVIATION_FROM_TARGET_TACK_ANGLE = "deviationFromTargetTackAngleInDegrees"; + public static final String DEVIATION_FROM_TARGET_JIBE_ANGLE = "deviationFromTargetJibeAngleInDegrees"; private final ManeuverCurveBoundariesJsonSerializer mainCurveSerializer; private final ManeuverCurveBoundariesJsonSerializer curveWithUnstableCourseAndSpeedSerializer; @@ -59,6 +62,12 @@ public class CompleteManeuverCurveWithEstimationDataJsonSerializer result.put(RELATIVE_BEARING_TO_NEXT_MARK_AFTER_MANEUVER, maneuverWithEstimationData.getRelativeBearingToNextMarkAfterManeuver() == null ? null : maneuverWithEstimationData.getRelativeBearingToNextMarkAfterManeuver().getDegrees()); + result.put(CLOSEST_DISTANCE_TO_MARK, maneuverWithEstimationData.getDistanceToClosestMark() == null ? null + : maneuverWithEstimationData.getDistanceToClosestMark().getMeters()); + result.put(DEVIATION_FROM_TARGET_TACK_ANGLE, + maneuverWithEstimationData.getDeviationOfManeuverAngleFromTargetTackAngleInDegrees()); + result.put(DEVIATION_FROM_TARGET_JIBE_ANGLE, + maneuverWithEstimationData.getDeviationOfManeuverAngleFromTargetJibeAngleInDegrees()); return result; } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java index b155633069e..6bb7a8fb7d6 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java @@ -3,6 +3,7 @@ package com.sap.sailing.server.gateway.serialization.impl; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; @@ -10,6 +11,7 @@ import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDa import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorWithEstimationDataSupportDecoratorImpl; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; +import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; @@ -29,12 +31,16 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst public static final String DISTANCE_TRAVELLED_IN_METERS = "distanceTravelledInMeters"; public static final String START_TIME_POINT = "startUnixTime"; public static final String END_TIME_POINT = "endUnixTime"; + public static final String FIXES_COUNT_FOR_POLARS = "fixesCountForPolars"; private final BoatClassJsonSerializer boatClassJsonSerializer; private final CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer; + private final PolarDataService polarDataService; - public CompleteManeuverCurvesWithEstimationDataJsonSerializer(BoatClassJsonSerializer boatClassJsonSerializer, + public CompleteManeuverCurvesWithEstimationDataJsonSerializer(PolarDataService polarDataService, + BoatClassJsonSerializer boatClassJsonSerializer, CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer) { + this.polarDataService = polarDataService; this.boatClassJsonSerializer = boatClassJsonSerializer; this.maneuverWithEstimationDataJsonSerializer = maneuverWithEstimationDataJsonSerializer; } @@ -59,6 +65,7 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst completeManeuverCurvesWithEstimationData .add(maneuverWithEstimationDataJsonSerializer.serialize(maneuver)); } + forCompetitorJson.put(FIXES_COUNT_FOR_POLARS, getFixesCountForPolars(trackedRace, competitor)); forCompetitorJson.put(MANEUVER_CURVES, completeManeuverCurvesWithEstimationData); Duration averageIntervalBetweenFixes = trackedRace.getTrack(competitor) .getAverageIntervalBetweenFixes(); @@ -84,7 +91,7 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst TrackedRace trackedRace, Competitor competitor) { Iterable maneuvers = trackedRace.getManeuvers(competitor, false); ManeuverDetectorWithEstimationDataSupport maneuverDetector = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( - new ManeuverDetectorImpl(trackedRace, competitor)); + new ManeuverDetectorImpl(trackedRace, competitor), polarDataService); Iterable maneuversWithEstimationData = null; try { Iterable maneuverCurves = maneuverDetector.getCompleteManeuverCurves(maneuvers); @@ -95,4 +102,10 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst return maneuversWithEstimationData; } + private long getFixesCountForPolars(TrackedRace trackedRace, Competitor competitor) { + BoatClass boatClass = trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass(); + Long fixesCountForBoatPolars = polarDataService.getFixCountPerBoatClass().get(boatClass); + return fixesCountForBoatPolars == null ? 0L : fixesCountForBoatPolars; + } + } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 15c76506ba3..6ae0be5b38d 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -1043,7 +1043,7 @@ public class RegattasResource extends AbstractSailingServerResource { } else { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); CompleteManeuverCurvesWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurvesWithEstimationDataJsonSerializer( - new DetailedBoatClassJsonSerializer(), + getService().getPolarDataService(), new DetailedBoatClassJsonSerializer(), new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), From fbe73105cee33b2c335c749fb53231880fec5e1d Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 5 Jul 2018 11:08:43 +0200 Subject: [PATCH 060/102] Bug 4654: Ensures that we use the filename only to determin the file extension --- .../sap/sailing/server/gateway/impl/FileUploadServlet.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java index 1b03f2db42a..cbe6e0dea44 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/FileUploadServlet.java @@ -3,6 +3,7 @@ package com.sap.sailing.server.gateway.impl; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; +import java.nio.file.Paths; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,13 +50,13 @@ public class FileUploadServlet extends AbstractFileUploadServlet { for (FileItem fileItem : fileItems) { final JSONObject result = new JSONObject(); final String fileExtension; + final String fileName = Paths.get(fileItem.getName()).getFileName().toString(); final String fileType = fileItem.getContentType(); if (fileType.equals("image/jpeg")) { fileExtension = ".jpg"; } else if (fileType.equals("image/png")) { fileExtension = ".png"; } else { - String fileName = fileItem.getName(); int lastDot = fileName.lastIndexOf("."); if (lastDot > 0) { fileExtension = fileName.substring(lastDot); @@ -66,13 +67,13 @@ public class FileUploadServlet extends AbstractFileUploadServlet { try { if (fileItem.getSize() > 1024 * 1024 * MAX_SIZE_IN_MB) { final String errorMessage = "Image is larger than " + MAX_SIZE_IN_MB + "MB"; - logger.warning("Ignoring file storage request because file "+fileItem.getName()+" is larger than "+MAX_SIZE_IN_MB+"MB"); + logger.warning("Ignoring file storage request because file "+fileName+" is larger than "+MAX_SIZE_IN_MB+"MB"); result.put("status", Status.INTERNAL_SERVER_ERROR.name()); result.put("message", errorMessage); } else { final URI fileUri = getService().getFileStorageManagementService().getActiveFileStorageService() .storeFile(fileItem.getInputStream(), fileExtension, fileItem.getSize()); - result.put(JSON_FILE_NAME, fileItem.getName()); + result.put(JSON_FILE_NAME, fileName); result.put(JSON_FILE_URI, fileUri.toString()); } } catch (IOException | OperationFailedException | InvalidPropertiesException | NoCorrespondingServiceRegisteredException e) { From e94ca51c1576788ebdd60b64a0b517843cf129d0 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Thu, 5 Jul 2018 12:37:32 +0200 Subject: [PATCH 061/102] Added export for gps fixes with estimation data --- .../impl/ManeuverDetectorImpl.java | 3 - ...ithEstimationDataSupportDecoratorImpl.java | 4 +- ...nDataSerializationDeserializationTest.java | 21 ++-- ...rveWithEstimationDataJsonDeserializer.java | 10 +- .../impl/ManeuverWindJsonDeserializer.java | 29 +++++ ...CurveWithEstimationDataJsonSerializer.java | 4 +- ...FixesWithEstimationDataJsonSerializer.java | 119 ++++++++++++++++++ .../gateway/jaxrs/api/RegattasResource.java | 37 +++++- 8 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/ManeuverWindJsonDeserializer.java create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index e7d32f9abb1..0d77148b553 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -800,9 +800,6 @@ public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { } } } - if(result == null) { - System.out.println("a"); - } return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java index 1281b2f14f3..00d7e544997 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorWithEstimationDataSupportDecoratorImpl.java @@ -385,7 +385,7 @@ public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl deviationFromTackAngle, deviationFromJibeAngle); } - private Distance getClosestDistanceToMark(TimePoint timePoint) { + public Distance getClosestDistanceToMark(TimePoint timePoint) { NonCachingMarkPositionAtTimePointCache markPositionAtTimePointCache = new NonCachingMarkPositionAtTimePointCache( maneuverDetector.trackedRace, timePoint); Distance result = null; @@ -442,7 +442,7 @@ public class ManeuverDetectorWithEstimationDataSupportDecoratorImpl * Gets the relative bearing of the next mark from the boat's position and course at {@code timePoint}. The relative * bearing is calculated by absolute bearing of next mark from the boat's position minus the boat's course. */ - private Bearing getRelativeBearingToNextMark(TimePoint timePoint, Bearing boatCourse) { + public Bearing getRelativeBearingToNextMark(TimePoint timePoint, Bearing boatCourse) { NonCachingMarkPositionAtTimePointCache markPositionAtTimePointCache = new NonCachingMarkPositionAtTimePointCache( maneuverDetector.trackedRace, timePoint); Bearing result = null; diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java index b4c77ee8dd9..fa01070bb1b 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/EstimationDataSerializationDeserializationTest.java @@ -28,14 +28,14 @@ import com.sap.sailing.server.gateway.deserialization.impl.CompleteManeuverCurve import com.sap.sailing.server.gateway.deserialization.impl.DetailedBoatClassJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.ManeuverMainCurveWithEstimationDataJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.ManeuverWindJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.PositionJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.WindJsonDeserializer; import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurveWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DetailedBoatClassJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuverMainCurveWithEstimationDataJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.ManeuverWindJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.WindJsonSerializer; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; @@ -120,12 +120,9 @@ public class EstimationDataSerializationDeserializationTest { longestIntervalBetweenTwoFixes, intervalBetweenLastFixOfCurveAndNextFix, intervalBetweenFirstFixOfCurveAndPreviousFix); - MillisecondsTimePoint windTimePoint = new MillisecondsTimePoint(dateFormat.parse("06/23/2011-15:28:25")); SpeedWithBearing windSpeedWithBearing = new KnotSpeedWithBearingImpl(2, new DegreeBearingImpl(340)); - DegreePosition windPosition = new DegreePosition(54.325246, 10.148556); - Wind wind = new WindImpl(windPosition, windTimePoint, windSpeedWithBearing); - DegreePosition maneuverPosition = new DegreePosition(50.325246, 11.148556); + Wind wind = new WindImpl(maneuverPosition, mainCurve.getTimePointOfMaxTurningRate(), windSpeedWithBearing); int jibingCount = 203; int tackingCount = 12345; boolean maneuverStartsByRunningAwayFromWind = true; @@ -144,12 +141,12 @@ public class EstimationDataSerializationDeserializationTest { CompleteManeuverCurveWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), - new WindJsonSerializer(new PositionJsonSerializer()), new PositionJsonSerializer()); + new ManeuverWindJsonSerializer(), new PositionJsonSerializer()); JSONObject json = serializer.serialize(toSerialize); CompleteManeuverCurveWithEstimationDataJsonDeserializer deserializer = new CompleteManeuverCurveWithEstimationDataJsonDeserializer( new ManeuverMainCurveWithEstimationDataJsonDeserializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonDeserializer(), - new WindJsonDeserializer(new PositionJsonDeserializer()), new PositionJsonDeserializer()); + new ManeuverWindJsonDeserializer(), new PositionJsonDeserializer()); CompleteManeuverCurveWithEstimationData deserialized = deserializer.deserialize(json); assertEquals(deviationFromTargetJibeAngle, @@ -165,8 +162,8 @@ public class EstimationDataSerializationDeserializationTest { assertEquals(relativeBearingToNextMarkBeforeManeuver, deserialized.getRelativeBearingToNextMarkBeforeManeuver()); assertEquals(relativeBearingToNextMarkAfterManeuver, deserialized.getRelativeBearingToNextMarkAfterManeuver()); - assertEquals(windTimePoint, deserialized.getWind().getTimePoint()); - assertEquals(windPosition, deserialized.getWind().getPosition()); + assertEquals(wind.getTimePoint(), deserialized.getWind().getTimePoint()); + assertEquals(wind.getPosition(), deserialized.getWind().getPosition()); assertEquals(windSpeedWithBearing.getBearing(), deserialized.getWind().getBearing()); assertEquals(windSpeedWithBearing.getMetersPerSecond(), deserialized.getWind().getMetersPerSecond(), DELTA); @@ -324,12 +321,12 @@ public class EstimationDataSerializationDeserializationTest { CompleteManeuverCurveWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), - new WindJsonSerializer(new PositionJsonSerializer()), new PositionJsonSerializer()); + new ManeuverWindJsonSerializer(), new PositionJsonSerializer()); JSONObject json = serializer.serialize(toSerialize); CompleteManeuverCurveWithEstimationDataJsonDeserializer deserializer = new CompleteManeuverCurveWithEstimationDataJsonDeserializer( new ManeuverMainCurveWithEstimationDataJsonDeserializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonDeserializer(), - new WindJsonDeserializer(new PositionJsonDeserializer()), new PositionJsonDeserializer()); + new ManeuverWindJsonDeserializer(), new PositionJsonDeserializer()); CompleteManeuverCurveWithEstimationData deserialized = deserializer.deserialize(json); assertEquals(deviationFromTargetJibeAngle, diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java index 0408e37fd08..886f0e3c745 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CompleteManeuverCurveWithEstimationDataJsonDeserializer.java @@ -3,8 +3,10 @@ package com.sap.sailing.server.gateway.deserialization.impl; import org.json.simple.JSONObject; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverMainCurveWithEstimationData; @@ -26,13 +28,13 @@ public class CompleteManeuverCurveWithEstimationDataJsonDeserializer private final ManeuverMainCurveWithEstimationDataJsonDeserializer mainCurveDeserializer; private final ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonDeserializer curveWithUnstableCourseAndSpeedDeserializer; - private final WindJsonDeserializer windDeserializer; + private final ManeuverWindJsonDeserializer windDeserializer; private final PositionJsonDeserializer positionDeserializer; public CompleteManeuverCurveWithEstimationDataJsonDeserializer( ManeuverMainCurveWithEstimationDataJsonDeserializer mainCurveDeserializer, ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonDeserializer curveWithUnstableCourseAndSpeedDeserializer, - WindJsonDeserializer windDeserializer, PositionJsonDeserializer positionDeserializer) { + ManeuverWindJsonDeserializer windDeserializer, PositionJsonDeserializer positionDeserializer) { this.mainCurveDeserializer = mainCurveDeserializer; this.curveWithUnstableCourseAndSpeedDeserializer = curveWithUnstableCourseAndSpeedDeserializer; this.windDeserializer = windDeserializer; @@ -50,7 +52,9 @@ public class CompleteManeuverCurveWithEstimationDataJsonDeserializer .deserialize((JSONObject) object.get( CompleteManeuverCurveWithEstimationDataJsonSerializer.CURVE_WITH_UNSTABLE_COURSE_AND_SPEED)); JSONObject windJson = (JSONObject) object.get(CompleteManeuverCurveWithEstimationDataJsonSerializer.WIND); - Wind wind = windJson == null ? null : windDeserializer.deserialize(windJson); + SpeedWithBearing windSpeedWithBearing = windJson == null ? null : windDeserializer.deserialize(windJson); + Wind wind = windSpeedWithBearing == null ? null + : new WindImpl(position, mainCurve.getTimePointOfMaxTurningRate(), windSpeedWithBearing); Integer tackingCount = getInteger( object.get(CompleteManeuverCurveWithEstimationDataJsonSerializer.TACKING_COUNT)); Integer jibingCount = getInteger( diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/ManeuverWindJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/ManeuverWindJsonDeserializer.java new file mode 100644 index 00000000000..ab20cd8fee8 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/ManeuverWindJsonDeserializer.java @@ -0,0 +1,29 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.ManeuverWindJsonSerializer; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.impl.DegreeBearingImpl; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public class ManeuverWindJsonDeserializer implements JsonDeserializer { + + public SpeedWithBearing deserialize(JSONObject object) throws JsonDeserializationException { + + Double directionInTrueDegrees = (Double) object.get(ManeuverWindJsonSerializer.DIRECTION_IN_TRUE_DEGREES); + Double speedInKnots = (Double) object.get(ManeuverWindJsonSerializer.SPEED_IN_KNOTS); + Bearing degreeBearing = new DegreeBearingImpl(directionInTrueDegrees); + SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(speedInKnots, degreeBearing); + return speedBearing; + } + +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java index 5c3ae7b119d..e1b807cf388 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurveWithEstimationDataJsonSerializer.java @@ -29,13 +29,13 @@ public class CompleteManeuverCurveWithEstimationDataJsonSerializer private final ManeuverCurveBoundariesJsonSerializer mainCurveSerializer; private final ManeuverCurveBoundariesJsonSerializer curveWithUnstableCourseAndSpeedSerializer; - private final WindJsonSerializer windSerializer; + private final ManeuverWindJsonSerializer windSerializer; private final PositionJsonSerializer positionSerializer; public CompleteManeuverCurveWithEstimationDataJsonSerializer( ManeuverCurveBoundariesJsonSerializer mainCurveSerializer, ManeuverCurveBoundariesJsonSerializer curveWithUnstableCourseAndSpeedSerializer, - WindJsonSerializer windSerializer, PositionJsonSerializer positionSerializer) { + ManeuverWindJsonSerializer windSerializer, PositionJsonSerializer positionSerializer) { this.mainCurveSerializer = mainCurveSerializer; this.curveWithUnstableCourseAndSpeedSerializer = curveWithUnstableCourseAndSpeedSerializer; this.windSerializer = windSerializer; diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java new file mode 100644 index 00000000000..4ad7c938008 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java @@ -0,0 +1,119 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorWithEstimationDataSupportDecoratorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; +import com.sap.sailing.domain.tracking.GPSFixTrack; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; +import com.sap.sse.common.Duration; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { + public static final String GPS_FIXES = "gpsFixes"; + public static final String BOAT_CLASS = "boatClass"; + public static final String COMPETITOR_NAME = "competitorName"; + public static final String AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS = "avgIntervalBetweenFixesInSeconds"; + public static final String DISTANCE_TRAVELLED_IN_METERS = "distanceTravelledInMeters"; + public static final String START_TIME_POINT = "startUnixTime"; + public static final String END_TIME_POINT = "endUnixTime"; + public static final String WIND = "wind"; + public static final String RELATIVE_BEARING_TO_NEXT_MARK = "relativeBearingToNextMark"; + public static final String CLOSEST_DISTANCE_TO_MARK = "closestDistanceToMarkInMeters"; + + private final BoatClassJsonSerializer boatClassJsonSerializer; + private final GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer; + private final boolean addWind; + private final boolean addNextWaypoint; + private final ManeuverWindJsonSerializer windJsonSerializer; + private final Boolean smoothFixes; + + public GpsFixesWithEstimationDataJsonSerializer(BoatClassJsonSerializer boatClassJsonSerializer, + GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer, ManeuverWindJsonSerializer windJsonSerializer, + boolean addWind, boolean addNextWaypoint, Boolean smoothFixes) { + this.boatClassJsonSerializer = boatClassJsonSerializer; + this.gpsFixMovingJsonSerializer = gpsFixMovingJsonSerializer; + this.windJsonSerializer = windJsonSerializer; + this.addWind = addWind; + this.addNextWaypoint = addNextWaypoint; + this.smoothFixes = smoothFixes; + } + + @Override + public JSONObject serialize(TrackedRace trackedRace) { + final JSONObject result = new JSONObject(); + JSONArray byCompetitorJson = new JSONArray(); + result.put(BYCOMPETITOR, byCompetitorJson); + for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + ManeuverDetectorWithEstimationDataSupportDecoratorImpl estimationDataSupportDecoratorImpl = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + maneuverDetector, null); + TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); + if (trackTimeInfo != null) { + final JSONObject forCompetitorJson = new JSONObject(); + byCompetitorJson.add(forCompetitorJson); + forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); + forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer + .serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); + final JSONArray gpsFixesWithEstimationData = new JSONArray(); + GPSFixTrack track = trackedRace.getTrack(competitor); + track.lockForRead(); + try { + for (GPSFixMoving gpsFix : track.getFixes()) { + JSONObject serializedGpsFix = gpsFixMovingJsonSerializer.serialize(gpsFix); + if (addWind) { + Wind wind = trackedRace.getWind(gpsFix.getPosition(), gpsFix.getTimePoint()); + JSONObject serializedWind = wind == null ? null : windJsonSerializer.serialize(wind); + serializedGpsFix.put(WIND, serializedWind); + } + if (addNextWaypoint) { + Distance closestDistanceToMark = estimationDataSupportDecoratorImpl + .getClosestDistanceToMark(gpsFix.getTimePoint()); + SpeedWithBearing speedWithBearing = smoothFixes + ? track.getEstimatedSpeed(gpsFix.getTimePoint()) : gpsFix.getSpeed(); + Bearing relativeBearingToNextMark = speedWithBearing == null ? null + : estimationDataSupportDecoratorImpl.getRelativeBearingToNextMark( + gpsFix.getTimePoint(), speedWithBearing.getBearing()); + serializedGpsFix.put(CLOSEST_DISTANCE_TO_MARK, + closestDistanceToMark == null ? null : closestDistanceToMark.getMeters()); + serializedGpsFix.put(RELATIVE_BEARING_TO_NEXT_MARK, + relativeBearingToNextMark == null ? null : relativeBearingToNextMark.getDegrees()); + } + gpsFixesWithEstimationData.add(serializedGpsFix); + } + } finally { + track.unlockAfterRead(); + } + forCompetitorJson.put(GPS_FIXES, gpsFixesWithEstimationData); + Duration averageIntervalBetweenFixes = trackedRace.getTrack(competitor) + .getAverageIntervalBetweenFixes(); + forCompetitorJson.put(AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS, + averageIntervalBetweenFixes == null ? 0 : averageIntervalBetweenFixes.asSeconds()); + Double distanceTravelledInMeters = null; + if (trackTimeInfo.getTrackStartTimePoint() != null && trackTimeInfo.getTrackEndTimePoint() != null) { + distanceTravelledInMeters = track.getDistanceTraveled(trackTimeInfo.getTrackStartTimePoint(), + trackTimeInfo.getTrackEndTimePoint()).getMeters(); + } + forCompetitorJson.put(DISTANCE_TRAVELLED_IN_METERS, distanceTravelledInMeters); + forCompetitorJson.put(START_TIME_POINT, trackTimeInfo.getTrackStartTimePoint() == null ? null + : trackTimeInfo.getTrackStartTimePoint().asMillis()); + forCompetitorJson.put(END_TIME_POINT, trackTimeInfo.getTrackEndTimePoint() == null ? null + : trackTimeInfo.getTrackEndTimePoint().asMillis()); + } + } + return result; + } + +} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 6ae0be5b38d..c2e4743c7dd 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -85,9 +85,12 @@ import com.sap.sailing.server.gateway.serialization.impl.DetailedBoatClassJsonSe import com.sap.sailing.server.gateway.serialization.impl.DistanceJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.GPSFixJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.GPSFixMovingJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.GpsFixesWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuverJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuverMainCurveWithEstimationDataJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.ManeuverWindJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ManeuversJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.MarkPassingsJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.NationalityJsonSerializer; @@ -1047,7 +1050,39 @@ public class RegattasResource extends AbstractSailingServerResource { new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), - new WindJsonSerializer(new PositionJsonSerializer()), new PositionJsonSerializer())); + new ManeuverWindJsonSerializer(), new PositionJsonSerializer())); + JSONObject jsonMarkPassings = serializer.serialize(trackedRace); + String json = jsonMarkPassings.toJSONString(); + return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } + } + return response; + } + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{regattaname}/races/{racename}/gpsFixesWithEstimationData") + public Response getGpsFixesWithEstimationData(@PathParam("regattaname") String regattaName, + @PathParam("racename") String raceName, @QueryParam("addWind") @DefaultValue("true") Boolean addWind, + @QueryParam("addNextWaypoint") @DefaultValue("true") Boolean addNextWaypoint, + @QueryParam("smoothFixes") @DefaultValue("true") Boolean smoothFixes) { + Response response; + Regatta regatta = findRegattaByName(regattaName); + if (regatta == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + RaceDefinition race = findRaceByName(regatta, raceName); + if (race == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { + TrackedRace trackedRace = findTrackedRace(regattaName, raceName); + GpsFixesWithEstimationDataJsonSerializer serializer = new GpsFixesWithEstimationDataJsonSerializer( + new DetailedBoatClassJsonSerializer(), new GPSFixMovingJsonSerializer(), + new ManeuverWindJsonSerializer(), addWind, addNextWaypoint, smoothFixes); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); From 02a2ff00c9d1ddd7b023c571bac738e68255f5d5 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 5 Jul 2018 13:06:39 +0200 Subject: [PATCH 062/102] bug4614: Changed getDoubleValue() in SortableMinMaxColumn to return the desired value according to the rendering order. --- .../racemap/maneuver/ManeuverTablePanel.java | 10 +++--- .../maneuver/SortableMinMaxColumn.java | 2 +- .../gwt/ui/leaderboard/MinMaxRenderer.java | 31 ++++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index 2d233399dbf..48a9d511e1e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -141,17 +141,17 @@ public class ManeuverTablePanel extends AbstractCompositeComponent { protected static final String BACKGROUND_BAR_STYLE_GOOD = "minMaxBackgroundBarGood"; private final HasStringAndDoubleValue valueProvider; + /** used to determine minimum and maximum values for the rendered bars.*/ private final Comparator comparator; private Double minimumValue; private Double maximumValue; @@ -119,7 +120,8 @@ public class MinMaxRenderer { * @param row * The row to get the percentage for. */ - protected int getPercentage(T row) { + + protected int getPercentage(T row) { int percentage = 0; Double value = valueProvider.getDoubleValue(row); if (value != null) { @@ -128,10 +130,8 @@ public class MinMaxRenderer { percentage = (int) (minBarLength + (100. - minBarLength) * (value - getMinimumDouble()) / (getMaximumDouble() - getMinimumDouble())); } - - } + } return percentage; - } private Double getMinimumDouble() { @@ -149,23 +149,24 @@ public class MinMaxRenderer { * The values of {@link LeaderboardRowDTO}s to determine the minimum and maximum values for. */ public void updateMinMax(Iterable displayedLeaderboardRowsProvider) { - T minimumRow = null; - T maximumRow = null; + T minimumOrderRow = null; + T maximumOrderRow = null; for (T row : displayedLeaderboardRowsProvider) { if (valueProvider.getDoubleValue(row) != null - && (minimumRow == null || comparator.compare(minimumRow, row) > 0)) { - minimumRow = row; + && (minimumOrderRow == null || comparator.compare(minimumOrderRow, row) > 0)) { + minimumOrderRow = row; } if (valueProvider.getDoubleValue(row) != null - && (maximumRow == null || comparator.compare(maximumRow, row) < 0)) { - maximumRow = row; + && (maximumOrderRow == null || comparator.compare(maximumOrderRow, row) < 0)) { + maximumOrderRow = row; } + } + if (minimumOrderRow != null) { + minimumValue = valueProvider.getDoubleValue(minimumOrderRow); + } - if (minimumRow != null) { - minimumValue = valueProvider.getDoubleValue(minimumRow); - } - if (maximumRow != null) { - maximumValue = valueProvider.getDoubleValue(maximumRow); + if (maximumOrderRow != null) { + maximumValue = valueProvider.getDoubleValue(maximumOrderRow); } } From 391ec77d3a2aad776717ac7f2ba98a910079aaf7 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 5 Jul 2018 13:11:08 +0200 Subject: [PATCH 063/102] Bug 4479: Allow arbitrary regatta/race pairs when adding wind via REST API (not only multiple races for a single regatta) --- .../gateway/jaxrs/api/WindResource.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 25a11d984f0..2ffe0f9d5ab 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -38,14 +38,18 @@ public class WindResource extends AbstractSailingServerResource { JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); JSONArray windDatas = (JSONArray) requestObject.get("windData"); - String regattaName = (String) requestObject.get("regattaName"); - JSONArray raceNames = (JSONArray) requestObject.get("raceNames"); + JSONArray regattaNamesAndRaceNames = (JSONArray) requestObject.get("regattaNamesAndRaceNames"); WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); - JSONObject answer = new JSONObject(); + JSONArray answer = new JSONArray(); String windSourceId = (String) requestObject.get("windSourceId"); - for (Object raceName : raceNames) { - RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, (String) raceName); + for (Object regattaNameAndRaceName : regattaNamesAndRaceNames) { + final JSONObject regattaNameAndRaceNameObject = Helpers.toJSONObjectSafe(regattaNameAndRaceName); + String regattaName = (String) regattaNameAndRaceNameObject.get("regattaName"); + String raceName = (String) regattaNameAndRaceNameObject.get("raceName"); + RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, raceName); + JSONObject answerForRace = new JSONObject(); + answerForRace.put("regattaNameAndRaceName", regattaNameAndRaceName); if (windSourceType == WindSourceType.EXPEDITION || windSourceType == WindSourceType.WEB) { DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier); WindSource windsource = new WindSourceWithAdditionalID(windSourceType, windSourceId); @@ -58,13 +62,14 @@ public class WindResource extends AbstractSailingServerResource { boolean success = trackedRace.recordWind(data, windsource); subAnswer.add(i, success); } - answer.put(identifier.getRaceName(), subAnswer); + answerForRace.put("answer", subAnswer); } else { - answer.put(identifier.getRaceName(), "Could not resolve traced race"); + answerForRace.put("answer", "Could not resolve traced race"); } } else { - answer.put(identifier.getRaceName(), "Only Windsourcetypes expedition or web are allowed"); + answerForRace.put("answer", "Only Windsourcetypes expedition or web are allowed"); } + answer.add(answerForRace); } return Response.ok(answer.toJSONString()).build(); } From 735d9c607e72efb2b6a3501b200cd2c9860d5ce4 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 5 Jul 2018 13:35:43 +0200 Subject: [PATCH 064/102] Bug 4479: Removed duplicate cast --- .../com/sap/sailing/server/gateway/jaxrs/api/WindResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 2ffe0f9d5ab..68576ff9840 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -40,7 +40,7 @@ public class WindResource extends AbstractSailingServerResource { JSONArray regattaNamesAndRaceNames = (JSONArray) requestObject.get("regattaNamesAndRaceNames"); - WindSourceType windSourceType = WindSourceType.valueOf((String) (String) requestObject.get("windSourceType")); + WindSourceType windSourceType = WindSourceType.valueOf((String) requestObject.get("windSourceType")); JSONArray answer = new JSONArray(); String windSourceId = (String) requestObject.get("windSourceId"); for (Object regattaNameAndRaceName : regattaNamesAndRaceNames) { From 1726bb2321a765d045cae464e34df242c22c4ea4 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 5 Jul 2018 13:45:02 +0200 Subject: [PATCH 065/102] Bug 4479: Updated documentation --- .../webservices/api/v1/putWind.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html index eab62f406bf..0eb95d18407 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/putWind.html @@ -30,9 +30,9 @@ Examples: - Request:
          {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120,"speedinknots":60}],"regattaName":"ESS 2016 Cardiff","raceNames":["Race 1"],"windSourceType":"WEB","windSourceId":"tracker02"} + Request:
          {"windData":[{"position":{"latitude_deg":1,"longitude_deg":1},"timepoint":1234,"direction":120,"speedinknots":60}],"regattaNamesAndRaceNames":[{"regattaName":"ESS 2016 Cardiff","raceName":"Race 1"}],"windSourceType":"WEB","windSourceId":"tracker02"}
          - Answer:
          {"R1":[true]} + Answer:
          [{"regattaNameAndRaceName":{"regattaName":"ESS 2016 Cardiff","raceName":"Race 1"},"answer":[true]}] From d374036bf672111ba170f49067ad7692dc1bcd95 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 5 Jul 2018 13:46:26 +0200 Subject: [PATCH 066/102] Bug 4479: Activated permission check --- .../sap/sailing/server/gateway/jaxrs/api/WindResource.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java index 68576ff9840..1a6a3c1c881 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/WindResource.java @@ -6,6 +6,7 @@ import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.shiro.SecurityUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -16,6 +17,8 @@ import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; +import com.sap.sailing.domain.common.security.Permission; +import com.sap.sailing.domain.common.security.Permission.Mode; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; @@ -32,7 +35,7 @@ public class WindResource extends AbstractSailingServerResource { @Consumes(MediaType.APPLICATION_JSON) @Path("putWind") public Response putWind(String json) throws ParseException, JsonDeserializationException { -// SecurityUtils.getSubject().checkPermission(Permission.TRACKED_RACE.getStringPermission(Mode.UPDATE)); + SecurityUtils.getSubject().checkPermission(Permission.TRACKED_RACE.getStringPermission(Mode.UPDATE)); Object requestBody = JSONValue.parseWithException(json); JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody); From 24f922bde2d3a0557c07be7dbe1077411c8027b6 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Thu, 5 Jul 2018 14:05:23 +0200 Subject: [PATCH 067/102] Added parameters to get maneuvers and gps fixes before and after start/finish line --- .../impl/ManeuverDetectorImpl.java | 2 +- ...urvesWithEstimationDataJsonSerializer.java | 77 ++++++++++++++++--- ...FixesWithEstimationDataJsonSerializer.java | 36 ++++++++- .../gateway/jaxrs/api/RegattasResource.java | 28 ++++++- 4 files changed, 124 insertions(+), 19 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 0d77148b553..b4068b215f9 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -128,7 +128,7 @@ public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { * @return an empty list if no maneuver spots are detected for competitor between from and * to, or else the list of maneuver spots with corresponding maneuvers detected. */ - protected List detectManeuvers(TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) { + public List detectManeuvers(TimePoint earliestManeuverStart, TimePoint latestManeuverEnd) { ApproximatedFixesCalculator approximatedFixesCalculator = new ApproximatedFixesCalculatorImpl(trackedRace, competitor); Iterable approximatedFixes = approximatedFixesCalculator.approximate(earliestManeuverStart, diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java index 6bb7a8fb7d6..357d034b0d4 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java @@ -1,5 +1,8 @@ package com.sap.sailing.server.gateway.serialization.impl; +import java.util.List; +import java.util.stream.Collectors; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -10,6 +13,7 @@ import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimat import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorWithEstimationDataSupportDecoratorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverSpot; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; @@ -17,6 +21,8 @@ import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsDurationImpl; /** * @@ -36,13 +42,23 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst private final BoatClassJsonSerializer boatClassJsonSerializer; private final CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer; private final PolarDataService polarDataService; + private final Integer startBeforeStartLineInSeconds; + private final Integer endBeforeStartLineInSeconds; + private final Integer startAfterFinishLineInSeconds; + private final Integer endAfterFinishLineInSeconds; public CompleteManeuverCurvesWithEstimationDataJsonSerializer(PolarDataService polarDataService, BoatClassJsonSerializer boatClassJsonSerializer, - CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer) { + CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer, + Integer startBeforeStartLineInSeconds, Integer endBeforeStartLineInSeconds, + Integer startAfterFinishLineInSeconds, Integer endAfterFinishLineInSeconds) { this.polarDataService = polarDataService; this.boatClassJsonSerializer = boatClassJsonSerializer; this.maneuverWithEstimationDataJsonSerializer = maneuverWithEstimationDataJsonSerializer; + this.startBeforeStartLineInSeconds = startBeforeStartLineInSeconds; + this.endBeforeStartLineInSeconds = endBeforeStartLineInSeconds; + this.startAfterFinishLineInSeconds = startAfterFinishLineInSeconds; + this.endAfterFinishLineInSeconds = endAfterFinishLineInSeconds; } @Override @@ -53,15 +69,41 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst for (Competitor competitor : trackedRace.getRace().getCompetitors()) { ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); + TimePoint from = null; + TimePoint to = null; + if (startBeforeStartLineInSeconds != Integer.MIN_VALUE) { + from = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(startBeforeStartLineInSeconds * 1000L)); + } else if (startAfterFinishLineInSeconds != Integer.MIN_VALUE) { + from = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(startAfterFinishLineInSeconds * 1000L)); + } + if (endAfterFinishLineInSeconds != Integer.MIN_VALUE) { + to = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(endAfterFinishLineInSeconds * 1000L)); + } else if (endBeforeStartLineInSeconds != Integer.MIN_VALUE) { + to = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(endBeforeStartLineInSeconds * 1000L)); + } if (trackTimeInfo != null) { + if (from != null ^ to != null) { + if (from == null) { + from = trackTimeInfo.getTrackStartTimePoint(); + } + if (to == null) { + to = trackTimeInfo.getTrackEndTimePoint(); + } + } final JSONObject forCompetitorJson = new JSONObject(); byCompetitorJson.add(forCompetitorJson); forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer .serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); final JSONArray completeManeuverCurvesWithEstimationData = new JSONArray(); - for (CompleteManeuverCurveWithEstimationData maneuver : getCompleteManeuverCurvesWithEstimationData( - trackedRace, competitor)) { + Iterable completeManeuvers = from != null + ? getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor, from, to) + : getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor); + for (CompleteManeuverCurveWithEstimationData maneuver : completeManeuvers) { completeManeuverCurvesWithEstimationData .add(maneuverWithEstimationDataJsonSerializer.serialize(maneuver)); } @@ -87,18 +129,29 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst return result; } + private Iterable getCompleteManeuverCurvesWithEstimationData( + TrackedRace trackedRace, Competitor competitor, TimePoint from, TimePoint to) { + ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + List maneuverSpots = maneuverDetector.detectManeuvers(from, to); + List maneuverCurves = maneuverSpots.stream() + .map(maneuverSpot -> maneuverSpot.getManeuverCurve()).collect(Collectors.toList()); + ManeuverDetectorWithEstimationDataSupport maneuverDetectorWithEstimationData = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + maneuverDetector, polarDataService); + Iterable maneuversWithEstimationData = maneuverDetectorWithEstimationData + .getCompleteManeuverCurvesWithEstimationData(maneuverCurves); + return maneuversWithEstimationData; + } + private Iterable getCompleteManeuverCurvesWithEstimationData( TrackedRace trackedRace, Competitor competitor) { Iterable maneuvers = trackedRace.getManeuvers(competitor, false); - ManeuverDetectorWithEstimationDataSupport maneuverDetector = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( - new ManeuverDetectorImpl(trackedRace, competitor), polarDataService); - Iterable maneuversWithEstimationData = null; - try { - Iterable maneuverCurves = maneuverDetector.getCompleteManeuverCurves(maneuvers); - maneuversWithEstimationData = maneuverDetector.getCompleteManeuverCurvesWithEstimationData(maneuverCurves); - } catch (Exception e) { - e.printStackTrace(); - } + ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + ManeuverDetectorWithEstimationDataSupport maneuverDetectorWithEstimationData = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + maneuverDetector, polarDataService); + Iterable maneuverCurves = maneuverDetectorWithEstimationData + .getCompleteManeuverCurves(maneuvers); + Iterable maneuversWithEstimationData = maneuverDetectorWithEstimationData + .getCompleteManeuverCurvesWithEstimationData(maneuverCurves); return maneuversWithEstimationData; } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java index 4ad7c938008..06cddcbfdd3 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java @@ -15,6 +15,8 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsDurationImpl; /** * @@ -39,16 +41,26 @@ public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRac private final boolean addNextWaypoint; private final ManeuverWindJsonSerializer windJsonSerializer; private final Boolean smoothFixes; + private Integer startBeforeStartLineInSeconds; + private Integer endBeforeStartLineInSeconds; + private Integer startAfterFinishLineInSeconds; + private Integer endAfterFinishLineInSeconds; public GpsFixesWithEstimationDataJsonSerializer(BoatClassJsonSerializer boatClassJsonSerializer, GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer, ManeuverWindJsonSerializer windJsonSerializer, - boolean addWind, boolean addNextWaypoint, Boolean smoothFixes) { + boolean addWind, boolean addNextWaypoint, Boolean smoothFixes, Integer startBeforeStartLineInSeconds, + Integer endBeforeStartLineInSeconds, Integer startAfterFinishLineInSeconds, + Integer endAfterFinishLineInSeconds) { this.boatClassJsonSerializer = boatClassJsonSerializer; this.gpsFixMovingJsonSerializer = gpsFixMovingJsonSerializer; this.windJsonSerializer = windJsonSerializer; this.addWind = addWind; this.addNextWaypoint = addNextWaypoint; this.smoothFixes = smoothFixes; + this.startBeforeStartLineInSeconds = startBeforeStartLineInSeconds; + this.endBeforeStartLineInSeconds = endBeforeStartLineInSeconds; + this.startAfterFinishLineInSeconds = startAfterFinishLineInSeconds; + this.endAfterFinishLineInSeconds = endAfterFinishLineInSeconds; } @Override @@ -61,6 +73,26 @@ public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRac ManeuverDetectorWithEstimationDataSupportDecoratorImpl estimationDataSupportDecoratorImpl = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( maneuverDetector, null); TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); + TimePoint from = null; + TimePoint to = null; + if (startBeforeStartLineInSeconds != Integer.MIN_VALUE) { + from = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(startBeforeStartLineInSeconds * 1000L)); + } else if (startAfterFinishLineInSeconds != Integer.MIN_VALUE) { + from = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(startAfterFinishLineInSeconds * 1000L)); + } else { + from = trackTimeInfo.getTrackStartTimePoint(); + } + if (endAfterFinishLineInSeconds != Integer.MIN_VALUE) { + to = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(endAfterFinishLineInSeconds * 1000L)); + } else if (endBeforeStartLineInSeconds != Integer.MIN_VALUE) { + to = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(endBeforeStartLineInSeconds * 1000L)); + } else { + to = trackTimeInfo.getTrackEndTimePoint(); + } if (trackTimeInfo != null) { final JSONObject forCompetitorJson = new JSONObject(); byCompetitorJson.add(forCompetitorJson); @@ -71,7 +103,7 @@ public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRac GPSFixTrack track = trackedRace.getTrack(competitor); track.lockForRead(); try { - for (GPSFixMoving gpsFix : track.getFixes()) { + for (GPSFixMoving gpsFix : track.getFixes(from, true, to, true)) { JSONObject serializedGpsFix = gpsFixMovingJsonSerializer.serialize(gpsFix); if (addWind) { Wind wind = trackedRace.getWind(gpsFix.getPosition(), gpsFix.getTimePoint()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index c2e4743c7dd..8215e8819d9 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -1030,7 +1030,15 @@ public class RegattasResource extends AbstractSailingServerResource { @Produces("application/json;charset=UTF-8") @Path("{regattaname}/races/{racename}/completeManeuverCurvesWithEstimationData") public Response getCompleteManeuverCurvesWithEstimationData(@PathParam("regattaname") String regattaName, - @PathParam("racename") String raceName) { + @PathParam("racename") String raceName, + @QueryParam("startBeforeStartLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer startBeforeStartLineInSeconds, + @QueryParam("endBeforeStartLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer endBeforeStartLineInSeconds, + @QueryParam("startAfterFinishLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer startAfterFinishLineInSeconds, + @QueryParam("endAfterFinishLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer endAfterFinishLineInSeconds) { Response response; Regatta regatta = findRegattaByName(regattaName); if (regatta == null) { @@ -1050,7 +1058,9 @@ public class RegattasResource extends AbstractSailingServerResource { new CompleteManeuverCurveWithEstimationDataJsonSerializer( new ManeuverMainCurveWithEstimationDataJsonSerializer(), new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), - new ManeuverWindJsonSerializer(), new PositionJsonSerializer())); + new ManeuverWindJsonSerializer(), new PositionJsonSerializer()), + startBeforeStartLineInSeconds, endBeforeStartLineInSeconds, startAfterFinishLineInSeconds, + endAfterFinishLineInSeconds); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); @@ -1065,7 +1075,15 @@ public class RegattasResource extends AbstractSailingServerResource { public Response getGpsFixesWithEstimationData(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName, @QueryParam("addWind") @DefaultValue("true") Boolean addWind, @QueryParam("addNextWaypoint") @DefaultValue("true") Boolean addNextWaypoint, - @QueryParam("smoothFixes") @DefaultValue("true") Boolean smoothFixes) { + @QueryParam("smoothFixes") @DefaultValue("true") Boolean smoothFixes, + @QueryParam("startBeforeStartLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer startBeforeStartLineInSeconds, + @QueryParam("endBeforeStartLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer endBeforeStartLineInSeconds, + @QueryParam("startAfterFinishLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer startAfterFinishLineInSeconds, + @QueryParam("endAfterFinishLineInSeconds") @DefaultValue(Integer.MIN_VALUE + + "") Integer endAfterFinishLineInSeconds) { Response response; Regatta regatta = findRegattaByName(regattaName); if (regatta == null) { @@ -1082,7 +1100,9 @@ public class RegattasResource extends AbstractSailingServerResource { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); GpsFixesWithEstimationDataJsonSerializer serializer = new GpsFixesWithEstimationDataJsonSerializer( new DetailedBoatClassJsonSerializer(), new GPSFixMovingJsonSerializer(), - new ManeuverWindJsonSerializer(), addWind, addNextWaypoint, smoothFixes); + new ManeuverWindJsonSerializer(), addWind, addNextWaypoint, smoothFixes, + startBeforeStartLineInSeconds, endBeforeStartLineInSeconds, startAfterFinishLineInSeconds, + endAfterFinishLineInSeconds); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); From e829e129a764017f581d635deecabe68b2af05a3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 5 Jul 2018 14:31:21 +0200 Subject: [PATCH 068/102] whitespace change only Change-Id: I30791c8726a6d6d3086274c43994b346dfa92bc7 --- .../src/com/sap/sse/gwt/adminconsole/AdminConsolePanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/AdminConsolePanel.java b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/AdminConsolePanel.java index 460c9c7ca03..38f41a48d09 100755 --- a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/AdminConsolePanel.java +++ b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/AdminConsolePanel.java @@ -210,7 +210,7 @@ public class AdminConsolePanel extends HeaderPanel implements HandleTabSelectabl final Anchor releaseNotesLink = new Anchor(new SafeHtmlBuilder().appendEscaped(releaseNotesAnchorLabel).toSafeHtml(), releaseNotesURL); sysinfoPanel.add(releaseNotesLink); informationPanel.add(sysinfoPanel, DockPanel.EAST); - informationPanel.setCellHorizontalAlignment(sysinfoPanel, HasHorizontalAlignment.ALIGN_RIGHT); + informationPanel.setCellHorizontalAlignment(sysinfoPanel, HasHorizontalAlignment.ALIGN_RIGHT); this.setFooterWidget(informationPanel); topLevelTabPanel.setSize("100%", "100%"); this.setContentWidget(topLevelTabPanel); From cbce7840561c22a77b669fe7769b3922625151a5 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 5 Jul 2018 17:59:44 +0200 Subject: [PATCH 069/102] fixed translation of "regattas" in German for apps Change-Id: Ib76a9e03966e3e525d1d2f1e64209f4f8e41d2c4 --- mobile/com.sap.sailing.android.shared/res/values-de/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/com.sap.sailing.android.shared/res/values-de/strings.xml b/mobile/com.sap.sailing.android.shared/res/values-de/strings.xml index 8028c52fd27..6b5907753ff 100644 --- a/mobile/com.sap.sailing.android.shared/res/values-de/strings.xml +++ b/mobile/com.sap.sailing.android.shared/res/values-de/strings.xml @@ -29,7 +29,7 @@ Ungültiger QR-Code - Ihre Regattas: + Ihre Regatten: Aktualisieren From 008bd93fec706282153f39d6b8ac5b077f29b793 Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Thu, 5 Jul 2018 18:04:34 +0200 Subject: [PATCH 070/102] First attempt to correct the maneuver count calculations of TrackedRaceOfCompetitor --- .../data/RaceOfCompetitorWithContext.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java index e1b5013233e..e3258e846e6 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/RaceOfCompetitorWithContext.java @@ -259,22 +259,29 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext { } private int getNumberOf(ManeuverType maneuverType) { - TrackedRace trackedRace = getTrackedRace(); int number = 0; - if (trackedRace != null && trackedRace.getStartOfRace() != null) { - final TimePoint end; - final TimePoint endOfTracking = trackedRace.getEndOfTracking(); - if (trackedRace.getEndOfRace() != null) { - end = trackedRace.getEndOfRace(); + TrackedRace trackedRace = getTrackedRace(); + if (trackedRace != null) { + Course course = trackedRace.getRace().getCourse(); + Waypoint startWaypoint = course.getFirstWaypoint(); + MarkPassing startPassing = trackedRace.getMarkPassing(getCompetitor(), startWaypoint); + TimePoint start = startPassing != null ? startPassing.getTimePoint() : trackedRace.getStartOfRace(); + + Waypoint finishWaypoint = course.getLastWaypoint(); + MarkPassing finishPassing = trackedRace.getMarkPassing(getCompetitor(), finishWaypoint); + TimePoint end; + if (finishPassing != null) { + end = finishPassing.getTimePoint(); } else { - final TimePoint now = MillisecondsTimePoint.now(); - if (endOfTracking != null && endOfTracking.before(now)) { - end = endOfTracking; - } else { - end = now; + end = trackedRace.getEndOfRace(); + if (end == null) { + TimePoint endOfTracking = trackedRace.getEndOfTracking(); + TimePoint now = MillisecondsTimePoint.now(); + end = endOfTracking != null && endOfTracking.before(now) ? endOfTracking : now; } } - for (Maneuver maneuver : trackedRace.getManeuvers(getCompetitor(), trackedRace.getStartOfRace(), end, false)) { + + for (Maneuver maneuver : trackedRace.getManeuvers(getCompetitor(), start, end, false)) { if (maneuver.getType() == maneuverType) { number++; } From de87882ad6b6db262693b53c84278d3b65cce9a8 Mon Sep 17 00:00:00 2001 From: "service.tip.git" Date: Thu, 5 Jul 2018 16:20:30 +0000 Subject: [PATCH 071/102] [INTERNAL] Translation delivery: commit by SLS Change-Id: I2b7b09481aaa3102c9a87abc1f52f0c77e06a0a9 --- .../sailing/gwt/ui/client/StringMessages_es.properties | 4 +++- .../sailing/gwt/ui/client/StringMessages_fr.properties | 10 ++++++---- .../sailing/gwt/ui/client/StringMessages_ja.properties | 4 +++- .../sailing/gwt/ui/client/StringMessages_pt.properties | 4 +++- .../sailing/gwt/ui/client/StringMessages_ru.properties | 4 +++- .../sailing/gwt/ui/client/StringMessages_zh.properties | 4 +++- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index c0699845849..6ff87aed27e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=Cuenta atrás. Solo falta {0,number,#,###} anniversaryMajorCountdownDescription=Estamos celebrando nuestra {0,number,#,###} carrera en www.sapsailing.com. ¿Qué carrera batirá la marca? El organizador de esta carrera de aniversario recibirá un total de 10 000 euros para fines benéficos. El ganador se anunciará en este sitio web. Estén atentos y cuenten con nosotros. anniversaryRepdigitCountdownTeaser=Cuenta atrás. Solo faltan {0,number,#,###} carreras hasta la {1,number,#,###} carrera. anniversaryRepdigitCountdownTeaser[one]=Cuenta atrás. Solo falta {0,number,#,###} carrera hasta la {1,number,#,###} carrera. -anniversaryRepdigitCountdownDescription=Celebramos nuestra carrera del número afortunado. ¿Quién realizará la {0,number, #,###} carrera en www.sapsailing.com? Los participantes de esta carrera obtendrán un festival de verano gratuito de primera clase por parte de SAP. Los ganadores se anunciarán en este sitio web. Estén atentos y cuenten con nosotros. +anniversaryRepdigitCountdownDescription=Celebramos nuestras carreras del número afortunado. ¿Quién realizará la {0,number, #,###} carrera en www.sapsailing.com? Los organizadores obtendrán una barbacoa gratuita con bebidas. Los ganadores se anunciarán en este sitio web. Estén atentos y cuenten con nosotros. anniversaryAnnouncementTeaser=Misión cumplida. {0,number,#,###} Carreras con SAP Sailing Analytics anniversaryAnnouncementDescription=3,2,1... Felicitamos a los participantes de la carrera {0}. ¡Lo conseguísteis! Os agradecemos vuestra confianza en SAP Sailing Analytics y esperamos continuar navegando más de 10 000 carreras con vosotros. anniversaryRaceLinkText=Mostrar carrera de aniversario @@ -2066,3 +2066,5 @@ multiUrlChangeSave=Grabar modificaciones de URL multiUrlChangeNewURL=URL nuevo multiUrlNoPrefixWarning=No existe ningún prefijo común, esto significa que normalmente no todos los vídeos seleccionados se alojan actualmente en la misma ubicación. Proceda bajo su propio riesgo. multiUrlChangeExplain=Este diálogo reemplazará en masa las partes comunes al inicio de los URLs del rastreo de medios. Asegúrate de que todas las URL empiezan con el mismo prefijo. Además, eche un vistazo a la nueva columna del URL, y pruebe los URLs resultantes antes de pulsar Grabar. +lastEvent=Último evento: {0} +teaserOverallLinkToolTip=Para visualizar las series generales, haga clic en la esquina amarilla diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index a2f012fd0e5..2f41442f776 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=Attention ! Plus qu''{0,number,#,###} cour anniversaryMajorCountdownDescription=Nous célébrons notre {0,number,#,###}e course sur www.sapsailing.com ! Quelle sera cette course ? L''organisateur de cette course anniversaire recevra 10 000 euros au profit de l''association caritative de son choix. Le gagnant sera annoncé sur ce site Web. Restez connecté et ne ratez pas le compte à rebours ! anniversaryRepdigitCountdownTeaser=Attention ! Plus que {0,number,#,###} courses avant la {1,number,#,###}e course. anniversaryRepdigitCountdownTeaser[one]=Attention ! Plus qu''{0,number,#,###} course avant la {1,number,#,###}e course. -anniversaryRepdigitCountdownDescription=Nous célébrons notre numéro de course porte-bonheur ! Qui effectuera la {0,number, #,###}e course sur www.sapsailing.com ? Les participants de cette course seront invités à un festival d''été de haut niveau par SAP. Les gagnants seront annoncés sur ce site Web. Restez connecté et ne ratez pas le compte à rebours ! +anniversaryRepdigitCountdownDescription=Nous célébrons notre numéro de course porte-bonheur ! Qui effectuera la {0,number, #,###}e course sur www.sapsailing.com ? Les organisateurs se verront offrir un barbecue et des boissons. Les gagnants seront annoncés sur ce site Web. Restez connecté et ne ratez pas le compte à rebours ! anniversaryAnnouncementTeaser=Mission accomplie ! {0,number,#,###} courses avec SAP Sailing Analytics ! anniversaryAnnouncementDescription=3, 2, 1... Félicitation aux participants de la course {0}. Vous avez gagné ! Nous vous remercions pour la confiance que vous accordez à SAP Sailing Analytics et nous espérons partager encore de nombreuses courses avec vous ! anniversaryRaceLinkText=Afficher la course anniversaire @@ -1988,9 +1988,9 @@ expeditionAws=eAWS expeditionTwa=eTWA expeditionTws=eTWS expeditionTwd=eTWD -expeditionTargTwa=eTWA cible -expeditionBoatSpeed=eVitesse du bateau -expeditionTargBoatSpeed=eVitesse du bateau cible +expeditionTargTwa=eTarget TWA +expeditionBoatSpeed=eBoat speed +expeditionTargBoatSpeed=eTarget boat speed expeditionBsSog=eVB Vf expeditionSOG=eVf expeditionCOG=eRf @@ -2066,3 +2066,5 @@ multiUrlChangeSave=Sauvegarder modifications apportées aux URL multiUrlChangeNewURL=Nouvelle URL multiUrlNoPrefixWarning=Aucun préfixe commun n''a été trouvé. Cela signifie généralement que les vidéos sélectionnées ne sont actuellement pas toutes hébergées au même endroit. Vous assumez les risques liés à leur utilisation. multiUrlChangeExplain=Cette boîte de dialogue remplacera en masse les éléments communs au début des URL de piste média. Assurez-vous que toutes les URL commencent par le même préfixe. Veuillez également prendre le temps d''observer la nouvelle colonne d''URL, et de tester les URL résultantes avant de sauvegarder. +lastEvent=Dernier événement : {0} +teaserOverallLinkToolTip=Pour afficher l''ensemble des séries, cliquez sur le coin jaune. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index fcb938dd4f3..766dfca323d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=カウントダウン情報です。{1,numb anniversaryMajorCountdownDescription=SAP では、www.sapsailing.com における {0,number,#,###} 番目のレースを祝うことにしています。どのレースがゴールテープを切るでしょうか。その記念レースの主催者には、義援目的で合計で 10,000 ユーロが贈呈されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 anniversaryRepdigitCountdownTeaser=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 anniversaryRepdigitCountdownTeaser[one]=カウントダウン情報です。{1,number,#,###} 番目のレースまでもうわずか {0,number,#,###} レースです。 -anniversaryRepdigitCountdownDescription=SAP では、縁起のいい番号のレースを祝うことにしています。www.sapsailing.com における {0,number, #,###} 番目のレースは誰が行うことになるでしょうか。そのレースの参加者には、SAP から最上級のサマーフェスティバルが無料で提供されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 +anniversaryRepdigitCountdownDescription=SAP では、縁起のいい番号のレースを祝うことにしています。www.sapsailing.com における {0,number, #,###} 番目のレースは誰が行うことになるでしょうか。その主催者にはドリンク込みのバーベキューが無料で提供されます。当選者はこの Web サイトで発表されます。引き続きご注目いただき、一緒にカウントダウンしていきましょう。 anniversaryAnnouncementTeaser=SAP Sailing Analytics で {0,number,#,###} レースというミッションが達成されました。 anniversaryAnnouncementDescription=3、2、1...。レース {0} の参加者のみなさん、おめでとうございます。当選をお知らせします。SAP Sailing Analytics をご信頼いただきありがとうございます。さらに 10,000 レースをご一緒に帆走できることを願っております。 anniversaryRaceLinkText=記念レース表示 @@ -2066,3 +2066,5 @@ multiUrlChangeSave=URL 変更の保存 multiUrlChangeNewURL=新規 URL multiUrlNoPrefixWarning=共通の接頭辞が見つかりませんでした。これは通常、選択した動画のうち現在同じ場所に置かれていないものがあることを意味します。自身の責任で進めてください。 multiUrlChangeExplain=このダイアログはメディアトラック URL 先頭の共通部分をまとめて置換します。すべての URL が同一の接頭辞で始まっていることを確認してください。また、新規 URL 列を参照し、保存を選択する前に結果として得られる URL を吟味してください。 +lastEvent=最終イベント: {0} +teaserOverallLinkToolTip=シリーズ全体を参照するには、黄色になっている隅の部分をクリックしてください diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index 6333aea7517..c1cb6c2c79f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=Contagem regressiva! Só resta {0,number,#, anniversaryMajorCountdownDescription=Estamos celebrando nossa {0,number,#,###}ª corrida em www.sapsailing.com! Que corrida ultrapassará a marca? O organizador desta corrida de comemoração receberá um total de 10.000 Euros para fins beneficentes. O vencedor será anunciado neste site. Fique atento e conte conosco. anniversaryRepdigitCountdownTeaser=Contagem regressiva! Só restam {0,number,#,###} corridas até a {1,number,#,###}ª corrida. anniversaryRepdigitCountdownTeaser[one]=Contagem regressiva! Só resta {0,number,#,###} corrida até a {1,number,#,###}ª corrida. -anniversaryRepdigitCountdownDescription=Estamos celebrando a corrida do nosso número da sorte! Quem fará parte da {0,number, #,###}ª corrida em www.sapsailing.com? Os participantes desta corrida receberão entradas gratuitas no festival de verão de topo da SAP. Os vencedores serão anunciados neste site. Fique atento e conte conosco. +anniversaryRepdigitCountdownDescription=Estamos celebrando a corrida do nosso número da sorte! Quem fará parte da {0,number, #,###}ª corrida em www.sapsailing.com? Os organizadores receberão um churrasco grátis com bebidas. Os vencedores serão anunciados neste site. Fique atento e conte conosco. anniversaryAnnouncementTeaser=Missão cumprida! {0,number,#,###} corridas com o SAP Sailing Analytics! anniversaryAnnouncementDescription=3,2,1... Felicitamos os participantes da corrida {0}. Você conseguiu! Agradecemos sua confiança no SAP Sailing Analytics e esperamos continuar velejando mais 10.000 corridas com você! anniversaryRaceLinkText=Exibir corrida de comemoração @@ -2066,3 +2066,5 @@ multiUrlChangeSave=Gravar modificações do URL multiUrlChangeNewURL=novo URL multiUrlNoPrefixWarning=Não foi encontrado um prefixo comum. Isso normalmente significa que nem todos os vídeos selecionados estão hospedados atualmente no mesmo local. Continue por sua própria conta e risco! multiUrlChangeExplain=Este diálogo irá substituir em massa as partes comuns no início dos URLs da faixa de mídia. Assegure que todos os URLs começam com o mesmo prefixo! Veja também a nova coluna do URL e teste os URLs resultantes antes de pressionar Gravar! +lastEvent=Último evento: {0} +teaserOverallLinkToolTip=Para ver a série completa, clique no canto amarelo diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 42efd3365a1..6ecc8d62bb2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=Обратный отсчет! До {1,numb anniversaryMajorCountdownDescription={0,number,#,###}-ая гонка на сайте www.sapsailing.com! Какая из них станет юбилейной? Организатор юбилейной гонки получит 10 000 евро, которые сможет потратить в благотворительных целях. Победитель будет объявлен на нашем сайте, оставайтесь с нами. anniversaryRepdigitCountdownTeaser=Обратный отсчет! До {1,number,#,###} гонки осталось всего {0,number,#,###} гонки(ок). anniversaryRepdigitCountdownTeaser[one]=Обратный отсчет! До {1,number,#,###} гонки осталась всего {0,number,#,###} гонка. -anniversaryRepdigitCountdownDescription=Ждем гонку под счастливым номером! Чья гонка станет {0,number, #,###}-ой на сайте www.sapsailing.com? Участники этой гонки получат право на участие в бесплатном высококлассном летнем фестивале от SAP Победитель будет объявлен на нашем сайте, оставайтесь с нами. +anniversaryRepdigitCountdownDescription=Ждем гонку под счастливым номером! Чья гонка станет {0,number, #,###}-ой на сайте www.sapsailing.com? Организаторы получат бесплатный барбекю-обед с напитками. Победители будут объявлен на нашем сайте. Оставайтесь с нами. anniversaryAnnouncementTeaser=Миссия выполнена! Проведено {0,number,#,###} гонок с SAP Sailing Analytics! anniversaryAnnouncementDescription=3, 2, 1... Поздравляем участников гонк {0}. Вы победили! Спасибо, что пользуетесь SAP Sailing Analytics. Надеюсь, нас с вами ждет еще множество гонок! anniversaryRaceLinkText=Показать юбилейную гонку @@ -2066,3 +2066,5 @@ multiUrlChangeSave=Сохранить изменения URL multiUrlChangeNewURL=Новый URL multiUrlNoPrefixWarning=Общий префикс не найден, это означает, что не все выбранные видео хранятся в одном и том же месте. Можете продолжить на свой риск! multiUrlChangeExplain=В этом диалоговом окне можно выполнить массовую замену общих частей в начале URL объектов мультимедиа. Убедитесь, что все URL имеют одинаковый префикс! Проверьте также столбец с новым URL и протестируйте новые URL перед сохранением! +lastEvent=Последнее событие: {0} +teaserOverallLinkToolTip=Чтобы увидеть все серии, нажмите желтый треугольник diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 5af00f85ac1..3db85a366b4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -1866,7 +1866,7 @@ anniversaryMajorCountdownTeaser[one]=倒计时!距离第{1,number,#,###}轮比 anniversaryMajorCountdownDescription=我们正在 www.sapsailing.com 上庆祝第{0,number,#,###}轮比赛!哪轮比赛会打破记录?本周年纪念赛的主办机构将获得共计 10,000 欧元作为慈善用途。获胜者将在本网站上公布。敬请关注,让我们拭目以待。 anniversaryRepdigitCountdownTeaser=倒计时!距离第{1,number,#,###}轮比赛仅剩{0,number,#,###}轮比赛。 anniversaryRepdigitCountdownTeaser[one]=倒计时!距离第{1,number,#,###}轮比赛仅剩{0,number,#,###}轮比赛。 -anniversaryRepdigitCountdownDescription=我们正在庆祝幸运号码赛!谁将在 www.sapsailing.com 上进行的第{0,number, #,###}轮比赛中获胜?这次比赛的参赛者将获得 SAP 提供的免费一流夏日嘉年华活动。获胜者将在本网站上公布。敬请关注,让我们拭目以待。 +anniversaryRepdigitCountdownDescription=我们正在庆祝幸运号码赛!谁将在 www.sapsailing.com 上进行的第{0,number, #,###}轮比赛中获胜?主办机构将获得免费烧烤和饮料。获胜者将在本网站上公布。敬请关注,让我们拭目以待。 anniversaryAnnouncementTeaser=任务圆满完成!使用 SAP Sailing Analytics 完成{0,number,#,###}轮比赛! anniversaryAnnouncementDescription=3,2,1...我们祝贺{0}比赛的参赛者。你们做到了!我们感谢您对 SAP Sailing Analytics 的信任,并希望与你们在以后的 10,000 多场比赛中继续并肩作战! anniversaryRaceLinkText=显示周年纪念赛 @@ -2066,3 +2066,5 @@ multiUrlChangeSave=保存 URL 更改 multiUrlChangeNewURL=新 URL multiUrlNoPrefixWarning=未找到通用前缀,这通常意味着并非所有选定的视频都被托管在当前的相同位置。如继续操作,后果自负! multiUrlChangeExplain=此对话框将在媒体轨道 URL 的起始处批量替换通用部分。确保所有 URL 都以相同的前缀开头!也请注意新的 URL 列,然后在保存之前测试生成的 URL! +lastEvent=上次活动:{0} +teaserOverallLinkToolTip=请查看整体系列赛,请点击黄色角落 From dcb837ed26b87a6353c186b3b57ef6e33048ef70 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 5 Jul 2018 19:00:28 +0200 Subject: [PATCH 072/102] fixed merge artifacts; added comment for null parameter Change-Id: I0c210cdd422c8880d4d14f271b2947e57b5b1be1 --- .../main/java/com/sap/sailing/gwt/ui/client/StringMessages.java | 2 -- .../controls/listedit/GenericStringListEditorComposite.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 91beb39517c..109d2206aa9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2075,8 +2075,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiUrlChangeNewURL(); String multiUrlNoPrefixWarning(); String multiUrlChangeExplain(); - String lastEvent(String locationOrVenue); - String teaserOverallLinkToolTip(); String resetZoom(); String lastEvent(String locationOrVenue); String teaserOverallLinkToolTip(); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java index 84d6e79c394..1a8a7732353 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java @@ -99,7 +99,7 @@ public abstract class GenericStringListEditorComposite extends ListEd */ public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable suggestValues, String placeholderTextForAddTextbox) { - this(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox, null); + this(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox, /* inputBoxSize */ null); } /** From 2a561092ba61b4c515ba91f7aabb4ef513c5f1f4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 5 Jul 2018 19:01:57 +0200 Subject: [PATCH 073/102] formatting changes only Change-Id: Idf090b6c79eadeb44cf052e928618c8d5c085aa2 --- .../sailing/gwt/ui/adminconsole/EventDialog.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java index 77a0afb9941..cbf77d1c367 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java @@ -125,10 +125,10 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox availableLeaderboardGroups, + public EventDialog(EventParameterValidator validator, SailingServiceAsync sailingService, + StringMessages stringMessages, List availableLeaderboardGroups, Iterable leaderboardGroupsOfEvent, DialogCallback callback) { - super(stringMessages.event(), null, stringMessages.ok(), stringMessages.cancel(), validator, - callback); + super(stringMessages.event(), null, stringMessages.ok(), stringMessages.cancel(), validator, callback); this.stringMessages = stringMessages; this.availableLeaderboardGroupsByName = new HashMap<>(); for (final LeaderboardGroupDTO lgDTO : availableLeaderboardGroups) { @@ -147,7 +147,6 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox emptyList(), new GenericStringListInlineEditorComposite.ExpandedUi(stringMessages, IconResources.INSTANCE.removeIcon(), /* suggestValues */ SuggestedCourseAreaNames.suggestedCourseAreaNames, stringMessages.enterCourseAreaName(), 50)); @@ -160,21 +159,17 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox suggestedWindFinderSpotCollections = AvailableWindFinderSpotCollections .getAllAvailableWindFinderSpotCollectionsInAlphabeticalOrder() == null ? Collections.emptyList() : AvailableWindFinderSpotCollections .getAllAvailableWindFinderSpotCollectionsInAlphabeticalOrder(); - windFinderSpotCollectionIdsComposite = new StringListInlineEditorComposite(Collections. emptyList(), new GenericStringListEditorComposite.ExpandedUi(stringMessages, IconResources.INSTANCE.removeIcon(), /* suggestValues */ - suggestedWindFinderSpotCollections, stringMessages.enterIdOfWindFinderReviewedSpotCollection(), - 35)); + suggestedWindFinderSpotCollections, stringMessages.enterIdOfWindFinderReviewedSpotCollection(), 35)); } @Override From 0c4e860850ff518b9ceeb57a898cee99d66aaecb Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 5 Jul 2018 19:04:21 +0200 Subject: [PATCH 074/102] formatting/whitespace change only Change-Id: Idd4225c1d15d23e028aa76b27f383005ade8eb76 --- .../ui/adminconsole/EventDetailsComposite.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java index d5bbc1c769b..c2ca47a4b13 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java @@ -47,16 +47,13 @@ public class EventDetailsComposite extends Composite { public EventDetailsComposite(final SailingServiceAsync sailingService, final ErrorReporter errorReporter, final StringMessages stringMessages) { super(); this.stringMessages = stringMessages; - event = null; mainPanel = new CaptionPanel(stringMessages.regatta()); VerticalPanel vPanel = new VerticalPanel(); mainPanel.add(vPanel); - int rows = 17; Grid grid = new Grid(rows, 2); vPanel.add(grid); - int currentRow = 0; eventId = createLabelAndValueWidget(grid, currentRow++, stringMessages.id(), "IdLabel"); eventName = createLabelAndValueWidget(grid, currentRow++, stringMessages.eventName(), "NameLabel"); @@ -72,16 +69,12 @@ public class EventDetailsComposite extends Composite { courseAreaNamesList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.courseAreas(), "CourseAreaValueList"); imageURLList = createLabelAndAnchorListWidget(grid, currentRow++, stringMessages.images(), "ImageURLValueList"); videoURLList = createLabelAndAnchorListWidget(grid, currentRow++, stringMessages.videos(), "VideoURLValueList"); - leaderboardGroupList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.leaderboardGroups(), - "LeaderboardGroupValueList"); - windfinderSpotCollectionsList = createLabelAndValueListWidget(grid, currentRow++, - stringMessages.windFinderSpotCollectionsList(), "WindFinderSpotCollectionsList"); - - for(int i=0; i < rows; i++) { + leaderboardGroupList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.leaderboardGroups(), "LeaderboardGroupValueList"); + windfinderSpotCollectionsList = createLabelAndValueListWidget(grid, currentRow++, stringMessages.windFinderSpotCollectionsList(), "WindFinderSpotCollectionsList"); + for (int i = 0; i < rows; i++) { grid.getCellFormatter().setVerticalAlignment(i, 0, HasVerticalAlignment.ALIGN_TOP); grid.getCellFormatter().setVerticalAlignment(i, 1, HasVerticalAlignment.ALIGN_TOP); } - initWidget(mainPanel); } @@ -146,7 +139,6 @@ public class EventDetailsComposite extends Composite { .createRegattaOverviewLink(new RegattaOverviewContextDefinition(event.id)); eventOverviewURL.setText(regattaOverviewLink); eventOverviewURL.setHref(UriUtils.fromString(regattaOverviewLink)); - List courseAreaNames = new ArrayList<>(); if (event.venue.getCourseAreas() != null && event.venue.getCourseAreas().size() > 0) { for (CourseAreaDTO courseArea : event.venue.getCourseAreas()) { @@ -154,7 +146,6 @@ public class EventDetailsComposite extends Composite { } } courseAreaNamesList.setValues(courseAreaNames); - List imageURLStringsAsList = new ArrayList<>(); for(ImageDTO image: event.getImages()) { imageURLStringsAsList.add(image.getSourceRef()); From cb025cc3b21eebeb1d5f9e2f97ca6334eb9efde4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 6 Jul 2018 09:40:21 +0200 Subject: [PATCH 075/102] fixed capitalization error in translated message key Change-Id: Ife75ad9e2291438479e92dc24cb31a05e6bbe2c7 --- .../com/sap/sailing/gwt/ui/client/StringMessages.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_de.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_es.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_fr.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_ja.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_pt.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_zh.properties | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index def37470705..2f5df3eabda 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1976,7 +1976,7 @@ windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Measured wind from one or more spots of www.windfinder.com windFinder=WindFinder enterTagsForTheVideo=Enter tags for the video -enterIdOfWindfinderReviewedSpotCollection=Enter ID of a reviewed WindFinder spot collection, e.g., "schilksee" +enterIdOfWindFinderReviewedSpotCollection=Enter ID of a reviewed WindFinder spot collection, e.g., "schilksee" windFinderSpotCollectionsList=WindFinder spot collections enterTagsForTheImage=Enter tags for the image unableToResolveWindFinderSpotId=Unable to resolve WindFinder spot with ID {0}: {1} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 13443823dc8..6239100004e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1974,7 +1974,7 @@ windFinderWindSourceTypeTooltip=Gemessener Wind von einer oder mehreren Statione windFinder=WindFinder windFinderSpotCollectionsList=WindFinder Messstellen-Sammlungen enterTagsForTheVideo=Tags zum Video erfassen -enterIdOfWindfinderReviewedSpotCollection=ID einer geprüften WindFinder Messstellen-Sammlung eingeben, z.B. "schilksee" +enterIdOfWindFinderReviewedSpotCollection=ID einer geprüften WindFinder Messstellen-Sammlung eingeben, z.B. "schilksee" enterTagsForTheImage=Tags zum Bild erfassen unableToResolveWindFinderSpotId=WindFinder-Station mit Kennung {0} wurde nicht gefunden: {1} windFinderWeatherData=Wetter​daten diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index eb527a6c840..e1ce930afcf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=Windfinder windFinderWindSourceTypeTooltip=Viento medido de uno o más puntos de www.windfinder.com windFinder=Windfinder enterTagsForTheVideo=Indique las etiquetas para el vídeo -enterIdOfWindfinderReviewedSpotCollection=Indique el ID de una colección de puntos de Windfinder revisada, por ejemplo "schilksee" +enterIdOfWindFinderReviewedSpotCollection=Indique el ID de una colección de puntos de Windfinder revisada, por ejemplo "schilksee" enterTagsForTheImage=Indique las etiquetas para la imagen unableToResolveWindFinderSpotId=Imposible resolver el punto Windfinder con ID {0}: {1} windFinderWeatherData=Datos meteorológicos diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index fa770dc2b88..d6d83ee0aa1 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Vent mesuré d''un ou plusieurs endroits de www.windfinder.com windFinder=WindFinder enterTagsForTheVideo=Entrez des balises pour la vidéo. -enterIdOfWindfinderReviewedSpotCollection=Entrez l''ID d''un ensemble d''endroits WindFinder révisé, par exemple, "Schilksee". +enterIdOfWindFinderReviewedSpotCollection=Entrez l''ID d''un ensemble d''endroits WindFinder révisé, par exemple, "Schilksee". enterTagsForTheImage=Entrez des balises pour l''image. unableToResolveWindFinderSpotId=Impossible de traiter l''endroit WindFinder ayant l''ID {0} : {1} windFinderWeatherData=Données météo diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index fc84b874550..1e4ace0d654 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=Windfinder windFinderWindSourceTypeTooltip=www.windfinder.com の 1 つ以上のスポットから風を測定しました windFinder=Windfinder enterTagsForTheVideo=動画のタグを入力 -enterIdOfWindfinderReviewedSpotCollection=レビューする Windfinder スポットコレクションの ID を入力 (例: "schilksee") +enterIdOfWindFinderReviewedSpotCollection=レビューする Windfinder スポットコレクションの ID を入力 (例: "schilksee") enterTagsForTheImage=画像のタグを入力 unableToResolveWindFinderSpotId=ID {0} で Windfinder スポットを決定できませんでした: {1} windFinderWeatherData=気象データ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index e4a167d5b5f..cbe1a4b9fe5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Vento medido de um ou mais locais de www.windfinder.com windFinder=WindFinder enterTagsForTheVideo=Inserir etiquetas para o vídeo -enterIdOfWindfinderReviewedSpotCollection=Inserir ID de uma coleção revisada de locais do WindFinder, por exemplo, "schilksee" +enterIdOfWindFinderReviewedSpotCollection=Inserir ID de uma coleção revisada de locais do WindFinder, por exemplo, "schilksee" enterTagsForTheImage=Inserir etiquetas para a imagem unableToResolveWindFinderSpotId=Impossível resolver local do WindFinder com ID {0}: {1} windFinderWeatherData=Dados metereológicos diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index ab370e8dc0d..fd4b81e04e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Измерения ветра из одного или нескольких мест www.windfinder.com windFinder=WindFinder enterTagsForTheVideo=Введите теги для видео -enterIdOfWindfinderReviewedSpotCollection=Введите ид. просмотренной коллекции мест WindFinder (например, "schilksee") +enterIdOfWindFinderReviewedSpotCollection=Введите ид. просмотренной коллекции мест WindFinder (например, "schilksee") enterTagsForTheImage=Введите теги для изображения unableToResolveWindFinderSpotId=Не удалось разрешить место WindFinder по ид. {0}: {1} windFinderWeatherData=Данные погоды diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 936062c31c8..ffb0bdf5360 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -2083,7 +2083,7 @@ windFinderWindSourceTypeName=WindFinder windFinderWindSourceTypeTooltip=Www.windfinder.com 中一个或多个地点的测量风力 windFinder=WindFinder enterTagsForTheVideo=为视频输入标记 -enterIdOfWindfinderReviewedSpotCollection=输入经检查的 WindFinder 地点集合的编号,例如 "schilksee" +enterIdOfWindFinderReviewedSpotCollection=输入经检查的 WindFinder 地点集合的编号,例如 "schilksee" enterTagsForTheImage=为图像输入标记 unableToResolveWindFinderSpotId=无法解析编号为{0}的 WindFinder 地点:{1} windFinderWeatherData=天气数据 From cff922c2c0d8af7b4fbcecd898ae44ada4d89b9c Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Fri, 6 Jul 2018 12:07:42 +0200 Subject: [PATCH 076/102] Fixed the source paths of the DataMiningResources --- .../ui/client/resources/DataMiningResources.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningResources.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningResources.java index a3bb8df0fb2..81122a16629 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningResources.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningResources.java @@ -5,19 +5,19 @@ import com.google.gwt.resources.client.ImageResource; public interface DataMiningResources extends ClientBundle { - @Source("com/sap/sailing/gwt/ui/client/images/close.png") + @Source("com/sap/sse/datamining/ui/images/close.png") ImageResource closeIcon(); - @Source("com/sap/sailing/gwt/ui/client/images/arrow_left.png") + @Source("com/sap/sse/datamining/ui/images/arrow_left.png") ImageResource arrowLeftIcon(); - @Source("com/sap/sailing/gwt/ui/client/images/arrow_right.png") + @Source("com/sap/sse/datamining/ui/images/arrow_right.png") ImageResource arrowRightIcon(); - @Source("com/sap/sailing/gwt/ui/client/images/plusicon_small.png") + @Source("com/sap/sse/datamining/ui/images/plusicon_small.png") ImageResource plusIcon(); - @Source("com/sap/sailing/gwt/ui/client/images/magnifier_small.png") + @Source("com/sap/sse/datamining/ui/images/magnifier_small.png") ImageResource searchIcon(); } From b262f492c9b407e75509721ae434a38d631d7c78 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 8 Jul 2018 23:10:12 +0200 Subject: [PATCH 077/102] added a comment Change-Id: I064852e27210946caafd3efbf61645b539bced4b --- .../com/sap/sailing/domain/tracking/impl/TimeRangeCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java index de43e8d22f7..b4ed5ace655 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TimeRangeCache.java @@ -144,7 +144,7 @@ public class TimeRangeCache { // no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using // the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry // modifies the internal parts of the data structure which is not thread safe. - synchronized (lruCache) { + synchronized (lruCache) { // ping the "perfect match" although it may not even have existed in the cache lruCache.get(new Util.Pair(from, to)); } return result; From b09437e1a7d516bce68d9b4a20339c2e5d475c2e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 8 Jul 2018 23:20:12 +0200 Subject: [PATCH 078/102] catch Throwable instead of Exception to also catch AssertionErrors --- .../java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 19180b1b143..3eeba3fab02 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -5062,7 +5062,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final MasterDataImporter importer = new MasterDataImporter(baseDomainFactory, getService()); importer.importFromStream(inputStream, importOperationId, override); - } catch (Exception e) { + } catch (Throwable e) { // do not assume that RuntimeException is logged properly logger.log(Level.SEVERE, e.getMessage(), e); getService() From cdf2e5d33cc8766fedee31d23ac1876028bb6433 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 8 Jul 2018 23:38:16 +0200 Subject: [PATCH 079/102] added a basic test frame for bug4629 Change-Id: Ic9829f46dc40ff974edbfadd493e6bdef75325c5 --- .../BravoFixTrackFoiledDistanceCacheTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java new file mode 100755 index 00000000000..96315024cf6 --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java @@ -0,0 +1,80 @@ +package com.sap.sailing.domain.test; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.base.CourseArea; +import com.sap.sailing.domain.base.impl.CourseAreaImpl; +import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.NauticalMileDistance; +import com.sap.sailing.domain.common.sensordata.BravoExtendedSensorDataMetadata; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.common.tracking.impl.BravoExtendedFixImpl; +import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; +import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.tracking.BravoFixTrack; +import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; +import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl; +import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; +import com.sap.sse.common.impl.DegreeBearingImpl; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +/** + * See bug4629. This test reproduces an order of fix insertion into a {@link BravoFixTrack}, cache invalidation, + * cache value calculation and cache value insertion that with bug 4629 existing will lead to an inconsistent + * cache entry that should have been invalidated by the fix insertion. + * + * @author Axel Uhl (d043530) + * + */ +public class BravoFixTrackFoiledDistanceCacheTest { + private DynamicBravoFixTrack track; + private DynamicGPSFixMovingTrackImpl gpsTrack; + + @Before + public void setUp() { + final CourseAreaImpl courseArea = new CourseAreaImpl("Test", UUID.randomUUID()); + gpsTrack = new DynamicGPSFixMovingTrackImpl<>(courseArea, /* millisecondsOverWhichToAverage */ 15000); + track = new BravoFixTrackImpl<>(courseArea, "test", /* hasExtendedFixes */ true, gpsTrack); + track.add(createFix(1000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); + track.add(createFix(2000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); + track.add(createFix(3000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); + gpsTrack.add(createGPSFix(1000l, 0, 0, 0, 1)); + gpsTrack.add(createGPSFix(2000l, 1./3600./60., 0, 0, 1)); + gpsTrack.add(createGPSFix(3000l, 2./3600./60., 0, 0, 1)); + } + + @Test + public void testDistanceSpentFoiling() { + assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); + } + + private BravoExtendedFixImpl createFix(long timePointAsMillis, Double rideHeightPort, Double rideHeightStarboard, Double heel, Double pitch) { + final Double[] fixData = new Double[Collections.max(Arrays.asList( + BravoExtendedSensorDataMetadata.HEEL.getColumnIndex()+1, + BravoExtendedSensorDataMetadata.PITCH.getColumnIndex()+1, + BravoExtendedSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex()+1, + BravoExtendedSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex()+1))]; + fixData[BravoExtendedSensorDataMetadata.HEEL.getColumnIndex()] = heel; + fixData[BravoExtendedSensorDataMetadata.PITCH.getColumnIndex()] = pitch; + fixData[BravoExtendedSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex()] = rideHeightPort; + fixData[BravoExtendedSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex()] = rideHeightStarboard; + return new BravoExtendedFixImpl(new DoubleVectorFixImpl(t(timePointAsMillis), fixData)); + } + + private GPSFixMoving createGPSFix(long timePointAsMillis, double lat, double lng, double cogInDeg, double sogInKnots) { + return new GPSFixMovingImpl(new DegreePosition(lat, lng), new MillisecondsTimePoint(timePointAsMillis), + new KnotSpeedWithBearingImpl(sogInKnots, new DegreeBearingImpl(cogInDeg))); + } + + private MillisecondsTimePoint t(long timePointAsMillis) { + return new MillisecondsTimePoint(timePointAsMillis); + } +} From 912d91c6853f2dd1ef55c8b1c4dd3fdd5c97520f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 00:01:04 +0200 Subject: [PATCH 080/102] started with some meaningful test logic for bug4629, asserting how the foiling distance cache shall behave Change-Id: I3da1f8beba23ed9ab9a6ad27e880ccfcad14930f --- .../BravoFixTrackFoiledDistanceCacheTest.java | 69 ++++++++++++++++++- .../tracking/impl/BravoFixTrackImpl.java | 6 +- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java index 96315024cf6..f33515e47e9 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import org.junit.Before; @@ -23,6 +25,8 @@ import com.sap.sailing.domain.tracking.BravoFixTrack; import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; +import com.sap.sailing.domain.tracking.impl.TimeRangeCache; +import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.DegreeBearingImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -37,23 +41,86 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class BravoFixTrackFoiledDistanceCacheTest { private DynamicBravoFixTrack track; private DynamicGPSFixMovingTrackImpl gpsTrack; + private TimeRangeCacheWithParallelTestSupport foilingDistanceCache; + + /** + * Supports blocking and releasing calls to {@link #invalidateAllAtOrLaterThan(TimePoint)} and + * {@link #cache(TimePoint, TimePoint, Object)}, so as to force lock acquisition and release + * in a specific order. + * + * @author Axel Uhl (d043530) + * + * @param + */ + private static class TimeRangeCacheWithParallelTestSupport extends TimeRangeCache { + private static Map> caches = new HashMap<>(); + private int callsToCache; + private int callsToInvalidateAllAtOrLaterThan; + + public TimeRangeCacheWithParallelTestSupport(String nameForLockLogging) { + super(nameForLockLogging); + caches.put(nameForLockLogging, this); + } + + static public TimeRangeCacheWithParallelTestSupport getCacheByName(String nameForLockLogging) { + @SuppressWarnings("unchecked") + TimeRangeCacheWithParallelTestSupport timeRangeCacheWithParallelTestSupport = (TimeRangeCacheWithParallelTestSupport) caches.get(nameForLockLogging); + return timeRangeCacheWithParallelTestSupport; + } + + @Override + public void invalidateAllAtOrLaterThan(TimePoint timePoint) { + super.invalidateAllAtOrLaterThan(timePoint); + callsToInvalidateAllAtOrLaterThan++; + } + + @Override + public void cache(TimePoint from, TimePoint to, T result) { + super.cache(from, to, result); + callsToCache++; + } + + public int getCallsToCache() { + return callsToCache; + } + + public int getCallsToInvalidateAllAtOrLaterThan() { + return callsToInvalidateAllAtOrLaterThan; + } + } @Before public void setUp() { final CourseAreaImpl courseArea = new CourseAreaImpl("Test", UUID.randomUUID()); gpsTrack = new DynamicGPSFixMovingTrackImpl<>(courseArea, /* millisecondsOverWhichToAverage */ 15000); - track = new BravoFixTrackImpl<>(courseArea, "test", /* hasExtendedFixes */ true, gpsTrack); + track = new BravoFixTrackImpl(courseArea, "test", /* hasExtendedFixes */ true, gpsTrack) { + private static final long serialVersionUID = 1473560197177750211L; + + @Override + protected TimeRangeCache createTimeRangeCache(CourseArea trackedItem, final String cacheName) { + return new TimeRangeCacheWithParallelTestSupport<>(cacheName); + } + }; track.add(createFix(1000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); track.add(createFix(2000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); track.add(createFix(3000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); gpsTrack.add(createGPSFix(1000l, 0, 0, 0, 1)); gpsTrack.add(createGPSFix(2000l, 1./3600./60., 0, 0, 1)); gpsTrack.add(createGPSFix(3000l, 2./3600./60., 0, 0, 1)); + foilingDistanceCache = TimeRangeCacheWithParallelTestSupport.getCacheByName("foilingDistanceCache"); } @Test public void testDistanceSpentFoiling() { assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); + assertEquals(1, foilingDistanceCache.getCallsToCache()); + assertEquals(6, foilingDistanceCache.getCallsToInvalidateAllAtOrLaterThan()); // the three sensor and three GPS fixes + assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); + assertEquals(1, foilingDistanceCache.getCallsToCache()); // still the same perfect cache hit, no new cached value + track.add(createFix(2500l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.)); + assertEquals(7, foilingDistanceCache.getCallsToInvalidateAllAtOrLaterThan()); // now one more sensor fix + assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); + assertEquals(2, foilingDistanceCache.getCallsToCache()); // had to be re-calculated and then was expected to be put to cache } private BravoExtendedFixImpl createFix(long timePointAsMillis, Double rideHeightPort, Double rideHeightStarboard, Double heel, Double pitch) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java index ef7452429b8..958f24cd24d 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackImpl.java @@ -170,7 +170,11 @@ public class BravoFixTrackImpl extends S } } - private TimeRangeCache createTimeRangeCache(ItemType trackedItem, final String cacheName) { + /** + * This method is protected in order to let test classes use other TimeRangeCache specializations + * that provide specific test support. + */ + protected TimeRangeCache createTimeRangeCache(ItemType trackedItem, final String cacheName) { return new TimeRangeCache<>(cacheName+" for "+trackedItem); } From ad698cdf6a02a2557c59f7e074fb5d27ccd90333 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 00:41:49 +0200 Subject: [PATCH 081/102] an always failing test case for bug4629 Change-Id: I83cb2529fcb0fa31ab4292d2e34da19838a4e1b9 --- .../BravoFixTrackFoiledDistanceCacheTest.java | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java index f33515e47e9..0f6c415dd40 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java @@ -7,6 +7,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; import org.junit.Before; import org.junit.Test; @@ -26,6 +30,7 @@ import com.sap.sailing.domain.tracking.DynamicBravoFixTrack; import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; import com.sap.sailing.domain.tracking.impl.TimeRangeCache; +import com.sap.sse.common.Distance; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.DegreeBearingImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -56,6 +61,9 @@ public class BravoFixTrackFoiledDistanceCacheTest { private static Map> caches = new HashMap<>(); private int callsToCache; private int callsToInvalidateAllAtOrLaterThan; + private CyclicBarrier cacheBarrier; + private CyclicBarrier invalidateBarrier; + private CyclicBarrier cacheInformBarrier; public TimeRangeCacheWithParallelTestSupport(String nameForLockLogging) { super(nameForLockLogging); @@ -72,10 +80,27 @@ public class BravoFixTrackFoiledDistanceCacheTest { public void invalidateAllAtOrLaterThan(TimePoint timePoint) { super.invalidateAllAtOrLaterThan(timePoint); callsToInvalidateAllAtOrLaterThan++; + if (invalidateBarrier != null) { + try { + invalidateBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + } } @Override public void cache(TimePoint from, TimePoint to, T result) { + try { + if (cacheInformBarrier != null) { + cacheInformBarrier.await(); + } + if (cacheBarrier != null) { + cacheBarrier.await(); + } + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } super.cache(from, to, result); callsToCache++; } @@ -87,6 +112,33 @@ public class BravoFixTrackFoiledDistanceCacheTest { public int getCallsToInvalidateAllAtOrLaterThan() { return callsToInvalidateAllAtOrLaterThan; } + + public void waitForCacheInvalidation() throws InterruptedException, BrokenBarrierException { + invalidateBarrier.await(); + invalidateBarrier = null; + } + + public void allowWaitingForCacheInvalidation() { + invalidateBarrier = new CyclicBarrier(2); + } + + public void letFoilingDistanceCacheContinueWithCaching() throws InterruptedException, BrokenBarrierException { + cacheBarrier.await(); + cacheBarrier = null; + } + + public void letFoilingDistanceCacheWaitBeforeCaching() { + cacheBarrier = new CyclicBarrier(2); + } + + public void letFoilingDistanceCacheInformUsBeforeCaching() { + cacheInformBarrier = new CyclicBarrier(2); + } + + public void waitForCacheToBeEntered() throws InterruptedException, BrokenBarrierException { + cacheInformBarrier.await(); + cacheInformBarrier = null; + } } @Before @@ -111,7 +163,7 @@ public class BravoFixTrackFoiledDistanceCacheTest { } @Test - public void testDistanceSpentFoiling() { + public void testDistanceSpentFoiling() throws InterruptedException, ExecutionException, BrokenBarrierException { assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); assertEquals(1, foilingDistanceCache.getCallsToCache()); assertEquals(6, foilingDistanceCache.getCallsToInvalidateAllAtOrLaterThan()); // the three sensor and three GPS fixes @@ -121,6 +173,32 @@ public class BravoFixTrackFoiledDistanceCacheTest { assertEquals(7, foilingDistanceCache.getCallsToInvalidateAllAtOrLaterThan()); // now one more sensor fix assertEquals(new NauticalMileDistance(2./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(3000l)).getMeters(), 0.01); assertEquals(2, foilingDistanceCache.getCallsToCache()); // had to be re-calculated and then was expected to be put to cache + gpsTrack.add(createGPSFix(4000l, 3./3600./60., 0, 0, 1)); + assertEquals(8, foilingDistanceCache.getCallsToInvalidateAllAtOrLaterThan()); // now one more GPS fix + + // now modify the cache such that it will stop before updating the cache + foilingDistanceCache.letFoilingDistanceCacheWaitBeforeCaching(); + foilingDistanceCache.letFoilingDistanceCacheInformUsBeforeCaching(); + FutureTask getDistanceFuture = new FutureTask<>(()->track.getDistanceSpentFoiling(t(1000l), t(4000l))); + new Thread(getDistanceFuture).start(); + foilingDistanceCache.waitForCacheToBeEntered(); + // now insert another sensor fix at t(4000l) that will have to invalidate the result of the previous request; + // adding the fix will trigger a cache invalidation; the TimeRangeCache.cache(...) call caused by the query above + // is still blocked: + FutureTask addFuture = new FutureTask<>(()->track.add(createFix(4000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.))); + foilingDistanceCache.allowWaitingForCacheInvalidation(); + new Thread(addFuture).start(); + foilingDistanceCache.waitForCacheInvalidation(); + // now that the cache invalidation has happened synchronously, let the request from above continue with its call to cache(...): + foilingDistanceCache.letFoilingDistanceCacheContinueWithCaching(); + // wait until the caching has completed: + getDistanceFuture.get(); + // and until adding the fixes has completed + addFuture.get(); + // Now for the getDistanceFuture, either it has delivered the new value already because it was passed by + // the addition of the fix, or it delivered the old value, but then the cache entry will have been invalidated. + // Now ask again; if the invalidation worked correctly, we should get a greater result now: + assertEquals(new NauticalMileDistance(3./3600.).getMeters(), track.getDistanceSpentFoiling(t(1000l), t(4000l)).getMeters(), 0.01); } private BravoExtendedFixImpl createFix(long timePointAsMillis, Double rideHeightPort, Double rideHeightStarboard, Double heel, Double pitch) { From 79e0fbf81a28434e2ce156d7d0fb5a53bf266ab5 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 01:09:17 +0200 Subject: [PATCH 082/102] bug4629: update caches while still under the track's read lock Change-Id: Ie07034aaec9b55f2f620ccab2d82e89fdf64d817 --- .../com/sap/sailing/domain/tracking/impl/TrackImpl.java | 8 +++++--- .../domain/test/BravoFixTrackFoiledDistanceCacheTest.java | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java index 4f6a752bbcc..0811adebd09 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/tracking/impl/TrackImpl.java @@ -506,12 +506,14 @@ public class TrackImpl implements Track { result = nullElement; } } + // run the cache update while still holding the read lock; this avoids bug4629 where a cache invalidation + // caused by fix insertions can come after the result calculation and before the cache update + if (!perfectCacheHit && recursionDepth == 0) { + cache.cache(from, to, result); + } } finally { unlockAfterRead(); } - if (!perfectCacheHit && recursionDepth == 0) { - cache.cache(from, to, result); - } } return result; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java index 0f6c415dd40..791fe2effdf 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BravoFixTrackFoiledDistanceCacheTest.java @@ -188,9 +188,13 @@ public class BravoFixTrackFoiledDistanceCacheTest { FutureTask addFuture = new FutureTask<>(()->track.add(createFix(4000l, /* rideHeightPort */ 0.6, /* rideHeightStarboard */ 0.6, /* heel */ 10., /* pitch */ 5.))); foilingDistanceCache.allowWaitingForCacheInvalidation(); new Thread(addFuture).start(); - foilingDistanceCache.waitForCacheInvalidation(); - // now that the cache invalidation has happened synchronously, let the request from above continue with its call to cache(...): + // with the fix for bug4629 the cache invalidation won't be reached because fix addition will require the write lock + // which isn't possible until the cache update has succeeded which now happens under the track's read lock. + Thread.sleep(500); // to continue to let the old broken version fail more or less reliably by waiting for track.add(...) to reach the invalidation + // So let the request from above continue with its call to cache(...) which eventually will release the track's read lock... foilingDistanceCache.letFoilingDistanceCacheContinueWithCaching(); + // ...so that now the cache invalidation will finally get on its way + foilingDistanceCache.waitForCacheInvalidation(); // wait until the caching has completed: getDistanceFuture.get(); // and until adding the fixes has completed From 65f3bf6ccd133fd07d4f5de58b6411096239ffc5 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 9 Jul 2018 10:49:51 +0200 Subject: [PATCH 083/102] Bug 4663: prevent NPE in FixLoaderAndTracker due to bad timing --- .../racelogtracking/impl/fixtracker/FixLoaderAndTracker.java | 3 ++- .../impl/fixtracker/RegattaLogDeviceMappings.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java index b1baf2b4f61..32e38f5afd9 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java @@ -619,9 +619,10 @@ public class FixLoaderAndTracker implements TrackingDataLoader { private void startTracking() { setStatusAndProgress(TrackedRaceStatusEnum.TRACKING, 0.0); - trackedRace.addListener(raceChangeListener); this.deviceMappings = new FixLoaderDeviceMappings(trackedRace.getAttachedRegattaLogs(), trackedRace.getRace().getName()); + trackedRace.addListener(raceChangeListener); + this.deviceMappings.updateMappings(); } private void loadFixesForExtendedTimeRange(final TimeRange extendedTimeRange) { diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java index bcf7c25dc7c..9ccc123f26d 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogDeviceMappings.java @@ -146,7 +146,7 @@ public abstract class RegattaLogDeviceMappings { }); } - private void updateMappings() { + public void updateMappings() { try { updateMappingsInternal(); } catch (Exception e) { From 28c542b6a1664cab85990f500ae74bd7d2ba36d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 9 Jul 2018 11:30:23 +0200 Subject: [PATCH 084/102] bug4659 ensure notifications are also properly shown in raceboard --- .../com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java index d9d48801fff..ac73cd69d40 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java @@ -40,6 +40,7 @@ public class Notification { ress.css().ensureInjected(); snackBar.addStyleName(ress.css().snackbar()); snackBar.getElement().getStyle().setCursor(Cursor.POINTER); + snackBar.getElement().getStyle().setZIndex(99); notificationAnimation = new Animation() { @Override From e984cb353de8b00d213c19956220fba051f9ab61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 9 Jul 2018 16:34:18 +0200 Subject: [PATCH 085/102] removed unnecessary very spamming log output --- .../src/com/sap/sailing/domain/sharding/ShardingContext.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java index 221d4ec164c..e98f0873c65 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java @@ -36,7 +36,6 @@ public class ShardingContext { */ public static ShardingType identifyAndSetShardingConstraint(String shardingInfo) { if (shardingInfo == null || shardingInfo.isEmpty()) { - logger.warning("Empty sharding constraint"); return null; } ThreadLocal identifiedShardingHolder = null; From 9ea6ddf7a17f2d7dbb652e3da0590511a0e5d4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 9 Jul 2018 17:38:58 +0200 Subject: [PATCH 086/102] hide meaneuvertable on mobile, so that more space is available for the map --- .../java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 8cd5dd24f94..4c430dcea21 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -397,7 +397,9 @@ public class RaceBoardPanel selectedRaceIdentifier, stringMessages, competitorSelectionProvider, errorReporter, timer, maneuverTableSettings, timeRangeWithZoomModel, new ClassicLeaderboardStyle(), userService); maneuverTablePanel.getEntryWidget().setTitle(stringMessages.maneuverTable()); - componentsForSideBySideViewer.add(maneuverTablePanel); + if (showChartMarkEditMediaButtonsAndVideo) { + componentsForSideBySideViewer.add(maneuverTablePanel); + } editMarkPassingPanel = new EditMarkPassingsPanel(this, getComponentContext(), sailingService, selectedRaceIdentifier, stringMessages, From 122dfb293c951da073fcf6ff40c9a14988ce0ff5 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 9 Jul 2018 19:27:48 +0200 Subject: [PATCH 087/102] Refactored estimation data serializers + added mark passings count --- .../GpsFixWithEstimationData.java | 20 +++ .../impl/GpsFixWithEstimationDataImpl.java | 48 ++++++ ...CompetitorTrackElementsJsonSerializer.java | 20 +++ ...TrackWithEstimationDataJsonSerializer.java | 137 ++++++++++++++++++ ...urvesWithEstimationDataJsonSerializer.java | 114 ++------------- ...FixesWithEstimationDataJsonSerializer.java | 131 +++++------------ .../gateway/jaxrs/api/RegattasResource.java | 37 +++-- 7 files changed, 297 insertions(+), 210 deletions(-) create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/GpsFixWithEstimationData.java create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/GpsFixWithEstimationDataImpl.java create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackElementsJsonSerializer.java create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/GpsFixWithEstimationData.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/GpsFixWithEstimationData.java new file mode 100644 index 00000000000..e0126209f4a --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/GpsFixWithEstimationData.java @@ -0,0 +1,20 @@ +package com.sap.sailing.domain.maneuverdetection; + +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public interface GpsFixWithEstimationData extends GPSFixMoving { + + Wind getWind(); + + Bearing getRelativeBearingToNextMarkAfterManeuver(); + + Distance getDistanceToClosestMark(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/GpsFixWithEstimationDataImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/GpsFixWithEstimationDataImpl.java new file mode 100644 index 00000000000..a9566317518 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/GpsFixWithEstimationDataImpl.java @@ -0,0 +1,48 @@ +package com.sap.sailing.domain.maneuverdetection.impl; + +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.maneuverdetection.GpsFixWithEstimationData; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; +import com.sap.sse.common.TimePoint; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public class GpsFixWithEstimationDataImpl extends GPSFixMovingImpl implements GpsFixWithEstimationData { + + private static final long serialVersionUID = -6952863819352430365L; + + private Wind wind; + private Bearing relativeBearingToNextMarkAfterManeuver; + private Distance distanceToClosestMark; + + public GpsFixWithEstimationDataImpl(Position position, TimePoint timePoint, SpeedWithBearing speedWithBearing, + Wind wind, Bearing relativeBearingToNextMarkAfterManeuver, Distance distanceToClosestMark) { + super(position, timePoint, speedWithBearing); + this.wind = wind; + this.relativeBearingToNextMarkAfterManeuver = relativeBearingToNextMarkAfterManeuver; + this.distanceToClosestMark = distanceToClosestMark; + } + + @Override + public Wind getWind() { + return wind; + } + + @Override + public Bearing getRelativeBearingToNextMarkAfterManeuver() { + return relativeBearingToNextMarkAfterManeuver; + } + + @Override + public Distance getDistanceToClosestMark() { + return distanceToClosestMark; + } + +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackElementsJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackElementsJsonSerializer.java new file mode 100644 index 00000000000..a1f57a3e224 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackElementsJsonSerializer.java @@ -0,0 +1,20 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONArray; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.common.TimePoint; + +/** + * + * @author Vladislav Chumak (D069712) + * @see CompetitorTrackWithEstimationDataJsonSerializer + */ +public interface CompetitorTrackElementsJsonSerializer { + + JSONArray serialize(TrackedRace trackedRace, Competitor competitor, TimePoint from, TimePoint to, + TrackTimeInfo trackTimeInfo); + +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java new file mode 100644 index 00000000000..ddaa977ff8f --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java @@ -0,0 +1,137 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import java.util.NavigableSet; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; +import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; +import com.sap.sailing.domain.polars.PolarDataService; +import com.sap.sailing.domain.tracking.GPSFixTrack; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; +import com.sap.sse.common.impl.MillisecondsDurationImpl; + +/** + * + * @author Vladislav Chumak (D069712) + * + */ +public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { + public static final String elements = "elements"; + public static final String BOAT_CLASS = "boatClass"; + public static final String COMPETITOR_NAME = "competitorName"; + public static final String AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS = "avgIntervalBetweenFixesInSeconds"; + public static final String DISTANCE_TRAVELLED_IN_METERS = "distanceTravelledInMeters"; + public static final String START_TIME_POINT = "startUnixTime"; + public static final String END_TIME_POINT = "endUnixTime"; + public static final String FIXES_COUNT_FOR_POLARS = "fixesCountForPolars"; + public static final String MARK_PASSINGS_COUNT = "markPassingsCount"; + + private final BoatClassJsonSerializer boatClassJsonSerializer; + private final CompetitorTrackElementsJsonSerializer elementsJsonSerializer; + private final PolarDataService polarDataService; + private final Integer startBeforeStartLineInSeconds; + private final Integer endBeforeStartLineInSeconds; + private final Integer startAfterFinishLineInSeconds; + private final Integer endAfterFinishLineInSeconds; + + public CompetitorTrackWithEstimationDataJsonSerializer(PolarDataService polarDataService, + BoatClassJsonSerializer boatClassJsonSerializer, + CompetitorTrackElementsJsonSerializer elementsJsonSerializer, Integer startBeforeStartLineInSeconds, + Integer endBeforeStartLineInSeconds, Integer startAfterFinishLineInSeconds, + Integer endAfterFinishLineInSeconds) { + this.polarDataService = polarDataService; + this.boatClassJsonSerializer = boatClassJsonSerializer; + this.elementsJsonSerializer = elementsJsonSerializer; + this.startBeforeStartLineInSeconds = startBeforeStartLineInSeconds; + this.endBeforeStartLineInSeconds = endBeforeStartLineInSeconds; + this.startAfterFinishLineInSeconds = startAfterFinishLineInSeconds; + this.endAfterFinishLineInSeconds = endAfterFinishLineInSeconds; + } + + @Override + public JSONObject serialize(TrackedRace trackedRace) { + final JSONObject result = new JSONObject(); + JSONArray byCompetitorJson = new JSONArray(); + result.put(BYCOMPETITOR, byCompetitorJson); + for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); + TimePoint from = null; + TimePoint to = null; + if (startBeforeStartLineInSeconds != null) { + from = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(startBeforeStartLineInSeconds * 1000L)); + } else if (startAfterFinishLineInSeconds != null) { + from = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(startAfterFinishLineInSeconds * 1000L)); + } else { + from = trackTimeInfo.getTrackStartTimePoint(); + } + if (endAfterFinishLineInSeconds != null) { + to = trackTimeInfo.getTrackEndTimePoint() + .plus(new MillisecondsDurationImpl(endAfterFinishLineInSeconds * 1000L)); + } else if (endBeforeStartLineInSeconds != null) { + to = trackTimeInfo.getTrackStartTimePoint() + .minus(new MillisecondsDurationImpl(endBeforeStartLineInSeconds * 1000L)); + } else { + to = trackTimeInfo.getTrackEndTimePoint(); + } + if (trackTimeInfo != null) { + final JSONObject forCompetitorJson = new JSONObject(); + byCompetitorJson.add(forCompetitorJson); + forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); + forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer + .serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); + forCompetitorJson.put(FIXES_COUNT_FOR_POLARS, getFixesCountForPolars(trackedRace, competitor)); + Duration averageIntervalBetweenFixes = trackedRace.getTrack(competitor) + .getAverageIntervalBetweenFixes(); + forCompetitorJson.put(AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS, + averageIntervalBetweenFixes == null ? 0 : averageIntervalBetweenFixes.asSeconds()); + GPSFixTrack track = trackedRace.getTrack(competitor); + Double distanceTravelledInMeters = null; + if (trackTimeInfo.getTrackStartTimePoint() != null && trackTimeInfo.getTrackEndTimePoint() != null) { + distanceTravelledInMeters = track.getDistanceTraveled(trackTimeInfo.getTrackStartTimePoint(), + trackTimeInfo.getTrackEndTimePoint()).getMeters(); + } + forCompetitorJson.put(DISTANCE_TRAVELLED_IN_METERS, distanceTravelledInMeters); + forCompetitorJson.put(START_TIME_POINT, trackTimeInfo.getTrackStartTimePoint() == null ? null + : trackTimeInfo.getTrackStartTimePoint().asMillis()); + forCompetitorJson.put(END_TIME_POINT, trackTimeInfo.getTrackEndTimePoint() == null ? null + : trackTimeInfo.getTrackEndTimePoint().asMillis()); + int markPassingsCount = getMarkPassingsCount(trackedRace, competitor); + forCompetitorJson.put(MARK_PASSINGS_COUNT, markPassingsCount); + forCompetitorJson.put(elements, + elementsJsonSerializer.serialize(trackedRace, competitor, from, to, trackTimeInfo)); + } + } + return result; + } + + private int getMarkPassingsCount(TrackedRace trackedRace, Competitor competitor) { + int markPassingsCount = 0; + NavigableSet markPassings = trackedRace.getMarkPassings(competitor, false); + trackedRace.lockForRead(markPassings); + try { + markPassingsCount = Util.size(markPassings); + } finally { + trackedRace.unlockAfterRead(markPassings); + } + return markPassingsCount; + } + + private long getFixesCountForPolars(TrackedRace trackedRace, Competitor competitor) { + BoatClass boatClass = trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass(); + Long fixesCountForBoatPolars = polarDataService.getFixCountPerBoatClass().get(boatClass); + return fixesCountForBoatPolars == null ? 0L : fixesCountForBoatPolars; + } + +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java index 357d034b0d4..01b1e4ac610 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompleteManeuverCurvesWithEstimationDataJsonSerializer.java @@ -4,11 +4,8 @@ import java.util.List; import java.util.stream.Collectors; import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.CompleteManeuverCurveWithEstimationData; import com.sap.sailing.domain.maneuverdetection.ManeuverDetectorWithEstimationDataSupport; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; @@ -17,116 +14,37 @@ import com.sap.sailing.domain.maneuverdetection.impl.ManeuverSpot; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.tracking.CompleteManeuverCurve; -import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; -import com.sap.sse.common.impl.MillisecondsDurationImpl; /** * * @author Vladislav Chumak (D069712) * */ -public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { - public static final String MANEUVER_CURVES = "maneuverCurves"; - public static final String BOAT_CLASS = "boatClass"; - public static final String COMPETITOR_NAME = "competitorName"; - public static final String AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS = "avgIntervalBetweenFixesInSeconds"; - public static final String DISTANCE_TRAVELLED_IN_METERS = "distanceTravelledInMeters"; - public static final String START_TIME_POINT = "startUnixTime"; - public static final String END_TIME_POINT = "endUnixTime"; - public static final String FIXES_COUNT_FOR_POLARS = "fixesCountForPolars"; - - private final BoatClassJsonSerializer boatClassJsonSerializer; - private final CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer; +public class CompleteManeuverCurvesWithEstimationDataJsonSerializer implements CompetitorTrackElementsJsonSerializer { private final PolarDataService polarDataService; - private final Integer startBeforeStartLineInSeconds; - private final Integer endBeforeStartLineInSeconds; - private final Integer startAfterFinishLineInSeconds; - private final Integer endAfterFinishLineInSeconds; + private final CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer; public CompleteManeuverCurvesWithEstimationDataJsonSerializer(PolarDataService polarDataService, - BoatClassJsonSerializer boatClassJsonSerializer, - CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer, - Integer startBeforeStartLineInSeconds, Integer endBeforeStartLineInSeconds, - Integer startAfterFinishLineInSeconds, Integer endAfterFinishLineInSeconds) { + CompleteManeuverCurveWithEstimationDataJsonSerializer maneuverWithEstimationDataJsonSerializer) { this.polarDataService = polarDataService; - this.boatClassJsonSerializer = boatClassJsonSerializer; this.maneuverWithEstimationDataJsonSerializer = maneuverWithEstimationDataJsonSerializer; - this.startBeforeStartLineInSeconds = startBeforeStartLineInSeconds; - this.endBeforeStartLineInSeconds = endBeforeStartLineInSeconds; - this.startAfterFinishLineInSeconds = startAfterFinishLineInSeconds; - this.endAfterFinishLineInSeconds = endAfterFinishLineInSeconds; } @Override - public JSONObject serialize(TrackedRace trackedRace) { - final JSONObject result = new JSONObject(); - JSONArray byCompetitorJson = new JSONArray(); - result.put(BYCOMPETITOR, byCompetitorJson); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { - ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); - TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); - TimePoint from = null; - TimePoint to = null; - if (startBeforeStartLineInSeconds != Integer.MIN_VALUE) { - from = trackTimeInfo.getTrackStartTimePoint() - .minus(new MillisecondsDurationImpl(startBeforeStartLineInSeconds * 1000L)); - } else if (startAfterFinishLineInSeconds != Integer.MIN_VALUE) { - from = trackTimeInfo.getTrackEndTimePoint() - .plus(new MillisecondsDurationImpl(startAfterFinishLineInSeconds * 1000L)); - } - if (endAfterFinishLineInSeconds != Integer.MIN_VALUE) { - to = trackTimeInfo.getTrackEndTimePoint() - .plus(new MillisecondsDurationImpl(endAfterFinishLineInSeconds * 1000L)); - } else if (endBeforeStartLineInSeconds != Integer.MIN_VALUE) { - to = trackTimeInfo.getTrackStartTimePoint() - .minus(new MillisecondsDurationImpl(endBeforeStartLineInSeconds * 1000L)); - } - if (trackTimeInfo != null) { - if (from != null ^ to != null) { - if (from == null) { - from = trackTimeInfo.getTrackStartTimePoint(); - } - if (to == null) { - to = trackTimeInfo.getTrackEndTimePoint(); - } - } - final JSONObject forCompetitorJson = new JSONObject(); - byCompetitorJson.add(forCompetitorJson); - forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); - forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer - .serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); - final JSONArray completeManeuverCurvesWithEstimationData = new JSONArray(); - Iterable completeManeuvers = from != null - ? getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor, from, to) - : getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor); - for (CompleteManeuverCurveWithEstimationData maneuver : completeManeuvers) { - completeManeuverCurvesWithEstimationData - .add(maneuverWithEstimationDataJsonSerializer.serialize(maneuver)); - } - forCompetitorJson.put(FIXES_COUNT_FOR_POLARS, getFixesCountForPolars(trackedRace, competitor)); - forCompetitorJson.put(MANEUVER_CURVES, completeManeuverCurvesWithEstimationData); - Duration averageIntervalBetweenFixes = trackedRace.getTrack(competitor) - .getAverageIntervalBetweenFixes(); - forCompetitorJson.put(AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS, - averageIntervalBetweenFixes == null ? 0 : averageIntervalBetweenFixes.asSeconds()); - GPSFixTrack track = trackedRace.getTrack(competitor); - Double distanceTravelledInMeters = null; - if (trackTimeInfo.getTrackStartTimePoint() != null && trackTimeInfo.getTrackEndTimePoint() != null) { - distanceTravelledInMeters = track.getDistanceTraveled(trackTimeInfo.getTrackStartTimePoint(), - trackTimeInfo.getTrackEndTimePoint()).getMeters(); - } - forCompetitorJson.put(DISTANCE_TRAVELLED_IN_METERS, distanceTravelledInMeters); - forCompetitorJson.put(START_TIME_POINT, trackTimeInfo.getTrackStartTimePoint() == null ? null - : trackTimeInfo.getTrackStartTimePoint().asMillis()); - forCompetitorJson.put(END_TIME_POINT, trackTimeInfo.getTrackEndTimePoint() == null ? null - : trackTimeInfo.getTrackEndTimePoint().asMillis()); - } + public JSONArray serialize(TrackedRace trackedRace, Competitor competitor, TimePoint from, TimePoint to, + TrackTimeInfo trackTimeInfo) { + final JSONArray completeManeuverCurvesWithEstimationData = new JSONArray(); + Iterable completeManeuvers = trackTimeInfo.getTrackStartTimePoint() + .equals(from) && trackTimeInfo.getTrackEndTimePoint().equals(to) + ? getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor) + : getCompleteManeuverCurvesWithEstimationData(trackedRace, competitor, from, to); + for (CompleteManeuverCurveWithEstimationData maneuver : completeManeuvers) { + completeManeuverCurvesWithEstimationData.add(maneuverWithEstimationDataJsonSerializer.serialize(maneuver)); } - return result; + return completeManeuverCurvesWithEstimationData; } private Iterable getCompleteManeuverCurvesWithEstimationData( @@ -155,10 +73,4 @@ public class CompleteManeuverCurvesWithEstimationDataJsonSerializer extends Abst return maneuversWithEstimationData; } - private long getFixesCountForPolars(TrackedRace trackedRace, Competitor competitor) { - BoatClass boatClass = trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass(); - Long fixesCountForBoatPolars = polarDataService.getFixCountPerBoatClass().get(boatClass); - return fixesCountForBoatPolars == null ? 0L : fixesCountForBoatPolars; - } - } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java index 06cddcbfdd3..a2148acf2fe 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GpsFixesWithEstimationDataJsonSerializer.java @@ -14,16 +14,14 @@ import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; -import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; -import com.sap.sse.common.impl.MillisecondsDurationImpl; /** * * @author Vladislav Chumak (D069712) * */ -public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRaceDataJsonSerializer { +public class GpsFixesWithEstimationDataJsonSerializer implements CompetitorTrackElementsJsonSerializer { public static final String GPS_FIXES = "gpsFixes"; public static final String BOAT_CLASS = "boatClass"; public static final String COMPETITOR_NAME = "competitorName"; @@ -35,117 +33,58 @@ public class GpsFixesWithEstimationDataJsonSerializer extends AbstractTrackedRac public static final String RELATIVE_BEARING_TO_NEXT_MARK = "relativeBearingToNextMark"; public static final String CLOSEST_DISTANCE_TO_MARK = "closestDistanceToMarkInMeters"; - private final BoatClassJsonSerializer boatClassJsonSerializer; private final GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer; private final boolean addWind; private final boolean addNextWaypoint; private final ManeuverWindJsonSerializer windJsonSerializer; private final Boolean smoothFixes; - private Integer startBeforeStartLineInSeconds; - private Integer endBeforeStartLineInSeconds; - private Integer startAfterFinishLineInSeconds; - private Integer endAfterFinishLineInSeconds; - public GpsFixesWithEstimationDataJsonSerializer(BoatClassJsonSerializer boatClassJsonSerializer, - GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer, ManeuverWindJsonSerializer windJsonSerializer, - boolean addWind, boolean addNextWaypoint, Boolean smoothFixes, Integer startBeforeStartLineInSeconds, - Integer endBeforeStartLineInSeconds, Integer startAfterFinishLineInSeconds, - Integer endAfterFinishLineInSeconds) { - this.boatClassJsonSerializer = boatClassJsonSerializer; + public GpsFixesWithEstimationDataJsonSerializer(GPSFixMovingJsonSerializer gpsFixMovingJsonSerializer, + ManeuverWindJsonSerializer windJsonSerializer, boolean addWind, boolean addNextWaypoint, + Boolean smoothFixes) { this.gpsFixMovingJsonSerializer = gpsFixMovingJsonSerializer; this.windJsonSerializer = windJsonSerializer; this.addWind = addWind; this.addNextWaypoint = addNextWaypoint; this.smoothFixes = smoothFixes; - this.startBeforeStartLineInSeconds = startBeforeStartLineInSeconds; - this.endBeforeStartLineInSeconds = endBeforeStartLineInSeconds; - this.startAfterFinishLineInSeconds = startAfterFinishLineInSeconds; - this.endAfterFinishLineInSeconds = endAfterFinishLineInSeconds; } @Override - public JSONObject serialize(TrackedRace trackedRace) { - final JSONObject result = new JSONObject(); - JSONArray byCompetitorJson = new JSONArray(); - result.put(BYCOMPETITOR, byCompetitorJson); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { - ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); - ManeuverDetectorWithEstimationDataSupportDecoratorImpl estimationDataSupportDecoratorImpl = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( - maneuverDetector, null); - TrackTimeInfo trackTimeInfo = maneuverDetector.getTrackTimeInfo(); - TimePoint from = null; - TimePoint to = null; - if (startBeforeStartLineInSeconds != Integer.MIN_VALUE) { - from = trackTimeInfo.getTrackStartTimePoint() - .minus(new MillisecondsDurationImpl(startBeforeStartLineInSeconds * 1000L)); - } else if (startAfterFinishLineInSeconds != Integer.MIN_VALUE) { - from = trackTimeInfo.getTrackEndTimePoint() - .plus(new MillisecondsDurationImpl(startAfterFinishLineInSeconds * 1000L)); - } else { - from = trackTimeInfo.getTrackStartTimePoint(); - } - if (endAfterFinishLineInSeconds != Integer.MIN_VALUE) { - to = trackTimeInfo.getTrackEndTimePoint() - .plus(new MillisecondsDurationImpl(endAfterFinishLineInSeconds * 1000L)); - } else if (endBeforeStartLineInSeconds != Integer.MIN_VALUE) { - to = trackTimeInfo.getTrackStartTimePoint() - .minus(new MillisecondsDurationImpl(endBeforeStartLineInSeconds * 1000L)); - } else { - to = trackTimeInfo.getTrackEndTimePoint(); - } - if (trackTimeInfo != null) { - final JSONObject forCompetitorJson = new JSONObject(); - byCompetitorJson.add(forCompetitorJson); - forCompetitorJson.put(COMPETITOR_NAME, competitor.getName()); - forCompetitorJson.put(BOAT_CLASS, boatClassJsonSerializer - .serialize(trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass())); - final JSONArray gpsFixesWithEstimationData = new JSONArray(); - GPSFixTrack track = trackedRace.getTrack(competitor); - track.lockForRead(); - try { - for (GPSFixMoving gpsFix : track.getFixes(from, true, to, true)) { - JSONObject serializedGpsFix = gpsFixMovingJsonSerializer.serialize(gpsFix); - if (addWind) { - Wind wind = trackedRace.getWind(gpsFix.getPosition(), gpsFix.getTimePoint()); - JSONObject serializedWind = wind == null ? null : windJsonSerializer.serialize(wind); - serializedGpsFix.put(WIND, serializedWind); - } - if (addNextWaypoint) { - Distance closestDistanceToMark = estimationDataSupportDecoratorImpl - .getClosestDistanceToMark(gpsFix.getTimePoint()); - SpeedWithBearing speedWithBearing = smoothFixes - ? track.getEstimatedSpeed(gpsFix.getTimePoint()) : gpsFix.getSpeed(); - Bearing relativeBearingToNextMark = speedWithBearing == null ? null - : estimationDataSupportDecoratorImpl.getRelativeBearingToNextMark( - gpsFix.getTimePoint(), speedWithBearing.getBearing()); - serializedGpsFix.put(CLOSEST_DISTANCE_TO_MARK, - closestDistanceToMark == null ? null : closestDistanceToMark.getMeters()); - serializedGpsFix.put(RELATIVE_BEARING_TO_NEXT_MARK, - relativeBearingToNextMark == null ? null : relativeBearingToNextMark.getDegrees()); - } - gpsFixesWithEstimationData.add(serializedGpsFix); - } - } finally { - track.unlockAfterRead(); + public JSONArray serialize(TrackedRace trackedRace, Competitor competitor, TimePoint from, TimePoint to, + TrackTimeInfo trackTimeInfo) { + final JSONArray gpsFixesWithEstimationData = new JSONArray(); + ManeuverDetectorImpl maneuverDetector = new ManeuverDetectorImpl(trackedRace, competitor); + ManeuverDetectorWithEstimationDataSupportDecoratorImpl estimationDataSupportDecoratorImpl = new ManeuverDetectorWithEstimationDataSupportDecoratorImpl( + maneuverDetector, null); + GPSFixTrack track = trackedRace.getTrack(competitor); + track.lockForRead(); + try { + for (GPSFixMoving gpsFix : track.getFixes(from, true, to, true)) { + JSONObject serializedGpsFix = gpsFixMovingJsonSerializer.serialize(gpsFix); + if (addWind) { + Wind wind = trackedRace.getWind(gpsFix.getPosition(), gpsFix.getTimePoint()); + JSONObject serializedWind = wind == null ? null : windJsonSerializer.serialize(wind); + serializedGpsFix.put(WIND, serializedWind); } - forCompetitorJson.put(GPS_FIXES, gpsFixesWithEstimationData); - Duration averageIntervalBetweenFixes = trackedRace.getTrack(competitor) - .getAverageIntervalBetweenFixes(); - forCompetitorJson.put(AVG_INTERVAL_BETWEEN_FIXES_IN_SECONDS, - averageIntervalBetweenFixes == null ? 0 : averageIntervalBetweenFixes.asSeconds()); - Double distanceTravelledInMeters = null; - if (trackTimeInfo.getTrackStartTimePoint() != null && trackTimeInfo.getTrackEndTimePoint() != null) { - distanceTravelledInMeters = track.getDistanceTraveled(trackTimeInfo.getTrackStartTimePoint(), - trackTimeInfo.getTrackEndTimePoint()).getMeters(); + if (addNextWaypoint) { + Distance closestDistanceToMark = estimationDataSupportDecoratorImpl + .getClosestDistanceToMark(gpsFix.getTimePoint()); + SpeedWithBearing speedWithBearing = smoothFixes ? track.getEstimatedSpeed(gpsFix.getTimePoint()) + : gpsFix.getSpeed(); + Bearing relativeBearingToNextMark = speedWithBearing == null ? null + : estimationDataSupportDecoratorImpl.getRelativeBearingToNextMark(gpsFix.getTimePoint(), + speedWithBearing.getBearing()); + serializedGpsFix.put(CLOSEST_DISTANCE_TO_MARK, + closestDistanceToMark == null ? null : closestDistanceToMark.getMeters()); + serializedGpsFix.put(RELATIVE_BEARING_TO_NEXT_MARK, + relativeBearingToNextMark == null ? null : relativeBearingToNextMark.getDegrees()); } - forCompetitorJson.put(DISTANCE_TRAVELLED_IN_METERS, distanceTravelledInMeters); - forCompetitorJson.put(START_TIME_POINT, trackTimeInfo.getTrackStartTimePoint() == null ? null - : trackTimeInfo.getTrackStartTimePoint().asMillis()); - forCompetitorJson.put(END_TIME_POINT, trackTimeInfo.getTrackEndTimePoint() == null ? null - : trackTimeInfo.getTrackEndTimePoint().asMillis()); + gpsFixesWithEstimationData.add(serializedGpsFix); } + } finally { + track.unlockAfterRead(); } - return result; + return gpsFixesWithEstimationData; } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 8215e8819d9..4b0f9768345 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -78,6 +78,7 @@ import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorAndBoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.CompetitorTrackWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurveWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurvesWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DefaultWindTrackJsonSerializer; @@ -1053,14 +1054,17 @@ public class RegattasResource extends AbstractSailingServerResource { .type(MediaType.TEXT_PLAIN).build(); } else { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); - CompleteManeuverCurvesWithEstimationDataJsonSerializer serializer = new CompleteManeuverCurvesWithEstimationDataJsonSerializer( + CompetitorTrackWithEstimationDataJsonSerializer serializer = new CompetitorTrackWithEstimationDataJsonSerializer( getService().getPolarDataService(), new DetailedBoatClassJsonSerializer(), - new CompleteManeuverCurveWithEstimationDataJsonSerializer( - new ManeuverMainCurveWithEstimationDataJsonSerializer(), - new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), - new ManeuverWindJsonSerializer(), new PositionJsonSerializer()), - startBeforeStartLineInSeconds, endBeforeStartLineInSeconds, startAfterFinishLineInSeconds, - endAfterFinishLineInSeconds); + new CompleteManeuverCurvesWithEstimationDataJsonSerializer(getService().getPolarDataService(), + new CompleteManeuverCurveWithEstimationDataJsonSerializer( + new ManeuverMainCurveWithEstimationDataJsonSerializer(), + new ManeuverCurveWithUnstableCourseAndSpeedWithEstimationDataJsonSerializer(), + new ManeuverWindJsonSerializer(), new PositionJsonSerializer())), + getNullableValueFromDefault(startBeforeStartLineInSeconds), + getNullableValueFromDefault(endBeforeStartLineInSeconds), + getNullableValueFromDefault(startAfterFinishLineInSeconds), + getNullableValueFromDefault(endAfterFinishLineInSeconds)); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); @@ -1068,7 +1072,7 @@ public class RegattasResource extends AbstractSailingServerResource { } return response; } - + @GET @Produces("application/json;charset=UTF-8") @Path("{regattaname}/races/{racename}/gpsFixesWithEstimationData") @@ -1098,11 +1102,14 @@ public class RegattasResource extends AbstractSailingServerResource { .type(MediaType.TEXT_PLAIN).build(); } else { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); - GpsFixesWithEstimationDataJsonSerializer serializer = new GpsFixesWithEstimationDataJsonSerializer( - new DetailedBoatClassJsonSerializer(), new GPSFixMovingJsonSerializer(), - new ManeuverWindJsonSerializer(), addWind, addNextWaypoint, smoothFixes, - startBeforeStartLineInSeconds, endBeforeStartLineInSeconds, startAfterFinishLineInSeconds, - endAfterFinishLineInSeconds); + CompetitorTrackWithEstimationDataJsonSerializer serializer = new CompetitorTrackWithEstimationDataJsonSerializer( + getService().getPolarDataService(), new DetailedBoatClassJsonSerializer(), + new GpsFixesWithEstimationDataJsonSerializer(new GPSFixMovingJsonSerializer(), + new ManeuverWindJsonSerializer(), addWind, addNextWaypoint, smoothFixes), + getNullableValueFromDefault(startBeforeStartLineInSeconds), + getNullableValueFromDefault(endBeforeStartLineInSeconds), + getNullableValueFromDefault(startAfterFinishLineInSeconds), + getNullableValueFromDefault(endAfterFinishLineInSeconds)); JSONObject jsonMarkPassings = serializer.serialize(trackedRace); String json = jsonMarkPassings.toJSONString(); return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); @@ -1111,6 +1118,10 @@ public class RegattasResource extends AbstractSailingServerResource { return response; } + private Integer getNullableValueFromDefault(Integer value) { + return Integer.MIN_VALUE == value ? null : value; + } + @GET @Produces("application/json;charset=UTF-8") @Path("{regattaname}/races") From 4998bfadeda3dd1c073e79a96cd6580491df9253 Mon Sep 17 00:00:00 2001 From: Vladislav Chumak Date: Mon, 9 Jul 2018 19:40:31 +0200 Subject: [PATCH 088/102] Extended estimation data serializer with waypointsCount attribute --- ...mpetitorTrackWithEstimationDataJsonSerializer.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java index ddaa977ff8f..eb2089ea0d5 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/CompetitorTrackWithEstimationDataJsonSerializer.java @@ -7,6 +7,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.TrackTimeInfo; @@ -34,6 +35,7 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra public static final String END_TIME_POINT = "endUnixTime"; public static final String FIXES_COUNT_FOR_POLARS = "fixesCountForPolars"; public static final String MARK_PASSINGS_COUNT = "markPassingsCount"; + public static final String WAYPOINTS_COUNT = "waypointsCount"; private final BoatClassJsonSerializer boatClassJsonSerializer; private final CompetitorTrackElementsJsonSerializer elementsJsonSerializer; @@ -107,8 +109,8 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra : trackTimeInfo.getTrackStartTimePoint().asMillis()); forCompetitorJson.put(END_TIME_POINT, trackTimeInfo.getTrackEndTimePoint() == null ? null : trackTimeInfo.getTrackEndTimePoint().asMillis()); - int markPassingsCount = getMarkPassingsCount(trackedRace, competitor); - forCompetitorJson.put(MARK_PASSINGS_COUNT, markPassingsCount); + forCompetitorJson.put(MARK_PASSINGS_COUNT, getMarkPassingsCount(trackedRace, competitor)); + forCompetitorJson.put(WAYPOINTS_COUNT, getWaypointsCount(trackedRace)); forCompetitorJson.put(elements, elementsJsonSerializer.serialize(trackedRace, competitor, from, to, trackTimeInfo)); } @@ -134,4 +136,9 @@ public class CompetitorTrackWithEstimationDataJsonSerializer extends AbstractTra return fixesCountForBoatPolars == null ? 0L : fixesCountForBoatPolars; } + private int getWaypointsCount(TrackedRace trackedRace) { + Iterable waypoints = trackedRace.getRace().getCourse().getWaypoints(); + return Util.size(waypoints); + } + } From 6c1c1e9dd65a678f0ef4fbcda5b6bf4d29fe3293 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 10 Jul 2018 14:59:30 +0200 Subject: [PATCH 089/102] added iOS Sail InSight signing specification file Change-Id: I57efa74171525d524858230e7f4ef189cbc57f81 --- .../central-signing/NAASSigningExportOptions.plist | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ios/SAPTracker/cfg/central-signing/NAASSigningExportOptions.plist diff --git a/ios/SAPTracker/cfg/central-signing/NAASSigningExportOptions.plist b/ios/SAPTracker/cfg/central-signing/NAASSigningExportOptions.plist new file mode 100644 index 00000000000..dc4d563fafd --- /dev/null +++ b/ios/SAPTracker/cfg/central-signing/NAASSigningExportOptions.plist @@ -0,0 +1,13 @@ + + + + +method +app-store +provisioningProfiles + +com.sap.sailing.ios.SAPTracker.release +SAP Sail InSight + + + From 780275bce6ab88e915d92aefffa99dd584f2a56f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 10 Jul 2018 17:26:22 +0200 Subject: [PATCH 090/102] increased iOS Sail InSight version from 1.0.22 to 1.0.23 Change-Id: I03d30bf137e56660693e18aca64ade172b516987 --- ios/SAPTracker/cfg/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/SAPTracker/cfg/VERSION b/ios/SAPTracker/cfg/VERSION index c787b213b07..154b9fce5be 100644 --- a/ios/SAPTracker/cfg/VERSION +++ b/ios/SAPTracker/cfg/VERSION @@ -1 +1 @@ -1.0.22 +1.0.23 From d8220118d9d9c776d5a805915edaf9add2ae5d40 Mon Sep 17 00:00:00 2001 From: Henri Kohlberg Date: Wed, 11 Jul 2018 07:18:06 +0000 Subject: [PATCH 091/102] Firefox needs to be maximzed for Selenium-tests --- wiki/howto/development/selenium-ui-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/howto/development/selenium-ui-tests.md b/wiki/howto/development/selenium-ui-tests.md index 388df7eab03..2282925de26 100644 --- a/wiki/howto/development/selenium-ui-tests.md +++ b/wiki/howto/development/selenium-ui-tests.md @@ -8,7 +8,7 @@ There are two ways to run the Selenium tests locally on your computer. Either, y ### Firefox Prerequisites -!Since old Firefox version do not work with WindowScaling, ensure that in windows the Font Scaling is set to 100%, else Firefox will not be able to click buttons. You can find this setting at "Settings>Display Settings>Change the size of text, apps and other items"! +!Since old Firefox version do not work with WindowScaling, ensure that in windows the Font Scaling is set to 100%, else Firefox will not be able to click buttons. You can find this setting at "Settings>Display Settings>Change the size of text, apps and other items". Also make sure that Firefox is running maximized (at least at Windows) as per default it is not running maximized! You have to ensure that your Firefox browser has a profile called "Selenium" and that in this profile the latest version of the GWT plugin is installed. To ensure this, launch the server by choosing the "Sailing Server (Proxy)" or "Sailing Server (No Proxy)" launch config. Then, run the "SailingGWT" launch to start the GWT UI in hosted / development mode. Afterwards you can launch Firefox from the command line with the -p option. On Windows machines, you can do this by pressing the Windows key, then typing "firefox.exe -p". In the profile manager create a profile called Selenium and start Firefow with that profile. Hit the entry page of the AdminConsole by entering `http://127.0.0.1:8888/gwt/AdminConsole.html?gwt.codesvr=127.0.0.1:9997` into the address bar. This will ask you to install the GWT plugin into your Selenium profile. When done, exit the browser. You may use the profile manager again to set your default profile to your original profile. From 1296ac91f157c6afd1c5b9b6252e6b43c48718af Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 12 Jul 2018 13:43:04 +0200 Subject: [PATCH 092/102] Added mueggelsee to AvailableWindFinderSpotCollections --- .../common/windfinder/AvailableWindFinderSpotCollections.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java index df9c8448488..992525c3dde 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java @@ -18,7 +18,8 @@ public enum AvailableWindFinderSpotCollections { SZCZECIN("szczecin"), SANKT_PETERSBURG("sankt_petersburg"), SANKT_MORITZ("sankt_moritz"), - PORTO_CERVO("porto_cervo"); + PORTO_CERVO("porto_cervo"), + MUEGGELSEE("mueggelsee"); private final String name; From 91c6a231ac94ad25f6720086c271141ccfc121e8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jul 2018 15:10:28 +0200 Subject: [PATCH 093/102] increased maximum number of discards to 15 Change-Id: I9dad6b61e3c917dc05bd206f4c556f4d764d36b2 --- .../AbstractLeaderboardDialog.java | 1 - .../adminconsole/DiscardThresholdBoxes.java | 20 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardDialog.java index b3187204d03..e25d879f40d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardDialog.java @@ -15,7 +15,6 @@ public abstract class AbstractLeaderboardDialog validator, DialogCallback callback) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java index 2a3d5c20c4b..9f2d6b16e4e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; import java.util.List; +import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; @@ -23,7 +24,9 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog; * */ public class DiscardThresholdBoxes { - private static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4; + private static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 15; + + private static final int NUMBER_OF_BOXES_PER_LINE = 5; private final LongBox[] discardThresholdBoxes; private final DataEntryDialog parent; @@ -82,16 +85,17 @@ public class DiscardThresholdBoxes { private Widget createDiscardThresholdBoxesPanel(StringMessages stringMessages) { assert discardThresholdBoxes != null && discardThresholdBoxes.length == MAX_NUMBER_OF_DISCARDED_RESULTS; - VerticalPanel vp = new VerticalPanel(); + final VerticalPanel vp = new VerticalPanel(); vp.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn())); - HorizontalPanel hp = new HorizontalPanel(); - vp.add(hp); - hp.setSpacing(3); + Grid grid = new Grid(MAX_NUMBER_OF_DISCARDED_RESULTS/NUMBER_OF_BOXES_PER_LINE+ + (MAX_NUMBER_OF_DISCARDED_RESULTS%NUMBER_OF_BOXES_PER_LINE==0?0:1), 2*NUMBER_OF_BOXES_PER_LINE); + grid.setCellSpacing(3); + vp.add(grid); for (int i = 0; i < discardThresholdBoxes.length; i++) { - hp.add(new Label("" + (i + 1) + ".")); - hp.add(discardThresholdBoxes[i]); + grid.setWidget(i/NUMBER_OF_BOXES_PER_LINE, 2*(i%NUMBER_OF_BOXES_PER_LINE), new Label("" + (i + 1) + ".")); + grid.setWidget(i/NUMBER_OF_BOXES_PER_LINE, 2*(i%NUMBER_OF_BOXES_PER_LINE)+1, discardThresholdBoxes[i]); } - parent.alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE); +// parent.alignAllPanelWidgetsVertically(grid, HasVerticalAlignment.ALIGN_MIDDLE); return vp; } From 3abbe2f21bae58c4ef70c1759b0c9ee29d5e5179 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jul 2018 17:22:29 +0200 Subject: [PATCH 094/102] removed warnings Change-Id: Ifd389f06aa06f3f2a0f82000551441e90859eff2 --- .../gwt/ui/adminconsole/DiscardThresholdBoxes.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java index 9f2d6b16e4e..d1c393ccde5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DiscardThresholdBoxes.java @@ -4,8 +4,6 @@ import java.util.ArrayList; import java.util.List; import com.google.gwt.user.client.ui.Grid; -import com.google.gwt.user.client.ui.HasVerticalAlignment; -import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.LongBox; import com.google.gwt.user.client.ui.VerticalPanel; @@ -29,7 +27,6 @@ public class DiscardThresholdBoxes { private static final int NUMBER_OF_BOXES_PER_LINE = 5; private final LongBox[] discardThresholdBoxes; - private final DataEntryDialog parent; /** * The widget used to represent the UI @@ -41,7 +38,6 @@ public class DiscardThresholdBoxes { } public DiscardThresholdBoxes(DataEntryDialog parent, int[] initialDiscardThresholds, StringMessages stringMessages) { - this.parent = parent; discardThresholdBoxes = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS]; for (int i = 0; i < discardThresholdBoxes.length; i++) { if (initialDiscardThresholds != null && i < initialDiscardThresholds.length) { @@ -87,15 +83,16 @@ public class DiscardThresholdBoxes { assert discardThresholdBoxes != null && discardThresholdBoxes.length == MAX_NUMBER_OF_DISCARDED_RESULTS; final VerticalPanel vp = new VerticalPanel(); vp.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn())); - Grid grid = new Grid(MAX_NUMBER_OF_DISCARDED_RESULTS/NUMBER_OF_BOXES_PER_LINE+ - (MAX_NUMBER_OF_DISCARDED_RESULTS%NUMBER_OF_BOXES_PER_LINE==0?0:1), 2*NUMBER_OF_BOXES_PER_LINE); + final Grid grid = new Grid(0, 2*NUMBER_OF_BOXES_PER_LINE); grid.setCellSpacing(3); vp.add(grid); for (int i = 0; i < discardThresholdBoxes.length; i++) { + if (i%NUMBER_OF_BOXES_PER_LINE == 0) { + grid.resizeRows(i/NUMBER_OF_BOXES_PER_LINE + 1); + } grid.setWidget(i/NUMBER_OF_BOXES_PER_LINE, 2*(i%NUMBER_OF_BOXES_PER_LINE), new Label("" + (i + 1) + ".")); grid.setWidget(i/NUMBER_OF_BOXES_PER_LINE, 2*(i%NUMBER_OF_BOXES_PER_LINE)+1, discardThresholdBoxes[i]); } -// parent.alignAllPanelWidgetsVertically(grid, HasVerticalAlignment.ALIGN_MIDDLE); return vp; } From a7c1d833d8d022e36864edd6f29193b27fa6b1ef Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jul 2018 17:24:08 +0200 Subject: [PATCH 095/102] added release notes for bug4633 Change-Id: I61e2f7282e14eea9f4f7dd7edc5fd96f28e0d1a9 --- java/com.sap.sailing.www/release_notes_admin.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index f501ac83cf8..daafe6c3709 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -30,6 +30,8 @@

        • In the "Tracked races" tab, within the eponymous section of the AdminConsole, the "Set start time received" dialog can be used to remove the currently set start time received, by simply leaving the value empty and confirming the dialog.
        • +
        • Up to 15 discards can now be configured for series and leaderboards. This enables, e.g., "Wednesday Night" scenarios + where over the season, say, 20 races are run but due to changing participation only, say, the five best ones shall be scored.

        June 2018

        From fc3d8003c3d05b3304aabaaf1750269d21a4abfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 16 Jul 2018 10:06:10 +0200 Subject: [PATCH 096/102] bug4671 ensure boxes are properly cleaned --- .../sap/sailing/gwt/ui/server/MediaServiceImpl.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index bc2f6336275..18c597538cf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -241,10 +241,13 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi try { tmp = createFileFromData(start, end, skipped); try (IsoFile isof = new IsoFile(tmp)) { - recordStartedTimer = determineRecordingStart(isof); - spherical = determine360(isof); - duration = determineDuration(isof); - removeTempFiles(isof); + try { + recordStartedTimer = determineRecordingStart(isof); + spherical = determine360(isof); + duration = determineDuration(isof); + } finally { + removeTempFiles(isof); + } } } catch (Exception e) { logger.log(Level.WARNING, "Error in video analysis ", e); From 6569cf6a7203ab6b3734e83da32ce2fca6c9257b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20B=C3=B6rnert?= Date: Mon, 16 Jul 2018 10:12:32 +0200 Subject: [PATCH 097/102] bug4671 removal of tmp files now via Files.delete --- .../com/sap/sailing/gwt/ui/server/MediaServiceImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java index 18c597538cf..4ac3bbd8d54 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/MediaServiceImpl.java @@ -18,6 +18,7 @@ import java.net.URLConnection; import java.net.URLEncoder; import java.nio.channels.Channels; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -254,7 +255,11 @@ public class MediaServiceImpl extends RemoteServiceServlet implements MediaServi message = e.getMessage(); } finally { if (tmp != null) { - tmp.delete(); + try { + Files.delete(tmp.toPath()); + } catch (IOException e) { + logger.log(Level.SEVERE, "Could not delete tmp mp4 file", e); + } } } return new VideoMetadataDTO(true, duration, spherical, recordStartedTimer, message); From 7d98cdbea33d92f55637de5e1c6aecb0eeef8eac Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 16 Jul 2018 10:47:00 +0200 Subject: [PATCH 098/102] added a script to aggregate settings of logged-in users from MongoDB Change-Id: Id60482999f4baef63da6d76ec4c1604d3c5da921 --- configuration/settings-agg.js | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 configuration/settings-agg.js diff --git a/configuration/settings-agg.js b/configuration/settings-agg.js new file mode 100755 index 00000000000..cf315db2201 --- /dev/null +++ b/configuration/settings-agg.js @@ -0,0 +1,38 @@ +allDatabases = db.adminCommand({ "listDatabases": 1 }).databases + +collection = 'PREFERENCES' + +acc = {} + +allDatabases.forEach(function(d) { + //database = connect('localhost:27017/' + d.name) + database = db.getSiblingDB(d.name) + collections = database.getCollectionNames() + if (collections.indexOf(collection) >= 0) { + usage = database.getCollection(collection).aggregate([{$unwind: "$KEYS_AND_VALUES"}, {$group: {_id:"$KEYS_AND_VALUES.VALUE", total:{$sum:1}}}]) + + usage.forEach(function(u) { + if (u._id[0] == '{') { + if (u._id in acc) { + acc[u._id] += u.total + } else { + acc[u._id] = u.total + } + } + }) + } +}) + +result = [] +for (var key in acc) { + if (acc.hasOwnProperty(key)) { + result.push({"setting": key, "count": acc[key]}) + } +} + +result.sort(function(a, b) { return b.count - a.count }) + +for (var i in result) { + print("Setting: " + result[i].setting + "\nCount: " + result[i].count + "\n") +} + From e6e29dd6a7ff3864d7429ee45c55cbc5a5f529e7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 16 Jul 2018 15:15:52 +0200 Subject: [PATCH 099/102] added comments to settings-agg.js Change-Id: Id5a047ee6f26d03e693bbf1ac392c9448187069c --- configuration/settings-agg.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configuration/settings-agg.js b/configuration/settings-agg.js index cf315db2201..5bbb25b21de 100755 --- a/configuration/settings-agg.js +++ b/configuration/settings-agg.js @@ -1,3 +1,5 @@ +// Aggregates settings by their frequency across all DBs. +// Usage: mongo --host --port settings-agg.js allDatabases = db.adminCommand({ "listDatabases": 1 }).databases collection = 'PREFERENCES' From e16569ae17403bc75b655e518bcd86ed3c9ae618 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 16 Jul 2018 22:09:20 +0200 Subject: [PATCH 100/102] removed setting of JAVA_HOME from env.sh Change-Id: I8187d94a2b0fa6ea5fe9fe97d87d46df7a28c14c --- java/target/env.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/java/target/env.sh b/java/target/env.sh index d3c61f6c659..5c56c44d898 100644 --- a/java/target/env.sh +++ b/java/target/env.sh @@ -88,7 +88,6 @@ ADDITIONAL_JAVA_ARGS="-Dpersistentcompetitors.clear=false -XX:ThreadPriorityPoli # Uncomment for use with SAP JVM only: #ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -XX:+GCHistory -XX:GCHistoryFilename=logs/sapjvm_gc@PID.prf" -JAVA_HOME=/opt/jdk1.8.0_20 if [[ ! -d $JAVA_HOME ]] && [[ -f "/usr/libexec/java_home" ]]; then JAVA_HOME=`/usr/libexec/java_home` fi From 283b12690d699b33591bd1128d03fa0cbacdb002 Mon Sep 17 00:00:00 2001 From: Steffen Wagner Date: Tue, 17 Jul 2018 02:14:24 +0200 Subject: [PATCH 101/102] added mvn path to sailing profile --- configuration/sailing.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/sailing.sh b/configuration/sailing.sh index 9e801102202..f096a276999 100755 --- a/configuration/sailing.sh +++ b/configuration/sailing.sh @@ -12,7 +12,7 @@ export JAVA_HOME=/opt/sapjvm_8 export JAVA_1_7_HOME=/opt/jdk1.7.0_75 export ANDROID_HOME=/opt/android-sdk-linux -export PATH=$PATH:$JAVA_HOME/bin:/opt/amazon/ec2-api-tools-1.6.8.0/bin:/opt/amazon/bin +export PATH=$PATH:$JAVA_HOME/bin:/opt/amazon/ec2-api-tools-1.6.8.0/bin:/opt/amazon/bin:/opt/apache-maven-3.2.1/bin export DISPLAY=:2.0 From 01f607d6e11ed8e994aed3fbd6e7081f3df02b99 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 17 Jul 2018 09:43:58 +0200 Subject: [PATCH 102/102] Added Varianta boatclass. --- .../com/sap/sailing/domain/common/BoatClassMasterdata.java | 1 + .../sap/sailing/gwt/common/client/BoatClassImageResolver.java | 1 + .../sailing/gwt/common/client/BoatClassImageResources.java | 4 ++++ .../shared/racemap/BoatClassVectorGraphicsResolver.java | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java index 211470e68cf..a5d8736439f 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java @@ -117,6 +117,7 @@ public enum BoatClassMasterdata { TOM_28_MAX ("Tom 28 MAX", true, 8.48, 2.48, BoatHullType.MONOHULL, true, "Tom 28"), TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true), TP52 ("TP52", true, 15.85, 4.35, BoatHullType.MONOHULL, true, "TP 52", "Transpac 52", "Transpac52"), + VARIANTA ("Varianta", true, 6.40, 2.10, BoatHullType.MONOHULL, true), VAURIEN ("Vaurien", true, 4.08, 1.47, BoatHullType.MONOHULL, true), VENT_D_OUEST ("Vent d'Ouest", true, 5.85, 1.75, BoatHullType.MONOHULL, true, "VENTDOUEST", "VENTD'OUEST"), VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java index b7ca2ca21a3..4d755860ec3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java @@ -128,6 +128,7 @@ public class BoatClassImageResolver { boatClassIconsMap.put(BoatClassMasterdata.TOM_28_MAX.getDisplayName(), imageResources.Tom28MaxIcon()); boatClassIconsMap.put(BoatClassMasterdata.TP52.getDisplayName(), imageResources.TP52Icon()); boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon()); + boatClassIconsMap.put(BoatClassMasterdata.VARIANTA.getDisplayName(), imageResources.VariantaIcon()); boatClassIconsMap.put(BoatClassMasterdata.VAURIEN.getDisplayName(), imageResources.VaurienIcon()); boatClassIconsMap.put(BoatClassMasterdata.VENT_D_OUEST.getDisplayName(), imageResources.VentdOuestIcon()); boatClassIconsMap.put(BoatClassMasterdata.VIPER_640.getDisplayName(), imageResources.Viper640Icon()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java index 0270a334a1f..027523c1035 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java @@ -443,4 +443,8 @@ public interface BoatClassImageResources extends ClientBundle { @Source("com/sap/sailing/gwt/ui/client/images/boatclass/VAURIEN.png") @ImageOptions(preventInlining = true) ImageResource VaurienIcon(); + + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/VARIANTA.png") + @ImageOptions(preventInlining = true) + ImageResource VariantaIcon(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java index 7a6e7de725c..cfa1fa73717 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java @@ -54,7 +54,7 @@ public class BoatClassVectorGraphicsResolver { BoatClassMasterdata.TOM_28_MAX, BoatClassMasterdata.DELPHIA_24, BoatClassMasterdata.RS200, BoatClassMasterdata.RS400, BoatClassMasterdata.RS500, BoatClassMasterdata.RS800, BoatClassMasterdata.STREAMLINE, BoatClassMasterdata.SWAN_45, BoatClassMasterdata.TEENY, BoatClassMasterdata.X_99, - BoatClassMasterdata.TRIAS, BoatClassMasterdata.VENT_D_OUEST, BoatClassMasterdata.FLYING_JUNIOR, BoatClassMasterdata.VAURIEN); + BoatClassMasterdata.TRIAS, BoatClassMasterdata.VENT_D_OUEST, BoatClassMasterdata.FLYING_JUNIOR, BoatClassMasterdata.VAURIEN, BoatClassMasterdata.VARIANTA); BoatClassVectorGraphics circle = new CircleVectorGraphics(BoatClassMasterdata.RUNNING); defaultBoatVectorGraphics = dinghyWithSpinnaker; // TODO see bug 2571; this should be a slup-rigged icon working for 470, 505, J/70 etc.