1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-14 09:00:04 +02:00

Garmin protocol: refactoring and fixes of BaseTypes

The boundaries are enforced on the stored value when decoding, before applying the adjustments for scale and offset.
Also add some tests for the BaseTypes
Introduce new FieldDefinition for Temperature and WeatherCondition (removing the static class)
Add accessors for field data in the containing RecordData, thus keeping the FieldData private
This commit is contained in:
Daniele Gobbetti 2024-03-26 07:07:27 +01:00 committed by José Rebelo
parent 1db2cc0354
commit 520a510dd9
13 changed files with 506 additions and 152 deletions

View File

@ -29,7 +29,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateA
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v1.CommunicatorV1; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v1.CommunicatorV1;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2.CommunicatorV2; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2.CommunicatorV2;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitWeatherConditions;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalDefinitionsEnum; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalDefinitionsEnum;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ConfigurationMessage; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ConfigurationMessage;
@ -144,19 +143,18 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
List<RecordData> weatherData = new ArrayList<>(); List<RecordData> weatherData = new ArrayList<>();
try { try {
RecordData today = new RecordData(GlobalDefinitionsEnum.TODAY_WEATHER_CONDITIONS.getRecordDefinition()); RecordData today = new RecordData(GlobalDefinitionsEnum.TODAY_WEATHER_CONDITIONS.getRecordDefinition());
today.setFieldByName("weather_report", 0); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast today.setFieldByName("weather_report", 0); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
today.setFieldByName("timestamp", weather.timestamp); today.setFieldByName("timestamp", weather.timestamp);
today.setFieldByName("observed_at_time", weather.timestamp); today.setFieldByName("observed_at_time", weather.timestamp);
today.setFieldByName("temperature", weather.currentTemp - 273.15); today.setFieldByName("temperature", weather.currentTemp);
today.setFieldByName("low_temperature", weather.todayMinTemp - 273.15); today.setFieldByName("low_temperature", weather.todayMinTemp);
today.setFieldByName("high_temperature", weather.todayMaxTemp - 273.15); today.setFieldByName("high_temperature", weather.todayMaxTemp);
today.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(weather.currentConditionCode)); today.setFieldByName("condition", weather.currentConditionCode);
today.setFieldByName("wind_direction", weather.windDirection); today.setFieldByName("wind_direction", weather.windDirection);
today.setFieldByName("precipitation_probability", weather.precipProbability); today.setFieldByName("precipitation_probability", weather.precipProbability);
today.setFieldByName("wind_speed", Math.round(weather.windSpeed)); today.setFieldByName("wind_speed", Math.round(weather.windSpeed));
today.setFieldByName("temperature_feels_like", weather.feelsLikeTemp - 273.15); today.setFieldByName("temperature_feels_like", weather.feelsLikeTemp);
today.setFieldByName("relative_humidity", weather.currentHumidity); today.setFieldByName("relative_humidity", weather.currentHumidity);
today.setFieldByName("observed_location_lat", weather.latitude); today.setFieldByName("observed_location_lat", weather.latitude);
today.setFieldByName("observed_location_long", weather.longitude); today.setFieldByName("observed_location_long", weather.longitude);
@ -169,8 +167,8 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
RecordData weatherHourlyForecast = new RecordData(GlobalDefinitionsEnum.HOURLY_WEATHER_FORECAST.getRecordDefinition()); RecordData weatherHourlyForecast = new RecordData(GlobalDefinitionsEnum.HOURLY_WEATHER_FORECAST.getRecordDefinition());
weatherHourlyForecast.setFieldByName("weather_report", 1); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast weatherHourlyForecast.setFieldByName("weather_report", 1); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp); weatherHourlyForecast.setFieldByName("timestamp", hourly.timestamp);
weatherHourlyForecast.setFieldByName("temperature", hourly.temp - 273.15); weatherHourlyForecast.setFieldByName("temperature", hourly.temp);
weatherHourlyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(hourly.conditionCode)); weatherHourlyForecast.setFieldByName("condition", hourly.conditionCode);
weatherHourlyForecast.setFieldByName("wind_direction", hourly.windDirection); weatherHourlyForecast.setFieldByName("wind_direction", hourly.windDirection);
weatherHourlyForecast.setFieldByName("wind_speed", Math.round(hourly.windSpeed)); weatherHourlyForecast.setFieldByName("wind_speed", Math.round(hourly.windSpeed));
weatherHourlyForecast.setFieldByName("precipitation_probability", hourly.precipProbability); weatherHourlyForecast.setFieldByName("precipitation_probability", hourly.precipProbability);
@ -185,9 +183,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
RecordData todayDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition()); RecordData todayDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition());
todayDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast todayDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
todayDailyForecast.setFieldByName("timestamp", weather.timestamp); todayDailyForecast.setFieldByName("timestamp", weather.timestamp);
todayDailyForecast.setFieldByName("low_temperature", weather.todayMinTemp - 273.15); todayDailyForecast.setFieldByName("low_temperature", weather.todayMinTemp);
todayDailyForecast.setFieldByName("high_temperature", weather.todayMaxTemp - 273.15); todayDailyForecast.setFieldByName("high_temperature", weather.todayMaxTemp);
todayDailyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(weather.currentConditionCode)); todayDailyForecast.setFieldByName("condition", weather.currentConditionCode);
todayDailyForecast.setFieldByName("precipitation_probability", weather.precipProbability); todayDailyForecast.setFieldByName("precipitation_probability", weather.precipProbability);
todayDailyForecast.setFieldByName("day_of_week", weather.timestamp); todayDailyForecast.setFieldByName("day_of_week", weather.timestamp);
weatherData.add(todayDailyForecast); weatherData.add(todayDailyForecast);
@ -200,9 +198,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
RecordData weatherDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition()); RecordData weatherDailyForecast = new RecordData(GlobalDefinitionsEnum.DAILY_WEATHER_FORECAST.getRecordDefinition());
weatherDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast weatherDailyForecast.setFieldByName("weather_report", 2); // 0 = current, 1 = hourly_forecast, 2 = daily_forecast
weatherDailyForecast.setFieldByName("timestamp", weather.timestamp); weatherDailyForecast.setFieldByName("timestamp", weather.timestamp);
weatherDailyForecast.setFieldByName("low_temperature", daily.minTemp - 273.15); weatherDailyForecast.setFieldByName("low_temperature", daily.minTemp);
weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp - 273.15); weatherDailyForecast.setFieldByName("high_temperature", daily.maxTemp);
weatherDailyForecast.setFieldByName("condition", FitWeatherConditions.openWeatherCodeToFitWeatherStatus(daily.conditionCode)); weatherDailyForecast.setFieldByName("condition", daily.conditionCode);
weatherDailyForecast.setFieldByName("precipitation_probability", daily.precipProbability); weatherDailyForecast.setFieldByName("precipitation_probability", daily.precipProbability);
weatherDailyForecast.setFieldByName("day_of_week", ts); weatherDailyForecast.setFieldByName("day_of_week", ts);
weatherData.add(weatherDailyForecast); weatherData.add(weatherDailyForecast);

