feat: add LocoTypeUtil
This commit is contained in:
142
app/src/main/assets/loco_number_info.csv
Normal file
142
app/src/main/assets/loco_number_info.csv
Normal file
@@ -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
|
||||||
|
@@ -110,16 +110,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
private var historyScrollPosition by mutableStateOf(0)
|
private var historyScrollPosition by mutableStateOf(0)
|
||||||
private var historyScrollOffset by mutableStateOf(0)
|
private var historyScrollOffset by mutableStateOf(0)
|
||||||
private var historyCardMapStates by mutableStateOf<Map<String, CardMapView>>(emptyMap())
|
private var historyCardMapStates by mutableStateOf<Map<String, CardMapView>>(emptyMap())
|
||||||
|
private var settingsScrollPosition by mutableStateOf(0)
|
||||||
private var mapCenterPosition by mutableStateOf<Pair<Double, Double>?>(null)
|
private var mapCenterPosition by mutableStateOf<Pair<Double, Double>?>(null)
|
||||||
private var mapZoomLevel by mutableStateOf(10.0)
|
private var mapZoomLevel by mutableStateOf(10.0)
|
||||||
private var mapRailwayLayerVisible by mutableStateOf(true)
|
private var mapRailwayLayerVisible by mutableStateOf(true)
|
||||||
|
|
||||||
private var settingsScrollPosition by mutableStateOf(0)
|
|
||||||
|
|
||||||
private var mergeSettings by mutableStateOf(MergeSettings())
|
private var mergeSettings by mutableStateOf(MergeSettings())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private var targetDeviceName = "LBJReceiver"
|
private var targetDeviceName = "LBJReceiver"
|
||||||
private var specifiedDeviceAddress by mutableStateOf<String?>(null)
|
private var specifiedDeviceAddress by mutableStateOf<String?>(null)
|
||||||
private var searchOrderList by mutableStateOf(listOf<String>())
|
private var searchOrderList by mutableStateOf(listOf<String>())
|
||||||
@@ -185,10 +183,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
TrainRecord.initializeLocoTypeUtil(this)
|
||||||
|
|
||||||
loadSettings()
|
loadSettings()
|
||||||
|
|
||||||
|
|
||||||
val permissions = mutableListOf<String>()
|
val permissions = mutableListOf<String>()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
@@ -199,99 +197,23 @@ class MainActivity : ComponentActivity() {
|
|||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
permissions.addAll(arrayOf(
|
permissions.addAll(arrayOf(
|
||||||
Manifest.permission.BLUETOOTH,
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
Manifest.permission.BLUETOOTH_ADMIN
|
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions.addAll(arrayOf(
|
if (permissions.isNotEmpty()) {
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
requestPermissions.launch(permissions.toTypedArray())
|
||||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
} else {
|
||||||
))
|
startAutoScanAndConnect()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
permissions.add(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestPermissions.launch(permissions.toTypedArray())
|
Configuration.getInstance().userAgentValue = packageName
|
||||||
|
|
||||||
|
|
||||||
bleClient.setTrainInfoCallback { jsonData ->
|
bleClient.setTrainInfoCallback { jsonData ->
|
||||||
handleTrainInfo(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 {
|
setContent {
|
||||||
LBJConsoleTheme {
|
LBJConsoleTheme {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -334,7 +256,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
Log.d(TAG, "Auto connect enabled: $enabled")
|
Log.d(TAG, "Auto connect enabled: $enabled")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
latestRecord = latestRecord,
|
latestRecord = latestRecord,
|
||||||
recentRecords = recentRecords,
|
recentRecords = recentRecords,
|
||||||
lastUpdateTime = lastUpdateTime,
|
lastUpdateTime = lastUpdateTime,
|
||||||
@@ -344,10 +265,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
},
|
},
|
||||||
onClearMonitorLog = {
|
onClearMonitorLog = {
|
||||||
recentRecords.clear()
|
recentRecords.clear()
|
||||||
|
latestRecord = null
|
||||||
|
lastUpdateTime = null
|
||||||
temporaryStatusMessage = null
|
temporaryStatusMessage = null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
allRecords = trainRecordManager.getMixedRecords(),
|
allRecords = trainRecordManager.getMixedRecords(),
|
||||||
mergedRecords = trainRecordManager.getMergedRecords(),
|
mergedRecords = trainRecordManager.getMergedRecords(),
|
||||||
recordCount = trainRecordManager.getRecordCount(),
|
recordCount = trainRecordManager.getRecordCount(),
|
||||||
@@ -499,7 +421,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,7 +683,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val settings = appSettingsRepository.getSettings()
|
val settings = appSettingsRepository.getSettings()
|
||||||
|
|
||||||
settingsDeviceName = settings.deviceName
|
settingsDeviceName = settings.deviceName
|
||||||
targetDeviceName = settingsDeviceName
|
targetDeviceName = settings.deviceName
|
||||||
currentTab = settings.currentTab
|
currentTab = settings.currentTab
|
||||||
historyEditMode = settings.historyEditMode
|
historyEditMode = settings.historyEditMode
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
package org.noxylva.lbjconsole.model
|
package org.noxylva.lbjconsole.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import org.osmdroid.util.GeoPoint
|
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) {
|
class TrainRecord(jsonData: JSONObject? = null) {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "TrainRecord"
|
const val TAG = "TrainRecord"
|
||||||
private var nextId = 0L
|
private var nextId = 0L
|
||||||
|
private var LocoTypeUtil: LocoTypeUtil? = null
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun generateUniqueId(): String {
|
private fun generateUniqueId(): String {
|
||||||
return "${System.currentTimeMillis()}_${++nextId}"
|
return "${System.currentTimeMillis()}_${++nextId}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun initializeLocoTypeUtil(context: Context) {
|
||||||
|
if (LocoTypeUtil == null) {
|
||||||
|
LocoTypeUtil = LocoTypeUtil(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val uniqueId: String
|
val uniqueId: String
|
||||||
@@ -75,20 +84,26 @@ class TrainRecord(jsonData: JSONObject? = null) {
|
|||||||
position = jsonData.optString("pos", "")
|
position = jsonData.optString("pos", "")
|
||||||
time = jsonData.optString("time", "")
|
time = jsonData.optString("time", "")
|
||||||
loco = jsonData.optString("loco", "")
|
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", "")
|
lbjClass = jsonData.optString("lbj_class", "")
|
||||||
route = jsonData.optString("route", "")
|
route = jsonData.optString("route", "")
|
||||||
positionInfo = jsonData.optString("position_info", "")
|
positionInfo = jsonData.optString("position_info", "")
|
||||||
rssi = jsonData.optDouble("rssi", 0.0)
|
rssi = jsonData.optDouble("rssi", 0.0)
|
||||||
|
|
||||||
|
|
||||||
_coordinates = null
|
_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) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "JSON parse error: ${e.message}", e)
|
Log.e(TAG, "JSON parse error: ${e.message}", e)
|
||||||
|
|
||||||
|
|
||||||
try { train = jsonData.optString("train", "") } catch (e: Exception) { }
|
try { train = jsonData.optString("train", "") } catch (e: Exception) { }
|
||||||
try { direction = jsonData.optInt("dir", 0) } catch (e: Exception) { }
|
try { direction = jsonData.optInt("dir", 0) } catch (e: Exception) { }
|
||||||
try { speed = jsonData.optString("speed", "") } 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
|
return _coordinates
|
||||||
}
|
}
|
||||||
private fun isValidValue(value: String): Boolean {
|
private fun isValidValue(value: String): Boolean {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import android.util.Log
|
|||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
|
|
||||||
|
|
||||||
object LocationUtils {
|
object LocationUtil {
|
||||||
private const val TAG = "LocationUtils"
|
private const val TAG = "LocationUtil"
|
||||||
|
|
||||||
|
|
||||||
fun parsePositionInfo(positionInfo: String): GeoPoint? {
|
fun parsePositionInfo(positionInfo: String): GeoPoint? {
|
||||||
@@ -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<String, String>()
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user