[BadCase]代码提交

This commit is contained in:
renwenjie
2022-01-19 15:54:30 +08:00
parent 63b99ee896
commit 48d9baa3bb
29 changed files with 1304 additions and 107 deletions

View File

@@ -4,75 +4,83 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'android-aspectjx'
apply plugin: 'bugly'
apply plugin: 'apm-plugin'
if (!isAndroidTestBuild()) {
apply plugin: 'apm-plugin'
}
//apply ByteX宿主
apply plugin: 'bytex'
ByteX {
enable true
enableInDebug true
logLevel "DEBUG"
if (!isAndroidTestBuild()) {
apply plugin: 'bytex'
ByteX {
enable true
enableInDebug true
logLevel "DEBUG"
}
}
apply plugin: 'bytex.threadOpt'
if (!isAndroidTestBuild()) {
apply plugin: 'bytex.threadOpt'
thread_opt {
enable true
enableInDebug true
rxJavaIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
rxJavaComputationReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineDefaultReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
}
thread_opt {
enable true
enableInDebug true
rxJavaIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
rxJavaComputationReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineDefaultReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
/**
* 方便使用systrace工具在工程侧打点便于分析工程侧性能问题
*/
apply plugin: 'bytex.systrace'
systrace {
/**
* 交付时要关闭,会有性能损耗
*/
enable false
enableInDebug false
/**
* - 是否使用[Trace.beginAsyncSection(String, int)/Trace.endAsyncSection(String, int)]进行打点
* - 默认使用[Trace.beginSection(String)/Trace.endSection()]进行打点
*/
isTraceAsync false
/**
* - 是否在运行时只针对主线程打点,其它线程不打
*/
isOnlyMainThread false
/**
* - 是否忽略对类的静态构造方法打点
* - 默认不忽略
*/
isIgnoreClinitMethod false
/**
* - 是否忽略对类中的简单方法打点
* 简单方法定义:
* - 空方法
* - get/set 方法
* - 单独的方法,方法体内没有调用其它方法
* - 默认不忽略
*/
isIgnoreSampleMethod false
/**
* - 针对特定类集合,配置打点白名单,在此集合中的类中的所有方法不打点
* - 支持正则表达式
*/
whiteListForClass = []
/**
* - 针对特定包名集合,配置打点白名单,所有类以此包名为前缀的类不打点
* - 支持正则表达式
*/
whiteListForPackage = []
}
}
/**
* 方便使用systrace工具在工程侧打点便于分析工程侧性能问题
*/
apply plugin: 'bytex.systrace'
systrace {
/**
* 交付时要关闭,会有性能损耗
*/
enable false
enableInDebug false
/**
* - 是否使用[Trace.beginAsyncSection(String, int)/Trace.endAsyncSection(String, int)]进行打点
* - 默认使用[Trace.beginSection(String)/Trace.endSection()]进行打点
*/
isTraceAsync false
/**
* - 是否在运行时只针对主线程打点,其它线程不打
*/
isOnlyMainThread false
/**
* - 是否忽略对类的静态构造方法打点
* - 默认不忽略
*/
isIgnoreClinitMethod false
/**
* - 是否忽略对类中的简单方法打点
* 简单方法定义:
* - 空方法
* - get/set 方法
* - 单独的方法,方法体内没有调用其它方法
* - 默认不忽略
*/
isIgnoreSampleMethod false
/**
* - 针对特定类集合,配置打点白名单,在此集合中的类中的所有方法不打点
* - 支持正则表达式
*/
whiteListForClass = []
/**
* - 针对特定包名集合,配置打点白名单,所有类以此包名为前缀的类不打点
* - 支持正则表达式
*/
whiteListForPackage = []
}
/*apply plugin: 'chain.log.hook'
hooklog{
@@ -146,6 +154,10 @@ android {
targetCompatibility 1.8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
@@ -199,6 +211,10 @@ android {
packagingOptions {
exclude 'META-INF/io.netty.versions.properties'
}
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
repositories {
@@ -236,31 +252,38 @@ dependencies {
apply from: "./functions/tts.gradle"
apply from: "./functions/och.gradle"
androidTestImplementation rootProject.ext.dependencies.androidx_test_core
androidTestImplementation rootProject.ext.dependencies.androidx_test_core_ktx
androidTestImplementation rootProject.ext.dependencies.androidx_unit_ext
androidTestImplementation rootProject.ext.dependencies.androidx_unit_ext_ktx
androidTestImplementation rootProject.ext.dependencies.androidx_runner
androidTestImplementation rootProject.ext.dependencies.androidx_espresso_core
}
ApmPlugin {
// 是否进行插桩
enable true
// 是否在Debug包插桩默认不插桩
enableInDebug true
// DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
logLevel "DEBUG"
// 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
startSwitch = true
// 页面响应开关监控Activity的生命周期耗时
pageLoadSwitch = true
// 网络监控开关监控okhttp3的网络请求
okHttp3Switch = true
// 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
whiteList = [
"com.mogo"
]
// 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
blackList = [
if (!isAndroidTestBuild()) {
ApmPlugin {
// 是否进行插桩
enable true
// 是否在Debug包插桩默认不插桩
enableInDebug true
// DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
logLevel "DEBUG"
// 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
startSwitch = true
// 页面响应开关监控Activity的生命周期耗时
pageLoadSwitch = true
// 网络监控开关:监控okhttp3的网络请求
okHttp3Switch = true
// 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
whiteList = [
"com.mogo"
]
// 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
blackList = [
]
]
}
}
android.applicationVariants.all { variant ->
@@ -305,3 +328,13 @@ def getWorkingBranchHash() {
println "Working branch hash: " + workingBranchHash
return workingBranchHash
}
boolean isAndroidTestBuild() {
for (String s : gradle.startParameter.taskNames) {
if (s.contains("AndroidTest")) {
return true
}
}
return false
}

View File

@@ -0,0 +1,145 @@
package com.mogo.functions.test
import androidx.lifecycle.lifecycleScope
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.function.hmi.ui.MoGoHmiFragment
import com.mogo.eagle.core.function.main.MainLauncherActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
class AutoPilotBadCaseTest {
lateinit var launch: ActivityScenario<MainLauncherActivity>
@Before
fun launch() {
launch = ActivityScenario.launch(MainLauncherActivity::class.java)
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance1(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
delay(TimeUnit.SECONDS.toMillis(5))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance2():Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
if (index in 1..4) {
delay(TimeUnit.SECONDS.toMillis(15))
} else {
delay(Random(20).nextLong())
}
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
if (index == 5) {
f.lifecycleScope.cancel()
}
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance3(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
delay(TimeUnit.SECONDS.toMillis(20))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
private suspend fun ensureMoGoHmiFragmentShow(): MoGoHmiFragment = suspendCancellableCoroutine {
launch.onActivity { itx ->
val executor = Executors.newSingleThreadScheduledExecutor()
executor.scheduleAtFixedRate({
var find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
while (find == null) {
find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
}
while (!find.isResumed) {
Thread.sleep(500)
}
it.resumeWith(Result.success(find))
try {
Thread.sleep(500)
executor.shutdownNow()
} catch (e: Throwable) {
e.printStackTrace()
}
}, 50, 500, TimeUnit.MILLISECONDS)
}
}
}

View File

@@ -246,7 +246,16 @@ ext {
life_cycle_scope : "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0",
view_model_scope : "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0",
live_data_scope : "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
live_data_scope : "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0",
//========================== Unit Test ======================
androidx_test_core : "androidx.test:core:1.2.1",
androidx_test_core_ktx : "androidx.test:core-ktx:1.2.0",
androidx_unit_ext : "androidx.test.ext:junit:1.1.2",
androidx_unit_ext_ktx : "androidx.test.ext:junit-ktx:1.1.2",
androidx_runner : "androidx.test:runner:1.3.0",
androidx_espresso_core : "androidx.test.espresso:espresso-core:3.3.0",
]
}

View File

@@ -109,4 +109,8 @@ class MoGoAutopilotProvider :
override fun setIPCReboot() {
AdasManager.getInstance().rebootIPC()
}
override fun recordCause(key: String?, name: String?, reason: String?) {
AdasManager.getInstance().recordCause(key, name, reason)
}
}

View File

@@ -147,6 +147,14 @@ class WarningFloat {
}
}
fun setWindowWidth(width: Int) = apply {
this.config.width = width
}
fun setWindowHeight(height: Int) = apply {
this.config.height = height
}
/**
* 创建浮窗包括Activity浮窗和系统浮窗如若系统浮窗无权限先进行权限申请
*/

View File

@@ -65,8 +65,8 @@ internal class WarningFloatWindowHelper(
// 没有边界限制,允许窗口扩展到屏幕外
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
else WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
width = config.width
height = config.height
// 如若设置了固定坐标,直接定位
if (config.locationPair != Pair(0, 0)) {

View File

@@ -1,6 +1,7 @@
package com.mogo.eagle.core.function.hmi.notification
import android.view.View
import android.view.WindowManager
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
import com.mogo.eagle.core.function.hmi.notification.enums.ShowPattern
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
@@ -59,4 +60,9 @@ data class WarningNotificationConfig(
// Callbacks
var callbacks: OnFloatCallbacks? = null,
// 窗口宽度
var width: Int = WindowManager.LayoutParams.WRAP_CONTENT,
// 窗口高度
var height: Int = WindowManager.LayoutParams.WRAP_CONTENT
)

View File

@@ -1,23 +1,30 @@
package com.mogo.eagle.core.function.hmi.ui
import android.animation.Animator
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.view.*
import android.view.animation.OvershootInterpolator
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.data.config.HmiBuildConfig
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
@@ -30,12 +37,21 @@ import com.mogo.eagle.core.function.hmi.ui.notice.NoticeBannerView
import com.mogo.eagle.core.function.hmi.ui.notice.NoticeNormalBannerView
import com.mogo.eagle.core.function.hmi.ui.setting.DebugSettingView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotAndCheckView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotBadCaseView
import com.mogo.eagle.core.function.hmi.ui.tools.post
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.module.common.enums.EventTypeEnum
import kotlinx.android.synthetic.main.fragment_hmi.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
/**
* @author xiaoyuzhou
@@ -45,7 +61,7 @@ import kotlinx.android.synthetic.main.fragment_hmi.*
@Route(path = MoGoFragmentPaths.PATH_FRAGMENT_HMI)
class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>(),
IMoGoWaringProvider,
MoGoWarningContract.View {
MoGoWarningContract.View, IMoGoAutopilotIdentifyListener {
private val TAG = "MoGoHmiFragment"
// DebugSettingView
@@ -64,9 +80,106 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
private var toolsView: AutoPilotAndCheckView? = null
private var autoPilotToolsFloat: WarningFloat.Builder? = null
// 检测、自动驾驶速度设置
private var toolsViewFloat: WarningFloat.Builder? = null
@Volatile
private var autoPilotBadCaseEntrance: View? = null
private var autoPilotBadCaseView: AutoPilotBadCaseView? = null
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val DURATION_FOR_DISMISS = TimeUnit.SECONDS.toMillis(10)
}
@ExperimentalCoroutinesApi
private val channel by lazy {
Channel<AutoPilotRecordResult?>(UNLIMITED).also {
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.Default) {
while (!it.isClosedForReceive) {
try {
val entrance = autoPilotBadCaseEntrance
val old = entrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
if (entrance == null || old == null || old.consumed) {
Logger.d(TAG, "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
var record: AutoPilotRecordResult? = null
var newT = try {
it.receive()?.also { record = it }?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < DURATION_FOR_DISMISS)) {
Logger.d(TAG, "-- step -- 2 --")
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d(TAG, "record: [$record] is displaying and consuming ~~~" )
showBadCaseEntrance(it)
}
continue
}
while (oldT != 0L && newT != 0L && (newT - oldT) >= DURATION_FOR_DISMISS) {
Logger.d(TAG, "record: [$record] has been discarded, because it has been timeout." )
oldT = newT
newT = try {
it.receive()?.also {
record = it
}?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d(TAG, "record: [$record] is displaying for rest ..." )
showBadCaseEntrance(it)
}
} else {
Logger.d(TAG, "record: [$old] hasn't been consumed~~~~" )
}
} finally {
delay(1000)
}
}
}
}
}
}
private val handler by lazy {
Handler(Handler.Callback {
if (it.what == MSG_WHAT_DISMISS_BAD_CASE_ENTRY) {
val entrance = autoPilotBadCaseEntrance
if (entrance != null && entrance.visibility == View.VISIBLE) {
Logger.d(TAG, "${TimeUnit.MILLISECONDS.toMinutes(DURATION_FOR_DISMISS)}分种后BadCase入口消失")
(entrance.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult)?.let { itx ->
itx.consumed = true
}
entrance.visibility = View.GONE
}
return@Callback true
}
return@Callback false
})
}
override fun vipIdentification(visible: Boolean) {
ThreadUtils.runOnUiThread {
@@ -138,6 +251,155 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
}
@ExperimentalCoroutinesApi
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
record ?: return
Logger.d(TAG, "onAutopilotRecordResult:$record")
if (record.type == 1 && record.stat == 100) {
lifecycleScope.launchWhenResumed {
channel.send(record)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
}
@VisibleForTesting
fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Logger.d(TAG, "showBadCaseEntrance:$record")
lifecycleScope.launch {
if (vs_bad_case_entrance?.parent != null) {
val inflateView = vs_bad_case_entrance.inflate()
autoPilotBadCaseEntrance = inflateView
}
val entrance = autoPilotBadCaseEntrance
if (entrance != null) {
if (entrance.visibility != View.VISIBLE) {
entrance.visibility = View.VISIBLE
}
entrance.setTag(R.id.autopilot_badcase_record, record)
entrance.onClick {
showBadCasesFloat {
it.visibility = View.GONE
}
}
dismissBadCaseEntryAfterSomeTime()
}
}
}
private fun showBadCasesFloat(dismiss: (() -> Unit)?) {
Logger.d(TAG, "showBadCaseToolsFloat")
context?.let { it ->
if (autoPilotToolsFloat == null) {
if (autoPilotBadCaseView == null) {
autoPilotBadCaseView = AutoPilotBadCaseView(it).also { itx ->
itx.onDismiss {
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
CallerAutoPilotManager.recordCause(record?.key, record?.fileName, null)
dismissBadCaseFloatView()
}
itx.onSelect {
lifecycleScope.launch {
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
try {
val params = mutableMapOf<String, String>()
autoPilotBadCaseEntrance?.apply {
params["carLicense"] =
MoGoAiCloudClientConfig.getInstance().sn
params["filename"] = record?.fileName ?: ""
params["filesize"] = record?.total.toString()
params["key"] = record?.key ?: ""
params["reason"] = it.reason ?: ""
params["timestamp"] = System.currentTimeMillis().toString()
}
val response = post(params)
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Logger.e(TAG, "返回的body是空的~~~")
return@launch
}
if (body.code == 200) {
Logger.i(TAG, "ok:${body}")
dismissBadCaseFloatView()
dismiss?.invoke()
ToastUtils.showShort("接管反馈成功~")
return@launch
}
Logger.e(TAG, "fail:${body}")
}
} catch (t: Throwable) {
t.printStackTrace()
ToastUtils.showShort("网络请求失败,请尝试联网~")
Logger.e(TAG, "exception:${t.message}")
} finally {
record?.consumed = true
CallerAutoPilotManager.recordCause(
record?.key,
record?.fileName,
it.id
)
}
}
}
}
}
autoPilotToolsFloat = WarningFloat.with(it)
.setTag("BadCaseCollectFloat")
.setLayout(autoPilotBadCaseView!!)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
})
.show()
} else {
autoPilotToolsFloat?.show()
}
}
}
private fun dismissBadCaseEntryAfterSomeTime() {
handler.removeMessages(MSG_WHAT_DISMISS_BAD_CASE_ENTRY)
handler.sendEmptyMessageDelayed(MSG_WHAT_DISMISS_BAD_CASE_ENTRY, DURATION_FOR_DISMISS)
}
private fun showToolsFloat() {
Logger.d(TAG, "showToolsFloat")
context?.let {
@@ -696,9 +958,18 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
// toolsView?.setStatus(true)
}
private fun dismissBadCaseFloatView() {
autoPilotToolsFloat?.let {
WarningFloat.dismiss(it.config.floatTag, false)
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
handler.removeCallbacksAndMessages(null)
}

View File

@@ -0,0 +1,338 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
import android.os.Handler
import android.util.AttributeSet
import android.util.StateSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.Keep
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.annotations.Expose
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.debug.DebugConfig.getNetMode
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.tools.BadCaseEntity.Reason
import com.mogo.eagle.core.network.RetrofitFactory
import com.mogo.eagle.core.network.utils.GsonUtil
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.Utils
import kotlinx.android.synthetic.main.layout_badcase_collect.view.*
import kotlinx.coroutines.*
import kotlinx.coroutines.android.asCoroutineDispatcher
import retrofit2.Response
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.POST
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
private typealias OnDismissCallback = () -> Unit
private typealias OnSelectCallback = (Reason) -> Unit
interface BadCaseApi {
@FormUrlEncoded
@POST("/yycp-vehicle-management-service/tool/badcase/add")
suspend fun post(@FieldMap map: Map<String, String>): Response<PostResult>
@GET("/yycp-vehicle-management-service/tool/badcase/reasons")
suspend fun get(): Response<BadCaseEntity>
}
@Keep
class BadCaseEntity {
var code: Int = -1
var data: List<Reason>? = null
var msg: String? = null
var success: Boolean = false
var total: Int = -1
@Expose(serialize = false, deserialize = false)
var isBuildIn: Boolean = false
@Keep
class Reason {
var id: String? = null
var reason: String? = null
/**
* 业务字段,不参与序列化和反序列化
*/
@Expose(deserialize = false, serialize = false)
var isChecked: Boolean = false
}
}
@Keep
class PostResult {
var code: Int = -1
var msg: String? = null
var data: Array<String>? = null
var success: Boolean = false
override fun toString(): String {
return "Result(code=$code, msg=$msg, data=${data?.contentToString()}, success=$success)"
}
}
private fun getHost(): String = if (getNetMode() == DebugConfig.NET_MODE_RELEASE) /*"http://dzt.zhidaozhixing.com" */"http://front.zdjs-private-test.myghost.zhidaoauto.com" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"
internal suspend fun post(map: Map<String, String>): Response<PostResult> {
return RetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).post(map)
}
private suspend fun get(): Response<BadCaseEntity>? {
return try { RetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).get() } catch (t: Throwable) { t.printStackTrace(); null}
}
private suspend fun updateCache(entity: BadCaseEntity) = suspendCancellableCoroutine<Unit> {
try {
val future = ThreadUtils.getIoPool().submit {
try {
val gson = GsonUtil.jsonFromObject(entity)
Sp.saveBody(gson)
it.resumeWith(success(Unit))
} catch (t: Throwable) {
it.resumeWith(failure(t))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
} catch (e: Throwable) {
it.resumeWith(failure(e))
}
}
private suspend fun getCache(): BadCaseEntity? = suspendCancellableCoroutine {
try {
val body = Sp.getBody()
if (body != null && body.isNotEmpty()) {
val future = ThreadUtils.getIoPool().submit {
try {
val result = GsonUtil.objectFromJson(body, BadCaseEntity::class.java)
it.resumeWith(success(result))
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
}
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
private fun getBuildIn(): BadCaseEntity = BadCaseEntity().also { itx ->
val data = mutableListOf<Reason>()
data += Reason().also {
it.reason = "变道有干扰"
}
data += Reason().also {
it.reason = "遇红绿灯未停车"
}
data += Reason().also {
it.reason = "遇障碍物未停车"
}
itx.data = data
itx.isBuildIn = true
}
internal object Sp {
private val sp by lazy {
Utils.getApp().getSharedPreferences("bad_case_prefs", MODE_PRIVATE)
}
@SuppressLint("ApplySharedPref")
fun saveBody(body: String) {
sp.edit().putString("prefs", body).commit()
}
fun getBody(): String? {
return sp.getString("prefs", null)
}
}
class AutoPilotBadCaseView: ConstraintLayout {
private var dismiss: OnDismissCallback? = null
private var select: OnSelectCallback? = null
private var cases: List<Reason>? = null
private var selectCase: Reason? = null
private val scope = CoroutineScope(Handler().asCoroutineDispatcher() + SupervisorJob())
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.layout_badcase_collect, this, true)
background = ColorDrawable(Color.parseColor("#F0151D41"))
isClickable = true
layoutParams = ViewGroup.LayoutParams(960.toPixels().toInt(), 1528.toPixels().toInt())
close?.onClick {
dismiss?.invoke()
}
cancel?.also {
it.background = shape(solid = Color.parseColor("#3B4577"), radius = 16)
it.onClick {
dismiss?.invoke()
}
}
ok?.also {
val enabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(35, 146, 252), endColor = Color.rgb(28, 75, 252))
val disabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(24, 71, 129), endColor = Color.rgb(21, 46, 129))
it.background = object : StateListDrawable() {}.also { itx ->
itx.addState(intArrayOf(android.R.attr.state_enabled), enabled)
itx.addState(StateSet.WILD_CARD, disabled)
}
it.onClick {
val case = selectCase
if (case != null) {
select?.invoke(case)
}
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val adapter = rv_take_over?.adapter
if (adapter != null && adapter.itemCount > 0) {
return
}
scope.launch {
showLoading()
try {
get()?.takeIf { it.isSuccessful && it.body() != null && it.body()?.code == 200 }?.let {
val entity = it.body()!!
try {
updateCache(entity)
} catch (t: Throwable) {
t.printStackTrace()
} finally {
updateBadCaseList(entity)
}
}
?:
getCache()?.also {
updateBadCaseList(it)
}
?:
updateBadCaseList(getBuildIn())
} finally {
hideLoading()
}
}
}
private fun updateBadCaseList(body: BadCaseEntity) {
cases = body.data
rv_take_over?.let {
it.layoutManager = LinearLayoutManager(it.context, LinearLayoutManager.VERTICAL, false)
it.adapter = object : RecyclerView.Adapter<BadCaseViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BadCaseViewHolder = BadCaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_badcase_item, parent, false))
override fun onBindViewHolder(holder: BadCaseViewHolder, position: Int) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
if (position >= cases.size) {
return
}
val case = cases[position]
holder.bindData(case)
}
override fun getItemCount(): Int = cases?.size ?: 0
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
scope.cancel("Cancel all for AutoPilotBadCaseView#onDetachedFromWindow")
}
private fun showLoading() {
pb?.let {
it.visibility = View.VISIBLE
}
}
private fun hideLoading() {
pb?.let {
it.visibility = View.INVISIBLE
}
}
private inner class BadCaseViewHolder(item: View) : RecyclerView.ViewHolder(item) {
private val check: ImageView = item.findViewById(R.id.check)
private val reason: TextView = item.findViewById(R.id.reason)
init {
check.background = StateListDrawable().also {
it.addState(intArrayOf(android.R.attr.state_selected), ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_check))
it.addState(StateSet.WILD_CARD, ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_default))
}
}
@SuppressLint("NotifyDataSetChanged")
fun bindData(case: Reason) {
check.isSelected = case.isChecked
reason.text = case.reason ?: ""
if (case.isChecked) {
ok?.isSelected = true
}
itemView.onClick {
case.isChecked = !case.isChecked
selectCase = case
cancelOtherChecked(case)
ok?.isEnabled = hasCheckedItem()
rv_take_over?.adapter?.notifyDataSetChanged()
}
}
private fun hasCheckedItem(): Boolean = cases?.find { it.isChecked } != null
private fun cancelOtherChecked(case: Reason) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
cases.filterNot { it == case }.forEach {
it.isChecked = false
}
}
}
fun onDismiss(dismiss:() -> Unit) {
this.dismiss = dismiss
}
fun onSelect(cb:(Reason) -> Unit) {
this.select = cb
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -97,6 +97,18 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/viewPerspectiveSwitch"
app:layout_goneMarginStart="50px" />
<ViewStub
android:id="@+id/vs_bad_case_entrance"
android:inflatedId="@+id/bad_case_entrance"
android:layout_width="@dimen/module_hmi_check_size"
android:layout_height="@dimen/module_hmi_check_size"
android:layout_marginStart="25px"
android:layout_marginBottom="40px"
android:layout="@layout/view_ap_badcase_entrance"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivToolsIcon"
app:layout_goneMarginStart="50px"/>
<View
android:id="@+id/viewUpgradeTips"

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:background="#151D41F0"
tools:layout_height="match_parent"
tools:layout_width="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/close"
android:layout_width="107px"
android:layout_height="107px"
android:layout_marginTop="66px"
android:layout_marginEnd="40px"
android:src="@drawable/icon_close_nor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/blue_block"
android:layout_width="14px"
android:layout_height="50px"
android:layout_marginStart="80px"
android:layout_marginTop="92px"
android:background="#2966EC"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="19px"
android:text="请选择本次接管原因"
android:textColor="#ffffff"
android:textSize="42px"
app:layout_constraintBottom_toBottomOf="@+id/blue_block"
app:layout_constraintStart_toEndOf="@+id/blue_block"
app:layout_constraintTop_toTopOf="@+id/blue_block" />
<TextView
android:id="@+id/time_of_take_over"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="113px"
android:layout_marginTop="10px"
android:textColor="#A7B6F0"
android:textSize="32px"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/blue_block"
tools:text="接管时间2021.12.21 14:32" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_take_over"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="72px"
android:overScrollMode="never"
app:layout_constraintBottom_toTopOf="@+id/ok"
android:layout_marginBottom="20px"
app:layout_constraintTop_toBottomOf="@+id/time_of_take_over" />
<TextView
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="123px"
android:paddingTop="31px"
android:paddingEnd="123px"
android:paddingBottom="31px"
android:text="确认"
android:textColor="#ffffff"
android:textSize="42px"
android:layout_marginStart="100px"
android:layout_marginEnd="100px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/cancel"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
android:enabled="false"
android:layout_marginBottom="100px"/>
<TextView
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="123px"
android:paddingTop="31px"
android:paddingEnd="123px"
android:paddingBottom="31px"
android:text="取消"
android:textColor="#ffffff"
android:textSize="42px"
android:layout_marginEnd="100px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/ok"
android:layout_marginBottom="100px"/>
<ProgressBar
android:id="@+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/Widget.Holo.ProgressBar.Large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</merge>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/dp_10"
android:paddingTop="@dimen/dp_10"
android:gravity="center_vertical">
<ImageView
android:id="@+id/check"
android:layout_width="70px"
android:layout_height="70px"
android:layout_marginStart="113px"/>
<TextView
android:id="@+id/reason"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:layout_marginStart="30px"
android:layout_marginEnd="20px"
android:textSize="42px"
android:lines="1"
android:ellipsize="end"
tools:text="转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧转弯过于靠近路侧"/>
</LinearLayout>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="@dimen/dp_10"
android:scaleType="center"
tools:layout_width="@dimen/module_hmi_check_size"
tools:layout_height="@dimen/module_hmi_check_size"
android:background="@drawable/icon_car_ap_badcase_entrance" />

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="autopilot_badcase_record" type="id" />
</resources>

View File

@@ -0,0 +1,80 @@
package com.mogo.eagle.core.data.autopilot
class AutoPilotRecordResult {
/**
* 磁盘可用空间(M)
*/
var diskFree: Int = 0
/**
* 采集时长
*/
var duration: Double = 0.0
/**
* 保存的文件名
*/
var fileName: String? = ""
/**
* 其他信息,包含错误信息等
*/
var note: String? = ""
/**
* 域控制器定义的bag key
*/
var key: String? = ""
/**
* 采集状态:
* 100 - 采集成功,自动结束
* 101 - 采集成功,收到结束指令
* 200 - 采集失败
* 201 - 采集中
* 300 - 开始采集
*/
var stat: Int = 0
/**
* 任务类型1-badcase采集任务,2-地图数据采集任务
*/
var type: Int = 0
/**
* 任务ID
*/
var id: Int = 0
/**
* 时间戳格式YYYY-MM-DD-hh-mm-ss
*/
var timestamp: String? = ""
/**
* 此次采集数据总大小M
*/
var total: Int? = 0
/**
* 记录此条数据是否已消费
*/
@Volatile
var consumed: Boolean = false
override fun toString(): String {
return "AutoPilotRecordResult(diskFree=$diskFree, duration=$duration, fileName=$fileName, note=$note, key=$key, stat=$stat, type=$type, id=$id, timestamp=$timestamp, total=$total)"
}
}

View File

@@ -1,5 +1,6 @@
package com.mogo.eagle.core.function.api.autopilot
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage
import com.mogo.eagle.core.data.traffic.TrafficData
import java.util.*
@@ -16,13 +17,19 @@ interface IMoGoAutopilotIdentifyListener {
*
* @param trafficData 交通元素信息列表
*/
fun onAutopilotIdentifyDataUpdate(trafficData: ArrayList<TrafficData>?)
fun onAutopilotIdentifyDataUpdate(trafficData: ArrayList<TrafficData>?) {}
/**
* 报警信息
*
* @param autopilotWarnMessage 预警信息
*/
fun onAutopilotWarnMessage(autopilotWarnMessage: AutopilotWarnMessage?)
fun onAutopilotWarnMessage(autopilotWarnMessage: AutopilotWarnMessage?) {}
/**
* 采集结果回调
*/
fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {}
}

View File

@@ -60,6 +60,14 @@ public interface IMoGoAutopilotProvider extends IMoGoFunctionServerProvider {
Boolean setAutoPilotSpeed(int speed);
/**
* 记录各种失败
* @param key 采集任务的标识
* @param name 文件名字
* @param reason 原因
*/
void recordCause(String key, String name, String reason);
/**
* 关机
*/

View File

@@ -13,7 +13,7 @@ import com.mogo.eagle.core.function.call.base.CallerBase
object CallerAutoPilotManager {
private val TAG = "CallerAutoPilotManager"
private val providerApi: IMoGoAutopilotProvider
private val providerApi: IMoGoAutopilotProvider?
get() = CallerBase.getApiInstance(
IMoGoAutopilotProvider::class.java,
MogoServicePaths.PATH_AUTO_PILOT
@@ -25,7 +25,7 @@ object CallerAutoPilotManager {
* @param autoPilotIp 指定与控制器IP
*/
fun resetIpAddress(autoPilotIp: String) {
providerApi.resetIpAddress(autoPilotIp)
providerApi?.resetIpAddress(autoPilotIp)
}
/**
@@ -38,7 +38,7 @@ object CallerAutoPilotManager {
//LogUtils.eTag(TAG, "自动驾驶控制参数异常,请检查参数信息")
return
}
providerApi.startAutoPilot(controlParameters)
providerApi?.startAutoPilot(controlParameters)
}
/**
@@ -46,40 +46,44 @@ object CallerAutoPilotManager {
* 具体的json格式需要与@宋克难 进行沟通
*/
fun sendDataToAutopilot(jsonString: String) {
providerApi.sendMessageToAutopilot(jsonString)
providerApi?.sendMessageToAutopilot(jsonString)
}
/**
* 结束自动驾驶
*/
fun cancelAutoPilot() {
providerApi.cancelAutoPilot()
providerApi?.cancelAutoPilot()
}
/**
* 开启域控制器录制bag包
*/
fun recordPackage() {
providerApi.recordPackage()
providerApi?.recordPackage()
}
fun setEnableLog(isEnableLog: Boolean) {
providerApi.setEnableLog(isEnableLog)
providerApi?.setEnableLog(isEnableLog)
}
fun setIsWriteLog(isWriteLog: Boolean) {
providerApi.setIsWriteLog(isWriteLog)
providerApi?.setIsWriteLog(isWriteLog)
}
fun setAutoPilotSpeed(speed: Int): Boolean {
return providerApi.setAutoPilotSpeed(speed)
return providerApi?.setAutoPilotSpeed(speed) ?: false
}
fun recordCause(key: String?, name: String?, reason: String?) {
providerApi?.recordCause(key, name, reason)
}
fun setIPCShutDown() {
providerApi.setIPCShutDown()
providerApi?.setIPCShutDown()
}
fun setIPCReboot() {
providerApi.setIPCReboot()
providerApi?.setIPCReboot()
}
}

View File

@@ -149,4 +149,5 @@ object CallerAutoPilotStatusListenerManager : CallerBase() {
}
}
}

View File

@@ -1,6 +1,7 @@
package com.mogo.eagle.core.function.call.autopilot
import androidx.annotation.Nullable
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage
import com.mogo.eagle.core.data.traffic.TrafficData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
@@ -89,4 +90,17 @@ object CallerAutopilotIdentifyListenerManager : CallerBase() {
}
}
/**
* 采集任务记录回调
*/
fun invokeAutopilotRecordResult(result: AutoPilotRecordResult) {
M_AUTOPILOT_IDENTIFY_LISTENERS.forEach {
val tag = it.key
val listener = it.value
LogUtils.dTag(TAG, "tag:$tag listener:$listener")
listener.onAutopilotRecordResult(result)
}
}
}