View File

@ -6,7 +6,10 @@ import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionDayOfWeek;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTemperature;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionTimestamp;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherCondition;
public enum GlobalDefinitionsEnum { public enum GlobalDefinitionsEnum {
TODAY_WEATHER_CONDITIONS(MesgType.TODAY_WEATHER_CONDITIONS, new RecordDefinition( TODAY_WEATHER_CONDITIONS(MesgType.TODAY_WEATHER_CONDITIONS, new RecordDefinition(
@ -16,14 +19,14 @@ public enum GlobalDefinitionsEnum {
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"), Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"), new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
new FieldDefinitionTimestamp(9, 4, BaseType.UINT32, "observed_at_time"), new FieldDefinitionTimestamp(9, 4, BaseType.UINT32, "observed_at_time"),
new FieldDefinition(1, 1, BaseType.SINT8, "temperature"), new FieldDefinitionTemperature(1, 1, BaseType.SINT8, "temperature"),
new FieldDefinition(14, 1, BaseType.SINT8, "low_temperature"), new FieldDefinitionTemperature(14, 1, BaseType.SINT8, "low_temperature"),
new FieldDefinition(13, 1, BaseType.SINT8, "high_temperature"), new FieldDefinitionTemperature(13, 1, BaseType.SINT8, "high_temperature"),
new FieldDefinition(2, 1, BaseType.ENUM, "condition"), new FieldDefinitionWeatherCondition(2, 1, BaseType.ENUM, "condition"),
new FieldDefinition(3, 2, BaseType.UINT16, "wind_direction"), new FieldDefinition(3, 2, BaseType.UINT16, "wind_direction"),
new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"), new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"),
new FieldDefinition(4, 2, BaseType.UINT16, "wind_speed", 298, 0), new FieldDefinition(4, 2, BaseType.UINT16, "wind_speed", 298, 0),
new FieldDefinition(6, 1, BaseType.SINT8, "temperature_feels_like"), new FieldDefinitionTemperature(6, 1, BaseType.SINT8, "temperature_feels_like"),
new FieldDefinition(7, 1, BaseType.UINT8, "relative_humidity"), new FieldDefinition(7, 1, BaseType.UINT8, "relative_humidity"),
new FieldDefinition(10, 4, BaseType.SINT32, "observed_location_lat"), new FieldDefinition(10, 4, BaseType.SINT32, "observed_location_lat"),
new FieldDefinition(11, 4, BaseType.SINT32, "observed_location_long"), new FieldDefinition(11, 4, BaseType.SINT32, "observed_location_long"),
@ -35,8 +38,8 @@ public enum GlobalDefinitionsEnum {
MesgType.HOURLY_WEATHER_FORECAST, MesgType.HOURLY_WEATHER_FORECAST,
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"), Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"), new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
new FieldDefinition(1, 1, BaseType.SINT8, "temperature"), new FieldDefinitionTemperature(1, 1, BaseType.SINT8, "temperature"),
new FieldDefinition(2, 1, BaseType.ENUM, "condition"), new FieldDefinitionWeatherCondition(2, 1, BaseType.ENUM, "condition"),
new FieldDefinition(3, 2, BaseType.UINT16, "wind_direction"), new FieldDefinition(3, 2, BaseType.UINT16, "wind_direction"),
new FieldDefinition(4, 2, BaseType.UINT16, "wind_speed", 298, 0), new FieldDefinition(4, 2, BaseType.UINT16, "wind_speed", 298, 0),
new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"), new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"),
@ -51,11 +54,11 @@ public enum GlobalDefinitionsEnum {
MesgType.DAILY_WEATHER_FORECAST, MesgType.DAILY_WEATHER_FORECAST,
Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"), Arrays.asList(new FieldDefinition(0, 1, BaseType.ENUM, "weather_report"),
new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"), new FieldDefinitionTimestamp(253, 4, BaseType.UINT32, "timestamp"),
new FieldDefinition(14, 1, BaseType.SINT8, "low_temperature"), new FieldDefinitionTemperature(14, 1, BaseType.SINT8, "low_temperature"),
new FieldDefinition(13, 1, BaseType.SINT8, "high_temperature"), new FieldDefinitionTemperature(13, 1, BaseType.SINT8, "high_temperature"),
new FieldDefinition(2, 1, BaseType.ENUM, "condition"), new FieldDefinitionWeatherCondition(2, 1, BaseType.ENUM, "condition"),
new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"), new FieldDefinition(5, 1, BaseType.UINT8, "precipitation_probability"),
new FieldDefinition(12, 1, BaseType.ENUM, "day_of_week")))), new FieldDefinitionDayOfWeek(12, 1, BaseType.ENUM, "day_of_week")))),
; ;
private final MesgType mesgType; private final MesgType mesgType;

