From a3d732f11a5684c1d895272e0d5c8bc6e515091a Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 12 Dec 2025 17:22:26 +0100 Subject: [PATCH] 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. --- .../garmin/fit/PredefinedLocalMessage.java | 58 ------------- .../garmin/messages/FitDataMessage.java | 87 +++++++++++++------ 2 files changed, 61 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java deleted file mode 100644 index a1bfc90fda..0000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/PredefinedLocalMessage.java +++ /dev/null @@ -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 fieldDefinitions = globalFITMessage.getFieldDefinitions(globalDefinitionIds); - return new RecordDefinition( - recordHeader, - ByteOrder.BIG_ENDIAN, - globalFITMessage, - fieldDefinitions, - null - ); - } - - public int getType() { - return type; - } - - public GlobalFITMessage getGlobalFITMessage() { - return globalFITMessage; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java index 66622df8f3..925c949e50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/FitDataMessage.java @@ -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 recordDataList; + private List recordDataList; + private GarminByteBufferReader reader; + private final boolean generateOutgoing; + private boolean isProcessed = false; - public FitDataMessage(List 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 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 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 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 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; } }