diff --git a/app/src/androidTest/java/com/mogo/functions/test/AutoPilotBadCaseTest.kt b/app/src/androidTest/java/com/mogo/functions/test/AutoPilotBadCaseTest.kt index 8657b3107e..a1b7cf49b3 100644 --- a/app/src/androidTest/java/com/mogo/functions/test/AutoPilotBadCaseTest.kt +++ b/app/src/androidTest/java/com/mogo/functions/test/AutoPilotBadCaseTest.kt @@ -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 diff --git a/core/function-impl/mogo-core-function-hmi/build.gradle b/core/function-impl/mogo-core-function-hmi/build.gradle index 6ba321c28a..8cd0c27a2c 100644 --- a/core/function-impl/mogo-core-function-hmi/build.gradle +++ b/core/function-impl/mogo-core-function-hmi/build.gradle @@ -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 diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt index 63ceb9eb7c..d457bf2f06 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/MoGoHmiFragment.kt @@ -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 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 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 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 } } 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 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 } } + @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 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 } 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 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 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 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() { diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/tools/AutoPilotBadCase.kt b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/tools/AutoPilotBadCase.kt index eeefae7d24..b25dc8388a 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/tools/AutoPilotBadCase.kt +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/tools/AutoPilotBadCase.kt @@ -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? + } +} + interface BadCaseApi { @FormUrlEncoded diff --git a/core/mogo-core-data/build.gradle b/core/mogo-core-data/build.gradle index cb2946cb56..618b44468c 100644 --- a/core/mogo-core-data/build.gradle +++ b/core/mogo-core-data/build.gradle @@ -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 { diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/autopilot/AutoPilotRecord.kt b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/autopilot/AutoPilotRecord.kt index ba553b39e9..6a0dfa92f8 100644 --- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/autopilot/AutoPilotRecord.kt +++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/autopilot/AutoPilotRecord.kt @@ -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)" }