From 39effddfc1956fd19cee434eaf390b86ba33282c Mon Sep 17 00:00:00 2001 From: Nedifinita Date: Tue, 19 Aug 2025 16:35:47 +0800 Subject: [PATCH] feat: add LocoTypeUtil --- app/src/main/assets/loco_number_info.csv | 142 ++++++++++++++++++ .../org/noxylva/lbjconsole/MainActivity.kt | 103 ++----------- .../noxylva/lbjconsole/model/TrainRecord.kt | 27 +++- .../{LocationUtils.kt => LocationUtil.kt} | 4 +- .../noxylva/lbjconsole/util/LocoTypeUtil.kt | 42 ++++++ 5 files changed, 219 insertions(+), 99 deletions(-) create mode 100644 app/src/main/assets/loco_number_info.csv rename app/src/main/java/org/noxylva/lbjconsole/util/{LocationUtils.kt => LocationUtil.kt} (96%) create mode 100644 app/src/main/java/org/noxylva/lbjconsole/util/LocoTypeUtil.kt diff --git a/app/src/main/assets/loco_number_info.csv b/app/src/main/assets/loco_number_info.csv new file mode 100644 index 0000000..347e896 --- /dev/null +++ b/app/src/main/assets/loco_number_info.csv @@ -0,0 +1,142 @@ +001,解放 +003,前进 +005,建设 +006,KD7 +055,蓝箭控车 +081,东风21 +101,东风 +102,东风2 +103,东风3 +104,东风4 +105,东风4客 +106,东风4C +107,东风5 +108,东风5宽 +109,东风6 +110,东风7 +111,东风8 +112,东风9 +113,东风10 +114,东方红1 +115,东方红2 +116,东方红3 +117,东方红5 +118,北京 +119,北京宽 +120,ND2 +121,ND3 +122,ND4 +123,ND5 +124,NY5 +125,NY6 +126,NY7 +127,轻油 +128,东方红21 +129,东风7B +130,东风5S +131,东风7C +132,东风7S +133,工矿1 +134,工矿1F +135,东风4E +136,东风7D +137,工矿1A +138,东风11 +139,天安 +140,东风10F +141,东风4D +142,东风8B +143,东风12 +144,东风7E +145,NYJ1 +146,NZJ1 +147,NZJ2 +148,东风4DJ +149,新曙光 +150,神州 +151,NJ2 +152,东风7G +153,NDJ3 +157,FXN3D +158,东风11G +160,HXN3 +161,HXN5 +162,HXN3B +163,HXN5B +167,FXN3B +169,FXN3C +170,FXN5C +171,FXN3-J +201,8G +202,8K +203,6G +204,6K +205,韶山1 +206,韶山3 +207,韶山4 +208,韶山5 +209,韶山6 +210,韶山3B +211,韶山7 +212,韶山8 +213,韶山7B +214,韶山7C +215,韶山6B +216,韶山9 +217,韶山7D +218,DJ熊猫 +219,DJ1 +220,DJ2 +221,DJF +222,蓝箭动车 +223,先锋号 +224,韶山7E +225,韶山4G +226,韶山3C +228,天梭 +229,DJ4和谐 +230,KTT +231,HXD1 +232,HXD2 +233,HXD3 +234,HXD1B +235,HXD2B +236,HXD3B +237,HXD1C +238,HXD2C +239,HXD3C +240,HXD1D +241,HXD2D +242,HXD3D +243,FXD1B +244,FXD2B +245,FXD1 +246,FXD3 +247,FXD1-J +248,FXD3-J +249,KZ25TA +251,KZ25TB +252,HXD1D-J +254,FXD1H +300,雪域神州 +301,CRH1 +302,CRH2 +303,CRH3 +305,CRH5 +306,CRH380A +307,CRH380B +308,CRH380C +309,CRH380D +310,CRH6A +311,CR400AF +312,CR400BF +313,CR300AF +314,CR300BF +315,CRH2E +316,CRH6F +330,CJ1 +331,CJ2 +332,CJ3 +333,CJ4 +334,CJ5 +335,CJ6 \ No newline at end of file diff --git a/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt b/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt index 8c820f1..e694686 100644 --- a/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt +++ b/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt @@ -110,15 +110,13 @@ class MainActivity : ComponentActivity() { private var historyScrollPosition by mutableStateOf(0) private var historyScrollOffset by mutableStateOf(0) private var historyCardMapStates by mutableStateOf>(emptyMap()) + private var settingsScrollPosition by mutableStateOf(0) private var mapCenterPosition by mutableStateOf?>(null) private var mapZoomLevel by mutableStateOf(10.0) private var mapRailwayLayerVisible by mutableStateOf(true) - private var settingsScrollPosition by mutableStateOf(0) - private var mergeSettings by mutableStateOf(MergeSettings()) - private var targetDeviceName = "LBJReceiver" private var specifiedDeviceAddress by mutableStateOf(null) @@ -185,10 +183,10 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + TrainRecord.initializeLocoTypeUtil(this) loadSettings() - val permissions = mutableListOf() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -199,99 +197,23 @@ class MainActivity : ComponentActivity() { )) } else { permissions.addAll(arrayOf( - Manifest.permission.BLUETOOTH, - Manifest.permission.BLUETOOTH_ADMIN + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION )) } - permissions.addAll(arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - )) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - permissions.add(Manifest.permission.POST_NOTIFICATIONS) + if (permissions.isNotEmpty()) { + requestPermissions.launch(permissions.toTypedArray()) + } else { + startAutoScanAndConnect() } - requestPermissions.launch(permissions.toTypedArray()) - + Configuration.getInstance().userAgentValue = packageName bleClient.setTrainInfoCallback { jsonData -> handleTrainInfo(jsonData) } - bleClient.setHighFrequencyReconnect(true) - bleClient.setConnectionLostCallback { - runOnUiThread { - deviceStatus = "连接丢失,正在重连..." - showDisconnectButton = false - if (showConnectionDialog) { - foundDevices = emptyList() - startScan() - } - } - } - - bleClient.setConnectionSuccessCallback { address -> - runOnUiThread { - deviceAddress = address - deviceStatus = "已连接" - showDisconnectButton = true - Log.d(TAG, "Connection success callback: address=$address") - } - } - - - lifecycleScope.launch { - try { - locoInfoUtil.loadLocoData() - Log.d(TAG, "Loaded locomotive data") - } catch (e: Exception) { - Log.e(TAG, "Load locomotive data failed", e) - } - - - } - - - - - try { - - val osmCacheDir = File(cacheDir, "osm").apply { mkdirs() } - val tileCache = File(osmCacheDir, "tiles").apply { mkdirs() } - - - Configuration.getInstance().apply { - userAgentValue = packageName - load(this@MainActivity, getSharedPreferences("osmdroid", Context.MODE_PRIVATE)) - osmdroidBasePath = osmCacheDir - osmdroidTileCache = tileCache - expirationOverrideDuration = 86400000L * 7 - tileDownloadThreads = 4 - tileFileSystemThreads = 4 - - setUserAgentValue("LBJConsole/1.0") - } - - Log.d(TAG, "OSM cache configured") - } catch (e: Exception) { - Log.e(TAG, "OSM cache config failed", e) - } - - saveSettings() - - lifecycleScope.launch { - if (SettingsActivity.isBackgroundServiceEnabled(this@MainActivity)) { - BackgroundService.startService(this@MainActivity) - } - } - - enableEdgeToEdge() - - WindowCompat.getInsetsController(window, window.decorView).apply { - isAppearanceLightStatusBars = false - } setContent { LBJConsoleTheme { val scope = rememberCoroutineScope() @@ -334,7 +256,6 @@ class MainActivity : ComponentActivity() { Log.d(TAG, "Auto connect enabled: $enabled") }, - latestRecord = latestRecord, recentRecords = recentRecords, lastUpdateTime = lastUpdateTime, @@ -344,10 +265,11 @@ class MainActivity : ComponentActivity() { }, onClearMonitorLog = { recentRecords.clear() + latestRecord = null + lastUpdateTime = null temporaryStatusMessage = null }, - allRecords = trainRecordManager.getMixedRecords(), mergedRecords = trainRecordManager.getMergedRecords(), recordCount = trainRecordManager.getRecordCount(), @@ -499,7 +421,6 @@ class MainActivity : ComponentActivity() { } } - } } } @@ -762,7 +683,7 @@ class MainActivity : ComponentActivity() { val settings = appSettingsRepository.getSettings() settingsDeviceName = settings.deviceName - targetDeviceName = settingsDeviceName + targetDeviceName = settings.deviceName currentTab = settings.currentTab historyEditMode = settings.historyEditMode diff --git a/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt index d3833f4..3a38845 100644 --- a/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt +++ b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt @@ -1,20 +1,29 @@ package org.noxylva.lbjconsole.model +import android.content.Context import android.util.Log import org.json.JSONObject import java.util.* import org.osmdroid.util.GeoPoint -import org.noxylva.lbjconsole.util.LocationUtils +import org.noxylva.lbjconsole.util.LocationUtil +import org.noxylva.lbjconsole.util.LocoTypeUtil class TrainRecord(jsonData: JSONObject? = null) { companion object { const val TAG = "TrainRecord" private var nextId = 0L + private var LocoTypeUtil: LocoTypeUtil? = null @Synchronized private fun generateUniqueId(): String { return "${System.currentTimeMillis()}_${++nextId}" } + + fun initializeLocoTypeUtil(context: Context) { + if (LocoTypeUtil == null) { + LocoTypeUtil = LocoTypeUtil(context) + } + } } val uniqueId: String @@ -75,20 +84,26 @@ class TrainRecord(jsonData: JSONObject? = null) { position = jsonData.optString("pos", "") time = jsonData.optString("time", "") loco = jsonData.optString("loco", "") - locoType = jsonData.optString("loco_type", "") + + // 不再直接从JSON获取loco_type,而是从loco字段前三位获取 + locoType = if (loco.isNotEmpty()) { + val prefix = if (loco.length >= 3) loco.take(3) else loco + LocoTypeUtil?.getLocoTypeByCode(prefix) ?: "" + } else { + "" + } + lbjClass = jsonData.optString("lbj_class", "") route = jsonData.optString("route", "") positionInfo = jsonData.optString("position_info", "") rssi = jsonData.optDouble("rssi", 0.0) - _coordinates = null - Log.d(TAG, "Successfully parsed: train=$train, dir=$direction, speed=$speed, lbjClass='$lbjClass'") + Log.d(TAG, "Successfully parsed: train=$train, dir=$direction, speed=$speed, lbjClass='$lbjClass', locoType='$locoType'") } catch (e: Exception) { Log.e(TAG, "JSON parse error: ${e.message}", e) - try { train = jsonData.optString("train", "") } catch (e: Exception) { } try { direction = jsonData.optInt("dir", 0) } catch (e: Exception) { } try { speed = jsonData.optString("speed", "") } catch (e: Exception) { } @@ -107,7 +122,7 @@ class TrainRecord(jsonData: JSONObject? = null) { } - _coordinates = LocationUtils.parsePositionInfo(positionInfo) + _coordinates = LocationUtil.parsePositionInfo(positionInfo) return _coordinates } private fun isValidValue(value: String): Boolean { diff --git a/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt b/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtil.kt similarity index 96% rename from app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt rename to app/src/main/java/org/noxylva/lbjconsole/util/LocationUtil.kt index 5ceea7a..119d202 100644 --- a/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt +++ b/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtil.kt @@ -4,8 +4,8 @@ import android.util.Log import org.osmdroid.util.GeoPoint -object LocationUtils { - private const val TAG = "LocationUtils" +object LocationUtil { + private const val TAG = "LocationUtil" fun parsePositionInfo(positionInfo: String): GeoPoint? { diff --git a/app/src/main/java/org/noxylva/lbjconsole/util/LocoTypeUtil.kt b/app/src/main/java/org/noxylva/lbjconsole/util/LocoTypeUtil.kt new file mode 100644 index 0000000..5929686 --- /dev/null +++ b/app/src/main/java/org/noxylva/lbjconsole/util/LocoTypeUtil.kt @@ -0,0 +1,42 @@ +package org.noxylva.lbjconsole.util + +import android.content.Context +import java.io.BufferedReader +import java.io.InputStreamReader + +class LocoTypeUtil(private val context: Context) { + private val locoTypeMap = mutableMapOf() + + init { + loadLocoTypeMapping() + } + + private fun loadLocoTypeMapping() { + try { + context.assets.open("loco_number_info.csv").use { inputStream -> + BufferedReader(InputStreamReader(inputStream)).use { reader -> + reader.lines().forEach { line -> + val parts = line.split(",") + if (parts.size >= 2) { + val code = parts[0].trim() + val type = parts[1].trim() + locoTypeMap[code] = type + } + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun getLocoTypeByCode(code: String): String? { + return locoTypeMap[code] + } + + fun getLocoTypeByLocoNumber(locoNumber: String): String? { + if (locoNumber.length < 3) return null + val prefix = locoNumber.take(3) + return getLocoTypeByCode(prefix) + } +} \ No newline at end of file