mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-09-09 07:46:37 +02:00
Garmin protocol: move field encode/decode interface to the FieldDefinition
This allows for semantic subclassing the FieldDefinition. A FieldDefinitionTimestamp subclass is introduced as example
This commit is contained in:
parent
4313fc833a
commit
e323e7fbde
@ -147,8 +147,8 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
||||
|
||||
RecordData today = new RecordData(GlobalDefinitionsEnum.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
|
||||
today.setFieldByName("weather_report", 0); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
|
||||
today.setFieldByName("timestamp", GarminTimeUtils.unixTimeToGarminTimestamp(weather.timestamp));
|
||||
today.setFieldByName("observed_at_time", GarminTimeUtils.unixTimeToGarminTimestamp(weather.timestamp));
|
||||
today.setFieldByName("timestamp", weather.timestamp);
|
||||
today.setFieldByName("observed_at_time", weather.timestamp);
|
||||
today.setFieldByName("temperature", weather.currentTemp - 273.15);
|
||||
today.setFieldByName("low_temperature", weather.todayMinTemp - 273.15);
|
||||
today.setFieldByName("high_temperature", weather.todayMaxTemp - 273.15);
|
||||
@ -168,7 +168,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
||||
WeatherSpec.Hourly hourly = weather.hourly.get(hour);
|
||||
RecordData weatherHourlyForecast = new RecordData(GlobalDefinitionsEnum.HOURLY_WEATHER_FORECAST.getRecordDefinition());
|
||||
weatherHourlyForecast.setFieldByName("weather_report", 1); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
|
||||
weatherHourlyForecast.setFieldByName("timestamp", GarminTimeUtils.unixTimeToGarminTimestamp(hourly.timestamp));
|
||||
weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp);
|
||||
weatherHourlyForecast.setFieldByName("temperature", hourly.temp - 273.15);
|
||||
weatherHourlyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(hourly.conditionCode));
|
||||
weatherHourlyForecast.setFieldByName("wind_direction", hourly.windDirection);
|
||||
@ -184,7 +184,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
||||
//
|
||||
RecordData todayDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition());
|
||||
todayDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
|
||||
todayDailyForecast.setFieldByName("timestamp", GarminTimeUtils.unixTimeToGarminTimestamp(weather.timestamp));
|
||||
todayDailyForecast.setFieldByName("timestamp", weather.timestamp);
|
||||
todayDailyForecast.setFieldByName("low_temperature", weather.todayMinTemp - 273.15);
|
||||
todayDailyForecast.setFieldByName("high_temperature", weather.todayMaxTemp - 273.15);
|
||||
todayDailyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(weather.currentConditionCode));
|
||||
@ -199,7 +199,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
||||
int ts = weather.timestamp + (day + 1) * 24 * 60 * 60; //TODO: is this needed?
|
||||
RecordData weatherDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition());
|
||||
weatherDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
|
||||
weatherDailyForecast.setFieldByName("timestamp", GarminTimeUtils.unixTimeToGarminTimestamp(weather.timestamp));
|
||||
weatherDailyForecast.setFieldByName("timestamp", weather.timestamp);
|
||||
weatherDailyForecast.setFieldByName("low_temperature", daily.minTemp - 273.15);
|
||||
weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp - 273.15);
|
||||
weatherDailyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(daily.conditionCode));
|
||||
|
@ -5,7 +5,7 @@ import org.threeten.bp.ZoneId;
|
||||
|
||||
public class GarminTimeUtils {
|
||||
|
||||
private static final int GARMIN_TIME_EPOCH = 631065600;
|
||||
public static final int GARMIN_TIME_EPOCH = 631065600;
|
||||
|
||||
public static int unixTimeToGarminTimestamp(int unixTime) {
|
||||
return unixTime - GARMIN_TIME_EPOCH;
|
||||
|
@ -1,16 +1,18 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageReader;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
|
||||
|
||||
public class FieldDefinition {
|
||||
public class FieldDefinition implements FieldInterface {
|
||||
private final int localNumber;
|
||||
private final int size;
|
||||
private final BaseType baseType;
|
||||
protected final BaseType baseType;
|
||||
private final String name;
|
||||
private final int scale;
|
||||
private final int offset;
|
||||
protected final int scale;
|
||||
protected final int offset;
|
||||
|
||||
public FieldDefinition(int localNumber, int size, BaseType baseType, String name, int scale, int offset) {
|
||||
this.localNumber = localNumber;
|
||||
@ -70,4 +72,18 @@ public class FieldDefinition {
|
||||
writer.writeByte(baseType.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuffer byteBuffer) {
|
||||
return baseType.decode(byteBuffer, scale, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuffer byteBuffer, Object o) {
|
||||
baseType.encode(byteBuffer, o, scale, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(ByteBuffer byteBuffer) {
|
||||
baseType.invalidate(byteBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface FieldInterface {
|
||||
Object decode(ByteBuffer byteBuffer);
|
||||
|
||||
void encode(ByteBuffer byteBuffer, Object o);
|
||||
|
||||
void invalidate(ByteBuffer byteBuffer);
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp;
|
||||
|
||||
public enum GlobalDefinitionsEnum {
|
||||
TODAY_WEATHER_CONDITIONS(MesgType.TODAY_WEATHER_CONDITIONS, new RecordDefinition(
|
||||
@ -13,8 +14,8 @@ public enum GlobalDefinitionsEnum {
|
||||
ByteOrder.BIG_ENDIAN,
|
||||
MesgType.TODAY_WEATHER_CONDITIONS,
|
||||
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
|
||||
new FieldDefinition(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinition(9, 4, BaseType.UINT32, "observed_at_time"),
|
||||
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinitionTimestamp(9, 4, BaseType.UINT32, "observed_at_time"),
|
||||
new FieldDefinition(1, 1, BaseType.SINT8, "temperature"),
|
||||
new FieldDefinition(14, 1, BaseType.SINT8, "low_temperature"),
|
||||
new FieldDefinition(13, 1, BaseType.SINT8, "high_temperature"),
|
||||
@ -33,7 +34,7 @@ public enum GlobalDefinitionsEnum {
|
||||
ByteOrder.BIG_ENDIAN,
|
||||
MesgType.HOURLY_WEATHER_FORECAST,
|
||||
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
|
||||
new FieldDefinition(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinition(1, 1, BaseType.SINT8, "temperature"),
|
||||
new FieldDefinition(2, 1, BaseType.ENUM, "condition"),
|
||||
new FieldDefinition(3, 2, BaseType.UINT16, "wind_direction"),
|
||||
@ -49,7 +50,7 @@ public enum GlobalDefinitionsEnum {
|
||||
ByteOrder.BIG_ENDIAN,
|
||||
MesgType.DAILY_WEATHER_FORECAST,
|
||||
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
|
||||
new FieldDefinition(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
|
||||
new FieldDefinition(14, 1, BaseType.SINT8, "low_temperature"),
|
||||
new FieldDefinition(13, 1, BaseType.SINT8, "high_temperature"),
|
||||
new FieldDefinition(2, 1, BaseType.ENUM, "condition"),
|
||||
|
@ -29,7 +29,7 @@ public class RecordData {
|
||||
|
||||
for (FieldDefinition fieldDef :
|
||||
recordDefinition.getFieldDefinitions()) {
|
||||
fieldDataList.add(new FieldData(fieldDef.getBaseType(), totalSize, fieldDef.getSize(), fieldDef.getName(), fieldDef.getLocalNumber(), fieldDef.getScale(), fieldDef.getOffset()));
|
||||
fieldDataList.add(new FieldData(fieldDef, totalSize));
|
||||
totalSize += fieldDef.getSize();
|
||||
|
||||
}
|
||||
@ -90,10 +90,10 @@ public class RecordData {
|
||||
StringBuilder oBuilder = new StringBuilder();
|
||||
for (FieldData fieldData :
|
||||
fieldDataList) {
|
||||
if (fieldData.getName() != null) {
|
||||
if (fieldData.getName() != null && !fieldData.getName().equals("")) {
|
||||
oBuilder.append(fieldData.getName());
|
||||
} else {
|
||||
oBuilder.append(fieldData.getNumber());
|
||||
oBuilder.append("unknown_" + fieldData.getNumber());
|
||||
}
|
||||
oBuilder.append(": ");
|
||||
oBuilder.append(fieldData.decode());
|
||||
@ -104,45 +104,46 @@ public class RecordData {
|
||||
}
|
||||
|
||||
|
||||
private class FieldData {
|
||||
private final BaseType baseType;
|
||||
public FieldData getFieldByNumber(int number) {
|
||||
for (FieldData fieldData :
|
||||
fieldDataList) {
|
||||
if (number == fieldData.getNumber()) {
|
||||
return fieldData;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public class FieldData {
|
||||
private FieldDefinition fieldDefinition;
|
||||
private final int position;
|
||||
private final int size;
|
||||
private final String name;
|
||||
private final int scale;
|
||||
private final int offset;
|
||||
private final int number;
|
||||
public FieldData(BaseType baseType, int position, int size, String name, int number, int scale, int offset) {
|
||||
this.baseType = baseType;
|
||||
|
||||
public FieldData(FieldDefinition fieldDefinition, int position) {
|
||||
this.fieldDefinition = fieldDefinition;
|
||||
this.position = position;
|
||||
this.size = size;
|
||||
this.name = name;
|
||||
this.number = number;
|
||||
this.scale = scale;
|
||||
this.offset = offset;
|
||||
this.size = fieldDefinition.getSize();
|
||||
}
|
||||
|
||||
public BaseType getBaseType() {
|
||||
return baseType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return fieldDefinition.getName();
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
return fieldDefinition.getLocalNumber();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
goToPosition();
|
||||
if (STRING.equals(getBaseType())) {
|
||||
if (STRING.equals(fieldDefinition.getBaseType())) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
valueHolder.put((byte) 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
baseType.invalidate(valueHolder);
|
||||
fieldDefinition.invalidate(valueHolder);
|
||||
}
|
||||
|
||||
private void goToPosition() {
|
||||
@ -159,18 +160,18 @@ public class RecordData {
|
||||
throw new IllegalArgumentException("Array of values not supported yet"); //TODO: handle arrays
|
||||
Object o = objects[0];
|
||||
goToPosition();
|
||||
if (STRING.equals(getBaseType())) {
|
||||
if (STRING.equals(fieldDefinition.getBaseType())) {
|
||||
final byte[] bytes = ((String) o).getBytes(StandardCharsets.UTF_8);
|
||||
valueHolder.put(Arrays.copyOf(bytes, Math.min(this.size - 1, bytes.length)));
|
||||
valueHolder.put((byte) 0);
|
||||
return;
|
||||
}
|
||||
getBaseType().encode(valueHolder, o, scale, offset);
|
||||
fieldDefinition.encode(valueHolder, o);
|
||||
}
|
||||
|
||||
public Object decode() {
|
||||
goToPosition();
|
||||
if (STRING.equals(getBaseType())) {
|
||||
if (STRING.equals(fieldDefinition.getBaseType())) {
|
||||
final byte[] bytes = new byte[size];
|
||||
valueHolder.get(bytes);
|
||||
final int zero = ArrayUtils.indexOf((byte) 0, bytes);
|
||||
@ -180,7 +181,8 @@ public class RecordData {
|
||||
return new String(bytes, 0, zero, StandardCharsets.UTF_8);
|
||||
}
|
||||
//TODO: handle arrays
|
||||
return getBaseType().decode(valueHolder, scale, offset);
|
||||
return fieldDefinition.decode(valueHolder);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUtils.GARMIN_TIME_EPOCH;
|
||||
|
||||
public class FieldDefinitionTimestamp extends FieldDefinition {
|
||||
public FieldDefinitionTimestamp(int localNumber, int size, BaseType baseType, String name) {
|
||||
super(localNumber, size, baseType, name, 1, GARMIN_TIME_EPOCH);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public Object decode(ByteBuffer byteBuffer) {
|
||||
// return new Timestamp((long) baseType.decode(byteBuffer, scale, offset) * 1000L);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void encode(ByteBuffer byteBuffer, Object o) {
|
||||
// if(o instanceof Timestamp) {
|
||||
// baseType.encode(byteBuffer, (int) (((Timestamp) o).getTime() / 1000L), scale, offset);
|
||||
// return;
|
||||
// }
|
||||
// baseType.encode(byteBuffer, o, scale, offset);
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue
Block a user