[6.9.2]AI大模型数据

This commit is contained in:
xuxinchao
2025-03-04 14:44:10 +08:00
parent 0d7c8165b3
commit 83cfab7fd9
13 changed files with 1641 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import com.mogo.aicloud.services.locationinfo.MogoLocationInfoServices
import com.mogo.aicloud.services.socket.IMogoLifecycleListener
import com.mogo.aicloud.services.socket.MogoAiCloudSocketManager
@@ -19,6 +20,7 @@ import com.mogo.commons.module.status.MogoStatusManager
import com.mogo.commons.network.NetConfigUtils
import com.mogo.commons.storage.SharedPrefsMgr
import com.mogo.commons.utils.MogoAnalyticUtils
import com.mogo.eagle.core.data.ai.V2XRepository
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.deva.chain.ChainConstant.Companion.CHAIN_CODE_CLOUD_INIT
import com.mogo.eagle.core.data.deva.chain.ChainConstant.Companion.CHAIN_CODE_CLOUD_PASSPORT_TOKEN
@@ -41,6 +43,7 @@ import com.mogo.eagle.core.utilcode.util.DeviceUtils
import com.mogo.eagle.core.utilcode.util.ProcessUtils
import com.mogo.eagle.core.utilcode.util.ThreadPoolService
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.service.v2n.MGV2NConst
import com.rousetime.android_startup.AndroidStartup
import com.zhjt.service.chain.ChainLog
import ly.count.android.sdk.Countly
@@ -50,6 +53,8 @@ class HttpDnsStartUp : AndroidStartup<Boolean>(), IMoGoCloudListener {
companion object {
private const val TAG = "HttpDnsStartUp"
private const val V2X_APP_ID = "lixiangCar"
private const val V2X_SECRET = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1z1vYOYKPhxbftTfN775Z6W8hxZEut/URomy+4aAxL7XcE1YWrXaT6C7hKjx+TFpUVmHXXW/tkeSX6HDUQOqUz5pwopoAjFmTYtgxP1uT0RTz9eryYb9BjCuuPkCehYxQV/8hLoUQ+mtnqqwILuDXYVizw9yBWmxG0tm4hoprWwIDAQAB"
}
// 配置云服务API
@@ -97,6 +102,21 @@ class HttpDnsStartUp : AndroidStartup<Boolean>(), IMoGoCloudListener {
this.context = context
initGDLoc()
preparePassportEnvironment()
//初始化NDE长链
V2XRepository.init(
context,
SharedPrefsMgr.getInstance().sn+"PAD",
V2X_APP_ID,
V2X_SECRET,
if(DebugConfig.getNetMode() == DebugConfig.NET_MODE_DEV){
MGV2NConst.MGEnv.QA
}else if(DebugConfig.getNetMode() == DebugConfig.NET_MODE_QA){
MGV2NConst.MGEnv.QA
} else{
MGV2NConst.MGEnv.SHANGHAI_PRO
},
DebugConfig.isDebug()
)
return true
}

View File

@@ -85,6 +85,11 @@ dependencies {
api 'com.zhidaoauto.machine:mapdata:1.0.0.3'
api project(":libraries:mogo-adas-data")
api project(':core:mogo-core-utils')
implementation rootProject.ext.dependencies.amapnavi3dmap
// implementation rootProject.ext.dependencies.amaplocation
implementation rootProject.ext.dependencies.amapsearch
// implementation "com.mogo.service:road-planning:1.1.1-SNAPSHOT"
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,20 @@
package com.mogo.eagle.core.data.ai
import com.amap.api.location.AMapLocation
object DataConfigs {
/**
* 是否使用云端纠偏坐标
*/
var useCorrectLocation = true
/**
* 使用固定坐标
*/
var fixedPoint: AMapLocation? = null
val gpsConfidence = 0.7f
}

View File

@@ -0,0 +1,10 @@
package com.mogo.eagle.core.data.ai
import android.content.Context
interface IRepository {
fun init(context: Context)
fun release()
}

View File

@@ -0,0 +1,56 @@
package com.mogo.eagle.core.data.ai
import android.location.Location
import com.amap.api.location.AMapLocation
import com.mogo.service.v2n.service.socket.bean.MGLocationBean
/**
* Create by wyy
* TIME : 2024/6/13 \ 15:05
* Description:
*/
/**
* COORD_TYPE_WGS84
* V2X 坐标转 高德坐标 慎用
* @receiver MGLocationModel
* @return Location
*/
fun MGLocationBean.toCG02Location(): Location {
return Location(this.provider).let {
it.speed = this.speed
it.latitude = this.latitude
it.longitude = this.longitude
it.altitude = this.altitude
it.time = this.timeStamp
it.bearing = this.bearing
it.accuracy = this.accuracy
it.provider = this.provider
it
}
}
/**
* MogoLocation 转 V2X 定位类
* @receiver MGLocationModel
* @return MGLocationBean
*/
fun AMapLocation.toMogoLocation(sourceType:Int = 0): MGLocationBean {
val bean = MGLocationBean()
bean.lockType = locationType
bean.coordType = MGLocationBean.COORD_TYPE_GCJ02
bean.accuracy = accuracy
bean.altitude = altitude
bean.bearing = bearing
bean.speed = speed
bean.satellites = satellites
bean.latitude = latitude
bean.longitude = longitude
bean.adCode = adCode
bean.cityCode = cityCode
bean.sourceType = sourceType
bean.gpsConfidence = DataConfigs.gpsConfidence
bean.timeStamp = System.currentTimeMillis()
return bean
}

View File

@@ -0,0 +1,127 @@
package com.mogo.eagle.core.data.ai
import android.content.Context
import android.util.Log
import com.amap.api.location.AMapLocation
import com.amap.api.location.AMapLocationClient
import com.amap.api.location.AMapLocationClientOption
import com.amap.api.location.AMapLocationListener
import com.mogo.service.v2n.service.socket.bean.MGLocationBean
import com.mogo.service.v2n.utils.CoordinateUtil
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
/**
* 高德定位
*/
class LocationRepository : IRepository, AMapLocationListener {
companion object {
private const val TAG = "LocationRepository"
@JvmStatic
val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
LocationRepository()
}
}
private val _cg02Flow = MutableSharedFlow<AMapLocation>(extraBufferCapacity = 1)
val cg02Flow: SharedFlow<AMapLocation> = _cg02Flow.asSharedFlow()
private var mLocationClient: AMapLocationClient? = null
private var mLocationOption: AMapLocationClientOption? = null
var lastCityCode: String? = null
var lastAdCode: String? = null
var lastLocationGcj02: AMapLocation? = null
var lastCorrLoc84: MGLocationBean? = null
var lastLastCorrLoc84: MGLocationBean? = null
var bear: Float = 0.0f
var stopLocation = false
override fun init(context: Context) {
mLocationOption = AMapLocationClientOption()
mLocationOption?.apply {
setInterval(800L)
setNeedAddress(true)
setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy)
}
mLocationClient = AMapLocationClient(context.applicationContext)
mLocationClient?.apply {
setLocationOption(mLocationOption)
setLocationListener(this@LocationRepository)
}
mLocationClient?.startLocation()
mLocationClient?.lastKnownLocation?.let {
lastCityCode = it.cityCode
lastLocationGcj02 = it
lastAdCode = it?.adCode
}
}
fun startLocation() {
mLocationClient?.startLocation()
//startJob()
}
override fun release() {
mLocationClient?.stopLocation()
mLocationClient?.onDestroy()
mLocationOption = null
mLocationClient = null
}
/**
* 高德定位返回
* @param location MGLocationModel
*/
override fun onLocationChanged(loc: AMapLocation) {
// Log.d(TAG, "onLocationChanged:${location.toStr()}")
var location = loc
Log.d(
TAG,
"onLocationChanged:$this , provider=${location.provider}#${location}#speed=${location.speed}#bearing=${location.bearing}"
)
if (location.latitude <= 0 && location.longitude <= 0) return
location.cityCode.takeIf { !it.isNullOrEmpty() }?.let {
lastCityCode = it
}
location.adCode.takeIf { !it.isNullOrEmpty() }?.let {
lastAdCode = it
}
if (location.bearing > 0) {
bear = location.bearing
}
location.bearing = bear
// 设置固定坐标
if (DataConfigs.fixedPoint != null) {
location = DataConfigs.fixedPoint!!
}
lastLocationGcj02 = location
_cg02Flow.tryEmit(location)
if (!DataConfigs.useCorrectLocation){
Log.e(TAG, "CorrectLoc 本地坐标应用为纠偏坐标")
location.toMogoLocation().let {
val latLng = CoordinateUtil.gcj02LngLatToWGS84(it.longitude, it.latitude)
it.latitude = latLng[1];
it.longitude = latLng[0]
if (!stopLocation) {
instance.lastLastCorrLoc84 = instance.lastCorrLoc84
instance.lastCorrLoc84 = it
V2XRepository._correctLocFlow.tryEmit(it)
}
}
return
}
}
}