View File

@@ -0,0 +1,48 @@
package com.mogo.eagle.core.utilcode.kotlin
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.util.TypedValue
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.IntRange
import com.mogo.eagle.core.utilcode.util.ClickUtils
import com.mogo.eagle.core.utilcode.util.Utils
import java.util.*
fun View.onClick(block: (View) -> Unit) {
this.setOnClickListener {
if (ClickUtils.isClickTooFrequent(this)) {
return@setOnClickListener
}
block(it)
}
}
fun shape(@ColorInt solid: Int = Color.TRANSPARENT, @ColorInt stroke: Int = Color.TRANSPARENT, @IntRange(from = 0) strokeWidth: Int = 0, @IntRange(from = 0) radius: Int = 0, radii: FloatArray = FloatArray(8).apply { Arrays.fill(this, radius.toFloat()) }, shape: Int = GradientDrawable.RECTANGLE): Drawable {
val drawable = GradientDrawable()
drawable.shape = shape
drawable.gradientType = GradientDrawable.LINEAR_GRADIENT
drawable.setColor(solid)
drawable.cornerRadii = radii
drawable.setStroke(strokeWidth, stroke)
return drawable
}
fun gradient(shape: Int = GradientDrawable.RECTANGLE, @IntRange(from = 0) radius: Int = 0, radii: FloatArray = FloatArray(8).apply { Arrays.fill(this, radius.toFloat()) }, gradientType: Int = GradientDrawable.LINEAR_GRADIENT, orientation: GradientDrawable.Orientation = GradientDrawable.Orientation.TOP_BOTTOM, centerX: Float = 0.5f, centerY: Float = 0.5f, @ColorInt startColor: Int, @ColorInt centerColor: Int = startColor, @ColorInt endColor: Int): Drawable{
val drawable = GradientDrawable(orientation, intArrayOf(endColor, centerColor, startColor))
drawable.shape = shape
drawable.gradientType = gradientType
drawable.orientation = orientation
drawable.cornerRadii = radii
drawable.setGradientCenter(centerX, centerY)
return drawable
}
fun Int.toPixels(context: Context? = null): Float {
val ctx = context ?: Utils.getApp()
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, this.toFloat(), ctx.resources.displayMetrics)
}

