bug6162: keep the last used fix to "hide" past buffered fixes

This commit is contained in:
Axel Uhl committed 2025-10-07 16:37:29 +02:00
1 parent 6d1074a628
commit eb33bf0253
14 files changed
+47 -20

No files matched your search

@@ -53,9 +53,9 @@ import com.sap.sse.replication.Replicable;
* can be registered with this server using the {@link #addListener(BulkFixReceiver)} method.
* <p>
*
* When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver},
* When combined with {@link IgtimiWindReceiver} (which is such a {@link BulkFixReceiver}),
* {@link IgtimiWindListener}s can be registered on the wind receiver, just as they can
* for a websocket connection.
* for a websocket connection.<p>
*
* The server uses <tt>java.nio</tt> and {@link ServerSocketChannel}s, avoiding the creation
* of a thread per connection.<p>
@@ -188,8 +188,9 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
}
/**
* Returns a wind fix produced out of an AWS/AWA and other fixes, as well as the fixes used for this, including a {@link BatteryLevel}
* fix, if any was found, although this did technically not contribute to the production of the {@link Wind} object.
* Returns a wind fix produced out of an AWS/AWA and other fixes, as well as the fixes used for this (in case of
* interpolation between two fixes, the older one of the two is returned), including a {@link BatteryLevel} fix, if
* any was found, although this did technically not contribute to the production of the {@link Wind} object.
*/
private Pair<Wind, Set<Fix>> getWind(final TimePoint timePoint, String deviceSerialNumber) throws ClassNotFoundException, IOException, ParseException {
final Wind result;
@@ -213,7 +214,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
final SpeedWithBearing apparentWindSpeedWithDirection = new KnotSpeedWithBearingImpl(aws.getKnots(), apparentWindDirection);
/*
* Hint from Brent Russell from Igtimi, at 2013-12-05 on the question whether to use GpsLatLong to
* improve precision of boat speed / coarse over SOG/COG measurements:
* improve precision of boat speed / course over SOG/COG measurements:
*
* "Personally I would use COG/SOG exclusively, and if unhappy with the result add a small amount of
* smoothing and consider dropping samples as outliers if they cause a SOG discontinuity. The latter
@@ -260,7 +261,8 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
/**
* Searches for the last fix at or before {@code timePoint} in the given {@link trackToCleanUp}. If such a fix is
* found, adds the given fix to {@code fixesUsed} and clears all fixes from the {@code trackToCleanUp} that are
* earlier than or at the {@code timePoint} given.
* earlier than the {@code timePoint} given. This in particular preserves at least one live fix which will "hide"
* older, e.g., buffered fixes that may be received when a WindBot device sends them out-of-order.
* <p>
*
* This assumes that the {@code fix} passed has been "consumed" now, and earlier fixes will not be relevant anymore.
@@ -270,7 +272,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
final FixType fix = trackToCleanUp.getLastFixAtOrBefore(timePoint);
if (fix != null) {
fixesUsed.add(fix);
trackToCleanUp.removeAllUpToAndIncluding(fix);
trackToCleanUp.removeAllUpToExcluding(fix);
}
}
@@ -292,6 +292,11 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
return result;
}
/**
* Calculates a linear interpolation of values based on their time points and a target time point that is expected
* to be in between (inclusive) the two time points for the two values. If the two time points for the two values
* are equal, the average of the two values is returned.
*/
private <V, T> T timeBasedAverage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) {
final T acc;
if (timePoint1.equals(timePoint2)) {
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test"/>
</listAttribute>
@@ -10,7 +11,7 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src=/test=/true=/&lt;com.sap.sailing.domain.test"/>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src&lt;com.sap.sailing.domain.test"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
@@ -26,5 +26,5 @@ public interface DynamicTrackWithRemove<FixType extends Timed> extends DynamicTr
*/
boolean remove(FixType fix);
void removeAllUpToAndIncluding(FixType fix);
void removeAllUpToExcluding(FixType fix);
}
@@ -27,7 +27,7 @@ public class DynamicTrackWithRemoveImpl<FixType extends Timed> extends DynamicTr
}
@Override
public void removeAllUpToAndIncluding(FixType fix) {
getInternalFixes().removeAllLessOrEqual(fix);
public void removeAllUpToExcluding(FixType fix) {
getInternalFixes().removeAllLessThan(fix);
}
}
@@ -341,6 +341,7 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race
contentPanel.add(new Label(stringMessages.seeIgtimiTabForAccountSettings()));
final CheckBox correctByDeclination = new CheckBox(stringMessages.declinationCheckbox());
correctByDeclination.setValue(true); // by default this is desirable because the Igtimi connector reads uncorrected magnetic values
final TextBox optionalBearerTokenBox = new TextBox();
final Button importButton = new Button(stringMessages.importWindFromIgtimi());
importButton.ensureDebugId("ImportWindFromIgtimi");
final HTML resultReport = new HTML();
@@ -357,7 +358,7 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race
if (Window.confirm(warningMessage)) {
resultReport.setText(stringMessages.loading());
sailingServiceWrite.importWindFromIgtimi(new ArrayList<>(refreshableRaceSelectionModel.getSelectedSet()),
correctByDeclination.getValue(), new AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>>() {
correctByDeclination.getValue(), optionalBearerTokenBox.getValue(), new AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorImportingIgtimiWind(caught.getMessage()));
@@ -383,6 +384,11 @@ public class WindPanel extends FormPanel implements FilterablePanelProvider<Race
}
});
contentPanel.add(correctByDeclination);
final HorizontalPanel tokenPanel = new HorizontalPanel();
tokenPanel.setSpacing(5);
tokenPanel.add(new Label(stringMessages.optionalBearerTokenForWindImport()));
tokenPanel.add(optionalBearerTokenBox);
contentPanel.add(tokenPanel);
contentPanel.add(importButton);
contentPanel.add(resultReport);
return igtimiWindImportRootPanel;
@@ -243,7 +243,7 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
throws NotFoundException, NotDenotableForRaceLogTrackingException;
Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces,
boolean correctByDeclination)
boolean correctByDeclination, String optionalBearerTokenOrNull)
throws IllegalStateException, Exception;
IgtimiDataAccessWindowWithSecurityDTO addIgtimiDataAccessWindow(String deviceSerialNumber, Date from, Date to);
@@ -586,7 +586,7 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
void removeIgtimiDevice(String deviceSerialNumber, AsyncCallback<Void> asyncCallback);
void importWindFromIgtimi(List<RaceDTO> selectedRaces, boolean correctByDeclination,
AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback);
String optionalBearerTokenOrNull, AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback);
/**
* The boolean result reflects whether a connection to the device identified by {@code serialNumber}
@@ -2563,4 +2563,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String selectToRaceColumn();
String exportTWAHistogramToCsv();
String exportWindSpeedHistogramToCsv();
String optionalBearerTokenForWindImport();
}
@@ -2600,4 +2600,5 @@ successfullyCopiedPairings=Successfully copied pairings
selectFromRaceColumn=Select race column from where to start copying pairings
selectToRaceColumn=Select race colunm to where to copy pairings
exportTWAHistogramToCsv=Export True Wind Angle histogram to CSV
exportWindSpeedHistogramToCsv=Export Wind Speed histogram to CSV
exportWindSpeedHistogramToCsv=Export Wind Speed histogram to CSV
optionalBearerTokenForWindImport=Optional bearer token for wind import
@@ -2594,4 +2594,5 @@ successfullyCopiedPairings=Zuordnungen erfolgreich kopiert
selectFromRaceColumn=Spalte auswählen, ab der die Zuordnungen kopiert werden sollen
selectToRaceColumn=Spalte auswählen, bis zu der die Zuordnungen kopiert werden sollen
exportTWAHistogramToCsv=Histogramm der wahren Windwinkel in CSV exportieren
exportWindSpeedHistogramToCsv=Histogramm der wahren Windgeschwindigkeiten in CSV exportieren
exportWindSpeedHistogramToCsv=Histogramm der wahren Windgeschwindigkeiten in CSV exportieren
optionalBearerTokenForWindImport=Optionales Bearer Token für Wind-Import
@@ -6099,7 +6099,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return Activator.getInstance().getGoogleMapsLoaderAuthenticationParams();
}
protected IgtimiConnection createIgtimiConnection() {
return getIgtimiConnectionFactory().getOrCreateConnection(()->getSecurityService().getCurrentUser() != null ? getSecurityService().getAccessToken(getSecurityService().getCurrentUser().getName()) : null);
/**
* @param optionalBearerToken
* if present, this bearer token is used to authenticate the Igtimi connection; otherwise, we try to use
* Igtimi default credentials provided at startup through the {@code igtimi.bearer.token} system
* property. Only if no such system property has been set, we look for a logged-in user in the
* {@link #getSecurityService() security service} and use its access token
*/
protected IgtimiConnection createIgtimiConnection(Optional<String> optionalBearerToken) {
return optionalBearerToken.map(bearerToken->getIgtimiConnectionFactory().getOrCreateConnection(bearerToken))
.orElse(getIgtimiConnectionFactory().getOrCreateConnection(()->getSecurityService().getCurrentUser() != null
? getSecurityService().getAccessToken(getSecurityService().getCurrentUser().getName())
: null));
}
}
@@ -2215,7 +2215,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
@Override
public Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces,
boolean correctByDeclination)
boolean correctByDeclination, String optionalBearerTokenOrNull)
throws IllegalStateException, ClientProtocolException, IOException, org.json.simple.parser.ParseException {
final List<DynamicTrackedRace> trackedRaces = new ArrayList<>();
if (selectedRaces != null && !selectedRaces.isEmpty()) {
@@ -2238,7 +2238,7 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
}
}
Map<RegattaAndRaceIdentifier, Integer> numberOfWindFixesImportedPerRace = new HashMap<RegattaAndRaceIdentifier, Integer>();
final IgtimiConnection conn = createIgtimiConnection();
final IgtimiConnection conn = createIgtimiConnection(Optional.ofNullable(optionalBearerTokenOrNull));
// filter account based on used permissions to read account:
Map<TrackedRace, Integer> resultsForAccounts = conn.importWindIntoRace(trackedRaces, correctByDeclination);
for (Entry<TrackedRace, Integer> resultForAccount : resultsForAccounts.entrySet()) {