Changeset 36192 in osm for applications/editors/josm/plugins
- Timestamp:
- 2023-11-02T17:28:05+01:00 (14 months ago)
- Location:
- applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/FitReader.java
r36160 r36192 12 12 import java.util.EnumSet; 13 13 import java.util.List; 14 import java.util.Set; 14 15 import java.util.logging.Level; 15 16 import java.util.logging.Logger; … … 34 35 */ 35 36 public final class FitReader { 37 private record LastTimestamp(long lastTimestamp, int offsetAddition, byte lastOffset){} 38 private record ParseFitRecordHeader(LastTimestamp lastTimestamp, FitDeveloperFieldDescriptionMessage[] devFields){} 36 39 private static final FitData[] EMPTY_FIT_DATA_ARRAY = new FitData[0]; 37 40 … … 45 48 * @param inputStream The stream to read 46 49 * @param options options for reading the file 50 * @return The parsed FIT data 47 51 * @throws FitException if there was an error reading the fit file <i>or</i> there was an {@link InputStream} read 48 52 * exception. … … 55 59 final var fitData = new ArrayList<FitData>(); 56 60 try { 57 final var header = readFitHeader(bufferedInputStream); 58 final var headerSize = bufferedInputStream.bytesRead(); 59 bufferedInputStream.mark(1); 60 var localMessageHeaders = new FitDefinitionMessage[1]; 61 var developerData = new FitDeveloperFieldDescriptionMessage[0]; 62 long lastTimeStamp = Long.MIN_VALUE; 63 var offsetAddition = 0; 64 byte lastOffset = 0; 65 while (bufferedInputStream.read() != -1 66 && bufferedInputStream.bytesRead() < header.dataSize() - headerSize) { 67 bufferedInputStream.reset(); 68 final var nextRecordHeader = readNextRecordHeader(bufferedInputStream); 69 if (nextRecordHeader instanceof FitRecordNormalHeader normalHeader) { 70 if (normalHeader.isDefinitionMessage()) { 71 final var fitDefinitionMessage = readNextDefinition(normalHeader, bufferedInputStream); 72 if (localMessageHeaders.length <= normalHeader.localMessageType()) { 73 localMessageHeaders = Arrays.copyOf(localMessageHeaders, 74 normalHeader.localMessageType() + 1); 75 } 76 localMessageHeaders[normalHeader.localMessageType()] = fitDefinitionMessage; 77 } else { 78 final Object obj = readNextRecord(localMessageHeaders[normalHeader.localMessageType()], 79 developerData, bufferedInputStream); 80 if (obj instanceof IFitTimestamp<?> timestamp) { 81 lastTimeStamp = timestamp.timestamp().getEpochSecond() & 0xFF_FFF_FE0L; 82 offsetAddition = 0; 83 lastOffset = 0; 84 } 85 if (obj instanceof FitData fd) { 86 fitData.add(fd); 87 } else if (obj instanceof FitDeveloperFieldDescriptionMessage developerFieldDescriptionMessage) { 88 if (developerData.length <= developerFieldDescriptionMessage.developerDataIndex()) { 89 developerData = Arrays.copyOf(developerData, 90 developerFieldDescriptionMessage.developerDataIndex() + 1); 91 } 92 developerData[developerFieldDescriptionMessage 93 .developerDataIndex()] = developerFieldDescriptionMessage; 94 } else { 95 handleException(optionsSet, new IllegalStateException("Unknown record type")); 96 } 97 } 98 } else if (nextRecordHeader instanceof FitRecordCompressedTimestampHeader compressedHeader) { 99 if (lastTimeStamp == Long.MIN_VALUE) { 100 handleException(optionsSet, 101 new FitException("No full timestamp found before compressed timestamp header")); 102 break; 103 } 104 if (compressedHeader.timeOffset() < lastOffset) { 105 offsetAddition++; 106 } 107 final Object obj = readNextRecord(localMessageHeaders[compressedHeader.localMessageType()], 108 developerData, bufferedInputStream); 109 if (obj instanceof FitData fd) { 110 // Note: this could be wrong 111 long actualTimestamp = lastTimeStamp + compressedHeader.timeOffset() + (offsetAddition * 0x20L); 112 if (fd instanceof IFitTimestamp<?> timestamp) { 113 fitData.add((FitData) timestamp.withTimestamp(Instant.ofEpochSecond(actualTimestamp))); 114 } else { 115 fitData.add(fd); 116 } 117 } else { 118 handleException(optionsSet, new IllegalStateException( 119 "Cannot handle non-data types with compressed timestamp headers")); 120 } 121 lastOffset = compressedHeader.timeOffset(); 122 } else { 123 handleException(optionsSet, 124 new UnsupportedOperationException("Unknown record type: " + nextRecordHeader.getClass())); 125 } 126 bufferedInputStream.mark(1); 127 } 61 parseData(bufferedInputStream, optionsSet, fitData); 128 62 } catch (FitException fitException) { 129 63 handleException(optionsSet, fitException); … … 134 68 } 135 69 136 private static <T extends Throwable> void handleException(EnumSet<FitReaderOptions> options, T throwable) throws T { 70 private static void parseData(CountingInputStream bufferedInputStream, 71 Set<FitReaderOptions> optionsSet, List<FitData> fitData) throws IOException { 72 final var header = readFitHeader(bufferedInputStream); 73 final var headerSize = bufferedInputStream.bytesRead(); 74 bufferedInputStream.mark(1); 75 var localMessageHeaders = new FitDefinitionMessage[1]; 76 var developerData = new FitDeveloperFieldDescriptionMessage[0]; 77 var lastTimeStamp = new LastTimestamp(Long.MIN_VALUE, 0, (byte) 0); 78 while (bufferedInputStream.read() != -1 79 && bufferedInputStream.bytesRead() < header.dataSize() + headerSize) { 80 bufferedInputStream.reset(); 81 final var nextRecordHeader = readNextRecordHeader(bufferedInputStream); 82 if (nextRecordHeader instanceof FitRecordNormalHeader normalHeader) { 83 if (normalHeader.isDefinitionMessage()) { 84 localMessageHeaders = parseFitRecordDefinitionHeader(bufferedInputStream, localMessageHeaders, normalHeader); 85 } else { 86 final var parsed = parseFitRecordHeader(optionsSet, bufferedInputStream, 87 localMessageHeaders, developerData, fitData, normalHeader); 88 if (parsed.lastTimestamp() != null) { 89 lastTimeStamp = parsed.lastTimestamp(); 90 } 91 developerData = parsed.devFields(); 92 } 93 } else if (nextRecordHeader instanceof FitRecordCompressedTimestampHeader compressedHeader) { 94 if (lastTimeStamp.lastTimestamp() == Long.MIN_VALUE) { 95 handleException(optionsSet, 96 new FitException("No full timestamp found before compressed timestamp header")); 97 break; 98 } 99 if (compressedHeader.timeOffset() < lastTimeStamp.lastOffset()) { 100 lastTimeStamp = new LastTimestamp(lastTimeStamp.lastTimestamp(), lastTimeStamp.offsetAddition() + 1, 101 lastTimeStamp.lastOffset()); 102 } 103 parseFitRecordCompressedHeader(optionsSet, bufferedInputStream, localMessageHeaders, developerData, 104 fitData, compressedHeader, lastTimeStamp); 105 lastTimeStamp = new LastTimestamp(lastTimeStamp.lastTimestamp(), lastTimeStamp.offsetAddition(), 106 compressedHeader.timeOffset()); 107 } else { 108 handleException(optionsSet, 109 new UnsupportedOperationException("Unknown record type: " + nextRecordHeader.getClass())); 110 } 111 bufferedInputStream.mark(1); 112 } 113 } 114 115 private static FitDefinitionMessage[] parseFitRecordDefinitionHeader(InputStream inputStream, 116 FitDefinitionMessage[] localMessageHeaders, 117 FitRecordNormalHeader normalHeader) throws IOException { 118 final var fitDefinitionMessage = readNextDefinition(normalHeader, inputStream); 119 if (localMessageHeaders.length <= normalHeader.localMessageType()) { 120 localMessageHeaders = Arrays.copyOf(localMessageHeaders, 121 normalHeader.localMessageType() + 1); 122 } 123 localMessageHeaders[normalHeader.localMessageType()] = fitDefinitionMessage; 124 return localMessageHeaders; 125 } 126 127 private static ParseFitRecordHeader parseFitRecordHeader(Set<FitReaderOptions> optionsSet, InputStream bufferedInputStream, 128 FitDefinitionMessage[] localMessageHeaders, FitDeveloperFieldDescriptionMessage[] developerData, 129 List<FitData> fitData, FitRecordNormalHeader normalHeader) throws IOException { 130 final Object obj = readNextRecord(localMessageHeaders[normalHeader.localMessageType()], 131 developerData, bufferedInputStream); 132 LastTimestamp lastTimeStamp = null; 133 if (obj instanceof IFitTimestamp<?> timestamp) { 134 lastTimeStamp = new LastTimestamp(timestamp.timestamp().getEpochSecond() & 0xFF_FFF_FE0L, 0, (byte) 0); 135 } 136 if (obj instanceof FitData fd) { 137 fitData.add(fd); 138 } else if (obj instanceof FitDeveloperFieldDescriptionMessage developerFieldDescriptionMessage) { 139 if (developerData.length <= developerFieldDescriptionMessage.developerDataIndex()) { 140 developerData = Arrays.copyOf(developerData, 141 developerFieldDescriptionMessage.developerDataIndex() + 1); 142 } 143 developerData[developerFieldDescriptionMessage 144 .developerDataIndex()] = developerFieldDescriptionMessage; 145 } else { 146 handleException(optionsSet, new IllegalStateException("Unknown record type")); 147 } 148 return new ParseFitRecordHeader(lastTimeStamp, developerData); 149 } 150 151 private static void parseFitRecordCompressedHeader(Set<FitReaderOptions> optionsSet, InputStream bufferedInputStream, 152 FitDefinitionMessage[] localMessageHeaders, 153 FitDeveloperFieldDescriptionMessage[] developerData, List<FitData> fitData, 154 FitRecordCompressedTimestampHeader compressedHeader, LastTimestamp lastTimeStamp) 155 throws IOException { 156 final Object obj = readNextRecord(localMessageHeaders[compressedHeader.localMessageType()], 157 developerData, bufferedInputStream); 158 if (obj instanceof FitData fd) { 159 // Note: this could be wrong 160 long actualTimestamp = lastTimeStamp.lastTimestamp() + compressedHeader.timeOffset() + (lastTimeStamp.offsetAddition() * 0x20L); 161 if (fd instanceof IFitTimestamp<?> timestamp) { 162 fitData.add((FitData) timestamp.withTimestamp(Instant.ofEpochSecond(actualTimestamp))); 163 } else { 164 fitData.add(fd); 165 } 166 } else { 167 handleException(optionsSet, new IllegalStateException( 168 "Cannot handle non-data types with compressed timestamp headers")); 169 } 170 } 171 172 private static <T extends Throwable> void handleException(Set<FitReaderOptions> options, T throwable) throws T { 137 173 if (!options.contains(FitReaderOptions.TRY_TO_FINISH)) { 138 174 throw throwable; -
applications/editors/josm/plugins/FIT/src/main/java/org/openstreetmap/josm/plugins/fit/lib/utils/DevDataUtils.java
r36158 r36192 25 25 } 26 26 27 /** 28 * Parse the dev fields 29 * @param littleEndian {@code true} if the bit ordering is little endian 30 * @param developerFieldList The current list of developer fiels 31 * @param devFields The array of developer field descriptions 32 * @param inputStream The stream to read 33 * @return The dev data record 34 * @throws IOException If something happened while reading the stream 35 */ 27 36 public static FitDevDataRecord parseDevFields(boolean littleEndian, 28 37 Collection<FitDeveloperField> developerFieldList, FitDeveloperFieldDescriptionMessage[] devFields,
Note:
See TracChangeset
for help on using the changeset viewer.