View File

@@ -0,0 +1,3 @@
package com.mogo.eagle.core.data.ai
data class V2XError(val errorCode: Int, val errorMsg: String?)

View File

@@ -0,0 +1,406 @@
package com.mogo.eagle.core.data.ai
import android.content.Context
import android.util.Log
import com.amap.api.location.AMapLocation
import com.mogo.service.v2n.MGV2N
import com.mogo.service.v2n.MGV2NConst
import com.mogo.service.v2n.bean.MGAccidentBean
import com.mogo.service.v2n.bean.MGAccidentVideoBean
import com.mogo.service.v2n.bean.MGCarDragonBean
import com.mogo.service.v2n.bean.MGCongestionBean
import com.mogo.service.v2n.bean.MGCongestionLmBean
import com.mogo.service.v2n.bean.MGConstructionBean
import com.mogo.service.v2n.bean.MGCrossRoadBean
import com.mogo.service.v2n.bean.MGCrossSpeedBean
import com.mogo.service.v2n.bean.MGDrivingAdviceDataBean
import com.mogo.service.v2n.bean.MGDynamicEventBean
import com.mogo.service.v2n.bean.MGLlmQueryBean
import com.mogo.service.v2n.bean.MGLlmQueryRespBean
import com.mogo.service.v2n.bean.MGMogoCarBean
import com.mogo.service.v2n.bean.MGNoaMapDataBean
import com.mogo.service.v2n.bean.MGObstacleBean
import com.mogo.service.v2n.bean.MGPerceptionBean
import com.mogo.service.v2n.bean.MGRoadWetBean
import com.mogo.service.v2n.bean.MGSelfMergeBean
import com.mogo.service.v2n.bean.MGTrafficLightResultBean
import com.mogo.service.v2n.bean.MGWaringCrossBean
import com.mogo.service.v2n.bean.MGWaringVehicleBean
import com.mogo.service.v2n.bean.MGWatchPedestrianBean
import com.mogo.service.v2n.bean.MGWatchTruckBean
import com.mogo.service.v2n.contract.IMGV2NListener
import com.mogo.service.v2n.service.socket.bean.MGLocationBean
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
object V2XRepository : IRepository, IMGV2NListener {
private val _v2xErrorFlow = MutableSharedFlow<V2XError>(extraBufferCapacity = 10)
val v2XErrorFlow: SharedFlow<V2XError> = _v2xErrorFlow
var testInputLocationStep = 0
var sn: String? = null
var lastCorrectLocTime = System.currentTimeMillis()
var lastUploadLocationTime = System.currentTimeMillis()
private val _lightFlow = MutableSharedFlow<MGTrafficLightResultBean>(extraBufferCapacity = 10)
val lightFlow: SharedFlow<MGTrafficLightResultBean> = _lightFlow
/**
* 车速引导
*/
private val _crossSpeedFlow = MutableSharedFlow<MGCrossSpeedBean>(extraBufferCapacity = 1)
val crossSpeedFlow: SharedFlow<MGCrossSpeedBean> = _crossSpeedFlow
/**
* 路口预告
*/
private val _crossRoadFlow = MutableSharedFlow<MGCrossRoadBean>(extraBufferCapacity = 1)
val crossRoadFlow: SharedFlow<MGCrossRoadBean> = _crossRoadFlow
/**
* 他车数据
*/
// private val _otherVehicleFlow =
// MutableSharedFlow<MGV2XCloudMapDataModel?>(extraBufferCapacity = 1)
// val otherVehicleFlow: SharedFlow<MGV2XCloudMapDataModel?> = _otherVehicleFlow
/**
* 拥堵状态
*/
private val _congestionStateFlow = MutableStateFlow(false)
val congestionStateFlow: StateFlow<Boolean> = _congestionStateFlow
/**
* 道路施工事件
*/
private val _constructionFlow = MutableSharedFlow<MGConstructionBean>(extraBufferCapacity = 10)
val constructionFlow: SharedFlow<MGConstructionBean> = _constructionFlow
/**
* 静止障碍物事件
*/
private val _obstacleFlow = MutableSharedFlow<MGObstacleBean>(extraBufferCapacity = 10)
val obstacleFlow: SharedFlow<MGObstacleBean> = _obstacleFlow
/**
* 交通施工事件
*/
private val _accidentFlow = MutableSharedFlow<MGAccidentBean>(extraBufferCapacity = 10)
val accidentFlow: SharedFlow<MGAccidentBean> = _accidentFlow
/**
* 拥堵事件
*/
private val _congestionFlow = MutableSharedFlow<MGCongestionBean>(extraBufferCapacity = 10)
val congestionFlow: SharedFlow<MGCongestionBean> = _congestionFlow
/**
* 行人横穿预警-road
*/
private val _pedestrianWarningFlow =
MutableSharedFlow<MGDynamicEventBean>(
extraBufferCapacity = 10,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val pedestrianWarningFlow: SharedFlow<MGDynamicEventBean> = _pedestrianWarningFlow
/**
* 他车逆行预警
*/
private val _vehicleFlow = MutableSharedFlow<MGDynamicEventBean>(extraBufferCapacity = 10)
val vehicleFlow: SharedFlow<MGDynamicEventBean> = _vehicleFlow
/**
* 协同感知数据
*/
private val _perceptionFlow = MutableSharedFlow<MGPerceptionBean>(extraBufferCapacity = 10)
val perceptionFlow: SharedFlow<MGPerceptionBean> = _perceptionFlow
/**
* 协同感知数据
*/
val _correctLocFlow = MutableSharedFlow<MGLocationBean>(extraBufferCapacity = 10)
val correctLocFlow: SharedFlow<MGLocationBean> = _correctLocFlow
/**
* 事故视频、二维码
*/
private val _accidentVideoFlow =
MutableSharedFlow<MGAccidentVideoBean>(extraBufferCapacity = 10)
val accidentVideoFLow: SharedFlow<MGAccidentVideoBean> = _accidentVideoFlow
/**
* 行人出没事件
*/
private val _watchPedFlow = MutableSharedFlow<MGWatchPedestrianBean>(extraBufferCapacity = 10)
val watchPedFlow: SharedFlow<MGWatchPedestrianBean> = _watchPedFlow
/**
* 大车出没事件
*/
private val _watchTruckFlow = MutableSharedFlow<MGWatchTruckBean>(extraBufferCapacity = 10)
val watchTruckFlow: SharedFlow<MGWatchTruckBean> = _watchTruckFlow
private val _mogoCarFlow = MutableSharedFlow<List<MGMogoCarBean>?>(extraBufferCapacity = 10)
val mogoCarFlow: SharedFlow<List<MGMogoCarBean>?> = _mogoCarFlow
private val _llmResultFlow = MutableSharedFlow<MGLlmQueryRespBean>(extraBufferCapacity = 10)
val llmResultFlow: SharedFlow<MGLlmQueryRespBean> = _llmResultFlow
private val _congestionLmFlow = MutableSharedFlow<MGCongestionLmBean>(extraBufferCapacity = 10)
val congestionLmFlow: SharedFlow<MGCongestionLmBean> = _congestionLmFlow
private val _roadWetFlow = MutableSharedFlow<MGRoadWetBean>(extraBufferCapacity = 10)
val roadWetFlow: SharedFlow<MGRoadWetBean> = _roadWetFlow
private val _carDragonLmFlow = MutableSharedFlow<MGCarDragonBean>(extraBufferCapacity = 10)
val carDragonLmFlow: SharedFlow<MGCarDragonBean> = _carDragonLmFlow
override fun init(context: Context) {
MGV2N.getInstance().start()
// MGV2N.getInstance().setLogListener {
// Log.d(it)
// }
}
fun init(
context: Context,
sn: String,
appId: String,
secret: String,
env: MGV2NConst.MGEnv,
debug: Boolean = false
) {
this.sn = sn
MGV2N.getInstance().init(context, sn, appId, secret, env, debug)
MGV2N.getInstance().setV2NListener(this)
}
fun uploadLlmQuery(b: MGLlmQueryBean) {
b.user = sn
MGV2N.getInstance().uploadLlmQuery(b)
}
/**
* 提供v2x坐标
*/
fun provideLocation(loc: AMapLocation, sourceType: Int = 0) {
loc.toMogoLocation(sourceType).also {
MGV2N.getInstance().uploadLocation(it)
}
Log.d(TAG, "provideLocation delay:${System.currentTimeMillis() - lastUploadLocationTime}")
lastUploadLocationTime = System.currentTimeMillis()
}
override fun release() {
MGV2N.getInstance().setV2NListener(null)
}
override fun onTrafficLightDataReceive(trafficLight: MGTrafficLightResultBean?) {
Log.d(TAG, "红绿灯:${trafficLight}")
trafficLight?.let { _lightFlow.tryEmit(it) }
}
override fun onConstructionReceive(construction: MGConstructionBean?) {
Log.d(TAG, "道路施工:${construction}")
construction?.let { _constructionFlow.tryEmit(it) }
}
override fun onAccidentReceive(accident: MGAccidentBean?) {
Log.d(TAG, "交通事故:${accident}")
accident?.let { _accidentFlow.tryEmit(it) }
}
override fun onObstacleReceive(obstacle: MGObstacleBean?) {
Log.d(TAG, "静止障碍物:${obstacle}")
obstacle?.let { _obstacleFlow.tryEmit(obstacle) }
}
override fun onCongestionReceive(congestion: MGCongestionBean?) {
Log.d(TAG, "拥堵事件:${congestion}")
// congestion?.let { _congestionFlow.tryEmit(it) }
}
override fun onCrossSpeedReceive(crossSpeed: MGCrossSpeedBean?) {
Log.d(TAG, "绿波:${crossSpeed}")
crossSpeed?.let { _crossSpeedFlow.tryEmit(it) }
}
override fun onPedestrianWarningReceive(pedestrian: MGDynamicEventBean?) {
// Log.d(
// TAG,
// "Pedestrian 行人横穿 【road】 ${System.currentTimeMillis()} [${Thread.currentThread().name}]${pedestrian}"
// )
// pedestrian?.let {
// _pedestrianWarningFlow.tryEmit(it)
// }
}
override fun onNdePedestrianWarningReceive(pedestrian: MGDynamicEventBean?) {
Log.d(
TAG,
"Pedestrian 行人横穿 【nde】 ${System.currentTimeMillis()} [${Thread.currentThread().name}]${pedestrian}"
)
V2XWarningRepository.instance.updateNdePedestrianWarning(pedestrian)
}
override fun onVehicleWarningReceive(vehicle: MGDynamicEventBean?) {
Log.d(TAG, "他车逆行预警:${vehicle}")
// vehicle?.let {
// _vehicleFlow.tryEmit(it)
// }
}
override fun onPerceptionReceive(perception: MGPerceptionBean?) {
Log.d(TAG, "Perception 感知数据 [${Thread.currentThread().name}]: ${perception}")
perception?.let {
_perceptionFlow.tryEmit(it)
}
}
override fun onCrossRoadReceive(crossRoad: MGCrossRoadBean?) {
Log.d(TAG, "CrossRoad 路口预告 [${Thread.currentThread().name}]: $crossRoad")
crossRoad?.let {
_crossRoadFlow.tryEmit(it)
}
}
override fun onCorrectLocReceive(loc: MGLocationBean?) {
Log.d(TAG, "CorrectLoc 纠正坐标 :delay:${System.currentTimeMillis() - lastCorrectLocTime}. loc: $loc")
lastCorrectLocTime = System.currentTimeMillis()
if (!DataConfigs.useCorrectLocation) {
Log.e(TAG, "CorrectLoc 纠正坐标 当前配置禁止使用纠偏坐标")
return
}
loc?.let {
// if (testInputLocationStep == 0) {
// MGLocationBean().apply {
// cityCode = "021"
// longitude = 121.17077571918865
// latitude = 31.285035266120133
// bearing = 174F
// timeStamp = 1733196610278
// }.let {
// LocationRepository.instance.lastLastCorrLoc84 = LocationRepository.instance.lastCorrLoc84
// LocationRepository.instance.lastCorrLoc84 = it
// _correctLocFlow.tryEmit(it)
// }
// }
LocationRepository.instance.lastLastCorrLoc84 =
LocationRepository.instance.lastCorrLoc84
LocationRepository.instance.lastCorrLoc84 = it
_correctLocFlow.tryEmit(it)
}
}
override fun onSelfMergeReceive(data: MGSelfMergeBean?) {
}
override fun onWatchPedestrianReceive(data: MGWatchPedestrianBean?) {
data?.let {
_watchPedFlow.tryEmit(it)
}
}
override fun onWatchTruckReceive(data: MGWatchTruckBean?) {
data?.let {
_watchTruckFlow.tryEmit(it)
}
}
override fun onMapDataReceive(data: MGNoaMapDataBean?) {
}
override fun onDriveAdviceReceive(data: MGDrivingAdviceDataBean?) {
}
override fun onWarningCrossReceive(data: MGWaringCrossBean?) {
Log.d(TAG, "onWarningCrossReceive:${data}")
V2XWarningRepository.instance.updateWarningCross(data)
}
override fun onWarningCarReceive(data: MGWaringVehicleBean?) {
Log.d(TAG, "onWarningCarReceive:${data}")
V2XWarningRepository.instance.updateWarningCar(data)
}
override fun onWarningTruckReceive(data: MGWaringVehicleBean?) {
Log.d(TAG, "onWarningTruckReceive:${data}")
V2XWarningRepository.instance.updateWarningTruck(data)
}
override fun onAccidentVideoReceive(data: MGAccidentVideoBean?) {
data?.let {
Log.d(
TAG,
"onAccidentVideoReceive:videoUrl=${data.videoUrl},qrCode:${data.qrCode}"
)
_accidentVideoFlow.tryEmit(it)
}
}
override fun onCarDragonReceive(data: MGCarDragonBean?) {
data?.let {
Log.d(TAG, "收到推荐车道, $it")
_carDragonLmFlow.tryEmit(it)
}
}
override fun onMogoCarReceive(data: List<MGMogoCarBean>?) {
Log.d(TAG, "onMogoCarReceive:size:${data?.size}, data:${data}")
_mogoCarFlow.tryEmit(data)
}
override fun onLlmQueryReceive(data: MGLlmQueryRespBean?) {
data?.let {
Log.d(TAG, "onLlmQueryReceive $it")
_llmResultFlow.tryEmit(it)
}
}
override fun onCongestionLmReceive(data: MGCongestionLmBean?) {
data?.let {
Log.d(TAG, "onCongestionLmReceive $it")
_congestionLmFlow.tryEmit(it)
}
}
override fun onRoadWetReceive(data: MGRoadWetBean?) {
data?.let {
Log.d(TAG, "onRoadWetReceive $it")
_roadWetFlow.tryEmit(it)
}
}
override fun onError(errorCode: Int, errorMsg: String?) {
Log.d(TAG, "onError:errorCode=${errorCode},errorMsg:${errorMsg}")
_v2xErrorFlow.tryEmit(V2XError(errorCode, errorMsg))
}
private const val TAG = "V2XRepository"
}

View File

@@ -0,0 +1,766 @@
package com.mogo.eagle.core.data.ai
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import com.mogo.service.v2n.bean.MGDynamicEventBean
import com.mogo.service.v2n.bean.MGPerceptionBean
import com.mogo.service.v2n.bean.MGWaringCrossBean
import com.mogo.service.v2n.bean.MGWaringVehicleBean
import com.mogo.service.v2n.service.socket.bean.MGLocationBean
import com.mogo.service.v2n.utils.CoordinateUtil
import com.zhidaoauto.map.data.point.LonLatPoint
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
class V2XWarningRepository {
companion object {
val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
V2XWarningRepository()
}
private const val TAG = "V2XWarningRepository"
private const val MSG_WHAT_CAR = 1
private const val MSG_WHAT_TRUCK = 2
private const val MSG_WHAT_CROSS = 3
private const val EVENT_CLEAR_DELAY = 1200L
}
private var waringCar: MGWaringVehicleBean? = null
private var waringTruck: MGWaringVehicleBean? = null
private var waringCross: MGWaringCrossBean? = null
private val mainHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_WHAT_CAR -> {
Log.d(TAG, "clean msg, MSG_WHAT_CAR")
updateWarningCarUI(null)
updateWarningCar(null)
lastWarningCarNotify = null
}
MSG_WHAT_TRUCK -> {
Log.d(TAG, "clean msg, MSG_WHAT_TRUCK")
updateWarningTruckUI(null)
updateWarningTruck(null)
lastWarningTruckNotify = null
}
MSG_WHAT_CROSS -> {
Log.d(TAG, "clean msg, MSG_WHAT_CROSS")
updateWarningCrossUI(null)
updateWarningCross(null)
lastWarningCrossNotify = null
}
}
}
}
/**
* 行人横穿预警-nde
*/
private val ndePedestrianMap = ConcurrentHashMap<String, Pair<Long, MGDynamicEventBean>>()
private val _warningPedestrianNdeFlow =
MutableSharedFlow<MGDynamicEventBean>(
extraBufferCapacity = 10,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val warningPedestrianNdeFlow: SharedFlow<MGDynamicEventBean> = _warningPedestrianNdeFlow
private var updateWaringCarTime = 0L
private var updateWaringTrucTime = 0L
private var updateWaringCrossTime = 0L
private var lastWarningCarUI: List<WaringUIModel>? = null
private var lastWarningTruckUI: List<WaringUIModel>? = null
private var lastWarningCrossUI: List<WaringUIModel>? = null
private var lastWarningCarNotify: Pair<Long, WaringUIModel>? = null
private var lastWarningTruckNotify: Pair<Long, WaringUIModel>? = null
private var lastWarningCrossNotify: Pair<Long, Set<WaringOrientationType>>? = null
private val _warningCarFlow = MutableSharedFlow<List<WaringUIModel>?>(extraBufferCapacity = 10)
val warningCarFlow: SharedFlow<List<WaringUIModel>?> = _warningCarFlow
private val _warningCarNotifyFlow = MutableSharedFlow<WaringUIModel>(extraBufferCapacity = 10)
val warningCarNotifyFlow: SharedFlow<WaringUIModel> = _warningCarNotifyFlow
private val _warningTruckFlow =
MutableSharedFlow<List<WaringUIModel>?>(extraBufferCapacity = 10)
val warningTruckFlow: SharedFlow<List<WaringUIModel>?> = _warningTruckFlow
private val _warningTruckNotifyFlow = MutableSharedFlow<WaringUIModel>(extraBufferCapacity = 10)
val warningTruckNotifyFlow: SharedFlow<WaringUIModel> = _warningTruckNotifyFlow
private val _warningCrossFlow =
MutableSharedFlow<List<WaringUIModel>?>(extraBufferCapacity = 10)
val warningCrossFlow: SharedFlow<List<WaringUIModel>?> = _warningCrossFlow
private val _warningCrossOriFlow =
MutableSharedFlow<Set<WaringOrientationType>>(extraBufferCapacity = 10)
val warningCrossOriFlow: SharedFlow<Set<WaringOrientationType>> = _warningCrossOriFlow
private val _warningOriFlow = MutableStateFlow(0)
val warningOriFlow: SharedFlow<Int> = _warningOriFlow
private val warningTruckMap = mutableMapOf<String, Long>()
private val warningCarMap = mutableMapOf<String, Long>()
private val warningCrossMap = mutableMapOf<String, Long>()
class WaringUIModel {
var uuid: String = ""
var orientation: WaringOrientationType = WaringOrientationType.none
var traj: List<LonLatPoint>? = null
var lat: Double? = null
var lng: Double? = null
var dis: Double = -1.0
var speed: Double = -1.0
var level: Int = 3
var isTowards: Boolean = false
override fun toString(): String {
return "WaringUIModel(uuid='$uuid', orientation=$orientation, traj=$traj, lat=$lat, lng=$lng, dis=$dis, speed=$speed, level=$level, isTowards=$isTowards)"
}
}
private fun estimateOrientationLatLng(
otherLat: Double, otherLng: Double
): WaringOrientationType {
val loc84 = getCurrPos()
val selfLat = loc84?.latitude ?: 0.0
val selfLng = loc84?.longitude ?: 0.0
val selfHeading = loc84?.bearing?.toDouble() ?: 0.0
if (selfLat == 0.0 || selfLng == 0.0) {
return WaringOrientationType.none
}
val orientation = getRelativeDirection(selfLat, selfLng, selfHeading, otherLat, otherLng)
Log.d(
TAG,
"estimateOrientationLatLng: ${selfLat},${selfLng} $otherLat,$otherLng selfHeading=$selfHeading ori=$orientation"
)
return orientation
}
// 获取相对方位
fun getRelativeDirection(
lat1: Double, lon1: Double, heading: Double,
lat2: Double, lon2: Double
): WaringOrientationType {
// 计算两点之间的方位角
val bearing = calculateBearing(lat1, lon1, lat2, lon2)
// 计算相对角度
val relativeAngle = (bearing - heading + 360) % 360
// 判断方位
return when {
relativeAngle <= 22.5 || relativeAngle > 337.5 -> WaringOrientationType.top
relativeAngle <= 67.5 -> WaringOrientationType.right_top
relativeAngle <= 112.5 -> WaringOrientationType.right
relativeAngle <= 157.5 -> WaringOrientationType.right_bottom
relativeAngle <= 202.5 -> WaringOrientationType.bottom
relativeAngle <= 247.5 -> WaringOrientationType.left_bottom
relativeAngle <= 292.5 -> WaringOrientationType.left
else -> WaringOrientationType.left_top
}
}
// 计算两点之间的方位角
private fun calculateBearing(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val radLat1 = Math.toRadians(lat1)
val radLat2 = Math.toRadians(lat2)
val deltaLon = Math.toRadians(lon2 - lon1)
val y = sin(deltaLon) * cos(radLat2)
val x = cos(radLat1) * sin(radLat2) - sin(radLat1) * cos(radLat2) * cos(deltaLon)
val bearing = Math.toDegrees(atan2(y, x))
// 标准化为 0°~360°
return (bearing + 360) % 360
}
// private fun estimateTrajectoryLatLng(
// uuid: String,
// selfLat: Double,
// selfLng: Double,
// bearing: Float
// ): List<LonLatPoint>? {
//
// Log.d(TAG, "estimateTrajectoryLatLng:uuid:${uuid},selfLat:${selfLat},selfLng:${selfLng} ")
// val lp = LonLatPoint().apply {
// latitude = selfLat
// longitude = selfLng
// angle = bearing.toDouble()
// }
//
// val clRoad = GuideUtils.instance.clRoad(uuid, lp, 100.0)
// Log.d(
// TAG,
// "estimateTrajectoryLatLng:uuid:${uuid},selfLat:${selfLat},selfLng:${selfLng}:${clRoad} "
// )
// return clRoad
// }
fun waringCheck(perceptionData: MGPerceptionBean, localBlackList: Set<String>, serverBlackList: Set<String>):Boolean {
val car: MutableList<WaringUIModel> = mutableListOf()
val truck: MutableList<WaringUIModel> = mutableListOf()
val cross: MutableList<WaringUIModel> = mutableListOf()
Log.d(TAG, "waringCheck: perception size=${perceptionData.data.size}")
var isServerBlackListExist = false
perceptionData.data.forEach { o ->
if (localBlackList.contains(o.uuid)) {
return@forEach
}
if (serverBlackList.contains(o.uuid)) {
isServerBlackListExist = true
return@forEach
}
checkWarningCar(o)?.also { car.add(it) }
checkWarningTruck(o)?.also { truck.add(it) }
checkWarningCross(o)?.also {
if (it.orientation != WaringOrientationType.none
&& it.orientation != WaringOrientationType.bottom
&& it.orientation != WaringOrientationType.left_bottom
&& it.orientation != WaringOrientationType.right_bottom
&& it.dis > 1) {
cross.add(it)
}
}
if (o.markerType == 0) {
checkWaningPedestrian(o.uuid)
}
}
updateWarningCarUI(car.takeIf { it.isNotEmpty() })
updateWarningTruckUI(truck.takeIf { it.isNotEmpty() })
updateWarningCrossUI(cross.takeIf { it.isNotEmpty() })
updateNotify(car, truck, cross)
return isServerBlackListExist
}
//TODO 逻辑重新梳理,改为flow
private fun updateNotify(
car: List<WaringUIModel>?,
truck: List<WaringUIModel>?,
cross: List<WaringUIModel>?
) {
Log.d(TAG, "updateNotify: car=${car?.size}, truck=${truck?.size}, cross=${cross?.size}")
if (car?.isNotEmpty() == true) {
handleWarningCarNotif(car)
}
if (truck?.isNotEmpty() == true) {
handleWarningTruckNotif(truck)
}
if (cross?.isNotEmpty() == true) {
handleWarningCrossNotif(cross)
}
val carOri =
car?.filter { it.level > 1 }?.map { it.orientation }?.distinct() ?: emptyList()
val truckOri =
truck?.filter { it.level > 1 }?.map { it.orientation }?.distinct() ?: emptyList()
val crossOri =
cross?.map { it.orientation }?.distinct() ?: emptyList()
val carOriInt = getOriInt(carOri)
val truckOriInt = getOriInt(truckOri)
val crossOriInt = getOriInt(crossOri)
Log.d(
TAG,
"updateNotify:carOriInt=${getOriStr(carOriInt)}, truckOriInt=${getOriStr(truckOriInt)}, crossOriInt=${
getOriStr(
crossOriInt
)
}"
)
val oriInt = carOriInt or truckOriInt or crossOriInt
//val r:Int = (Math.random() * 255.0).toInt()
_warningOriFlow.tryEmit(oriInt)
}
private fun getOriInt(carOri: List<WaringOrientationType>): Int {
var oriInt = 0
carOri.forEach {
oriInt = oriInt or it.mask
}
return oriInt
}
private fun getOriStr(oriInt: Int): String {
var oriStr = ""
listOf(
WaringOrientationType.left_top,
WaringOrientationType.top,
WaringOrientationType.right_top,
WaringOrientationType.right,
WaringOrientationType.right_bottom,
WaringOrientationType.bottom,
WaringOrientationType.left_bottom,
WaringOrientationType.left,
WaringOrientationType.top_big,
WaringOrientationType.bottom_big,
).forEach {
oriStr += if (oriInt and it.mask != 0) {
it.text + ","
} else ""
}
return oriStr
}
private fun handleWarningCrossNotif(cross: List<WaringUIModel>) {
val oriList = cross.map {
it.orientation
}.filterNotNull().toSet()
val lastTime = lastWarningCrossNotify?.first
val last = lastWarningCrossNotify?.second
if (last == null || lastTime == null) {
lastWarningCrossNotify = System.currentTimeMillis() to oriList
_warningCrossOriFlow.tryEmit(oriList)
return
}
var newOriCount = 0
for (type in oriList) {
if (!last.contains(type)) {
newOriCount++
}
}
if (newOriCount <= 0) {
Log.d(TAG, "忽略路口预警通知:无新方向")
return
}
val delay = System.currentTimeMillis() - lastTime
if (delay < 3000) {
Log.d(TAG, "忽略路口预警通知距离上次小于3秒")
return
}
lastWarningCrossNotify = System.currentTimeMillis() to oriList
Log.d(TAG, "路口碰撞预警通知: $oriList")
_warningCrossOriFlow.tryEmit(oriList)
}
private fun handleWarningTruckNotif(truck: List<WaringUIModel>) {
val truckList = mutableListOf<WaringUIModel>()
truckList.addAll(truck)
truckList.sortByLevel()
val warningTruck = truckList.first()
val lastTime = lastWarningTruckNotify?.first
val last = lastWarningTruckNotify?.second
if (last == null || lastTime == null) {
lastWarningTruckNotify = System.currentTimeMillis() to warningTruck
_warningTruckNotifyFlow.tryEmit(warningTruck)
return
}
val delay = System.currentTimeMillis() - lastTime
if (delay < 3000) {
Log.d(TAG, "忽略大车预警通知离上一个通知小于3秒")
return
}
if (warningTruck.level <= last.level /*&& delay < 20000*/) {
Log.d(TAG, "忽略大车预警通知:小于或同等级当前序列不展示")
return
}
lastWarningTruckNotify = System.currentTimeMillis() to warningTruck
Log.d(TAG, "大车预警通知: $warningTruck")
_warningTruckNotifyFlow.tryEmit(warningTruck)
}
private fun handleWarningCarNotif(car: List<WaringUIModel>) {
val carList = mutableListOf<WaringUIModel>()
carList.addAll(car)
carList.sortByLevel()
val warningCar = carList.first()
val lastTime = lastWarningCarNotify?.first
val last = lastWarningCarNotify?.second
if (last == null || lastTime == null) {
lastWarningCarNotify = System.currentTimeMillis() to warningCar
_warningCarNotifyFlow.tryEmit(warningCar)
return
}
val delay = System.currentTimeMillis() - lastTime
if (delay < 3000) {
Log.d(TAG, "忽略快速小车预警通知离上一个通知小于3秒")
return
}
if (warningCar.level <= last.level/* && delay < 20000*/) {
Log.d(TAG, "忽略快速小车预警通知:小于或同等级当前序列不展示")
return
}
lastWarningCarNotify = System.currentTimeMillis() to warningCar
Log.d(TAG, "快速小车预警通知: $warningCar")
_warningCarNotifyFlow.tryEmit(warningCar)
}
private fun updateWarningCarUI(ui: List<WaringUIModel>?) {
//
Log.d(TAG, "updateWarningCarUI:${ui}")
if (ui == null) {
if (lastWarningCarUI != null) {
_warningCarFlow.tryEmit(null)
}
} else {
_warningCarFlow.tryEmit(ui)
}
lastWarningCarUI = ui
}
private fun updateWarningTruckUI(ui: List<WaringUIModel>?) {
Log.d(TAG, "updateWarningTruckUI:${ui}")
if (ui == null) {
if (lastWarningTruckUI != null) {
_warningTruckFlow.tryEmit(null)
}
} else {
_warningTruckFlow.tryEmit(ui)
}
lastWarningTruckUI = ui
}
private fun updateWarningCrossUI(ui: List<WaringUIModel>?) {
Log.d(TAG, "updateWarningCrossUI:${ui}")
if (ui == null) {
if (lastWarningCrossUI != null) {
_warningCrossFlow.tryEmit(null)
}
} else {
_warningCrossFlow.tryEmit(ui)
}
lastWarningCrossUI = ui
}
private fun checkWarningCar(item: MGPerceptionBean.Item): WaringUIModel? {
return waringCar?.infos?.firstOrNull { item.uuid == it.uuid }?.let {
WaringUIModel().apply {
uuid = it.uuid
orientation = WaringOrientationType.fromType(it.orientation)
lat = item.lat
lng = item.lon
//traj = estimateTrajectoryLatLng(it.uuid, item.lat, item.lon, item.rotateAngle)
dis = it.distance
speed = it.speed
level = it.level
isTowards = isTowards(item.rotateAngle)
}
}
}
// 取第一辆车
private fun checkWarningTruck(item: MGPerceptionBean.Item): WaringUIModel? {
return waringTruck?.infos?.firstOrNull { item.uuid == it.uuid }?.let {
WaringUIModel().apply {
uuid = it.uuid
orientation = WaringOrientationType.fromType(it.orientation)
lat = item.lat
lng = item.lon
//traj = estimateTrajectoryLatLng(it.uuid, item.lat, item.lon, item.rotateAngle)
dis = it.distance
speed = it.speed
level = it.level
}
}
}
// 取第一辆车
private fun checkWarningCross(item: MGPerceptionBean.Item): WaringUIModel? {
return waringCross?.targetInfo?.firstOrNull { item.uuid == it.uuid }?.let {
WaringUIModel().apply {
uuid = it.uuid
orientation = estimateOrientationLatLng(item.lat, item.lon)
lat = item.lat
lng = item.lon
traj = it.traj?.map { it.toTrajectoryLatLonPoint() }
dis = culDisWithSelf(item)
speed = item.speed * 3.6
level = 3
}
}
}
private fun checkWaningPedestrian(uuid: String) {
Log.d(TAG, "checkWaningPedestrian:uuid:${uuid}")
ndePedestrianMap[uuid]?.second?.let {
_warningPedestrianNdeFlow.tryEmit(it)
}
}
private fun isTowards(rotateAngle: Float): Boolean {
val loc84 = LocationRepository.instance.lastCorrLoc84 ?: return false
var angle: Double = abs(loc84.bearing - rotateAngle.toDouble())
if (angle > 180) {
angle = min(angle, 360 - angle)
}
return angle > 135
}
private fun culDisWithSelf(item: MGPerceptionBean.Item): Double {
val loc84 = LocationRepository.instance.lastCorrLoc84
if (loc84 == null || loc84.latitude == 0.0 || loc84.longitude == 0.0) {
return -1.0
}
return CoordinateUtil.calculateLineDistanceLatLon(
item.lat,
item.lon,
loc84.latitude,
loc84.longitude
).toDouble()
}
private fun MGLocationBean.toTrajectoryLatLonPoint(): LonLatPoint {
return LonLatPoint().let {
it.latitude = this.latitude
it.longitude = this.longitude
it
}
}
fun updateWarningCar(waringCar: MGWaringVehicleBean?) {
Log.d(TAG, "updateWarningCar :${waringCar}")
updateWaringCarTime = System.currentTimeMillis()
this.waringCar = waringCar
val t = System.currentTimeMillis()
warningCarMap.clear()
waringCar?.let { car ->
car.infos.forEach {
warningCarMap[it.uuid] = t
}
}
mainHandler.removeMessages(MSG_WHAT_CAR)
mainHandler.sendEmptyMessageDelayed(MSG_WHAT_CAR, EVENT_CLEAR_DELAY)
}
fun updateWarningCross(waringCross: MGWaringCrossBean?) {
Log.d(TAG, "updateWarningCross :${waringCross}")
updateWaringCrossTime = System.currentTimeMillis()
this.waringCross = waringCross
val t = System.currentTimeMillis()
warningCrossMap.clear()
waringCross?.let { cross ->
cross.targetInfo.forEach {
warningCrossMap[it.uuid] = t
}
}
mainHandler.removeMessages(MSG_WHAT_CROSS)
mainHandler.sendEmptyMessageDelayed(MSG_WHAT_CROSS, EVENT_CLEAR_DELAY)
}
fun updateWarningTruck(waringTruck: MGWaringVehicleBean?) {
Log.d(TAG, "updateWarningTruck :${waringTruck}")
updateWaringTrucTime = System.currentTimeMillis()
this.waringTruck = waringTruck
val t = System.currentTimeMillis()
warningTruckMap.clear()
waringTruck?.let { truck ->
truck.infos.forEach {
warningTruckMap[it.uuid] = t
}
}
mainHandler.removeMessages(MSG_WHAT_TRUCK)
mainHandler.sendEmptyMessageDelayed(MSG_WHAT_TRUCK, EVENT_CLEAR_DELAY)
}
fun updateNdePedestrianWarning(waringPedestrian: MGDynamicEventBean?) {
waringPedestrian?.let {
if (it.targetIds.isNotEmpty()) {
val uuid = it.targetIds[0]
ndePedestrianMap[uuid] = System.currentTimeMillis() to it
}
}
}
fun isWarningTruck(uuid: String): Boolean {
return warningTruckMap[uuid] != null
}
fun isWarningCar(uuid: String): Boolean {
return warningCarMap[uuid] != null
}
fun isWarningCross(uuid: String): Boolean {
return warningCrossMap[uuid] != null
}
fun isWarningPedestrian(uuid: String): Boolean {
return ndePedestrianMap[uuid] != null
}
fun clearWarning() {
Log.d(TAG, "clearWarning")
updateWarningCarUI(null)
updateWarningTruckUI(null)
updateWarningCrossUI(null)
updateWarningCar(null)
updateWarningTruck(null)
updateWarningCross(null)
lastWarningCarNotify = null
lastWarningTruckNotify = null
lastWarningCrossNotify = null
}
fun MutableList<WaringUIModel>.sortByLevel() {
this.sortWith(Comparator { a, b ->
val l = a.level - b.level
if (l != 0) {
return@Comparator l
}
return@Comparator if (a.dis > b.dis) -1 else if (a.dis == b.dis) 0 else 1
})
}
fun removeOvertime(map: ConcurrentHashMap<String, Pair<Long, MGDynamicEventBean>>) {
val iterator = map.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
val time = System.currentTimeMillis() - entry.value.first
// 1分钟移除缓存
if (time > 60_000) {
Log.d(TAG, "pedestriansRefresh remove: ${entry.key}")
iterator.remove()
}
}
}
fun pedestriansRefresh() {
removeOvertime(ndePedestrianMap)
Log.d(TAG, "pedestriansRefresh nde: size=${ndePedestrianMap.size}")
}
private fun getCurrPos(): MGLocationBean? {
val lr = LocationRepository.instance
val start = lr.lastLastCorrLoc84
val end = lr.lastCorrLoc84
// Log.d(
// TAG,
// "getCurrPos: start=${start?.longitude},${start?.latitude},${start?.bearing}, end=${end?.longitude},${end?.latitude},${end?.bearing}"
// )
if (start == null) {
return end
}
if (end == null) {
return start
}
return interpolatePoints(start, end)
}
}
fun interpolateHeading(startHeading: Double, endHeading: Double, fraction: Double): Double {
val clampedFraction = fraction.coerceIn(0.0, 1.0)
// 将航向角转换到 [-180, 180)
val normalizedStart = ((startHeading + 180) % 360) - 180
val normalizedEnd = ((endHeading + 180) % 360) - 180
// 计算最短差值
val delta = normalizedEnd - normalizedStart
val shortestDelta = if (delta > 180) delta - 360 else if (delta < -180) delta + 360 else delta
// 插值并转换回 [0, 360)
val interpolated = normalizedStart + shortestDelta * clampedFraction
return (interpolated + 360) % 360
}
fun interpolatePoints(start: MGLocationBean, end: MGLocationBean): MGLocationBean {
val clampedFraction = 0.5
// 经纬度线性插值
val interpolatedLat = start.latitude + (end.latitude - start.latitude) * clampedFraction
val interpolatedLon = start.longitude + (end.longitude - start.longitude) * clampedFraction
// 修正后的航向角插值
val interpolatedHeading = interpolateHeading(start.bearing.toDouble(), end.bearing.toDouble(), clampedFraction)
return MGLocationBean().apply {
longitude = interpolatedLon
latitude = interpolatedLat
bearing = interpolatedHeading.toFloat()
}
}
enum class WaringOrientationType(val text: String, val mask: Int) {
left_top("左前方", 1 shl 0),// 00000001
top("前方", 1 shl 1),// 00000010
right_top("右前方", 1 shl 2),//00000100
right("右侧", 1 shl 3),//00001000
right_bottom("右后方", 1 shl 4),//00010000
bottom("后方", 1 shl 5),//00100000
left_bottom("左后方", 1 shl 6),//01000000
left("左侧", 1 shl 7),//10000000
top_big("前方", 1 shl 8),//100000000
bottom_big("后方", 1 shl 9),//1000000000
none("未知", 0);
companion object {
//a上左b上中c上右 d中左e中f中右 g下左h下中i下右
fun fromType(type: String?): WaringOrientationType {
return when (type) {
"a" -> left_top
"b" -> top
"c" -> right_top
"d" -> left
"e" -> top
"f" -> right
"g" -> left_bottom
"h" -> bottom
"i" -> right_bottom
"front" -> top_big
"rear" -> bottom_big
else -> none
}
}
fun fromMask(mask: Int): WaringOrientationType {
return when (mask) {
left_top.mask -> left_top
top.mask -> top
right_top.mask -> right_top
right.mask -> right
right_bottom.mask -> right_bottom
bottom.mask -> bottom
left_bottom.mask -> left_bottom
left.mask -> left
top_big.mask -> top_big
bottom_big.mask -> bottom_big
else -> none
}
}
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.eagle.core.data.ai.roadplanning;
import com.mogo.service.v2n.utils.CoordinateUtil;
import java.util.ArrayList;
import java.util.List;
public class PlanningUtils {
public static List<double[]> setNavigationGcj(List<double[]> gpsList) {
if (gpsList == null || gpsList.isEmpty()) {
return null;
}
List<double[]> list = new ArrayList<>();
double preLon = 0;
double preLat = 0;
for (double[] gps : gpsList) {
if (String.format("%.6f", gps[0]).equals(String.format("%.6f", preLon)) &&
String.format("%.6f", gps[1]).equals(String.format("%.6f", preLat))) {
continue;
}
double[] doubles = CoordinateUtil.gcj02LngLatToWGS84(gps[0], gps[1]);
//double[] doubles = CoordinateUtils.transformGcj02toWgs84(, );
list.add(doubles);
preLon = gps[0];
preLat = gps[1];
}
return list;
}
}

View File

@@ -0,0 +1,126 @@
import android.util.Log
import com.mogo.service.v2n.utils.CoordinateUtil
import com.zhidaoauto.map.data.point.LonLatPoint
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
class DynamicInterpolator {
companion object {
private const val TAG = "DynamicInterpolator"
}
private val channel = Channel<LonLatPoint>(Channel.CONFLATED) // 最新输入的坐标
private var job: Job? = null // 当前插值任务
var lastCoordinate: LonLatPoint? = null
var lastLineCoordinate: LonLatPoint? = null
private fun areNumbersEqual(num1: Double, num2: Double): Boolean {
// 将 num1 和 num2 乘以 1,000,000 然后转换为整数
val num1Scaled = (num1 * 1_000_000).toLong()
val num2Scaled = (num2 * 1_000_000).toLong()
// 直接比较两个整数
return num1Scaled == num2Scaled
}
private fun isLocationEqual(l1:LonLatPoint, l2:LonLatPoint):Boolean {
return areNumbersEqual(l1.latitude, l2.latitude) && areNumbersEqual(l1.longitude, l2.longitude)
}
fun clear() {
lastCoordinate = null
lastLineCoordinate = null
}
fun start(onInterpolated: (LonLatPoint) -> Unit) { // 回调函数) {
CoroutineScope(Dispatchers.Default).launch {
for (newCoordinate in channel) {
// 如果有未完成的任务,取消并立即回调最后一个插值点
job?.let {
if (it.isActive) {
it.cancel() // 取消当前任务
Log.d(TAG, "job is active, cancelAndJoin, call back lastCoordinate")
// lastCoordinate?.let {
// onInterpolated(it) // 回调当前任务的最后一个点
// }
}
}
// 如果有前一个坐标,则进行插值计算
lastCoordinate?.let { previous ->
val locationEqual = isLocationEqual(previous, newCoordinate)
var isNeedUpdate = true
lastLineCoordinate?.let {
val dis = CoordinateUtil.calculateLineDistanceLatLon(
it.latitude,
it.longitude,
newCoordinate.latitude,
newCoordinate.longitude
)
if (dis < 1.0) {
isNeedUpdate = false
}
}
if (!locationEqual && isNeedUpdate) {
lastLineCoordinate = newCoordinate
job = launch {
calculateInterpolations(previous, newCoordinate, onInterpolated)
}
}
}
// 更新最后坐标
lastCoordinate = newCoordinate
}
}
}
// 启动监听输入数据
// 计算插值并回调
private suspend fun calculateInterpolations(
start: LonLatPoint,
end: LonLatPoint,
onInterpolated: (LonLatPoint) -> Unit
) {
val times = end.satelliteTime - start.satelliteTime
//val numPoints = (i/intervalMillis).toInt() // 设置插值点的数量
var newTime = times
if (newTime > 1000) {
newTime = 1000
}
newTime -= 150
val numPoints = 2
val delay = newTime / (numPoints + 1)
//val t = i/numPoints
val interpolatedPoints = GeoInterpolationUtils.greatCircleInterpolateWithLatLon(
start.latitude, start.longitude, start.angle,
end.latitude, end.longitude, end.angle,
numPoints
)
Log.d(TAG, "start:${start},end:${end}, times:${times},numPoints:${numPoints},delay:${delay}, interpolatedPoints:${interpolatedPoints}")
// 按照时间间隔回调每个插值点1
for (point in interpolatedPoints) {
val (latLon, azimuth) = point
LonLatPoint().apply {
latitude = latLon.first
longitude = latLon.second
angle = azimuth
speed = end.speed
}.let {
onInterpolated(it)
delay(delay)
}
// 回调
// 延迟时间间隔
}
onInterpolated(end)
}
// 外部调用来传入新的数据
fun inputData(data: LonLatPoint) {
CoroutineScope(Dispatchers.Default).launch {
Log.d(TAG, "inputData location:${data}, time:${data.satelliteTime}, delay:${System.currentTimeMillis() - data.satelliteTime}")
channel.send(data)
}
}
}

View File

@@ -0,0 +1,65 @@
import kotlin.math.*
object GeoInterpolationUtils {
// 计算两点之间的大圆距离(弧长)
private fun haversineDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val earthRadius = 6371.0 // 地球半径,单位:公里
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val a = sin(dLat / 2).pow(2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(dLon / 2).pow(2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return earthRadius * c // 返回距离,单位:公里
}
// 计算方位角的最短差值处理跨越0度的情况
private fun calculateAzimuthDifference(startAzimuth: Double, endAzimuth: Double): Double {
var delta = endAzimuth - startAzimuth
if (delta > 180) {
delta -= 360 // 跨越 0 度的情况
} else if (delta < -180) {
delta += 360 // 跨越 0 度的情况
}
return delta
}
// 使用球面线性插值Slerp进行大圆路径插值经纬度
fun greatCircleInterpolateWithLatLon(
startLat: Double, startLon: Double, startAzimuth: Double,
endLat: Double, endLon: Double, endAzimuth: Double,
numPoints: Int
): List<Pair<Pair<Double, Double>, Double>> {
val interpolatedPoints = mutableListOf<Pair<Pair<Double, Double>, Double>>()
// 计算大圆距离
val distance = haversineDistance(startLat, startLon, endLat, endLon)
// 插值
for (i in 1..numPoints) {
val fraction = i.toDouble() / (numPoints + 1) // 插值比例,确保均匀分布
// 使用球面线性插值Slerp计算经纬度
val a = sin((1 - fraction) * distance) / sin(distance)
val b = sin(fraction * distance) / sin(distance)
val x = a * cos(Math.toRadians(startLat)) * cos(Math.toRadians(startLon)) +
b * cos(Math.toRadians(endLat)) * cos(Math.toRadians(endLon))
val y = a * cos(Math.toRadians(startLat)) * sin(Math.toRadians(startLon)) +
b * cos(Math.toRadians(endLat)) * sin(Math.toRadians(endLon))
val z = a * sin(Math.toRadians(startLat)) + b * sin(Math.toRadians(endLat))
val interpolatedLat = Math.toDegrees(atan2(z, sqrt(x * x + y * y)))
val interpolatedLon = Math.toDegrees(atan2(y, x))
// 计算角度差并进行线性插值
val azimuthDifference = calculateAzimuthDifference(startAzimuth, endAzimuth)
val interpolatedAzimuth = startAzimuth + fraction * azimuthDifference
// 保存插值结果
interpolatedPoints.add(Pair(Pair(interpolatedLat, interpolatedLon), interpolatedAzimuth))
}
return interpolatedPoints
}
}

View File

@@ -89,6 +89,13 @@ dependencies {
api rootProject.ext.dependencies.mogochainbase
api rootProject.ext.dependencies.mogoservicebiz
api ('com.mogo.service:mogo-v2x-enhanced:1.3.8'){
exclude group: 'com.google.protobuf', module: 'protoc'
exclude group: 'com.google.protobuf', module: 'protobuf-java'
exclude group: 'com.google.protobuf', module: 'protobuf-java-util'
exclude group: 'com.google.protobuf', module: 'protobuf-javalite'
}
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()