View File

@ -86,6 +86,36 @@ public class RecordData {
} }
} }
public Object getFieldByNumber(int number) {
for (FieldData fieldData :
fieldDataList) {
if (fieldData.getNumber() == number) {
return fieldData.decode();
}
}
throw new IllegalArgumentException("Unknown field number " + number);
}
public Object getFieldByName(String name) {
for (FieldData fieldData :
fieldDataList) {
if (fieldData.getName().equals(name)) {
return fieldData.decode();
}
}
throw new IllegalArgumentException("Unknown field name " + name);
}
public int[] getFieldsNumbers() {
int[] arr = new int[fieldDataList.size()];
int count = 0;
for (FieldData fieldData : fieldDataList) {
int number = fieldData.getNumber();
arr[count++] = number;
}
return arr;
}
public String toString() { public String toString() {
StringBuilder oBuilder = new StringBuilder(); StringBuilder oBuilder = new StringBuilder();
for (FieldData fieldData : for (FieldData fieldData :
@ -102,20 +132,7 @@ public class RecordData {
return oBuilder.toString(); return oBuilder.toString();
} }
private class FieldData {
public FieldData getFieldByNumber(int number) {
for (FieldData fieldData :
fieldDataList) {
if (number == fieldData.getNumber()) {
return fieldData;
}
}
return null;
}
public class FieldData {
private FieldDefinition fieldDefinition; private FieldDefinition fieldDefinition;
private final int position; private final int position;
private final int size; private final int size;

View File

@ -10,7 +10,7 @@ public enum BaseType {
SINT16(0x83, new BaseTypeShort(false, 0x7FFF)), SINT16(0x83, new BaseTypeShort(false, 0x7FFF)),
UINT16(0x84, new BaseTypeShort(true, 0xFFFF)), UINT16(0x84, new BaseTypeShort(true, 0xFFFF)),
SINT32(0x85, new BaseTypeInt(false, 0x7FFFFFFF)), SINT32(0x85, new BaseTypeInt(false, 0x7FFFFFFF)),
UINT32(0x86, new BaseTypeInt(true, 0xFFFFFFFF)), UINT32(0x86, new BaseTypeInt(true, 0xFFFFFFFFL)),
STRING(0x07, new BaseTypeByte(true, 0x00)), STRING(0x07, new BaseTypeByte(true, 0x00)),
FLOAT32(0x88, new BaseTypeFloat()), FLOAT32(0x88, new BaseTypeFloat()),
FLOAT64(0x89, new BaseTypeDouble()), FLOAT64(0x89, new BaseTypeDouble()),

View File

@ -29,17 +29,23 @@ public class BaseTypeByte implements BaseTypeInterface {
@Override @Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) { public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
int i = (byteBuffer.get() + offset) / scale; int b = unsigned ? Byte.toUnsignedInt(byteBuffer.get()) : byteBuffer.get();
if (i < min || i > max) if (b < min || b > max)
return invalid; return null;
return i; if (b == invalid)
return null;
return (b + offset) / scale;
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
int i = ((Number) o).intValue() * scale - offset; int i = ((Number) o).intValue() * scale - offset;
if (!unsigned && (i < min || i > max)) { if (i < min || i > max) {
byteBuffer.put((byte) invalid); invalidate(byteBuffer);
return; return;
} }
byteBuffer.put((byte) i); byteBuffer.put((byte) i);

View File

@ -9,7 +9,7 @@ public class BaseTypeDouble implements BaseTypeInterface {
private final double invalid; private final double invalid;
BaseTypeDouble() { BaseTypeDouble() {
this.min = Double.MIN_VALUE; this.min = -Double.MAX_VALUE;
this.max = Double.MAX_VALUE; this.max = Double.MAX_VALUE;
this.invalid = Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL); this.invalid = Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL);
} }
@ -20,14 +20,24 @@ public class BaseTypeDouble implements BaseTypeInterface {
@Override @Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) { public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
return (byteBuffer.getDouble() + offset) / scale; double d = byteBuffer.getDouble();
if (d < min || d > max) {
return null;
}
if (Double.isNaN(d) || d == invalid)
return null;
return (d + offset) / scale;
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
double d = ((Number) o).doubleValue() * scale - offset; double d = ((Number) o).doubleValue() * scale - offset;
if (d < min || d > max) { if (d < min || d > max) {
byteBuffer.putDouble(invalid); invalidate(byteBuffer);
return; return;
} }
byteBuffer.putDouble(d); byteBuffer.putDouble(d);

View File

@ -7,13 +7,11 @@ public class BaseTypeFloat implements BaseTypeInterface {
private final double min; private final double min;
private final double max; private final double max;
private final double invalid; private final double invalid;
private final boolean unsigned;
BaseTypeFloat() { BaseTypeFloat() {
this.min = -Float.MAX_VALUE; this.min = -Float.MAX_VALUE;
this.max = Float.MAX_VALUE; this.max = Float.MAX_VALUE;
this.invalid = Float.intBitsToFloat(0xFFFFFFFF); this.invalid = Float.intBitsToFloat(0xFFFFFFFF);
this.unsigned = false;
} }
public int getByteSize() { public int getByteSize() {
@ -22,14 +20,24 @@ public class BaseTypeFloat implements BaseTypeInterface {
@Override @Override
public Object decode(ByteBuffer byteBuffer, int scale, int offset) { public Object decode(ByteBuffer byteBuffer, int scale, int offset) {
return (byteBuffer.getFloat() + offset) / scale; float f = byteBuffer.getFloat();
if (f < min || f > max) {
return null;
}
if (Float.isNaN(f) || f == invalid)
return null;
return (f + offset) / scale;
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
float f = ((Number) o).floatValue() * scale - offset; float f = ((Number) o).floatValue() * scale - offset;
if (!unsigned && (f < min || f > max)) { if (f < min || f > max) {
byteBuffer.putFloat((float) invalid); invalidate(byteBuffer);
return; return;
} }
byteBuffer.putFloat((float) f); byteBuffer.putFloat((float) f);

View File

@ -3,16 +3,16 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseType
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class BaseTypeInt implements BaseTypeInterface { public class BaseTypeInt implements BaseTypeInterface {
private final int min; private final long min;
private final int max; private final long max;
private final int invalid; private final long invalid;
private final boolean unsigned; private final boolean unsigned;
private final int size = 4; private final int size = 4;
BaseTypeInt(boolean unsigned, int invalid) { BaseTypeInt(boolean unsigned, long invalid) {
if (unsigned) { if (unsigned) {
this.min = 0; this.min = 0;
this.max = 0xffffffff; this.max = 0xffffffffL;
} else { } else {
this.min = Integer.MIN_VALUE; this.min = Integer.MIN_VALUE;
this.max = Integer.MAX_VALUE; this.max = Integer.MAX_VALUE;
@ -27,23 +27,23 @@ public class BaseTypeInt implements BaseTypeInterface {
@Override @Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) { public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
if (unsigned) { long i = unsigned ? Integer.toUnsignedLong(byteBuffer.getInt()) : byteBuffer.getInt();
long i = ((byteBuffer.getInt() & 0xffffffffL) + offset) / scale; if (i < min || i > max)
return i; return null;
} else { if (i == invalid)
int i = (byteBuffer.getInt() + offset) / scale; return null;
if (i < min || i > max) return (int) ((i + offset) / scale);
return invalid;
return i;
}
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
long l = ((Number) o).longValue() * scale - offset; long l = ((Number) o).longValue() * scale - offset;
if (!unsigned && (l < min || l > max)) { if (l < min || l > max) {
byteBuffer.putInt((int) invalid); invalidate(byteBuffer);
return; return;
} }
byteBuffer.putInt((int) l); byteBuffer.putInt((int) l);

View File

@ -1,21 +1,22 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes; package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes;
import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class BaseTypeLong implements BaseTypeInterface { public class BaseTypeLong implements BaseTypeInterface {
private final int size = 8; private final int size = 8;
private final double min; private final BigInteger min;
private final double max; private final BigInteger max;
private final double invalid; private final long invalid;
private final boolean unsigned; private final boolean unsigned;
BaseTypeLong(boolean unsigned, long invalid) { BaseTypeLong(boolean unsigned, long invalid) {
if (unsigned) { if (unsigned) {
this.min = 0; this.min = BigInteger.valueOf(0);
this.max = 0xFFFFFFFFFFFFFFFFL; this.max = BigInteger.valueOf(0xFFFFFFFFFFFFFFFFL);
} else { } else {
this.min = Long.MIN_VALUE; this.min = BigInteger.valueOf(Long.MIN_VALUE);
this.max = Long.MAX_VALUE; this.max = BigInteger.valueOf(Long.MAX_VALUE);
} }
this.invalid = invalid; this.invalid = invalid;
this.unsigned = unsigned; this.unsigned = unsigned;
@ -27,24 +28,26 @@ public class BaseTypeLong implements BaseTypeInterface {
@Override @Override
public Object decode(ByteBuffer byteBuffer, int scale, int offset) { public Object decode(ByteBuffer byteBuffer, int scale, int offset) {
if (unsigned) { BigInteger i = unsigned ? BigInteger.valueOf(byteBuffer.getLong() & 0xFFFFFFFFFFFFFFFFL) : BigInteger.valueOf(byteBuffer.getLong());
return ((byteBuffer.getLong() & 0xFFFFFFFFFFFFFFFFL + offset) / scale); if (!unsigned && (i.compareTo(min) < 0 || i.compareTo(max) > 0))
} else { return null;
long l = (byteBuffer.getLong() + offset) / scale; if (i.compareTo(BigInteger.valueOf(invalid)) == 0)
if (l < min || l > max) return null;
return invalid; return (i.longValue() + offset) / scale;
return l;
}
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
long l = ((Number) o).longValue() * scale - offset; if (null == o) {
if (!unsigned && (l < min || l > max)) { invalidate(byteBuffer);
byteBuffer.putLong((long) invalid);
return; return;
} }
byteBuffer.putLong(l); BigInteger i = BigInteger.valueOf(((Number) o).longValue() * scale - offset);
if (!unsigned && (i.compareTo(min) < 0 || i.compareTo(max) > 0)) {
invalidate(byteBuffer);
return;
}
byteBuffer.putLong(i.longValue());
} }
@Override @Override

View File

@ -27,23 +27,23 @@ public class BaseTypeShort implements BaseTypeInterface {
@Override @Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) { public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
if (unsigned) { int s = unsigned ? Short.toUnsignedInt(byteBuffer.getShort()) : byteBuffer.getShort();
int s = (((byteBuffer.getShort() & 0xffff) + offset) / scale); if (s < min || s > max)
return s; return null;
} else { if (s == invalid)
short s = (short) ((byteBuffer.getShort() + offset) / scale); return null;
if (s < min || s > max) return (s + offset) / scale;
return invalid;
return s;
}
} }
@Override @Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
int i = ((Number) o).intValue() * scale - offset; int i = ((Number) o).intValue() * scale - offset;
if (!unsigned && (i < min || i > max)) { if (i < min || i > max) {
byteBuffer.putShort((short) invalid); invalidate(byteBuffer);
return; return;
} }
byteBuffer.putShort((short) i); byteBuffer.putShort((short) i);

View File

@ -0,0 +1,12 @@
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;
public class FieldDefinitionTemperature extends FieldDefinition {
public FieldDefinitionTemperature(int localNumber, int size, BaseType baseType, String name) {
super(localNumber, size, baseType, name, 1, 273);
}
}

View File

@ -1,29 +1,32 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit; package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions;
public final class FitWeatherConditions { import java.nio.ByteBuffer;
public static final int CLEAR = 0;
public static final int PARTLY_CLOUDY = 1;
public static final int MOSTLY_CLOUDY = 2;
public static final int RAIN = 3;
public static final int SNOW = 4;
public static final int WINDY = 5;
public static final int THUNDERSTORMS = 6;
public static final int WINTRY_MIX = 7;
public static final int FOG = 8;
public static final int HAZY = 11;
public static final int HAIL = 12;
public static final int SCATTERED_SHOWERS = 13;
public static final int SCATTERED_THUNDERSTORMS = 14;
public static final int UNKNOWN_PRECIPITATION = 15;
public static final int LIGHT_RAIN = 16;
public static final int HEAVY_RAIN = 17;
public static final int LIGHT_SNOW = 18;
public static final int HEAVY_SNOW = 19;
public static final int LIGHT_RAIN_SNOW = 20;
public static final int HEAVY_RAIN_SNOW = 21;
public static final int CLOUDY = 22;
public static int openWeatherCodeToFitWeatherStatus(int openWeatherCode) { import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
public class FieldDefinitionWeatherCondition extends FieldDefinition {
public FieldDefinitionWeatherCondition(int localNumber, int size, BaseType baseType, String name) {
super(localNumber, size, baseType, name, 1, 0);
}
@Override
public Object decode(ByteBuffer byteBuffer) {
int idx = (int) baseType.decode(byteBuffer, scale, offset);
return Condition.values()[idx];
}
@Override
public void encode(ByteBuffer byteBuffer, Object o) {
if (o instanceof Condition) {
baseType.encode(byteBuffer, ((Condition) o).ordinal(), scale, offset);
return;
}
baseType.encode(byteBuffer, openWeatherCodeToFitWeatherStatus((int) o), scale, offset);
}
private int openWeatherCodeToFitWeatherStatus(int openWeatherCode) {
switch (openWeatherCode) { switch (openWeatherCode) {
//Group 2xx: Thunderstorm //Group 2xx: Thunderstorm
case 200: //thunderstorm with light rain: //11d case 200: //thunderstorm with light rain: //11d
@ -35,56 +38,56 @@ public final class FitWeatherConditions {
case 230: //thunderstorm with light drizzle: //11d case 230: //thunderstorm with light drizzle: //11d
case 231: //thunderstorm with drizzle: //11d case 231: //thunderstorm with drizzle: //11d
case 232: //thunderstorm with heavy drizzle: //11d case 232: //thunderstorm with heavy drizzle: //11d
return THUNDERSTORMS; return Condition.THUNDERSTORMS.ordinal();
case 221: //ragged thunderstorm: //11d case 221: //ragged thunderstorm: //11d
return SCATTERED_THUNDERSTORMS; return Condition.SCATTERED_THUNDERSTORMS.ordinal();
//Group 3xx: Drizzle //Group 3xx: Drizzle
case 300: //light intensity drizzle: //09d case 300: //light intensity drizzle: //09d
case 310: //light intensity drizzle rain: //09d case 310: //light intensity drizzle rain: //09d
case 313: //shower rain and drizzle: //09d case 313: //shower rain and drizzle: //09d
return LIGHT_RAIN; return Condition.LIGHT_RAIN.ordinal();
case 301: //drizzle: //09d case 301: //drizzle: //09d
case 311: //drizzle rain: //09d case 311: //drizzle rain: //09d
return RAIN; return Condition.RAIN.ordinal();
case 302: //heavy intensity drizzle: //09d case 302: //heavy intensity drizzle: //09d
case 312: //heavy intensity drizzle rain: //09d case 312: //heavy intensity drizzle rain: //09d
case 314: //heavy shower rain and drizzle: //09d case 314: //heavy shower rain and drizzle: //09d
return HEAVY_RAIN; return Condition.HEAVY_RAIN.ordinal();
case 321: //shower drizzle: //09d case 321: //shower drizzle: //09d
return SCATTERED_SHOWERS; return Condition.SCATTERED_SHOWERS.ordinal();
//Group 5xx: Rain //Group 5xx: Rain
case 500: //light rain: //10d case 500: //light rain: //10d
case 520: //light intensity shower rain: //09d case 520: //light intensity shower rain: //09d
case 521: //shower rain: //09d case 521: //shower rain: //09d
return LIGHT_RAIN; return Condition.LIGHT_RAIN.ordinal();
case 501: //moderate rain: //10d case 501: //moderate rain: //10d
case 531: //ragged shower rain: //09d case 531: //ragged shower rain: //09d
return RAIN; return Condition.RAIN.ordinal();
case 502: //heavy intensity rain: //10d case 502: //heavy intensity rain: //10d
case 503: //very heavy rain: //10d case 503: //very heavy rain: //10d
case 504: //extreme rain: //10d case 504: //extreme rain: //10d
case 522: //heavy intensity shower rain: //09d case 522: //heavy intensity shower rain: //09d
return HEAVY_RAIN; return Condition.HEAVY_RAIN.ordinal();
case 511: //freezing rain: //13d case 511: //freezing rain: //13d
return UNKNOWN_PRECIPITATION; return Condition.UNKNOWN_PRECIPITATION.ordinal();
//Group 6xx: Snow //Group 6xx: Snow
case 600: //light snow: //[[file:13d.png]] case 600: //light snow: //[[file:13d.png]]
return LIGHT_SNOW; return Condition.LIGHT_SNOW.ordinal();
case 601: //snow: //[[file:13d.png]] case 601: //snow: //[[file:13d.png]]
case 620: //light shower snow: //[[file:13d.png]] case 620: //light shower snow: //[[file:13d.png]]
case 621: //shower snow: //[[file:13d.png]] case 621: //shower snow: //[[file:13d.png]]
return SNOW; return Condition.SNOW.ordinal();
case 602: //heavy snow: //[[file:13d.png]] case 602: //heavy snow: //[[file:13d.png]]
case 622: //heavy shower snow: //[[file:13d.png]] case 622: //heavy shower snow: //[[file:13d.png]]
return HEAVY_SNOW; return Condition.HEAVY_SNOW.ordinal();
case 611: //sleet: //[[file:13d.png]] case 611: //sleet: //[[file:13d.png]]
case 612: //light shower sleet: //[[file:13d.png]] case 612: //light shower sleet: //[[file:13d.png]]
case 613: //shower sleet: //[[file:13d.png]] case 613: //shower sleet: //[[file:13d.png]]
return WINTRY_MIX; return Condition.WINTRY_MIX.ordinal();
case 615: //light rain and snow: //[[file:13d.png]] case 615: //light rain and snow: //[[file:13d.png]]
return LIGHT_RAIN_SNOW; return Condition.LIGHT_RAIN_SNOW.ordinal();
case 616: //rain and snow: //[[file:13d.png]] case 616: //rain and snow: //[[file:13d.png]]
return HEAVY_RAIN_SNOW; return Condition.HEAVY_RAIN_SNOW.ordinal();
//Group 7xx: Atmosphere //Group 7xx: Atmosphere
case 701: //mist: //[[file:50d.png]] case 701: //mist: //[[file:50d.png]]
@ -94,29 +97,29 @@ public final class FitWeatherConditions {
case 751: //sand: //[[file:50d.png]] case 751: //sand: //[[file:50d.png]]
case 761: //dust: //[[file:50d.png]] case 761: //dust: //[[file:50d.png]]
case 762: //volcanic ash: //[[file:50d.png]] case 762: //volcanic ash: //[[file:50d.png]]
return HAZY; return Condition.HAZY.ordinal();
case 741: //fog: //[[file:50d.png]] case 741: //fog: //[[file:50d.png]]
return FOG; return Condition.FOG.ordinal();
case 771: //squalls: //[[file:50d.png]] case 771: //squalls: //[[file:50d.png]]
case 781: //tornado: //[[file:50d.png]] case 781: //tornado: //[[file:50d.png]]
return WINDY; return Condition.WINDY.ordinal();
//Group 800: Clear //Group 800: Clear
case 800: //clear sky: //[[file:01d.png]] [[file:01n.png]] case 800: //clear sky: //[[file:01d.png]] [[file:01n.png]]
return CLEAR; return Condition.CLEAR.ordinal();
//Group 80x: Clouds //Group 80x: Clouds
case 801: //few clouds: //[[file:02d.png]] [[file:02n.png]] case 801: //few clouds: //[[file:02d.png]] [[file:02n.png]]
case 802: //scattered clouds: //[[file:03d.png]] [[file:03d.png]] case 802: //scattered clouds: //[[file:03d.png]] [[file:03d.png]]
return PARTLY_CLOUDY; return Condition.PARTLY_CLOUDY.ordinal();
case 803: //broken clouds: //[[file:04d.png]] [[file:03d.png]] case 803: //broken clouds: //[[file:04d.png]] [[file:03d.png]]
return MOSTLY_CLOUDY; return Condition.MOSTLY_CLOUDY.ordinal();
case 804: //overcast clouds: //[[file:04d.png]] [[file:04d.png]] case 804: //overcast clouds: //[[file:04d.png]] [[file:04d.png]]
return CLOUDY; return Condition.CLOUDY.ordinal();
//Group 90x: Extreme //Group 90x: Extreme
case 901: //tropical storm case 901: //tropical storm
return THUNDERSTORMS; return Condition.THUNDERSTORMS.ordinal();
case 906: //hail case 906: //hail
return HAIL; return Condition.HAIL.ordinal();
case 903: //cold case 903: //cold
case 904: //hot case 904: //hot
case 905: //windy case 905: //windy
@ -138,4 +141,31 @@ public final class FitWeatherConditions {
throw new IllegalArgumentException("Unknown weather code " + openWeatherCode); throw new IllegalArgumentException("Unknown weather code " + openWeatherCode);
} }
} }
enum Condition {
CLEAR,
PARTLY_CLOUDY,
MOSTLY_CLOUDY,
RAIN,
SNOW,
WINDY,
THUNDERSTORMS,
WINTRY_MIX,
FOG,
UNK9,
UNK10,
HAZY,
HAIL,
SCATTERED_SHOWERS,
SCATTERED_THUNDERSTORMS,
UNKNOWN_PRECIPITATION,
LIGHT_RAIN,
HEAVY_RAIN,
LIGHT_SNOW,
HEAVY_SNOW,
LIGHT_RAIN_SNOW,
HEAVY_RAIN_SNOW,
CLOUDY,
;
}
} }

View File

@ -2,8 +2,21 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.threeten.bp.Instant;
import org.threeten.bp.ZoneId;
import java.math.BigInteger;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.MesgType;
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.fit.baseTypes.BaseType;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -97,4 +110,258 @@ public class GarminSupportTest {
} }
} }
@Test
public void testBaseFields() {
RecordDefinition recordDefinition = new RecordDefinition(new RecordHeader((byte) 6), ByteOrder.LITTLE_ENDIAN, MesgType.TODAY_WEATHER_CONDITIONS, 123); //just some random data
List<FieldDefinition> fieldDefinitionList = new ArrayList<>();
for (BaseType baseType :
BaseType.values()) {
fieldDefinitionList.add(new FieldDefinition(baseType.getIdentifier(), baseType.getSize(), baseType, baseType.name()));
}
recordDefinition.setFieldDefinitions(fieldDefinitionList);
RecordData test = new RecordData(recordDefinition);
for (BaseType baseType :
BaseType.values()) {
System.out.println(baseType.getIdentifier());
Object startVal, endVal;
switch (baseType.name()) {
case "ENUM":
case "UINT8":
case "BASE_TYPE_BYTE":
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) 0xff - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = -1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "SINT8":
startVal = (int) Byte.MIN_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Byte.MAX_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Byte.MIN_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "SINT16":
startVal = (int) Short.MIN_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Short.MAX_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Short.MIN_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT16":
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) 0xffff - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = -1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "SINT32":
startVal = (int) Integer.MIN_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Integer.MAX_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) Integer.MIN_VALUE - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT32":
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (long) 0xffffffffL - 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, (long) ((int) endVal & 0xffffffffL));
startVal = 0xffffffff;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "FLOAT32":
startVal = 0.0f;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = -Float.MAX_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = Float.MAX_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (double) -Float.MAX_VALUE * 2;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "FLOAT64":
startVal = 0.0d;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = Double.MIN_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = Double.MAX_VALUE;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (double) -Double.MAX_VALUE * 2;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT8Z":
startVal = 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) 0xff;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
startVal = -1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT16Z":
startVal = 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (int) 0xffff;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = -1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT32Z":
startVal = 1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = (long) 0xffffffffL;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, (long) ((int) endVal & 0xffffffffL));
startVal = -1;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
startVal = 0;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "SINT64":
startVal = BigInteger.valueOf(Long.MIN_VALUE);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(((BigInteger) startVal).longValue(), endVal);
startVal = BigInteger.valueOf(Long.MAX_VALUE - 1);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(((BigInteger) startVal).longValue(), endVal);
startVal = BigInteger.valueOf(Long.MAX_VALUE);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT64":
startVal = 0L;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = BigInteger.valueOf(0xFFFFFFFFFFFFFFFFL - 1);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(((BigInteger) startVal).longValue() & 0xFFFFFFFFFFFFFFFFL, endVal);
startVal = BigInteger.valueOf(0xFFFFFFFFFFFFFFFFL);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "UINT64Z":
startVal = 1L;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(startVal, endVal);
startVal = BigInteger.valueOf(0xFFFFFFFFFFFFFFFFL);
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertEquals(((BigInteger) startVal).longValue() & 0xFFFFFFFFFFFFFFFFL, endVal);
startVal = 0L;
test.setFieldByName(baseType.name(), startVal);
endVal = test.getFieldByName(baseType.name());
Assert.assertNull(endVal);
break;
case "STRING":
//TODO
break;
default:
System.out.println(baseType.name());
Assert.assertFalse(true); //we should not end up here, if it happen we forgot a case in the switch
}
}
}
@Test
public void runningTest() {
System.out.println(Instant.ofEpochSecond(System.currentTimeMillis()).atZone(ZoneId.systemDefault()).getDayOfWeek().getValue());
}
} }