Garmin: Remove PredefinedLocalMessage class

Refactor FitDataMessage to support a two-step message parsing: at first hold a copy of the incoming bytestream and later parse it according to a list of RecordDefinitions.
This allows to remove PredefinedLocalMessage which was already made obsolete by previous changes.
This should also allow to support incoming FitDefinition/FitData exchanges.
This commit is contained in:
Daniele Gobbetti
2025-12-21 13:54:21 +00:00
committed by José Rebelo
parent 63e0ecba63
commit a3d732f11a
2 changed files with 61 additions and 84 deletions
@@ -1,58 +0,0 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit;
import androidx.annotation.Nullable;
import java.nio.ByteOrder;
import java.util.List;
public enum PredefinedLocalMessage {
TODAY_WEATHER_CONDITIONS(6, GlobalFITMessage.WEATHER,
new int[]{0, 253, 9, 1, 14, 13, 2, 3, 5, 4, 6, 7, 10, 11, 17, 15, 8}
),
HOURLY_WEATHER_FORECAST(9, GlobalFITMessage.WEATHER,
new int[]{0, 253, 1, 2, 3, 4, 5, 6, 7, 15, 16, 17}
),
DAILY_WEATHER_FORECAST(10, GlobalFITMessage.WEATHER,
new int[]{0, 253, 14, 13, 2, 5, 12, 17}
);
private final int type;
private final GlobalFITMessage globalFITMessage;
private final int[] globalDefinitionIds;
PredefinedLocalMessage(int type, GlobalFITMessage globalFITMessage, int[] globalDefinitionIds) {
this.type = type;
this.globalFITMessage = globalFITMessage;
this.globalDefinitionIds = globalDefinitionIds;
}
@Nullable
public static PredefinedLocalMessage fromType(int type) {
for (final PredefinedLocalMessage predefinedLocalMessage : PredefinedLocalMessage.values()) {
if (predefinedLocalMessage.getType() == type) {
return predefinedLocalMessage;
}
}
return null;
}
public RecordDefinition getRecordDefinition() {
final RecordHeader recordHeader = new RecordHeader(true, false, type, null);
final List<FieldDefinition> fieldDefinitions = globalFITMessage.getFieldDefinitions(globalDefinitionIds);
return new RecordDefinition(
recordHeader,
ByteOrder.BIG_ENDIAN,
globalFITMessage,
fieldDefinitions,
null
);
}
public int getType() {
return type;
}
public GlobalFITMessage getGlobalFITMessage() {
return globalFITMessage;
}
}
@@ -2,52 +2,87 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.PredefinedLocalMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminByteBufferReader;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.FitDataStatusMessage;
public class FitDataMessage extends GFDIMessage {
private final List<RecordData> recordDataList;
private List<RecordData> recordDataList;
private GarminByteBufferReader reader;
private final boolean generateOutgoing;
private boolean isProcessed = false;
public FitDataMessage(List<RecordData> recordDataList, GarminMessage garminMessage) {
this.recordDataList = recordDataList;
public FitDataMessage(GarminByteBufferReader messageReader, GarminMessage garminMessage) {
this.reader = messageReader;
this.recordDataList = new ArrayList<>();
this.garminMessage = garminMessage;
this.statusMessage = new FitDataStatusMessage(garminMessage, Status.ACK, FitDataStatusMessage.FitDataStatusCode.APPLIED);
this.generateOutgoing = false;
}
public FitDataMessage(List<RecordData> recordDataList) {
this.reader = null;
this.recordDataList = recordDataList;
this.garminMessage = GarminMessage.FIT_DATA;
this.statusMessage = null;
this.isProcessed = true;
this.generateOutgoing = true;
}
public static FitDataMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
final List<RecordData> recordDataList = new ArrayList<>();
while (reader.remaining() > 0) {
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte());
if (recordHeader.isDefinition())
return null;
PredefinedLocalMessage predefinedLocalMessage = PredefinedLocalMessage.fromType(recordHeader.getLocalMessageType());
if (predefinedLocalMessage == null) {
LOG.warn("Local message is null");
return null;
}
RecordData recordData = new RecordData(
predefinedLocalMessage.getRecordDefinition(),
predefinedLocalMessage.getRecordDefinition().getRecordHeader()
);
recordData.parseDataMessage(reader, null);
recordDataList.add(recordData);
}
return new FitDataMessage(recordDataList, garminMessage);
return new FitDataMessage(new GarminByteBufferReader(reader.readBytes(reader.remaining())), garminMessage);
}
public void applyDefinitions(List<RecordDefinition> recordDefinitions) {
Objects.requireNonNull(reader, "reader cannot be null");
try {
while (reader.remaining() > 0) {
RecordHeader recordHeader = new RecordHeader((byte) reader.readByte());
if (recordHeader.isDefinition())
return;
RecordDefinition localMessageDefinition = recordDefinitions.stream()
.filter(d -> d.getRecordHeader().getLocalMessageType() == recordHeader.getLocalMessageType())
.findFirst()
.orElse(null);
if (localMessageDefinition == null) {
LOG.warn("Cannot find a local message definition for type: {}", recordHeader.getLocalMessageType());
return ;
}
RecordData recordData = new RecordData(
localMessageDefinition,
localMessageDefinition.getRecordHeader()
);
recordData.parseDataMessage(reader, null);
recordDataList.add(recordData);
}
this.isProcessed = true;
} catch (Exception e) {
LOG.error("Error while applying definitions", e);
throw new IllegalStateException("Failed to apply definitions to FIT data", e);
} finally {
reader = null;
}
}
public List<RecordData> getRecordDataList() {
if(!isProcessed())
throw new IllegalStateException(
"Message has not been processed yet. Call applyDefinitions first."
);
return recordDataList;
}
public boolean isProcessed() {
return isProcessed;
}
@Override
protected boolean generateOutgoing() {
final MessageWriter writer = new MessageWriter(response);
@@ -56,6 +91,6 @@ public class FitDataMessage extends GFDIMessage {
for (RecordData recordData : recordDataList) {
recordData.generateOutgoingDataPayload(writer);
}
return true;
return this.generateOutgoing;
}
}