[BadCase]BadCase记录添加持久化逻辑

This commit is contained in:
renwj
2022-01-25 16:36:17 +08:00
parent 46bdb9d200
commit a108a1f955
6 changed files with 120 additions and 37 deletions

View File

@@ -39,7 +39,7 @@ class AutoPilotBadCaseTest {
.onEach {
delay(TimeUnit.SECONDS.toMillis(5))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.diskFree = (100 + index).toLong()
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
@@ -70,7 +70,7 @@ class AutoPilotBadCaseTest {
delay(Random(20).nextLong())
}
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.diskFree = (100 + index).toLong()
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
@@ -101,7 +101,7 @@ class AutoPilotBadCaseTest {
.onEach {
delay(TimeUnit.SECONDS.toMillis(20))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.diskFree = (100 + index).toLong()
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index

View File

@@ -54,6 +54,11 @@ dependencies {
kapt rootProject.ext.dependencies.aroutercompiler
kapt rootProject.ext.dependencies.androidxroomcompiler
implementation rootProject.ext.dependencies.androidxroomruntime
implementation rootProject.ext.dependencies.androidxroomktx
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation rootProject.ext.dependencies.modulecommon

View File

@@ -7,7 +7,6 @@ import android.text.TextUtils
import android.util.Log
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
@@ -38,6 +37,7 @@ 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.Repository
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
@@ -92,7 +92,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val DURATION_FOR_DISMISS = TimeUnit.HOURS.toMillis(4)
private val CASE_EXPIRE_DURATION = TimeUnit.HOURS.toMillis(4)
}
@ExperimentalCoroutinesApi
@@ -105,7 +105,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
val entrance = autoPilotBadCaseEntrance
val old = entrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
if (entrance == null || old == null || old.consumed) {
Logger.d("QQQ", "-- step -- 1 --")
Log.d("QQQ", "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time ?: 0L
@@ -125,17 +125,17 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
0L
}
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < DURATION_FOR_DISMISS)) {
Logger.d("QQQ", "-- step -- 2 --")
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < CASE_EXPIRE_DURATION)) {
Log.d("QQQ", "-- step -- 2 --")
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d("QQQ", "record: [$record] is displaying and consuming ~~~" )
Log.d("QQQ", "record: [$record] is displaying and consuming ~~~" )
showBadCaseEntrance(it)
}
continue
}
while (oldT != 0L && newT != 0L && (newT - oldT) >= DURATION_FOR_DISMISS) {
Logger.d("QQQ", "record: [$record] has been discarded, because it has been timeout." )
while (oldT != 0L && newT != 0L && (newT - oldT) >= CASE_EXPIRE_DURATION) {
Log.d("QQQ", "record: [$record] has been discarded, because it has been timeout." )
oldT = newT
newT = try {
it.receive()?.also {
@@ -149,11 +149,11 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d("QQQ", "record: [$record] is displaying for rest ..." )
Log.d("QQQ", "record: [$record] is displaying for rest ..." )
showBadCaseEntrance(it)
}
} else {
Logger.d("QQQ", "record: [$old] hasn't been consumed~~~~" )
Log.d("QQQ", "record: [$old] hasn't been consumed~~~~" )
}
} finally {
delay(1000)
@@ -169,10 +169,19 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
if (it.what == MSG_WHAT_DISMISS_BAD_CASE_ENTRY) {
val entrance = autoPilotBadCaseEntrance
if (entrance != null && entrance.visibility == View.VISIBLE) {
Logger.d(TAG, "${DURATION_FOR_DISMISS}毫秒后BadCase入口消失")
(entrance.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult)?.let { itx ->
itx.consumed = true
val record = entrance.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
record?.consumed = true
record?.let { itx ->
lifecycleScope.launch(Dispatchers.IO) {
try {
val i = Repository.dao().deleteRecord(itx)
Log.d("QQQ", "delete result: $i")
} catch (t: Throwable) {
Log.d("QQQ", "---- delete error: ${t.message}")
}
}
}
dismissBadCaseFloatView()
entrance.visibility = View.GONE
}
return@Callback true
@@ -212,18 +221,41 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
@ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.IO) {
val dao = Repository.dao()
try {
dao.getAllUnConsumedRecords()?.forEach {
channel.send(it)
}
} catch (t: Throwable) {
t.printStackTrace()
}
}
}
}
@ExperimentalCoroutinesApi
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
record ?: return
Logger.d("QQQ", "onAutopilotRecordResult:$record")
Log.d("QQQ", "onAutopilotRecordResult:$record")
if (record.type == 1 && record.stat == 100) {
lifecycleScope.launchWhenResumed {
channel.send(record)
withContext(Dispatchers.IO) {
val dao = Repository.dao()
try {
dao.insertRecord(record)
} catch (t: Throwable) {
t.printStackTrace()
}
record.also {
channel.send(it)
}
}
}
}
}
@@ -233,15 +265,15 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
}
@VisibleForTesting
fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Logger.d("QQQ", "showBadCaseEntrance:$record")
private fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Log.d("QQQ", "showBadCaseEntrance:$record")
lifecycleScope.launch {
if (vs_bad_case_entrance?.parent != null) {
val inflateView = vs_bad_case_entrance.inflate()
autoPilotBadCaseEntrance = inflateView
}
val entrance = autoPilotBadCaseEntrance
Log.d("QQQ", "show --- 1 ----")
if (entrance != null) {
if (entrance.visibility != View.VISIBLE) {
entrance.visibility = View.VISIBLE
@@ -258,7 +290,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
private fun showBadCasesFloat(dismiss: (() -> Unit)?) {
Logger.d("QQQ", "showBadCaseToolsFloat")
Log.d("QQQ", "showBadCaseToolsFloat")
context?.let { it ->
if (autoPilotToolsFloat == null) {
if (autoPilotBadCaseView == null) {
@@ -286,7 +318,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Logger.e("QQQ", "返回的body是空的~~~")
Log.e("QQQ", "返回的body是空的~~~")
return@launch
}
if (body.code == 200) {
@@ -298,15 +330,24 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
record?.fileName,
it.id, it.reason)
ToastUtils.showShort("接管反馈成功~")
record?.consumed = true
record?.also {
it.consumed = true
withContext(Dispatchers.IO) {
try {
Repository.dao().deleteRecord(record)
} catch (t: Throwable) {
Log.d("QQQ", "---- delete error 2: ${t.message}")
}
}
}
return@launch
}
Logger.e("QQQ", "fail:${body}")
Log.e("QQQ", "fail:${body}")
}
} catch (t: Throwable) {
t.printStackTrace()
ToastUtils.showShort("网络请求失败,请尝试联网~")
Logger.e("QQQ", "exception:${t.message}")
Log.e("QQQ", "exception:${t.message}")
}
}
}
@@ -354,7 +395,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
private fun dismissBadCaseEntryAfterSomeTime() {
handler.removeMessages(MSG_WHAT_DISMISS_BAD_CASE_ENTRY)
handler.sendEmptyMessageDelayed(MSG_WHAT_DISMISS_BAD_CASE_ENTRY, DURATION_FOR_DISMISS)
handler.sendEmptyMessageDelayed(MSG_WHAT_DISMISS_BAD_CASE_ENTRY, CASE_EXPIRE_DURATION)
}
private fun showToolsFloat() {

View File

@@ -20,9 +20,11 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.room.*
import com.google.gson.annotations.Expose
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.debug.DebugConfig.getNetMode
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
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
@@ -41,10 +43,41 @@ import retrofit2.http.POST
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
private typealias OnDismissCallback = () -> Unit
private typealias OnSelectCallback = (Reason) -> Unit
object Repository {
fun dao(): Dao {
return Room.databaseBuilder(Utils.getApp(), RecordDb::class.java, "bad-cases").build().dao()
}
@Database(entities = [
AutoPilotRecordResult::class
],
version = 1,
exportSchema = false)
abstract class RecordDb : RoomDatabase() {
abstract fun dao(): Dao
}
@androidx.room.Dao
interface Dao {
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRecord(record: AutoPilotRecordResult): Long
@Transaction
@Delete
suspend fun deleteRecord(record: AutoPilotRecordResult): Int
@Query("SELECT * FROM record ORDER BY timestamp ASC")
suspend fun getAllUnConsumedRecords(): List<AutoPilotRecordResult>?
}
}
interface BadCaseApi {
@FormUrlEncoded

View File

@@ -50,7 +50,9 @@ dependencies {
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.androidxroomruntime
implementation rootProject.ext.dependencies.androidxroomktx
kapt rootProject.ext.dependencies.androidxroomcompiler
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogo_core_res
} else {

View File

@@ -1,13 +1,18 @@
package com.mogo.eagle.core.data.autopilot
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.text.SimpleDateFormat
import java.util.*
@Entity(tableName = "record")
class AutoPilotRecordResult {
/**
* 磁盘可用空间(M)
*/
@ColumnInfo(name = "disk_free")
var diskFree: Long = 0
/**
@@ -19,6 +24,7 @@ class AutoPilotRecordResult {
/**
* 保存的文件名
*/
@ColumnInfo(name = "file_name")
var fileName: String? = ""
@@ -27,7 +33,6 @@ class AutoPilotRecordResult {
*/
var note: String? = ""
/**
* 域控制器定义的bag key
*/
@@ -54,26 +59,23 @@ class AutoPilotRecordResult {
*/
var id: Int = 0
/**
* 时间戳格式YYYY-MM-DD-hh-mm-ss
*/
var timestamp: String? = ""
@PrimaryKey
var timestamp: String = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).format(Date())
/**
* 此次采集数据总大小M
*/
var total: Long? = 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)"
}