bug6076: add .vak file extension, convert forestay load to tons

This commit is contained in:
Axel Uhl
2025-02-04 16:45:16 +01:00
parent a2c8bb7ea4
commit 06cc9faff4
13 changed files with 195 additions and 96 deletions
@@ -8,7 +8,12 @@ import com.sap.sse.common.Timed;
* not throw an exception. * not throw an exception.
*/ */
public interface DoubleVectorFix extends Timed { public interface DoubleVectorFix extends Timed {
/**
* Obtains a copy of the array of {@link Double} values in this fix. Changes to the array returned
* do <em>not</em> modify this fix.
*/
Double[] get(); Double[] get();
Double get(int index); Double get(int index);
/** /**
* Tells whether at least one component is not {@code null} * Tells whether at least one component is not {@code null}
@@ -44,56 +44,61 @@ public class ExpeditionGPSFixImporter implements GPSFixImporter {
TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifierImpl(sourceName, getType() + "@" + new Date()); TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifierImpl(sourceName, getType() + "@" + new Date());
final AtomicBoolean importedFixes = new AtomicBoolean(false); final AtomicBoolean importedFixes = new AtomicBoolean(false);
CompressedStreamsUtil.handlePotentiallyCompressedFiles(sourceName, inputStream, CompressedStreamsUtil.handlePotentiallyCompressedFiles(sourceName, inputStream,
charset, new ExpeditionImportFileHandler() { charset, getExpeditionImportFileHandler(callback, device, importedFixes));
@Override return importedFixes.get();
protected void handleExpeditionFile(String fileName, InputStream stream, Charset charset) throws IOException { }
final BufferedReader br = new BufferedReader(new InputStreamReader(stream, charset));
final String headerLine = br.readLine(); protected ExpeditionImportFileHandler getExpeditionImportFileHandler(Callback callback,
final ExpeditionExtendedDataImporterImpl importer = new ExpeditionExtendedDataImporterImpl(); TrackFileImportDeviceIdentifier device, final AtomicBoolean importedFixes) {
final Map<String, Integer> columnDefinitions = importer.parseHeader(headerLine); return new ExpeditionImportFileHandler() {
final AtomicInteger lineNr = new AtomicInteger(0); @Override
br.lines().forEach(line -> { protected void handleExpeditionFile(String fileName, InputStream stream, Charset charset) throws IOException {
if (!line.trim().isEmpty()) { final BufferedReader br = new BufferedReader(new InputStreamReader(stream, charset));
importer.parseLine(lineNr.incrementAndGet(), fileName, line, final String headerLine = br.readLine();
columnDefinitions, (timePoint, columnValues, columns) -> { final ExpeditionExtendedDataImporterImpl importer = new ExpeditionExtendedDataImporterImpl();
final double latDeg = Double final Map<String, Integer> columnDefinitions = importer.parseHeader(headerLine);
.parseDouble(columnValues[columns.get(LAT_COLUMN_HEADING)]); final AtomicInteger lineNr = new AtomicInteger(0);
final double lonDeg = Double br.lines().forEach(line -> {
.parseDouble(columnValues[columns.get(LON_COLUMN_HEADING)]); if (!line.trim().isEmpty()) {
final double cogDeg = Double importer.parseLine(lineNr.incrementAndGet(), fileName, line,
.parseDouble(columnValues[columns.get(COG_COLUMN_HEADING)]); columnDefinitions, (timePoint, columnValues, columns) -> {
final double sogKnots = Double final double latDeg = Double
.parseDouble(columnValues[columns.get(SOG_COLUMN_HEADING)]); .parseDouble(columnValues[columns.get(LAT_COLUMN_HEADING)]);
final DegreePosition position = new DegreePosition(latDeg, lonDeg); final double lonDeg = Double
Bearing optionalTrueHeading; .parseDouble(columnValues[columns.get(LON_COLUMN_HEADING)]);
if (columns.containsKey(HDT_COLUMN_HEADING)) { final double cogDeg = Double
try { .parseDouble(columnValues[columns.get(COG_COLUMN_HEADING)]);
optionalTrueHeading = new DegreeBearingImpl(Double.parseDouble(columnValues[columns.get(HDT_COLUMN_HEADING)])) final double sogKnots = Double
.add(DeclinationService.INSTANCE.getDeclination(timePoint, position, /* timeout ms */ 1000).getBearingCorrectedTo(timePoint)); .parseDouble(columnValues[columns.get(SOG_COLUMN_HEADING)]);
} catch (NumberFormatException | IOException | ParseException e) { final DegreePosition position = new DegreePosition(latDeg, lonDeg);
logger.log(Level.WARNING, "Problem obtaining declination for Expedition fix heading", e); Bearing optionalTrueHeading;
optionalTrueHeading = null; if (columns.containsKey(HDT_COLUMN_HEADING)) {
} try {
} else { optionalTrueHeading = new DegreeBearingImpl(Double.parseDouble(columnValues[columns.get(HDT_COLUMN_HEADING)]))
optionalTrueHeading = null; .add(DeclinationService.INSTANCE.getDeclination(timePoint, position, /* timeout ms */ 1000).getBearingCorrectedTo(timePoint));
} } catch (NumberFormatException | IOException | ParseException e) {
final GPSFixMoving fix = new GPSFixMovingImpl( logger.log(Level.WARNING, "Problem obtaining declination for Expedition fix heading", e);
position, timePoint, optionalTrueHeading = null;
new KnotSpeedWithBearingImpl(sogKnots, }
new DegreeBearingImpl(cogDeg)), optionalTrueHeading); } else {
callback.addFix(fix, device); optionalTrueHeading = null;
importedFixes.set(true); }
}); final GPSFixMoving fix = new GPSFixMovingImpl(
} position, timePoint,
}); new KnotSpeedWithBearingImpl(sogKnots,
new DegreeBearingImpl(cogDeg)), optionalTrueHeading);
callback.addFix(fix, device);
importedFixes.set(true);
});
} }
}); });
return importedFixes.get(); }
};
} }
@Override @Override
public Iterable<String> getSupportedFileExtensions() { public Iterable<String> getSupportedFileExtensions() {
return ExpeditionImportFileHandler.supportedExpeditionLogFileExtensions; return getExpeditionImportFileHandler(null, null, null).getSupportedFileExtensions();
} }
@Override @Override
@@ -70,7 +70,7 @@ public class TestReadingVakarosFiles {
Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.HEEL.getColumnIndex()) - -1.7) < EPSILON && Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.HEEL.getColumnIndex()) - -1.7) < EPSILON &&
Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.PITCH.getColumnIndex()) - 4.6) < EPSILON && Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.PITCH.getColumnIndex()) - 4.6) < EPSILON &&
Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.EXPEDITION_KICKER_TENSION.getColumnIndex()) - 1.4) < EPSILON && Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.EXPEDITION_KICKER_TENSION.getColumnIndex()) - 1.4) < EPSILON &&
Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.FORESTAY_LOAD.getColumnIndex()) - 141.2) < EPSILON) { Math.abs(sensorFix.get(BravoExtendedSensorDataMetadata.FORESTAY_LOAD.getColumnIndex()) - 0.1412) < EPSILON) {
foundSensorFix = true; foundSensorFix = true;
break; break;
} }
@@ -1,17 +1,26 @@
package com.sap.sailing.domain.vakarosadapter; package com.sap.sailing.domain.vakarosadapter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.sap.sailing.domain.common.sensordata.ExpeditionExtendedSensorDataMetadata; import com.sap.sailing.domain.common.sensordata.ExpeditionExtendedSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl; import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.domain.trackimport.FormatNotSupportedException; import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
import com.sap.sailing.server.trackfiles.impl.ExpeditionExtendedDataImporterImpl; import com.sap.sailing.server.trackfiles.impl.ExpeditionExtendedDataImporterImpl;
import com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -24,10 +33,44 @@ public class VakarosExtendedDataImporterImpl extends ExpeditionExtendedDataImpor
private static final String HEEL_COLUMN_HEADING = "roll"; private static final String HEEL_COLUMN_HEADING = "roll";
private static final String PITCH_COLUMN_HEADING = "pitch"; private static final String PITCH_COLUMN_HEADING = "pitch";
private static final String LOAD_GDF1_COLUMN_HEADING = "load_gdf1"; private static final String LOAD_GDF1_COLUMN_HEADING = "load_gdf1";
private static final double LOAD_GDF1_CONVERSION_FACTOR_TO_FORESTAY_LOAD_IN_TONS = 1./1000.;
private static final String LOAD_GDF2_COLUMN_HEADING = "load_gdf2"; private static final String LOAD_GDF2_COLUMN_HEADING = "load_gdf2";
public VakarosExtendedDataImporterImpl() { public VakarosExtendedDataImporterImpl() {
super(VAKAROS_EXTENDED); // FIXME the ExpeditionImportFileHandler has a hard-coded set of file name extensions; we want our own! super(VAKAROS_EXTENDED);
}
/**
* The file handler returned by this method uses a special {@link Callback} implementation that inspects the
* {@link DoubleVectorFix} parsed from the stream, and if it finds a value for {@link #LOAD_GDF1_COLUMN_HEADING}, it
* applies the conversion factor {@link #LOAD_GDF1_CONVERSION_FACTOR_TO_FORESTAY_LOAD_IN_TONS} to this metric before
* passing the fix on to the actual {@code callback}.
*/
@Override
protected ExpeditionImportFileHandler getFileHandler(TrackFileImportDeviceIdentifier trackIdentifier, AtomicBoolean importedFixes, Callback callback) {
final Map<String, Integer> columnNamesToIndexInDoubleFix = getColumnNamesToIndexInDoubleFix();
return new ExpeditionImportFileHandler(Arrays.asList("vak", "csv", "log", "txt")) {
@Override
protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset) throws IOException, FormatNotSupportedException {
final Callback unitConvertingCallback = new Callback() {
@Override
public void addFixes(Iterable<DoubleVectorFix> fixes, TrackFileImportDeviceIdentifier device) {
callback.addFixes(Util.map(fixes, fix->{
final DoubleVectorFix newFix;
if (fix.get(columnNamesToIndexInDoubleFix.get(LOAD_GDF1_COLUMN_HEADING)) != null) {
final Double[] values = fix.get();
values[columnNamesToIndexInDoubleFix.get(LOAD_GDF1_COLUMN_HEADING)] = values[columnNamesToIndexInDoubleFix.get(LOAD_GDF1_COLUMN_HEADING)] * LOAD_GDF1_CONVERSION_FACTOR_TO_FORESTAY_LOAD_IN_TONS;
newFix = new DoubleVectorFixImpl(fix.getTimePoint(), values);
} else {
newFix = fix; // LOAD_GDF1 was not set, so no conversion necessary, use fix as-is
}
return newFix;
}), device);
}
};
VakarosExtendedDataImporterImpl.this.handleExpeditionFile(fileName, inputStream, charset, trackIdentifier, importedFixes, unitConvertingCallback);
}
};
} }
@Override @Override
@@ -49,7 +49,7 @@ public class VakarosGPSFixImporter implements GPSFixImporter {
final TrackFileImportDeviceIdentifier gpsDevice = new TrackFileImportDeviceIdentifierImpl(sourceName, getType() + "@" + new Date()); final TrackFileImportDeviceIdentifier gpsDevice = new TrackFileImportDeviceIdentifierImpl(sourceName, getType() + "@" + new Date());
final AtomicBoolean importedFixes = new AtomicBoolean(false); final AtomicBoolean importedFixes = new AtomicBoolean(false);
CompressedStreamsUtil.handlePotentiallyCompressedFiles(sourceName, inputStream, CompressedStreamsUtil.handlePotentiallyCompressedFiles(sourceName, inputStream,
charset, new ExpeditionImportFileHandler() { charset, new ExpeditionImportFileHandler(Arrays.asList("vak", "csv", "log", "txt")) {
@Override @Override
protected void handleExpeditionFile(String fileName, InputStream stream, Charset charset) throws IOException { protected void handleExpeditionFile(String fileName, InputStream stream, Charset charset) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(stream, charset)); final BufferedReader br = new BufferedReader(new InputStreamReader(stream, charset));
@@ -5,6 +5,11 @@
<div id="mainContent"> <div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4> <h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent"> <div class="innerContent">
<h5 class="articleSubheadline">February 2025</h5>
<ul class="bulletList">
<li>There are new importers for the Vakaros CSV file format. See details in the
<a href="/release_notes_admin.html">administrative release notes</a>.</li>
</ul>
<h5 class="articleSubheadline">January 2025</h5> <h5 class="articleSubheadline">January 2025</h5>
<ul class="bulletList"> <ul class="bulletList">
<li>Export of Polar data as CSV has been fixed now in Data Mining.</li> <li>Export of Polar data as CSV has been fixed now in Data Mining.</li>
@@ -1,8 +1,15 @@
package com.sap.sailing.server.gateway.trackfiles.impl; package com.sap.sailing.server.gateway.trackfiles.impl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
import com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler;
public class ExpeditionImportFilenameUtilsTest { public class ExpeditionImportFilenameUtilsTest {
@Test @Test
@@ -69,7 +76,14 @@ public class ExpeditionImportFilenameUtilsTest {
} }
private void testCase(String actualFilename, String expectedResult) { private void testCase(String actualFilename, String expectedResult) {
Assert.assertEquals(expectedResult, ExpeditionImportFilenameUtils.truncateFilenameExtentions(actualFilename)); Assert.assertEquals(expectedResult, ExpeditionImportFilenameUtils.truncateFilenameExtentions(actualFilename, new ExpeditionImportFileHandler() {
@Override
protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset)
throws IOException, FormatNotSupportedException {
// used only to resolve the file name extensions
}
}));
} }
} }
@@ -96,6 +96,7 @@ import com.sap.sailing.server.operationaltransformation.CreateLeaderboardGroup;
import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard;
import com.sap.sailing.server.operationaltransformation.UpdateEvent; import com.sap.sailing.server.operationaltransformation.UpdateEvent;
import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler; import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler;
import com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler;
import com.sap.sailing.server.util.WaitForTrackedRaceUtil; import com.sap.sailing.server.util.WaitForTrackedRaceUtil;
import com.sap.sse.common.Distance; import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration; import com.sap.sse.common.Duration;
@@ -291,7 +292,11 @@ public class ExpeditionAllInOneImporter {
securityService.checkCurrentUserServerPermission(ServerActions.CREATE_OBJECT); securityService.checkCurrentUserServerPermission(ServerActions.CREATE_OBJECT);
final List<ErrorImportDTO> errors = new ArrayList<>(); final List<ErrorImportDTO> errors = new ArrayList<>();
final String importTimeString = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now(ZoneOffset.UTC)); final String importTimeString = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now(ZoneOffset.UTC));
final String filename = ExpeditionImportFilenameUtils.truncateFilenameExtentions(filenameWithSuffix); final String filename = ExpeditionImportFilenameUtils.truncateFilenameExtentions(filenameWithSuffix, new ExpeditionImportFileHandler() {
@Override // used only to resolve the standard file name extensions defined by the Expedition import file handler
protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset)
throws IOException, FormatNotSupportedException {
}});
final String filenameWithDateTimeSuffix = filename + "_" + importTimeString; final String filenameWithDateTimeSuffix = filename + "_" + importTimeString;
final String trackedRaceName = filenameWithDateTimeSuffix; final String trackedRaceName = filenameWithDateTimeSuffix;
final String windSourceId = filenameWithDateTimeSuffix; final String windSourceId = filenameWithDateTimeSuffix;
@@ -1,13 +1,15 @@
package com.sap.sailing.server.gateway.trackfiles.impl; package com.sap.sailing.server.gateway.trackfiles.impl;
import static com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler.supportedExpeditionArchiveFileExtensions; import java.util.Arrays;
import static com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler.supportedExpeditionLogFileExtensions;
import com.sap.sailing.server.trackfiles.impl.ExpeditionImportFileHandler;
class ExpeditionImportFilenameUtils { class ExpeditionImportFilenameUtils {
private static final Iterable<String> supportedExpeditionArchiveFileExtensions = Arrays.asList("gz", "zip");
static String truncateFilenameExtentions(String filenameWithExtensions) { static String truncateFilenameExtentions(String filenameWithExtensions, ExpeditionImportFileHandler fileHandler) {
return removeTailIfExists(removeTailIfExists(filenameWithExtensions, supportedExpeditionArchiveFileExtensions), return removeTailIfExists(removeTailIfExists(filenameWithExtensions, supportedExpeditionArchiveFileExtensions),
supportedExpeditionLogFileExtensions); fileHandler.getSupportedFileExtensions());
} }
private static String removeTailIfExists(String source, Iterable<String> extensionsToRemove) { private static String removeTailIfExists(String source, Iterable<String> extensionsToRemove) {
@@ -21,7 +21,7 @@ public class GPSFixImporterRegistration {
/** /**
* Registers the <code>importer</code> as an OSGi service for the {@link GPSFixImporter} interface, once for each * Registers the <code>importer</code> as an OSGi service for the {@link GPSFixImporter} interface, once for each
* {@link GPSFixImporter#getSupportedFileExtensions() supported file extension} , using its * {@link GPSFixImporter#getSupportedFileExtensions() supported file extension}, using its
* {@link GPSFixImporter#getType() type} as the {@link TypeBasedServiceFinder#TYPE} property and the * {@link GPSFixImporter#getType() type} as the {@link TypeBasedServiceFinder#TYPE} property and the
* file extension as the {@link GPSFixImporter#FILE_EXTENSION_PROPERTY} property. * file extension as the {@link GPSFixImporter#FILE_EXTENSION_PROPERTY} property.
*/ */
@@ -106,42 +106,50 @@ public class ExpeditionExtendedDataImporterImpl extends AbstractDoubleVectorFixI
String sourceName, boolean downsample) throws FormatNotSupportedException, IOException { String sourceName, boolean downsample) throws FormatNotSupportedException, IOException {
final TrackFileImportDeviceIdentifier trackIdentifier = getTrackIdentifier(filename, sourceName); final TrackFileImportDeviceIdentifier trackIdentifier = getTrackIdentifier(filename, sourceName);
final AtomicBoolean importedFixes = new AtomicBoolean(false); final AtomicBoolean importedFixes = new AtomicBoolean(false);
CompressedStreamsUtil.handlePotentiallyCompressedFiles(filename, inputStream, charset, new ExpeditionImportFileHandler() { CompressedStreamsUtil.handlePotentiallyCompressedFiles(filename, inputStream, charset, getFileHandler(trackIdentifier, importedFixes, callback));
return importedFixes.get();
}
protected ExpeditionImportFileHandler getFileHandler(TrackFileImportDeviceIdentifier trackIdentifier, AtomicBoolean importedFixes, Callback callback) {
return new ExpeditionImportFileHandler() {
@Override @Override
protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset) throws IOException, FormatNotSupportedException { protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset) throws IOException, FormatNotSupportedException {
logger.fine("Start parsing Expedition file"); ExpeditionExtendedDataImporterImpl.this.handleExpeditionFile(fileName, inputStream, charset, trackIdentifier, importedFixes, callback);
final AtomicLong lineNr = new AtomicLong(); }
final BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream)); };
String headerLine = buffer.readLine(); }
lineNr.incrementAndGet();
logger.fine("Validate and parse header columns"); protected void handleExpeditionFile(String fileName, InputStream inputStream, Charset charset, TrackFileImportDeviceIdentifier trackIdentifier, AtomicBoolean importedFixes, Callback callback) throws IOException, FormatNotSupportedException {
final Map<String, Integer> colIndices = parseHeader(headerLine); logger.fine("Start parsing Expedition file");
validateHeader(colIndices); final AtomicLong lineNr = new AtomicLong();
buffer.lines().forEach(line -> { final BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
lineNr.incrementAndGet(); String headerLine = buffer.readLine();
if (!line.trim().isEmpty()) { lineNr.incrementAndGet();
parseLine(lineNr.get(), filename, line, colIndices, logger.fine("Validate and parse header columns");
(timePoint, lineContentTokens, columnsInFileFromHeader) -> { final Map<String, Integer> colIndices = parseHeader(headerLine);
final Double[] trackFixData = new Double[trackColumnCount]; validateHeader(colIndices);
for (final Entry<String, Integer> columnFromFile : columnsInFileFromHeader.entrySet()) { buffer.lines().forEach(line -> {
final Integer indexInDoubleVectorFix = columnNamesInFileAndTheirValueIndexInResultingDoubleVectorFix.get(columnFromFile.getKey()); lineNr.incrementAndGet();
if (indexInDoubleVectorFix != null) { if (!line.trim().isEmpty()) {
final Double value = columnFromFile.getValue() >= lineContentTokens.length ? null parseLine(lineNr.get(), fileName, line, colIndices,
: lineContentTokens[columnFromFile.getValue()].trim().isEmpty() ? null (timePoint, lineContentTokens, columnsInFileFromHeader) -> {
: Double.parseDouble(lineContentTokens[columnFromFile.getValue()]); final Double[] trackFixData = new Double[trackColumnCount];
trackFixData[indexInDoubleVectorFix] = value; for (final Entry<String, Integer> columnFromFile : columnsInFileFromHeader.entrySet()) {
} final Integer indexInDoubleVectorFix = columnNamesInFileAndTheirValueIndexInResultingDoubleVectorFix.get(columnFromFile.getKey());
} if (indexInDoubleVectorFix != null) {
importedFixes.set(true); final Double value = columnFromFile.getValue() >= lineContentTokens.length ? null
callback.addFixes( : lineContentTokens[columnFromFile.getValue()].trim().isEmpty() ? null
Collections.singleton(new DoubleVectorFixImpl(timePoint, trackFixData)), : Double.parseDouble(lineContentTokens[columnFromFile.getValue()]);
trackIdentifier); trackFixData[indexInDoubleVectorFix] = value;
}); }
} }
}); importedFixes.set(true);
callback.addFixes(
Collections.singleton(new DoubleVectorFixImpl(timePoint, trackFixData)),
trackIdentifier);
});
} }
}); });
return importedFixes.get();
} }
protected TrackFileImportDeviceIdentifierImpl getTrackIdentifier(String filename, String sourceName) { protected TrackFileImportDeviceIdentifierImpl getTrackIdentifier(String filename, String sourceName) {
@@ -3,23 +3,24 @@ package com.sap.sailing.server.trackfiles.impl;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.sap.sailing.domain.trackimport.FormatNotSupportedException; import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
public abstract class ExpeditionImportFileHandler implements CompressedStreamsUtil.FileHandler { public abstract class ExpeditionImportFileHandler implements CompressedStreamsUtil.FileHandler {
public static final List<String> supportedExpeditionLogFileExtensions = Arrays.asList("csv", "log", "txt"); public final Iterable<String> supportedExpeditionLogFileExtensions;
public static final List<String> supportedExpeditionArchiveFileExtensions = Arrays.asList("gz", "zip");
public static final List<String> supportedExpeditionFileExtensions;
static { public Iterable<String> getSupportedFileExtensions() {
final List<String> allExtensions = new ArrayList<>(supportedExpeditionLogFileExtensions); return supportedExpeditionLogFileExtensions;
allExtensions.addAll(supportedExpeditionArchiveFileExtensions); }
supportedExpeditionFileExtensions = Collections.unmodifiableList(allExtensions);
protected ExpeditionImportFileHandler(Iterable<String> supportedFileExtensions) {
this.supportedExpeditionLogFileExtensions = supportedFileExtensions;
}
protected ExpeditionImportFileHandler() {
this(Arrays.asList("csv", "log", "txt"));
} }
@Override @Override
@@ -23,6 +23,17 @@
<div class="mainContent"> <div class="mainContent">
<h2 class="releaseHeadline">Release Notes - Administration Console</h2> <h2 class="releaseHeadline">Release Notes - Administration Console</h2>
<div class="innerContent"> <div class="innerContent">
<h2 class="articleSubheadline">February 2025</h2>
<ul class="bulletList">
<li>In the <a href="/gwt/AdminConsole.html#SmartphoneTrackingPlace:">admin console's</a>
Smartphone Tracking panel, when importing data from files to create new device mappings
there is now a "Vakaros" / "VAKAROS_EXTENDED" menu entry when using "Import fixes"
or "Import additional sensor data," respectively. With these, files obtained from
Vakaros devices can be imported with their time stamps, GPS information (lat/lon,
COG, SOG) and, when available, from the "additional" sensor data heel, pitch,
and the two special values load_GDF1 and load_GDF2, which we map to the
forestay load and the kicker tension values, respectively.</li>
</ul>
<h2 class="articleSubheadline">January 2025</h2> <h2 class="articleSubheadline">January 2025</h2>
<ul class="bulletList"> <ul class="bulletList">
<li>When exporting wind from the Admin Console's Tracked Races / Tracked Races panel, <li>When exporting wind from the Admin Console's Tracked Races / Tracked Races panel,