From 385234bfd9c6b68ddaa5fc63fe365d52b0d65a35 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Mon, 21 Aug 2023 16:18:40 +0200 Subject: [PATCH 1/8] Mapbox control class --- .../main/java/eu/ztsh/garmin/MainActivity.kt | 45 ++---------- .../main/java/eu/ztsh/garmin/MapboxToolbox.kt | 69 +++++++++++++++++++ 2 files changed, 74 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt diff --git a/app/src/main/java/eu/ztsh/garmin/MainActivity.kt b/app/src/main/java/eu/ztsh/garmin/MainActivity.kt index 4d02d83..1f230a3 100644 --- a/app/src/main/java/eu/ztsh/garmin/MainActivity.kt +++ b/app/src/main/java/eu/ztsh/garmin/MainActivity.kt @@ -31,65 +31,30 @@ class MainActivity : AppCompatActivity() { lateinit var garmin: Garmin private lateinit var binding: ActivityMainBinding - private val mapboxObserver = MapboxObserver() - - init { - lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onResume(owner: LifecycleOwner) { - MapboxNavigationApp.attach(owner) - MapboxNavigationApp.registerObserver(mapboxObserver) - } - - override fun onPause(owner: LifecycleOwner) { - MapboxNavigationApp.detach(owner) - MapboxNavigationApp.unregisterObserver(mapboxObserver) - } - }) - } + private val mapboxToolbox = MapboxToolbox(lifecycle, this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - if (!MapboxNavigationApp.isSetup()) { - MapboxNavigationApp.setup { - NavigationOptions.Builder(applicationContext) - .accessToken(BuildConfig.MAPBOX_DOWNLOADS_TOKEN) - .build() - } - } - MapboxNavigationApp.current()?.startTripSession() + mapboxToolbox.onCreate() bluetoothInit() } override fun onStart() { super.onStart() - MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver) + mapboxToolbox.onStart() } override fun onStop() { super.onStop() - MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver) + mapboxToolbox.onStop() } override fun onDestroy() { super.onDestroy() - MapboxNavigationApp.current()?.stopTripSession() - maneuverApi.cancel() + mapboxToolbox.onDestroy() } - - // Define distance formatter options - private val distanceFormatter: DistanceFormatterOptions by lazy { - DistanceFormatterOptions.Builder(this).build() - } - // Create an instance of the Maneuver API - private val maneuverApi: MapboxManeuverApi by lazy { - MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter)) - } - - private val routeProgressObserver = - RouteProgressObserver { routeProgress -> maneuverApi.getManeuvers(routeProgress).value?.apply { garmin.process(this[0]) } } - private fun bluetoothInit() { val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java) val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter diff --git a/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt new file mode 100644 index 0000000..7afeffe --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt @@ -0,0 +1,69 @@ +package eu.ztsh.garmin + +import android.annotation.SuppressLint +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import com.mapbox.navigation.base.formatter.DistanceFormatterOptions +import com.mapbox.navigation.base.options.NavigationOptions +import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter +import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp +import com.mapbox.navigation.core.trip.session.RouteProgressObserver +import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi + +@SuppressLint("MissingPermission") +class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) { + + private val mapboxObserver = MapboxObserver() + + init { + lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onResume(owner: LifecycleOwner) { + MapboxNavigationApp.attach(owner) + MapboxNavigationApp.registerObserver(mapboxObserver) + } + + override fun onPause(owner: LifecycleOwner) { + MapboxNavigationApp.detach(owner) + MapboxNavigationApp.unregisterObserver(mapboxObserver) + } + }) + } + + fun onCreate() { + if (!MapboxNavigationApp.isSetup()) { + MapboxNavigationApp.setup { + NavigationOptions.Builder(context) + .accessToken(BuildConfig.MAPBOX_DOWNLOADS_TOKEN) + .build() + } + } + MapboxNavigationApp.current()?.startTripSession() + } + + fun onStart() { + MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver) + } + + fun onStop() { + MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver) + } + + fun onDestroy() { + MapboxNavigationApp.current()?.stopTripSession() + maneuverApi.cancel() + } + + // Define distance formatter options + private val distanceFormatter: DistanceFormatterOptions by lazy { + DistanceFormatterOptions.Builder(context).build() + } + // Create an instance of the Maneuver API + private val maneuverApi: MapboxManeuverApi by lazy { + MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter)) + } + + private val routeProgressObserver = + RouteProgressObserver { routeProgress -> maneuverApi.getManeuvers(routeProgress).value?.apply { context.garmin.process(this[0]) } } + +} \ No newline at end of file From 379dd7118ab22202e6c019a9fd71b81285514e9e Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Mon, 21 Aug 2023 16:57:48 +0200 Subject: [PATCH 2/8] Unified with python var names --- app/src/main/java/eu/ztsh/garmin/Garmin.kt | 15 +++++++------- .../java/eu/ztsh/garmin/ManeuverMapper.kt | 15 ++++---------- app/src/main/java/eu/ztsh/garmin/Model.kt | 20 +++++++++---------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/eu/ztsh/garmin/Garmin.kt b/app/src/main/java/eu/ztsh/garmin/Garmin.kt index f71221f..702a123 100644 --- a/app/src/main/java/eu/ztsh/garmin/Garmin.kt +++ b/app/src/main/java/eu/ztsh/garmin/Garmin.kt @@ -41,6 +41,7 @@ class Garmin( private inner class ProcessingThread(val maneuver: Maneuver) : Thread() { override fun run() { + // TODO: check for equality before mapping! send(ManeuverMapper.apply(maneuver)) } @@ -57,23 +58,23 @@ class Garmin( private fun setDistance(state: eu.ztsh.garmin.State) { connection.enqueue(intArrayOf( 0x03, asDigit(state.distance / 1000), asDigit(state.distance / 100), asDigit(state.distance / 10), - 0x00, asDigit(state.distance), state.unit.data + 0x00, asDigit(state.distance), state.unit.value )) } private fun setDirection(direction: Direction) { - val param1 = when (direction.outAngle) { + val param1: Int = when (direction.angle) { OutAngle.LeftDown -> 0x10 OutAngle.RightDown -> 0x20 - else -> direction.outType.data + else -> direction.out.value } - val param2: Int = if (direction.outType == OutType.RightRoundabout - || direction.outType == OutType.LeftRoundabout) { - if (direction.roundabout == OutAngle.AsDirection) direction.outAngle.data else direction.roundabout.data + val param2: Int = if (direction.out == OutType.RightRoundabout + || direction.out == OutType.LeftRoundabout) { + if (direction.roundabout == OutAngle.AsDirection) direction.angle.value else direction.roundabout.value } else { 0x00 } - val param3: Int = if (direction.outAngle == OutAngle.LeftDown || direction.outAngle == OutAngle.RightDown) 0x00 else direction.outAngle.data + val param3: Int = if (direction.angle == OutAngle.LeftDown || direction.angle == OutAngle.RightDown) 0x00 else direction.angle.value connection.enqueue(intArrayOf(0x01, param1, param2, param3)) } diff --git a/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt b/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt index 3b8b54f..11c7fc3 100644 --- a/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt +++ b/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt @@ -10,17 +10,10 @@ class ManeuverMapper { val state = State() maneuver.apply { this.primary.apply { - state.direction = Direction() when (this.type) { - "turn" -> { - state.direction.outType = OutType.Lane - state.direction.roundabout = OutAngle.AsDirection - } - "roundabout" -> { - state.direction.outType = OutType.RightRoundabout + state.direction.out = OutType.RightRoundabout } - "arrive" -> { state.flag = true } @@ -28,17 +21,17 @@ class ManeuverMapper { when (this.modifier) { "right" -> { when (this.type) { - "turn" -> state.direction.outAngle = OutAngle.Right + "turn" -> state.direction.angle = OutAngle.Right "roundabout" -> { when (this.degrees) { - 137.0 -> state.direction.outAngle = OutAngle.EasyRight + 137.0 -> state.direction.angle = OutAngle.EasyRight } } } } "left" -> { when (this.type) { - "turn" -> state.direction.outAngle = OutAngle.Left + "turn" -> state.direction.angle = OutAngle.Left } } } diff --git a/app/src/main/java/eu/ztsh/garmin/Model.kt b/app/src/main/java/eu/ztsh/garmin/Model.kt index 15e77bf..27a46f6 100644 --- a/app/src/main/java/eu/ztsh/garmin/Model.kt +++ b/app/src/main/java/eu/ztsh/garmin/Model.kt @@ -1,6 +1,6 @@ package eu.ztsh.garmin -enum class OutType(val data: Int) { +enum class OutType(val value: Int) { Off(0x00), Lane(0x01), @@ -13,7 +13,7 @@ enum class OutType(val data: Int) { } -enum class OutAngle(val data: Int) { +enum class OutAngle(val value: Int) { Down(0x01), SharpRight(0x02), @@ -29,7 +29,7 @@ enum class OutAngle(val data: Int) { } -enum class Unit(val data: Int) { +enum class Unit(val value: Int) { Any(0), Metres(1), @@ -39,7 +39,7 @@ enum class Unit(val data: Int) { } -enum class Lane(val data: Int) { +enum class Lane(val value: Int) { DotsRight(0x01), OuterRight(0x02), @@ -70,8 +70,8 @@ class State { } class Direction { - var outAngle: OutAngle = OutAngle.AsDirection - var outType: OutType = OutType.Lane + var angle: OutAngle = OutAngle.AsDirection + var out: OutType = OutType.Lane var roundabout: OutAngle = OutAngle.AsDirection override fun equals(other: Any?): Boolean { if (this === other) return true @@ -79,16 +79,16 @@ class Direction { other as Direction - if (outAngle != other.outAngle) return false - if (outType != other.outType) return false + if (angle != other.angle) return false + if (out != other.out) return false if (roundabout != other.roundabout) return false return true } override fun hashCode(): Int { - var result = outAngle.hashCode() - result = 31 * result + outType.hashCode() + var result = angle.hashCode() + result = 31 * result + out.hashCode() result = 31 * result + roundabout.hashCode() return result } From a868d08474a763ecf0f179419ec683f9a5fc9e79 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 23 Aug 2023 20:45:54 +0200 Subject: [PATCH 3/8] Python test suite --- app/test.py | 31 ------------ {app => python}/main.py | 53 ++++++++------------- python/test.py | 103 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 65 deletions(-) delete mode 100644 app/test.py rename {app => python}/main.py (84%) create mode 100644 python/test.py diff --git a/app/test.py b/app/test.py deleted file mode 100644 index 10388cc..0000000 --- a/app/test.py +++ /dev/null @@ -1,31 +0,0 @@ -from numpy import uint8 - - -def send_hud(buf: list): - n = len(buf) - chars = [] - crc = uint8(0xeb + n + n) - chars.append(0x10) - chars.append(0x7b) - chars.append(n + 6) - if n == 0xa: - chars.append(0x10) - chars.append(n) - chars.append(0x00) - chars.append(0x00) - chars.append(0x00) - chars.append(0x55) - chars.append(0x15) - for char in buf: - crc = uint8(crc + char) - chars.append(char) - if char == 0x10: - chars.append(0x10) - chars.append((-crc) & 0xff) - chars.append(0x10) - chars.append(0x03) - print(chars) - print([bytes(chr(char), 'raw_unicode_escape') for char in chars]) - - -send_hud([0x04, 0x01]) diff --git a/app/main.py b/python/main.py similarity index 84% rename from app/main.py rename to python/main.py index 62a2a7a..73a55ab 100644 --- a/app/main.py +++ b/python/main.py @@ -52,11 +52,13 @@ class Lane(Enum): class Controller: - def __init__(self): + def __init__(self, device: str = None): self.gps = False - self.ser = None#serial.Serial('COM9', 9600) - time.sleep(2) - # print(self.ser.read_all()) + self.debug = device is None + if not self.debug: + self.ser = serial.Serial(device, 9600) + time.sleep(2) + print(self.ser.read_all()) def clear(self): self.send_hud([0x03, 0, 0, 0, 0x00, 0, 0]) @@ -123,8 +125,8 @@ class Controller: self.send_hud([0x07, 0x01]) self.gps = state - def set_speed_control(self): - self.send_hud([0x04, 0x01]) + def set_speed_control(self, on: bool = True): + self.send_hud([0x04, 0x01 if on else 0x00]) def set_compass(self, direction: float): if direction > 337.5 or direction <= 22.5: @@ -172,35 +174,18 @@ class Controller: self.send_packet(chars) def send_packet(self, buff): - print("raw", buff) encoded = [bytes(chr(char), 'raw_unicode_escape') for char in buff] - print("enc", encoded) - # for char in buff: - # self.ser.write(bytes(chr(char), 'raw_unicode_escape')) - # time.sleep(0.2) - # print(self.ser.read_all()) - - -def test(): - c.set_direction(OutAngle.Straight, OutType.Lane) - target = 100 - for i in range(10): - c.set_distance(target, Unit.Kilometres) - time.sleep(0.5) - target -= 1 - c.set_speed_control() - time.sleep(0.5) - for i in range(10): - c.set_distance(target, Unit.Kilometres) - time.sleep(0.5) - target -= 1 - c.set_direction(OutAngle.Right, OutType.LongerLane) - for i in range(10): - c.set_distance(target, Unit.Kilometres) - time.sleep(0.5) - target -= 1 + if self.debug: + print("raw", buff) + print("enc", encoded) + else: + for char in buff: + self.ser.write(bytes(chr(char), 'raw_unicode_escape')) + time.sleep(0.2) + print(self.ser.read_all()) if __name__ == '__main__': - c = Controller() - test() + import sys + device = sys.argv[1] if len(sys.argv) > 1 else None + c = Controller(device) diff --git a/python/test.py b/python/test.py new file mode 100644 index 0000000..2808632 --- /dev/null +++ b/python/test.py @@ -0,0 +1,103 @@ +from main import * +from time import sleep + +interval = 0.2 + + +def suite(controller: Controller): + lines(controller) + direction(controller) + distance(controller) + speed(controller) + time(controller) + control(controller) + + +def lines(controller: Controller): + controller.set_lines([Lane.DotsLeft], []) + controller.set_lines([Lane.OuterRight], [Lane.OuterLeft]) + controller.set_lines([Lane.MiddleRight], [Lane.MiddleLeft]) + controller.set_lines([Lane.InnerRight], [Lane.InnerLeft]) + controller.set_lines([Lane.InnerLeft], [Lane.InnerRight]) + controller.set_lines([Lane.MiddleLeft], [Lane.MiddleRight]) + controller.set_lines([Lane.OuterLeft], [Lane.OuterRight]) + controller.set_lines( + [Lane.DotsRight], + [Lane.OuterRight, Lane.MiddleRight, Lane.InnerRight, Lane.InnerLeft, Lane.MiddleLeft, Lane.OuterLeft] + ) + controller.set_lines([], []) + + +def direction(controller: Controller): + controller.set_direction(OutAngle.RightDown) + controller.set_direction(OutAngle.SharpRight) + controller.set_direction(OutAngle.Right) + controller.set_direction(OutAngle.EasyRight) + controller.set_direction(OutAngle.Straight) + controller.set_direction(OutAngle.EasyLeft) + controller.set_direction(OutAngle.Left) + controller.set_direction(OutAngle.SharpLeft) + controller.set_direction(OutAngle.LeftDown) + controller.set_direction(OutAngle.Down) + controller.set_direction(OutAngle.SharpRight, OutType.LongerLane) + controller.set_direction(OutAngle.Right, OutType.LongerLane) + controller.set_direction(OutAngle.EasyRight, OutType.LongerLane) + controller.set_direction(OutAngle.Straight, OutType.LongerLane) + controller.set_direction(OutAngle.EasyLeft, OutType.LongerLane) + controller.set_direction(OutAngle.Left, OutType.LongerLane) + controller.set_direction(OutAngle.SharpLeft, OutType.LongerLane) + controller.set_direction(OutAngle.SharpRight, OutType.RightRoundabout) + controller.set_direction(OutAngle.Right, OutType.RightRoundabout) + controller.set_direction(OutAngle.EasyRight, OutType.RightRoundabout) + controller.set_direction(OutAngle.Straight, OutType.RightRoundabout) + controller.set_direction(OutAngle.EasyLeft, OutType.RightRoundabout) + controller.set_direction(OutAngle.Left, OutType.RightRoundabout) + controller.set_direction(OutAngle.SharpLeft, OutType.RightRoundabout) + controller.set_direction(OutAngle.Left, OutType.Flag) + controller.set_direction(OutAngle.Right, OutType.Flag) + + +def distance(controller: Controller): + controller.set_distance(999) + controller.set_distance(999, Unit.Kilometres) + controller.set_distance(999, Unit.Metres) + controller.set_distance(999, Unit.Foot) + controller.set_distance(999, Unit.Miles) + pass + + +def speed(controller: Controller): + controller.set_gps(True) + controller.set_speed(50) + controller.set_speed(50, 100) + controller.set_speed(150, 100) + controller.set_speed(50, 100, True) + pass + + +def time(controller: Controller): + controller.set_time(22, 22) + controller.set_time(22, 22, traffic=True) + controller.set_time(22, 22, flag=True) + pass + + +def control(controller: Controller): + controller.set_speed_control() + controller.set_speed_control(False) + + +def compass(controller: Controller): + controller.set_compass(22.5) + controller.set_compass(67.5) + controller.set_compass(112.5) + controller.set_compass(157.5) + controller.set_compass(202.5) + controller.set_compass(247.5) + controller.set_compass(292.5) + controller.set_compass(337.5) + pass + + +if __name__ == '__main__': + instance = Controller('/dev/rfcomm0') From 2b0746f6c48269dc3259c3f5d0150ac33a9e4a47 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Wed, 23 Aug 2023 20:53:06 +0200 Subject: [PATCH 4/8] Line endings & .gitignore update --- .gitattributes | 1 + .gitignore | 1 + gradlew.bat | 178 ++++++++++++++++++++++++------------------------- 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/.gitattributes b/.gitattributes index fcadb2c..4fbd8ef 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text eol=lf +*.bat text eol=crlf diff --git a/.gitignore b/.gitignore index 10cfdbf..4d4fa7f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ .externalNativeBuild .cxx local.properties +__pycache__/ diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From ff2e1639834e85a02f393327e6cbf8e6d45464f9 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Thu, 24 Aug 2023 00:23:40 +0200 Subject: [PATCH 5/8] State to IntArray mapper --- app/src/main/java/eu/ztsh/garmin/Garmin.kt | 43 ++--- .../java/eu/ztsh/garmin/data/GarminMapper.kt | 151 ++++++++++++++++++ .../ztsh/garmin/{ => data}/ManeuverMapper.kt | 5 +- .../java/eu/ztsh/garmin/{ => data}/Model.kt | 6 +- 4 files changed, 173 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt rename app/src/main/java/eu/ztsh/garmin/{ => data}/ManeuverMapper.kt (94%) rename app/src/main/java/eu/ztsh/garmin/{ => data}/Model.kt (93%) diff --git a/app/src/main/java/eu/ztsh/garmin/Garmin.kt b/app/src/main/java/eu/ztsh/garmin/Garmin.kt index 702a123..d6b3113 100644 --- a/app/src/main/java/eu/ztsh/garmin/Garmin.kt +++ b/app/src/main/java/eu/ztsh/garmin/Garmin.kt @@ -6,6 +6,12 @@ import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothSocket import android.util.Log import com.mapbox.navigation.ui.maneuver.model.Maneuver +import eu.ztsh.garmin.data.Direction +import eu.ztsh.garmin.data.GarminMapper +import eu.ztsh.garmin.data.ManeuverMapper +import eu.ztsh.garmin.data.OutAngle +import eu.ztsh.garmin.data.OutType +import eu.ztsh.garmin.data.State import java.io.IOException import java.util.* import java.util.concurrent.SynchronousQueue @@ -45,46 +51,29 @@ class Garmin( send(ManeuverMapper.apply(maneuver)) } - fun send(incoming: eu.ztsh.garmin.State) { + fun send(incoming: eu.ztsh.garmin.data.State) { if (stateCache.distance != incoming.distance) { setDistance(incoming) } if (stateCache.direction != incoming.direction) { - setDirection(stateCache.direction) + setDirection(stateCache) } stateCache = incoming } - private fun setDistance(state: eu.ztsh.garmin.State) { - connection.enqueue(intArrayOf( - 0x03, asDigit(state.distance / 1000), asDigit(state.distance / 100), asDigit(state.distance / 10), - 0x00, asDigit(state.distance), state.unit.value - )) + + private fun setLines(state: eu.ztsh.garmin.data.State) { + } - private fun setDirection(direction: Direction) { - val param1: Int = when (direction.angle) { - OutAngle.LeftDown -> 0x10 - OutAngle.RightDown -> 0x20 - else -> direction.out.value - } - val param2: Int = if (direction.out == OutType.RightRoundabout - || direction.out == OutType.LeftRoundabout) { - if (direction.roundabout == OutAngle.AsDirection) direction.angle.value else direction.roundabout.value - } else { - 0x00 - } - val param3: Int = if (direction.angle == OutAngle.LeftDown || direction.angle == OutAngle.RightDown) 0x00 else direction.angle.value - connection.enqueue(intArrayOf(0x01, param1, param2, param3)) + private fun setDistance(state: eu.ztsh.garmin.data.State) { + connection.enqueue(GarminMapper.setDistance(state)) } - private fun asDigit(n: Int): Int { - if (n == 0) { - return 0 - } - val m = n % 10 - return if (m == 0) 10 else m + private fun setDirection(direction: eu.ztsh.garmin.data.State) { + connection.enqueue(GarminMapper.setDirection(direction)) } + } private inner class ConnectThread : Thread() { diff --git a/app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt b/app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt new file mode 100644 index 0000000..44182c8 --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt @@ -0,0 +1,151 @@ +package eu.ztsh.garmin.data + +class GarminMapper { + + companion object { + + fun setLines(state: State): IntArray { + return intArrayOf(0x02, state.lineOutlines.sumOf { it.value }, state.lineArrows.sumOf { it.value }) + } + + fun setDirection(state: State): IntArray { + return setDirection(state.direction.angle, state.direction.out, state.direction.roundabout) + } + + fun setDistance(state: State): IntArray { + return setDistance(state.distance, state.unit) + } + + fun setSpeed(state: State): IntArray { + return setSpeed(state.speed, state.limit, state.speed > state.limit) + } + + fun setSpeedFreeRide(state: State): Pair { + return Pair(setDistance(state.speed), setSpeed(state.limit, limitWarning = state.speed > state.limit)) + } + + fun setTime(hours: Int, minutes: Int, traffic: Boolean = false, flag: Boolean = false): IntArray { + val trafficChar = asChar(traffic) + val flagChar = asChar(flag) + return if (hours > 99) { + intArrayOf( + 0x05, + trafficChar, + asDigit(hours / 1000), + asDigit(hours / 100), + 0x00, + asDigit(hours / 10), + asDigit(hours), + 0xff, + flagChar + ) + } else { + intArrayOf( + 0x05, + trafficChar, + asDigit(hours / 10), + asDigit(hours), + 0xff, + asDigit(minutes / 10), + asDigit(minutes), + 0x00, + flagChar + ) + } + } + + fun setSpeedControl(state: State): IntArray { + return intArrayOf(0x04, if (state.control) 0x01 else 0x02) + } + + fun setCompass(state: State): IntArray { + // TODO: Implement + return setDirection(OutAngle.Straight, OutType.ArrowOnly) + } + + fun cleanDistance(): IntArray { + return intArrayOf(0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + } + + private fun setDirection( + angle: OutAngle, + out: OutType = OutType.Lane, + roundabout: OutAngle = OutAngle.AsDirection + ): IntArray { + val param1: Int = when (angle) { + OutAngle.LeftDown -> 0x10 + OutAngle.RightDown -> 0x20 + else -> out.value + } + val param2: Int = if (out == OutType.RightRoundabout + || out == OutType.LeftRoundabout + ) { + if (roundabout == OutAngle.AsDirection) angle.value else roundabout.value + } else { + 0x00 + } + val param3: Int = + if (angle == OutAngle.LeftDown || angle == OutAngle.RightDown) 0x00 else angle.value + return intArrayOf(0x01, param1, param2, param3) + } + + private fun setDistance(distance: Int, unit: Unit = Unit.Any): IntArray { + return intArrayOf( + 0x03, asDigit(distance / 1000), asDigit(distance / 100), asDigit(distance / 10), + 0x00, asDigit(distance), unit.value + ) + } + + private fun setSpeed( + speed: Int, + limit: Int = 0, + limitWarning: Boolean = false, + acc: Boolean = false + ): IntArray { + // TODO: car connection + val accChar = asChar(acc) + val limitWarningChar = asChar(limitWarning) + return if (limit > 0) { + intArrayOf( + 0x06, + asDigit(speed / 100), + asDigit(speed / 10), + asDigit(speed), + 0xff, + asDigit(limit / 100), + asDigit(limit / 10), + asDigit(limit), + limitWarningChar, + accChar + ) + } else { + intArrayOf( + 0x06, + 0x00, + 0x00, + 0x00, + 0x00, + asDigit(speed / 100), + asDigit(speed / 10), + asDigit(speed), + limitWarningChar, + accChar + ) + } + } + + private fun asDigit(n: Int): Int { + if (n == 0) { + return 0 + } + val m = n % 10 + return if (m == 0) 10 else m + } + + private fun asChar(boolean: Boolean): Int { + return if (boolean) 0xff else 0x00 + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt b/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt similarity index 94% rename from app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt rename to app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt index 11c7fc3..89f1996 100644 --- a/app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt @@ -1,4 +1,4 @@ -package eu.ztsh.garmin +package eu.ztsh.garmin.data import com.mapbox.navigation.ui.maneuver.model.Maneuver @@ -43,7 +43,8 @@ class ManeuverMapper { state.unit = when (this[1]) { "m" -> Unit.Metres "km" -> Unit.Kilometres - else -> {Unit.Any} + else -> { + Unit.Any} } } diff --git a/app/src/main/java/eu/ztsh/garmin/Model.kt b/app/src/main/java/eu/ztsh/garmin/data/Model.kt similarity index 93% rename from app/src/main/java/eu/ztsh/garmin/Model.kt rename to app/src/main/java/eu/ztsh/garmin/data/Model.kt index 27a46f6..9092bbb 100644 --- a/app/src/main/java/eu/ztsh/garmin/Model.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/Model.kt @@ -1,4 +1,4 @@ -package eu.ztsh.garmin +package eu.ztsh.garmin.data enum class OutType(val value: Int) { @@ -54,8 +54,8 @@ enum class Lane(val value: Int) { class State { - var lineArrows: Int = 0 - var lineOutlines: Int = 0 + var lineArrows: List = listOf() + var lineOutlines: List = listOf() var direction = Direction() var distance: Int = 0 var unit: Unit = Unit.Any From 89e16fb805c723cdad13df60752b7f3c147816b8 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Thu, 24 Aug 2023 14:01:44 +0200 Subject: [PATCH 6/8] WIP: unit tests --- app/build.gradle | 1 + .../eu/ztsh/garmin/data/GarminMapperTest.kt | 279 ++++++++++++++++++ python/test.py | 8 + 3 files changed, 288 insertions(+) create mode 100644 app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt diff --git a/app/build.gradle b/app/build.gradle index 855563f..b906a80 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,6 +46,7 @@ dependencies { implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' + testImplementation 'org.assertj:assertj-core:3.24.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt b/app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt new file mode 100644 index 0000000..f0f1269 --- /dev/null +++ b/app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt @@ -0,0 +1,279 @@ +package eu.ztsh.garmin.data + +import eu.ztsh.garmin.Garmin +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class GarminMapperTest { + + @Test + fun linesTest() { + linesTest( + listOf(Lane.DotsLeft), + listOf(), + intArrayOf(2, 128, 0), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 128, 0, 141, 16, 3) + ) + linesTest( + Lane.OuterRight, + Lane.OuterLeft, + intArrayOf(2, 2, 64), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 2, 64, 203, 16, 3) + ) + linesTest( + Lane.MiddleRight, + Lane.MiddleLeft, + intArrayOf(2, 4, 32), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 4, 32, 233, 16, 3) + ) + linesTest( + Lane.InnerRight, + Lane.InnerLeft, + intArrayOf(2, 8, 16), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 8, 16, 16, 245, 16, 3) + ) + linesTest( + Lane.InnerLeft, + Lane.InnerRight, + intArrayOf(2, 16, 8), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 16, 16, 8, 245, 16, 3) + ) + linesTest( + Lane.MiddleLeft, + Lane.MiddleRight, + intArrayOf(2, 32, 4), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 32, 4, 233, 16, 3) + ) + linesTest( + Lane.OuterLeft, + Lane.OuterRight, + intArrayOf(2, 64, 2), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 64, 2, 203, 16, 3) + ) + linesTest( + listOf(Lane.DotsRight), + listOf(Lane.OuterRight, Lane.MiddleRight, Lane.InnerRight, Lane.InnerLeft, Lane.MiddleLeft, Lane.OuterLeft), + intArrayOf(2, 1, 126), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 1, 126, 142, 16, 3) + ) + linesTest( + listOf(), + listOf(), + intArrayOf(2, 0, 0), + intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 0, 0, 13, 16, 3) + ) + } + + @Test + fun directionTest() { + directionTest( + OutAngle.RightDown, + intArrayOf(1, 32, 0, 0), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 32, 0, 0, 236, 16, 3) + ) + directionTest( + OutAngle.SharpRight, + intArrayOf(1, 1, 0, 2), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 2, 9, 16, 3) + ) + directionTest( + OutAngle.Right, + intArrayOf(1, 1, 0, 4), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 4, 7, 16, 3) + ) + directionTest( + OutAngle.EasyRight, + intArrayOf(1, 1, 0, 8), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 8, 3, 16, 3) + ) + directionTest( + OutAngle.Straight, + intArrayOf(1, 1, 0, 16), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 16, 16, 251, 16, 3) + ) + directionTest( + OutAngle.EasyLeft, + intArrayOf(1, 1, 0, 32), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 32, 235, 16, 3) + ) + directionTest( + OutAngle.Left, + intArrayOf(1, 1, 0, 64), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 64, 203, 16, 3) + ) + directionTest( + OutAngle.SharpLeft, + intArrayOf(1, 1, 0, 128), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 128, 139, 16, 3) + ) + directionTest( + OutAngle.LeftDown, + intArrayOf(1, 16, 0, 0), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 16, 16, 0, 0, 252, 16, 3) + ) + directionTest( + OutAngle.Down, + intArrayOf(1, 1, 0, 1), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 1, 10, 16, 3) + ) + directionTest( + OutAngle.SharpRight, OutType.LongerLane, + intArrayOf(1, 2, 0, 2), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 2, 8, 16, 3) + ) + directionTest( + OutAngle.Right, OutType.LongerLane, + intArrayOf(1, 2, 0, 4), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 4, 6, 16, 3) + ) + directionTest( + OutAngle.EasyRight, OutType.LongerLane, + intArrayOf(1, 2, 0, 8), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 8, 2, 16, 3) + ) + directionTest( + OutAngle.Straight, OutType.LongerLane, + intArrayOf(1, 2, 0, 16), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 16, 16, 250, 16, 3) + ) + directionTest( + OutAngle.EasyLeft, OutType.LongerLane, + intArrayOf(1, 2, 0, 32), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 32, 234, 16, 3) + ) + directionTest( + OutAngle.Left, OutType.LongerLane, + intArrayOf(1, 2, 0, 64), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 64, 202, 16, 3) + ) + directionTest( + OutAngle.SharpLeft, OutType.LongerLane, + intArrayOf(1, 2, 0, 128), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 128, 138, 16, 3) + ) + directionTest( + OutAngle.SharpRight, OutType.RightRoundabout, + intArrayOf(1, 8, 2, 2), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 2, 2, 0, 16, 3) + ) + directionTest( + OutAngle.Right, OutType.RightRoundabout, + intArrayOf(1, 8, 4, 4), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 4, 4, 252, 16, 3) + ) + directionTest( + OutAngle.EasyRight, OutType.RightRoundabout, + intArrayOf(1, 8, 8, 8), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 8, 8, 244, 16, 3) + ) + directionTest( + OutAngle.Straight, OutType.RightRoundabout, + intArrayOf(1, 8, 16, 16), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 16, 16, 16, 16, 228, 16, 3) + ) + directionTest( + OutAngle.EasyLeft, OutType.RightRoundabout, + intArrayOf(1, 8, 32, 32), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 32, 32, 196, 16, 3) + ) + directionTest( + OutAngle.Left, OutType.RightRoundabout, + intArrayOf(1, 8, 64, 64), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 64, 64, 132, 16, 3) + ) + directionTest( + OutAngle.SharpLeft, OutType.RightRoundabout, + intArrayOf(1, 8, 128, 128), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 128, 128, 4, 16, 3) + ) + directionTest( + OutAngle.Left, OutType.Flag, + intArrayOf(1, 64, 0, 64), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 64, 0, 64, 140, 16, 3) + ) + directionTest( + OutAngle.Right, OutType.Flag, + intArrayOf(1, 64, 0, 4), + intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 64, 0, 4, 200, 16, 3) + ) + } + + @Test + fun distanceTest() { + // TODO + intArrayOf(3, 0, 9, 9, 0, 9, 0) + intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 0, 233, 16, 3) + intArrayOf(3, 0, 9, 9, 0, 9, 3) + intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 3, 230, 16, 3) + intArrayOf(3, 0, 9, 9, 0, 9, 1) + intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 1, 232, 16, 3) + intArrayOf(3, 0, 9, 9, 0, 9, 8) + intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 8, 225, 16, 3) + intArrayOf(3, 0, 9, 9, 0, 9, 5) + intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 5, 228, 16, 3) + } + + @Test + fun speedTest() { + // TODO + intArrayOf(7, 1) + intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 7, 1, 9, 16, 3) + intArrayOf(6, 0, 0, 0, 0, 0, 5, 10, 0, 0) + intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 0, 0, 0, 0, 5, 10, 0, 0, 236, 16, 3) + intArrayOf(6, 0, 5, 10, 255, 1, 10, 10, 0, 0) + intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 5, 10, 255, 1, 10, 10, 0, 0, 216, 16, 3) + intArrayOf(6, 1, 5, 10, 255, 1, 10, 10, 255, 0) + intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 1, 5, 10, 255, 1, 10, 10, 255, 0, 216, 16, 3) + intArrayOf(6, 0, 5, 10, 255, 1, 10, 10, 0, 255) + intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 5, 10, 255, 1, 10, 10, 0, 255, 217, 16, 3) + } + + @Test + fun timeTest() { + // TODO + intArrayOf(5, 0, 2, 2, 255, 2, 2, 0, 0) + intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 0, 2, 2, 255, 2, 2, 0, 0, 247, 16, 3) + intArrayOf(5, 255, 2, 2, 255, 2, 2, 0, 0) + intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 255, 2, 2, 255, 2, 2, 0, 0, 248, 16, 3) + intArrayOf(5, 0, 2, 2, 255, 2, 2, 0, 255) + intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 0, 2, 2, 255, 2, 2, 0, 255, 248, 16, 3) + } + + @Test + fun controlTest() { + // TODO + intArrayOf(4, 1) + intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 4, 1, 12, 16, 3) + intArrayOf(4, 0) + intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 4, 0, 13, 16, 3) + } + + private fun linesTest(outlines: Lane, arrows: Lane, expectedRaw: IntArray, expectedBoxed: IntArray) { + linesTest(listOf(outlines), listOf(arrows), expectedRaw, expectedBoxed) + } + + private fun linesTest(outlines: List, arrows: List, expectedRaw: IntArray, expectedBoxed: IntArray) { + val state = State() + state.lineOutlines = outlines + state.lineArrows = arrows + makeAssertions(GarminMapper.setLines(state), expectedRaw, expectedBoxed) + } + + private fun directionTest(outAngle: OutAngle, expectedRaw: IntArray, expectedBoxed: IntArray) { + directionTest(outAngle, OutType.Lane, expectedRaw, expectedBoxed) + } + + private fun directionTest(outAngle: OutAngle, outType: OutType, expectedRaw: IntArray, expectedBoxed: IntArray) { + val state = State() + state.direction.angle = outAngle + state.direction.out = outType + makeAssertions(GarminMapper.setDirection(state), expectedRaw, expectedBoxed) + } + + private fun makeAssertions(resultRaw: IntArray, expectedRaw: IntArray, expectedBoxed: IntArray) { + assertThat(resultRaw).containsExactly(expectedRaw.toTypedArray()) + val resultBoxed = Garmin.prepareData(resultRaw) + assertThat(resultBoxed).containsExactly(expectedBoxed.toTypedArray()) + } + +} \ No newline at end of file diff --git a/python/test.py b/python/test.py index 2808632..68addc9 100644 --- a/python/test.py +++ b/python/test.py @@ -11,9 +11,11 @@ def suite(controller: Controller): speed(controller) time(controller) control(controller) + compass(controller) def lines(controller: Controller): + print("Lines") controller.set_lines([Lane.DotsLeft], []) controller.set_lines([Lane.OuterRight], [Lane.OuterLeft]) controller.set_lines([Lane.MiddleRight], [Lane.MiddleLeft]) @@ -29,6 +31,7 @@ def lines(controller: Controller): def direction(controller: Controller): + print("Direction") controller.set_direction(OutAngle.RightDown) controller.set_direction(OutAngle.SharpRight) controller.set_direction(OutAngle.Right) @@ -58,6 +61,7 @@ def direction(controller: Controller): def distance(controller: Controller): + print("Distance") controller.set_distance(999) controller.set_distance(999, Unit.Kilometres) controller.set_distance(999, Unit.Metres) @@ -67,6 +71,7 @@ def distance(controller: Controller): def speed(controller: Controller): + print("Speed") controller.set_gps(True) controller.set_speed(50) controller.set_speed(50, 100) @@ -76,6 +81,7 @@ def speed(controller: Controller): def time(controller: Controller): + print("Time") controller.set_time(22, 22) controller.set_time(22, 22, traffic=True) controller.set_time(22, 22, flag=True) @@ -83,11 +89,13 @@ def time(controller: Controller): def control(controller: Controller): + print("Speed Control") controller.set_speed_control() controller.set_speed_control(False) def compass(controller: Controller): + print("Compass") controller.set_compass(22.5) controller.set_compass(67.5) controller.set_compass(112.5) From b5cc580cf81c2ee294fcf1cd82209de90cd6d592 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 25 Aug 2023 08:25:06 +0200 Subject: [PATCH 7/8] Data cache --- app/src/main/java/eu/ztsh/garmin/Garmin.kt | 34 +++++----- .../main/java/eu/ztsh/garmin/MapboxToolbox.kt | 24 ++++++- .../java/eu/ztsh/garmin/data/DataCache.kt | 66 ++++++++++++++++++ .../eu/ztsh/garmin/data/ManeuverMapper.kt | 26 ++++--- .../main/java/eu/ztsh/garmin/data/Model.kt | 68 +++++++++++++++---- 5 files changed, 178 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/eu/ztsh/garmin/data/DataCache.kt diff --git a/app/src/main/java/eu/ztsh/garmin/Garmin.kt b/app/src/main/java/eu/ztsh/garmin/Garmin.kt index d6b3113..e9836d5 100644 --- a/app/src/main/java/eu/ztsh/garmin/Garmin.kt +++ b/app/src/main/java/eu/ztsh/garmin/Garmin.kt @@ -6,17 +6,13 @@ import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothSocket import android.util.Log import com.mapbox.navigation.ui.maneuver.model.Maneuver -import eu.ztsh.garmin.data.Direction +import eu.ztsh.garmin.data.DataCache import eu.ztsh.garmin.data.GarminMapper import eu.ztsh.garmin.data.ManeuverMapper -import eu.ztsh.garmin.data.OutAngle -import eu.ztsh.garmin.data.OutType -import eu.ztsh.garmin.data.State import java.io.IOException import java.util.* import java.util.concurrent.SynchronousQueue - @SuppressLint("MissingPermission") class Garmin( val context: MainActivity, @@ -26,7 +22,7 @@ class Garmin( private lateinit var connection: ConnectThread private lateinit var processing: ProcessingThread - private var stateCache: State = State() + private val cache = DataCache() fun start() { connection = ConnectThread() @@ -46,22 +42,24 @@ class Garmin( private inner class ProcessingThread(val maneuver: Maneuver) : Thread() { + override fun run() { - // TODO: check for equality before mapping! - send(ManeuverMapper.apply(maneuver)) + if (cache.hasChanged(maneuver)) { + cache.update(maneuver) + send(ManeuverMapper.apply(maneuver)) + } } fun send(incoming: eu.ztsh.garmin.data.State) { - if (stateCache.distance != incoming.distance) { + if (cache.hasChanged(incoming.distance)) { setDistance(incoming) } - if (stateCache.direction != incoming.direction) { - setDirection(stateCache) + if (cache.hasChanged(incoming.direction)) { + setDirection(incoming) } - stateCache = incoming + cache.update(incoming) } - private fun setLines(state: eu.ztsh.garmin.data.State) { } @@ -94,10 +92,13 @@ class Garmin( sleep(3000) readAll() while (true) { - val newCurrent = Optional.ofNullable(queue.poll()).orElse(current) - current = newCurrent + val newCurrent = queue.poll() + if (newCurrent == null) { + sleep(500) + } else { + current = newCurrent + } send(current) - sleep(900) } } @@ -138,6 +139,7 @@ class Garmin( } companion object { + fun prepareData(input: IntArray): IntArray { val n = input.size var crc = (0xeb + n + n).toUInt() diff --git a/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt index 7afeffe..8711206 100644 --- a/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt +++ b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt @@ -1,6 +1,8 @@ package eu.ztsh.garmin import android.annotation.SuppressLint +import android.location.Location +import android.util.Log import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner @@ -8,6 +10,8 @@ import com.mapbox.navigation.base.formatter.DistanceFormatterOptions import com.mapbox.navigation.base.options.NavigationOptions import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp +import com.mapbox.navigation.core.trip.session.LocationMatcherResult +import com.mapbox.navigation.core.trip.session.LocationObserver import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi @@ -43,10 +47,13 @@ class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) { fun onStart() { MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver) + MapboxNavigationApp.current()?.registerLocationObserver(locationObserver) +// MapboxNavigationApp.current()?.registerNavigationSessionStateObserver() } fun onStop() { MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver) + MapboxNavigationApp.current()?.unregisterLocationObserver(locationObserver) } fun onDestroy() { @@ -64,6 +71,21 @@ class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) { } private val routeProgressObserver = - RouteProgressObserver { routeProgress -> maneuverApi.getManeuvers(routeProgress).value?.apply { context.garmin.process(this[0]) } } + RouteProgressObserver { routeProgress -> + maneuverApi.getManeuvers(routeProgress).value?.apply { + context.garmin.process( + this[0] + ) + } + } + + private val locationObserver = object : LocationObserver { + override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) { + Log.d("LOCATION", "") + } + + override fun onNewRawLocation(rawLocation: Location) { + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt b/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt new file mode 100644 index 0000000..1b7eb16 --- /dev/null +++ b/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt @@ -0,0 +1,66 @@ +package eu.ztsh.garmin.data + +import com.mapbox.navigation.ui.maneuver.model.Maneuver + +class DataCache { + + private val stateCache: State = State() + private var maneuverCache: Maneuver? = null + + // state + fun hasChanged(lanes: Lanes?): Boolean { + return stateCache.lineArrows == null || stateCache.lineArrows != lanes + } + + fun hasChanged(outlines: Outlines?): Boolean { + return stateCache.lineOutlines == null || stateCache.lineOutlines != outlines + } + + fun hasChanged(distance: Distance?): Boolean { + return stateCache.distance == null || stateCache.distance != distance + } + + fun hasChanged(direction: Direction?): Boolean { + return stateCache.direction == null || stateCache.direction != direction + } + + fun hasChanged(speed: Speed?): Boolean { + return stateCache.speed == null || stateCache.speed != speed + } + + fun hasChanged(arrival: Arrival?): Boolean { + return stateCache.arrival == null || stateCache.arrival != arrival + } + + // Merge states + fun update(state: State) { + if (state.lineArrows != null) { + stateCache.lineArrows = state.lineArrows + } + if (state.lineOutlines != null) { + state.lineOutlines = state.lineOutlines + } + if (state.direction != null) { + stateCache.direction = state.direction + } + if (state.distance != null) { + stateCache.distance = state.distance + } + if (state.speed != null) { + stateCache.speed = state.speed + } + if (state.arrival != null) { + stateCache.arrival = state.arrival + } + } + + // maneuver + fun hasChanged(maneuver: Maneuver): Boolean { + return maneuverCache == null || maneuverCache!! != maneuver + } + + fun update(maneuver: Maneuver) { + maneuverCache = maneuver + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt b/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt index 89f1996..fe75879 100644 --- a/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt @@ -10,10 +10,12 @@ class ManeuverMapper { val state = State() maneuver.apply { this.primary.apply { + state.direction = Direction() when (this.type) { "roundabout" -> { - state.direction.out = OutType.RightRoundabout + state.direction!!.out = OutType.RightRoundabout } + "arrive" -> { state.flag = true } @@ -21,17 +23,18 @@ class ManeuverMapper { when (this.modifier) { "right" -> { when (this.type) { - "turn" -> state.direction.angle = OutAngle.Right + "turn" -> state.direction!!.angle = OutAngle.Right "roundabout" -> { when (this.degrees) { - 137.0 -> state.direction.angle = OutAngle.EasyRight + 137.0 -> state.direction!!.angle = OutAngle.EasyRight } } } } + "left" -> { when (this.type) { - "turn" -> state.direction.angle = OutAngle.Left + "turn" -> state.direction!!.angle = OutAngle.Left } } } @@ -39,13 +42,14 @@ class ManeuverMapper { this.stepDistance.apply { this.distanceRemaining?.apply { distanceFormatter.formatDistance(distanceRemaining!!).split(" ").apply { - state.distance = this[0].toInt() - state.unit = when (this[1]) { - "m" -> Unit.Metres - "km" -> Unit.Kilometres - else -> { - Unit.Any} - } + state.distance = Distance( + this[0].toInt(), + when (this[1]) { + "m" -> Unit.Metres + "km" -> Unit.Kilometres + else -> Unit.Any + } + ) } } diff --git a/app/src/main/java/eu/ztsh/garmin/data/Model.kt b/app/src/main/java/eu/ztsh/garmin/data/Model.kt index 9092bbb..e4a8262 100644 --- a/app/src/main/java/eu/ztsh/garmin/data/Model.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/Model.kt @@ -52,20 +52,64 @@ enum class Lane(val value: Int) { } +open class Arrows(val lanes: List) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Arrows + + return lanes == other.lanes + } + + override fun hashCode(): Int { + return lanes.hashCode() + } +} + +class Lanes(lanes: List) : Arrows(lanes) + +class Outlines(lanes: List) : Arrows(lanes) + +class Distance(val distance: Int, val unit: Unit) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Distance + + if (distance != other.distance) return false + if (unit != other.unit) return false + + return true + } + + override fun hashCode(): Int { + var result = distance + result = 31 * result + unit.hashCode() + return result + } + +} + +class Speed(val speed: Int, val limit: Int) + +class Arrival(val hours: Int, val minutes: Int) + class State { - var lineArrows: List = listOf() - var lineOutlines: List = listOf() - var direction = Direction() - var distance: Int = 0 - var unit: Unit = Unit.Any - var speed: Int = 0 - var limit: Int = 0 - var hours: Int = 0 - var minutes: Int = 0 - var traffic: Boolean = false - var flag: Boolean = false - var control: Boolean = false + var lineArrows: Lanes? = null + var lineOutlines: Outlines? = null + var direction : Direction? = null + var distance: Distance? = null + var speed: Speed? = null + var arrival: Arrival? = null + // TODO: support + var traffic: Boolean? = null + var flag: Boolean? = null + var control: Boolean? = null } From 9a553cf563ae8b4a1e60de5425ed8c077ac7918d Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 25 Aug 2023 14:34:43 +0200 Subject: [PATCH 8/8] Navigation state & location observer --- app/src/main/java/eu/ztsh/garmin/Garmin.kt | 50 ++++++++++++------- .../main/java/eu/ztsh/garmin/MapboxToolbox.kt | 9 +++- .../java/eu/ztsh/garmin/data/DataCache.kt | 22 ++++++++ .../{ManeuverMapper.kt => MapboxMapper.kt} | 9 +++- .../main/java/eu/ztsh/garmin/data/Model.kt | 1 + 5 files changed, 70 insertions(+), 21 deletions(-) rename app/src/main/java/eu/ztsh/garmin/data/{ManeuverMapper.kt => MapboxMapper.kt} (88%) diff --git a/app/src/main/java/eu/ztsh/garmin/Garmin.kt b/app/src/main/java/eu/ztsh/garmin/Garmin.kt index e9836d5..62555be 100644 --- a/app/src/main/java/eu/ztsh/garmin/Garmin.kt +++ b/app/src/main/java/eu/ztsh/garmin/Garmin.kt @@ -5,10 +5,12 @@ import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothSocket import android.util.Log +import com.mapbox.navigation.core.trip.session.LocationMatcherResult +import com.mapbox.navigation.core.trip.session.NavigationSessionState import com.mapbox.navigation.ui.maneuver.model.Maneuver import eu.ztsh.garmin.data.DataCache import eu.ztsh.garmin.data.GarminMapper -import eu.ztsh.garmin.data.ManeuverMapper +import eu.ztsh.garmin.data.MapboxMapper import java.io.IOException import java.util.* import java.util.concurrent.SynchronousQueue @@ -34,44 +36,56 @@ class Garmin( } fun process(maneuver: Maneuver) { - processing = ProcessingThread(maneuver) + processing = ManeuverProcessingThread(maneuver) processing.start() processing.join() } + fun process(location: LocationMatcherResult) { + processing = LocationProcessingThread(location) + processing.start() + processing.join() + } - private inner class ProcessingThread(val maneuver: Maneuver) : Thread() { + fun process(navigationSessionState: NavigationSessionState) { + cache.update(navigationSessionState) + } + + private inner class ManeuverProcessingThread(val maneuver: Maneuver) : ProcessingThread() { override fun run() { if (cache.hasChanged(maneuver)) { cache.update(maneuver) - send(ManeuverMapper.apply(maneuver)) + send(MapboxMapper.apply(maneuver)) } } + } + + private inner class LocationProcessingThread(val location: LocationMatcherResult) : ProcessingThread() { + + override fun run() { + if (cache.hasChanged(location)) { + cache.update(location) + send(MapboxMapper.apply(location)) + } + } + + } + + private open inner class ProcessingThread : Thread() { + fun send(incoming: eu.ztsh.garmin.data.State) { if (cache.hasChanged(incoming.distance)) { - setDistance(incoming) + connection.enqueue(GarminMapper.setDistance(incoming)) } if (cache.hasChanged(incoming.direction)) { - setDirection(incoming) + connection.enqueue(GarminMapper.setDirection(incoming)) } cache.update(incoming) } - private fun setLines(state: eu.ztsh.garmin.data.State) { - - } - - private fun setDistance(state: eu.ztsh.garmin.data.State) { - connection.enqueue(GarminMapper.setDistance(state)) - } - - private fun setDirection(direction: eu.ztsh.garmin.data.State) { - connection.enqueue(GarminMapper.setDirection(direction)) - } - } private inner class ConnectThread : Thread() { diff --git a/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt index 8711206..5dabf1e 100644 --- a/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt +++ b/app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt @@ -12,6 +12,8 @@ import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp import com.mapbox.navigation.core.trip.session.LocationMatcherResult import com.mapbox.navigation.core.trip.session.LocationObserver +import com.mapbox.navigation.core.trip.session.NavigationSessionState +import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi @@ -48,12 +50,13 @@ class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) { fun onStart() { MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver) MapboxNavigationApp.current()?.registerLocationObserver(locationObserver) -// MapboxNavigationApp.current()?.registerNavigationSessionStateObserver() + MapboxNavigationApp.current()?.registerNavigationSessionStateObserver(navigationStateObserver) } fun onStop() { MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver) MapboxNavigationApp.current()?.unregisterLocationObserver(locationObserver) + MapboxNavigationApp.current()?.unregisterNavigationSessionStateObserver(navigationStateObserver) } fun onDestroy() { @@ -81,11 +84,13 @@ class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) { private val locationObserver = object : LocationObserver { override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) { - Log.d("LOCATION", "") + context.garmin.process(locationMatcherResult) } override fun onNewRawLocation(rawLocation: Location) { } } + private val navigationStateObserver = NavigationSessionStateObserver { context.garmin.process(it) } + } \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt b/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt index 1b7eb16..6b5622b 100644 --- a/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/DataCache.kt @@ -1,11 +1,15 @@ package eu.ztsh.garmin.data +import com.mapbox.navigation.core.trip.session.LocationMatcherResult +import com.mapbox.navigation.core.trip.session.NavigationSessionState import com.mapbox.navigation.ui.maneuver.model.Maneuver class DataCache { private val stateCache: State = State() private var maneuverCache: Maneuver? = null + private var locationCache: LocationMatcherResult? = null + private var session: NavigationSessionState? = null // state fun hasChanged(lanes: Lanes?): Boolean { @@ -63,4 +67,22 @@ class DataCache { maneuverCache = maneuver } + // location + fun hasChanged(locationMatcherResult: LocationMatcherResult): Boolean { + return locationCache == null || locationCache!! != locationMatcherResult + } + + fun update(locationMatcherResult: LocationMatcherResult) { + locationCache = locationMatcherResult + } + + // session + fun isActive(): Boolean { + return session != null && session is NavigationSessionState.ActiveGuidance + } + + fun update(sessionState: NavigationSessionState) { + session = sessionState + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt b/app/src/main/java/eu/ztsh/garmin/data/MapboxMapper.kt similarity index 88% rename from app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt rename to app/src/main/java/eu/ztsh/garmin/data/MapboxMapper.kt index fe75879..22fc742 100644 --- a/app/src/main/java/eu/ztsh/garmin/data/ManeuverMapper.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/MapboxMapper.kt @@ -1,8 +1,9 @@ package eu.ztsh.garmin.data +import com.mapbox.navigation.core.trip.session.LocationMatcherResult import com.mapbox.navigation.ui.maneuver.model.Maneuver -class ManeuverMapper { +class MapboxMapper { companion object { @@ -59,6 +60,12 @@ class ManeuverMapper { return state } + fun apply(locationMatcherResult: LocationMatcherResult): State { + val state = State() + // TODO: speed, limit, location?, bearing + return state + } + } } diff --git a/app/src/main/java/eu/ztsh/garmin/data/Model.kt b/app/src/main/java/eu/ztsh/garmin/data/Model.kt index e4a8262..146d4ed 100644 --- a/app/src/main/java/eu/ztsh/garmin/data/Model.kt +++ b/app/src/main/java/eu/ztsh/garmin/data/Model.kt @@ -106,6 +106,7 @@ class State { var distance: Distance? = null var speed: Speed? = null var arrival: Arrival? = null + // TODO: Bearing // TODO: support var traffic: Boolean? = null var flag: Boolean? = null