View File

@@ -618,4 +618,25 @@ public class ClickUtils {
mBitmapDrawable.draw(canvas);
}
}
public static boolean isClickTooFrequent(View view) {
return isClickTooFrequent(view, 500);
}
public static boolean isClickTooFrequent(View view, int duration) {
try {
Object tag = view.getTag(R.id.tag_click_time);
long past = tag == null ? 0L : (Long)tag;
long now = System.currentTimeMillis();
if (Math.abs(now - past) < (long)duration) {
return true;
}
view.setTag(R.id.tag_click_time, now);
} catch (Exception var7) {
}
return false;
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="tag_click_time" type="id" />
</resources>

View File

@@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
import com.google.gson.Gson;
import com.mogo.eagle.core.data.autopilot.ADASTrajectoryInfo;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
@@ -281,4 +282,14 @@ public class AdasEventManager implements
}
}
@Override
public void onAutopilotRecordResult(AutoPilotRecordResult result) {
if (result != null) {
for (IAdasDataListener listener : iAdasEventListeners) {
if (listener != null) {
listener.onAutopilotRecordResult(result);
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
package com.mogo.module.adas;
import com.mogo.eagle.core.data.autopilot.ADASTrajectoryInfo;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
@@ -62,4 +63,10 @@ public interface IAdasDataListener {
}
/**
* 采集任务结果回调
* @param record 结果数据
*/
default void onAutopilotRecordResult(AutoPilotRecordResult record) {}
}

View File

@@ -3,6 +3,7 @@ package com.mogo.module.adas;
import android.util.Log;
import com.mogo.eagle.core.data.autopilot.ADASTrajectoryInfo;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
@@ -168,7 +169,20 @@ public class OnAdasListenerAdapter implements OnAdasListener {
@Override
public void onAutopilotRecord(AutopilotRecordResult result) {
if (result != null) {
AutoPilotRecordResult real = new AutoPilotRecordResult();
real.setDiskFree(result.getDiskFree());
real.setId(result.getId());
real.setDuration(result.getDuration());
real.setNote(result.getNote());
real.setTotal(result.getTotalSize());
real.setType(result.getType());
real.setFileName(result.getFilename());
real.setKey(result.getKey());
real.setStat(result.getStat());
real.setTimestamp(result.getTimestamp());
CallerAutopilotIdentifyListenerManager.INSTANCE.invokeAutopilotRecordResult(real);
}
}
@@ -199,8 +213,6 @@ public class OnAdasListenerAdapter implements OnAdasListener {
}
/**
* 工控机升级状态
* @param info 工控机升级状态