This commit is contained in:
zhongchao
2022-03-11 19:41:19 +08:00
parent 9275ed5ff2
commit 180bdfd50a
35 changed files with 1260 additions and 834 deletions

View File

@@ -3,6 +3,7 @@ package com.mogo.och.bus.fragment;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -13,6 +14,8 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group; import androidx.constraintlayout.widget.Group;
import com.mogo.commons.AbsMogoApplication; import com.mogo.commons.AbsMogoApplication;
@@ -21,8 +24,15 @@ import com.mogo.commons.mvp.IView;
import com.mogo.commons.mvp.MvpFragment; import com.mogo.commons.mvp.MvpFragment;
import com.mogo.commons.mvp.Presenter; import com.mogo.commons.mvp.Presenter;
import com.mogo.commons.voice.AIAssist; import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage;
import com.mogo.eagle.core.data.config.HmiBuildConfig;
import com.mogo.eagle.core.data.traffic.TrafficData;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener; import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager; import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager; import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger; import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler; import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
@@ -35,6 +45,8 @@ import com.mogo.och.bus.R;
import com.mogo.och.bus.view.BusArcView; import com.mogo.och.bus.view.BusArcView;
import com.mogo.och.bus.view.SlidePanelView; import com.mogo.och.bus.view.SlidePanelView;
import java.util.ArrayList;
/** /**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况 * 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况
* <p> * <p>
@@ -42,7 +54,7 @@ import com.mogo.och.bus.view.SlidePanelView;
* *
* @author tongchenfei * @author tongchenfei
*/ */
public abstract class BaseOchBusTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener { public abstract class BaseOchBusTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener, IMoGoAutopilotIdentifyListener {
private static final String TAG = "BaseOchFragment"; private static final String TAG = "BaseOchFragment";
@@ -200,17 +212,45 @@ public abstract class BaseOchBusTabFragment<V extends IView, P extends Presenter
// mBadcaseBtn的visible显示逻辑在showBadcaseEntrance内处理 // mBadcaseBtn的visible显示逻辑在showBadcaseEntrance内处理
mBadcaseBtn = findViewById(R.id.module_mogo_och_badcase_iv); mBadcaseBtn = findViewById(R.id.module_mogo_och_badcase_iv);
CallerHmiManager.INSTANCE.registerBadCaseCallback( // CallerHmiManager.INSTANCE.registerBadCaseCallback(
() -> { // onShow() // () -> { // onShow()
return mBadcaseBtn; }, // return mBadcaseBtn; },
() -> { // onHide() // () -> { // onHide()
return null; }); // return null; });
if (mBadcaseBtn != null) {
CallerDevaToolsManager.INSTANCE.initBadCase(mBadcaseBtn, null, null);
if (!HmiBuildConfig.isShowBadCaseView) {
CallerAutopilotIdentifyListenerManager.INSTANCE.addListener(TAG, this);
}
}
//设置升级小红点提示 默认隐藏 //设置升级小红点提示 默认隐藏
mUpgradeTipIv = findViewById(R.id.module_och_bus_upgrade_red_tip); mUpgradeTipIv = findViewById(R.id.module_och_bus_upgrade_red_tip);
CallerHmiManager.INSTANCE.registerUpgradeTipsCallback(() -> mUpgradeTipIv); CallerHmiManager.INSTANCE.registerUpgradeTipsCallback(() -> mUpgradeTipIv);
} }
@Override
public void onDestroyView() {
super.onDestroyView();
if (!HmiBuildConfig.isShowBadCaseView) {
CallerAutopilotIdentifyListenerManager.INSTANCE.removeListener(TAG);
}
}
@Override
public void onAutopilotRecordResult(@Nullable AutoPilotRecordResult record) {
if (!HmiBuildConfig.isShowBadCaseView && record != null && record.getType() == 1 && record.getStat() == 100) {
CallerDevaToolsManager.INSTANCE.onReceiveBadCaseRecord(record);
}
}
@Override
public void onAutopilotIdentifyDataUpdate(@Nullable ArrayList<TrafficData> trafficData) { }
@Override
public void onAutopilotWarnMessage(@Nullable AutopilotWarnMessage autopilotWarnMessage) { }
/** /**
* 测试到站 * 测试到站
*/ */
@@ -367,11 +407,6 @@ public abstract class BaseOchBusTabFragment<V extends IView, P extends Presenter
return null; return null;
} }
@Override
public void onDestroyView() {
super.onDestroyView();
}
/** /**
* 获取站点面板view在{@link #initViews()}时候添加到container中 * 获取站点面板view在{@link #initViews()}时候添加到container中
* *

View File

@@ -3,6 +3,7 @@ package com.mogo.och.taxi.ui;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
@@ -15,6 +16,8 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group; import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
@@ -24,10 +27,17 @@ import com.mogo.commons.mvp.IView;
import com.mogo.commons.mvp.MvpFragment; import com.mogo.commons.mvp.MvpFragment;
import com.mogo.commons.mvp.Presenter; import com.mogo.commons.mvp.Presenter;
import com.mogo.commons.voice.AIAssist; import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters; import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage;
import com.mogo.eagle.core.data.config.HmiBuildConfig;
import com.mogo.eagle.core.data.traffic.TrafficData;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener; import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager; import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager; import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager; import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerSmpManager; import com.mogo.eagle.core.function.call.map.CallerSmpManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger; import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
@@ -38,6 +48,8 @@ import com.mogo.module.common.constants.DataTypes;
import com.mogo.module.common.view.OnPreventFastClickListener; import com.mogo.module.common.view.OnPreventFastClickListener;
import com.mogo.och.taxi.R; import com.mogo.och.taxi.R;
import java.util.ArrayList;
/** /**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况 * 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况
@@ -46,7 +58,7 @@ import com.mogo.och.taxi.R;
* *
* @author tongchenfei * @author tongchenfei
*/ */
public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener { public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener, IMoGoAutopilotIdentifyListener {
private static final String TAG = "BaseOchFragment"; private static final String TAG = "BaseOchFragment";
private LinearLayout ctvAutopilotStatus; private LinearLayout ctvAutopilotStatus;
@@ -133,12 +145,18 @@ public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presente
// mBadcaseBtn的visible显示逻辑在showBadcaseEntrance内处理 // mBadcaseBtn的visible显示逻辑在showBadcaseEntrance内处理
mBadcaseBtn = findViewById(R.id.module_och_taxi_badcase_iv); mBadcaseBtn = findViewById(R.id.module_och_taxi_badcase_iv);
CallerHmiManager.INSTANCE.registerBadCaseCallback( // CallerHmiManager.INSTANCE.registerBadCaseCallback(
() -> { // onShow() // () -> { // onShow()
return mBadcaseBtn; }, // return mBadcaseBtn; },
() -> { // onHide() // () -> { // onHide()
return null; }); // return null; });
if (mBadcaseBtn != null) {
CallerDevaToolsManager.INSTANCE.initBadCase(mBadcaseBtn, null, null);
if (!HmiBuildConfig.isShowBadCaseView) {
CallerAutopilotIdentifyListenerManager.INSTANCE.addListener(TAG, this);
}
}
//设置升级小红点提示 默认隐藏 //设置升级小红点提示 默认隐藏
mUpgradeTipIv = findViewById(R.id.module_och_taxi_upgrade_red_tip); mUpgradeTipIv = findViewById(R.id.module_och_taxi_upgrade_red_tip);
CallerHmiManager.INSTANCE.registerUpgradeTipsCallback(() -> mUpgradeTipIv); CallerHmiManager.INSTANCE.registerUpgradeTipsCallback(() -> mUpgradeTipIv);
@@ -240,6 +258,29 @@ public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presente
protected void onGoToTaxiOrders(){ protected void onGoToTaxiOrders(){
} }
@Override
public void onDestroyView() {
super.onDestroyView();
if (!HmiBuildConfig.isShowBadCaseView) {
CallerAutopilotIdentifyListenerManager.INSTANCE.removeListener(TAG);
}
}
@Override
public void onAutopilotIdentifyDataUpdate(@Nullable ArrayList<TrafficData> trafficData) { }
@Override
public void onAutopilotRecordResult(@Nullable AutoPilotRecordResult record) {
if (!HmiBuildConfig.isShowBadCaseView && record != null && record.getType() == 1 && record.getStat() == 100) {
CallerDevaToolsManager.INSTANCE.onReceiveBadCaseRecord(record);
}
}
@Override
public void onAutopilotWarnMessage(@Nullable AutopilotWarnMessage autopilotWarnMessage) { }
public void showNotice(String notice) { public void showNotice(String notice) {
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
AIAssist.getInstance(getContext()).speakTTSVoice(notice); AIAssist.getInstance(getContext()).speakTTSVoice(notice);
@@ -409,11 +450,6 @@ public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presente
return panelView; return panelView;
} }
@Override
public void onDestroyView() {
super.onDestroyView();
}
/** /**
* 获取站点面板view在{@link #initViews()}时候添加到container中 * 获取站点面板view在{@link #initViews()}时候添加到container中
* *

View File

@@ -234,16 +234,23 @@ dependencies {
compileOnly rootProject.ext.dependencies.adasapi compileOnly rootProject.ext.dependencies.adasapi
compileOnly rootProject.ext.dependencies.adasconfigapi compileOnly rootProject.ext.dependencies.adasconfigapi
debugImplementation rootProject.ext.dependencies.debugleakcanary debugImplementation rootProject.ext.dependencies.debugleakcanary
releaseImplementation rootProject.ext.dependencies.releaseleakcanary releaseImplementation rootProject.ext.dependencies.releaseleakcanary
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogo_core_function_main implementation rootProject.ext.dependencies.mogo_core_function_main
implementation rootProject.ext.dependencies.ttspad implementation rootProject.ext.dependencies.ttspad
androidTestImplementation rootProject.ext.dependencies.mogo_core_function_call
androidTestImplementation rootProject.ext.dependencies.mogo_core_res
androidTestImplementation rootProject.ext.dependencies.mogo_core_function_hmi
androidTestImplementation rootProject.ext.dependencies.mogo_core_function_notice
} else { } else {
implementation project(':core:function-impl:mogo-core-function-main') implementation project(':core:function-impl:mogo-core-function-main')
implementation project(':tts:tts-pad') implementation project(':tts:tts-pad')
androidTestImplementation project(':core:mogo-core-function-call')
androidTestImplementation project(':core:mogo-core-res')
androidTestImplementation project(':core:function-impl:mogo-core-function-hmi')
androidTestImplementation project(':core:function-impl:mogo-core-function-notice')
} }
apply from: "./functions/och.gradle" apply from: "./functions/och.gradle"

View File

@@ -1,11 +1,10 @@
package com.mogo.functions.test package com.mogo.functions.test
import androidx.lifecycle.lifecycleScope
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.function.hmi.ui.MoGoHmiFragment import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import com.mogo.eagle.core.function.main.MainLauncherActivity import com.mogo.eagle.core.function.main.MainLauncherActivity
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@@ -14,7 +13,6 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.random.Random import kotlin.random.Random
@@ -31,14 +29,14 @@ class AutoPilotBadCaseTest {
@ExperimentalCoroutinesApi @Test @ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance1(): Unit = runBlocking(Dispatchers.Main) { fun showBadCaseEntrance1(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow() delay(5000)
var index = 0 var index = 0
(1 until 50) (1 until 50)
.map { it } .map { it }
.asFlow() .asFlow()
.onEach { .onEach {
delay(TimeUnit.SECONDS.toMillis(5)) delay(TimeUnit.SECONDS.toMillis(5))
f.onAutopilotRecordResult(AutoPilotRecordResult().also { CallerDevaToolsManager.onReceiveBadCaseRecord(AutoPilotRecordResult().also {
it.diskFree = (100 + index).toLong() it.diskFree = (100 + index).toLong()
it.duration = 60.0 it.duration = 60.0
it.fileName = "/user/general/record_$index.log" it.fileName = "/user/general/record_$index.log"
@@ -58,7 +56,7 @@ class AutoPilotBadCaseTest {
@ExperimentalCoroutinesApi @Test @ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance2():Unit = runBlocking(Dispatchers.Main) { fun showBadCaseEntrance2():Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow() delay(5000)
var index = 0 var index = 0
(1 until 50) (1 until 50)
.map { it } .map { it }
@@ -69,7 +67,7 @@ class AutoPilotBadCaseTest {
} else { } else {
delay(Random(20).nextLong()) delay(Random(20).nextLong())
} }
f.onAutopilotRecordResult(AutoPilotRecordResult().also { CallerDevaToolsManager.onReceiveBadCaseRecord(AutoPilotRecordResult().also {
it.diskFree = (100 + index).toLong() it.diskFree = (100 + index).toLong()
it.duration = 60.0 it.duration = 60.0
it.fileName = "/user/general/record_$index.log" it.fileName = "/user/general/record_$index.log"
@@ -80,11 +78,6 @@ class AutoPilotBadCaseTest {
it.timestamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date()) it.timestamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
index++ index++
}) })
if (index == 5) {
f.lifecycleScope.cancel()
}
} }
.flowOn(Dispatchers.Default) .flowOn(Dispatchers.Default)
.collect() .collect()
@@ -93,14 +86,13 @@ class AutoPilotBadCaseTest {
@ExperimentalCoroutinesApi @Test @ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance3(): Unit = runBlocking(Dispatchers.Main) { fun showBadCaseEntrance3(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0 var index = 0
(1 until 50) (1 until 50)
.map { it } .map { it }
.asFlow() .asFlow()
.onEach { .onEach {
delay(TimeUnit.SECONDS.toMillis(20)) delay(TimeUnit.SECONDS.toMillis(20))
f.onAutopilotRecordResult(AutoPilotRecordResult().also { CallerDevaToolsManager.onReceiveBadCaseRecord(AutoPilotRecordResult().also {
it.diskFree = (100 + index).toLong() it.diskFree = (100 + index).toLong()
it.duration = 60.0 it.duration = 60.0
it.fileName = "/user/general/record_$index.log" it.fileName = "/user/general/record_$index.log"
@@ -116,30 +108,4 @@ class AutoPilotBadCaseTest {
.collect() .collect()
delay(TimeUnit.HOURS.toMillis(2)) 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

@@ -4,6 +4,7 @@ plugins {
id 'kotlin-android-extensions' id 'kotlin-android-extensions'
id 'kotlin-kapt' id 'kotlin-kapt'
id 'com.alibaba.arouter' id 'com.alibaba.arouter'
id 'com.google.protobuf'
} }
android { android {
@@ -39,21 +40,45 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += [
"-Xopt-in=kotlin.RequiresOptIn"
]
}
protobuf {
protoc {
artifact = rootProject.ext.dependencies.google_protoc
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {}
}
}
}
}
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
implementation rootProject.ext.dependencies.coroutinescore
implementation rootProject.ext.dependencies.arouter implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.mogologlib implementation rootProject.ext.dependencies.mogologlib
implementation rootProject.ext.dependencies.mogochainbase implementation rootProject.ext.dependencies.mogochainbase
kapt rootProject.ext.dependencies.androidxroomcompiler
implementation rootProject.ext.dependencies.androidxroomruntime
implementation rootProject.ext.dependencies.androidxroomktx
implementation rootProject.ext.dependencies.androidx_datastore
implementation rootProject.ext.dependencies.google_proto_java
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.androidxrecyclerview
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi implementation rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.modulecommon implementation rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.mogo_core_utils implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_function_api implementation rootProject.ext.dependencies.mogo_core_function_api
implementation rootProject.ext.dependencies.mogo_core_function_call implementation rootProject.ext.dependencies.mogo_core_function_call

View File

@@ -2,8 +2,10 @@ package com.zhjt.mogo_core_function_devatools
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.View
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_INIT import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_INIT
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_MSG import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_MSG
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_CONNECT_STATUS import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_CONNECT_STATUS
@@ -18,6 +20,7 @@ import com.mogo.eagle.core.utilcode.util.DeviceUtils
import com.mogo.eagle.core.utilcode.util.Utils import com.mogo.eagle.core.utilcode.util.Utils
import com.zhidao.loglib.fw.FileWriteManager import com.zhidao.loglib.fw.FileWriteManager
import com.zhidao.loglib.fw.FwBuild import com.zhidao.loglib.fw.FwBuild
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
import com.zhjt.service.chain.core.ChainTraceStarter import com.zhjt.service.chain.core.ChainTraceStarter
@@ -100,6 +103,14 @@ class DevaToolsProvider : IDevaToolsProvider {
FileWriteManager.getInstance().operateChainMap(fwBuildMap) FileWriteManager.getInstance().operateChainMap(fwBuildMap)
} }
override fun initBadCase(view: View, onShow: (() -> Unit)?, onHide: (() -> Unit)?) {
BadCaseManager.init(view, onShow, onHide)
}
override fun onReceiveBadCaseRecord(record: AutoPilotRecordResult) {
BadCaseManager.onReceiveBadCaseRecord(record)
}
override fun onDestroy() { override fun onDestroy() {
MogoLogCatchManager.onDestroy() MogoLogCatchManager.onDestroy()
} }

View File

@@ -0,0 +1,281 @@
package com.zhjt.mogo_core_function_devatools.badcase
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle.Event
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.mvp.BadCasePresenter
import com.zhjt.mogo_core_function_devatools.badcase.mvp.BadCaseView
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import java.lang.ref.WeakReference
import java.util.concurrent.TimeUnit
internal object BadCaseManager : LifecycleEventObserver {
const val TAG = "BadCase"
/**
* 超过此时间case入口自动消失
*/
private val CASE_EXPIRE_DURATION: Long = TimeUnit.HOURS.toMillis(4)/* TimeUnit.SECONDS.toMillis(10) */
private var onShow: (() -> Unit)? = null
private var onHide: (() -> Unit)? = null
private var hideFloat: (() -> Unit)? = null
@Volatile
private var record: AutoPilotRecord? = null
@Volatile
private var viewHolder : WeakReference<View>? = null
@Volatile
private var dismissJob: Job? = null
@OptIn(ExperimentalCoroutinesApi::class)
private var channel: Channel<AutoPilotRecord> = Channel(Channel.RENDEZVOUS)
get() = if (field.isClosedForReceive || field.isClosedForSend) {
field = Channel(Channel.RENDEZVOUS)
field
} else {
field
}
private val presenter by lazy {
BadCasePresenter()
}
@Volatile
private var scope: LifecycleCoroutineScope? = null
get() = if (field == null) {
field = viewHolder?.get()?.lifecycleOwner?.lifecycleScope
field
} else {
field
}
fun init(view: View, onShow: (() -> Unit)?, onHide: (() -> Unit)?) {
this.viewHolder = WeakReference(view)
view.lifecycleOwner.lifecycle.addObserver(this)
this.onShow = onShow
this.onHide = onHide
register()
recoverBadCase()
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun register() {
scope?.launch(Dispatchers.Default) {
while (true) {
Log.d(TAG, "---- 开始监听BadCase事件 ----")
val old = record
if (old == null || old.consumed) {
Log.d(TAG, "---- 当前事件已消费 -- value: $old")
var receive = channel.receive()
var oldT = record?.toLongTime() ?: 0L
var newT = receive.toLongTime()
if (isValid(oldT, newT)) {
record = receive
Log.d(TAG, "---- 时间有效,开始展示入口 ---")
withContext(Dispatchers.Main) {
showBadCaseInternal(receive)
}
continue
}
Log.d(TAG, "---- 时间无效,移除管道中无用数据 ---")
presenter.deleteRecord(receive)
while (oldT != 0L && newT != 0L && (newT - oldT) >= CASE_EXPIRE_DURATION) {
oldT = newT
receive = channel.receive()
newT = receive.toLongTime()
presenter.deleteRecord(receive)
}
receive.takeIf { it.key != old?.key }?.also {
Log.d(TAG, "record: [$record] is displaying for rest ...")
record = receive
withContext(Dispatchers.Main) {
showBadCaseInternal(it)
}
}
} else {
Log.d(TAG, "record: [$old] hasn't been consumed~~~~")
withContext(Dispatchers.Main) {
showEntry()
}
delay(1000)
}
}
}
}
private fun recoverBadCase() {
scope?.launchWhenCreated {
val lastModified = presenter.getLastModified()
val list = withContext(Dispatchers.IO) {
try {
Log.d(TAG, " --- 1 ----")
Log.d(TAG, "恢复持久化的数据 - 最后修改时间:$lastModified")
presenter.getUnConsumedRecords().fold(mutableListOf<AutoPilotRecord>()) {
acc, record ->
if (isValid(lastModified, record.toLongTime())) {
acc.add(record)
} else {
presenter.deleteRecord(record)
}
acc
}
} catch (t: Throwable) {
emptyList()
}
}
if (list.isEmpty()) {
Log.d(TAG, "没有要恢复的数据")
} else {
list.forEach {
Log.d(TAG, "恢复的接管数据:$it")
channel.send(it)
}
}
}
}
private fun isValid(oldT: Long, newT: Long): Boolean {
return oldT == 0L || newT == 0L || (newT - oldT >= 0 && (newT - oldT) < CASE_EXPIRE_DURATION)
}
fun onReceiveBadCaseRecord(record: AutoPilotRecordResult) {
scope?.launch {
val newRecord = record.toRecord()
withContext(Dispatchers.IO) {
presenter.insertRecord(newRecord)
channel.send(newRecord)
}
}
}
private fun CoroutineScope.showBadCaseInternal(record: AutoPilotRecord) = launch {
viewHolder?.get()?.also {
presenter.updateLastModified(record.toLongTime())
showEntry()
it.onClick {
showBadCaseFloat(
onDismiss = {
hideFloat?.invoke()
hideFloat = null
},
onSelect = { reason ->
val uploadResult = presenter.upload(mutableMapOf<String, String>().also { itx ->
itx["carLicense"] = MoGoAiCloudClientConfig.getInstance().sn
itx["filename"] = record.fileName ?: ""
itx["filesize"] = record.total.toString()
itx["key"] = record.key ?: ""
itx["reason"] = reason.reason ?: ""
itx["duration"] = record.duration.toInt().toString()
itx["timestamp"] = record.timestamp
})
if (uploadResult == null || uploadResult.code != 200) {
ToastUtils.showShort("接管反馈失败")
} else {
ToastUtils.showShort("接管反馈成功")
record.consumed = true
withContext(Dispatchers.IO) {
presenter.deleteRecord(record)
}
hideEntry()
hideFloat?.invoke()
hideFloat = null
}
})
}
dismissAfterDelay()?.also { dismissJob = it }
}
}
private fun dismissAfterDelay(): Job? {
dismissJob?.takeIf { it.isActive }?.cancel()
return scope?.launch {
delay(CASE_EXPIRE_DURATION)
hideEntry()
record?.also {
it.consumed = true
withContext(Dispatchers.IO) {
presenter.deleteRecord(it)
}
}
}
}
private fun showEntry() {
viewHolder?.get()?.takeIf { it.visibility != View.VISIBLE }?.also {
it.toggle(true)
onShow?.invoke()
}
}
private fun hideEntry() {
viewHolder?.get()?.takeIf { it.visibility == View.VISIBLE }?.also {
it.toggle(false)
onHide?.invoke()
}
}
private fun showBadCaseFloat(onDismiss: () -> Unit, onSelect:suspend (reason: Reason) -> Unit) {
val context = viewHolder?.get()?.context ?: Utils.getApp()
BadCaseView(context).also {
it.register(record, onDismiss, onSelect)
hideFloat = CallerHmiManager.showBadCaseFloat(floatView = it)
}
}
override fun onStateChanged(source: LifecycleOwner, event: Event) {
if (event == ON_DESTROY) {
dismissJob?.takeIf { it.isActive }?.cancel()
onHide = null
onShow = null
hideFloat = null
}
}
}
fun <T: View> T.toggle(show: Boolean) {
val group = (parent as? ViewGroup) ?: return
val target = if (show) View.VISIBLE else View.GONE
takeIf { it.visibility != target }?.also {
TransitionManager.beginDelayedTransition(group, AutoTransition())
it.visibility = target
}
}
internal fun AutoPilotRecordResult.toRecord(): AutoPilotRecord = AutoPilotRecord().also {
it.id = this.id
it.stat = this.stat
it.key = this.key
it.note = this.note
it.type = this.type
it.total = this.total
it.fileName = this.fileName
it.duration = this.duration
it.diskFree = this.diskFree
it.consumed = false
}

View File

@@ -0,0 +1,19 @@
package com.zhjt.mogo_core_function_devatools.badcase.api
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.UploadResult
import retrofit2.Response
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.POST
internal interface BadCaseApi {
@FormUrlEncoded
@POST("/yycp-vehicle-management-service/tool/badcase/add")
suspend fun post(@FieldMap map: Map<String, String>): Response<UploadResult>
@GET("/yycp-vehicle-management-service/tool/badcase/reasons")
suspend fun get(): Response<BadCaseResponse>
}

View File

@@ -0,0 +1,28 @@
package com.zhjt.mogo_core_function_devatools.badcase.api.entity
import androidx.annotation.Keep
import com.google.gson.annotations.Expose
@Keep
internal class BadCaseResponse {
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
}
}

View File

@@ -0,0 +1,15 @@
package com.zhjt.mogo_core_function_devatools.badcase.api.entity
import androidx.annotation.Keep
@Keep
internal class UploadResult {
var code: Int = -1
var msg: String? = null
var data: Array<String>? = null
var success: Boolean = false
override fun toString(): String {
return "UploadResult(code=$code, msg=$msg, data=${data?.contentToString()}, success=$success)"
}
}

View File

@@ -0,0 +1,8 @@
package com.zhjt.mogo_core_function_devatools.badcase.consts
import com.mogo.commons.debug.DebugConfig
internal object BadCaseHost {
fun getHost(): String = if (DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE) "http://dzt.zhidaozhixing.com" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"
}

View File

@@ -0,0 +1,54 @@
package com.zhjt.mogo_core_function_devatools.badcase.mvp
import android.util.Log
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.UploadResult
import com.zhjt.mogo_core_function_devatools.badcase.mvp.biz.IBadCasePresenter
import com.zhjt.mogo_core_function_devatools.badcase.repository.Repository
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import kotlinx.coroutines.flow.Flow
internal class BadCasePresenter: IBadCasePresenter {
private val repository by lazy {
Repository()
}
override suspend fun loadBadCases() = repository.loadBadCases()
override suspend fun insertRecord(record: AutoPilotRecord) {
try {
repository.insert(record)
} catch (t: Throwable) {
Log.d(BadCaseManager.TAG, "-- 插入数据失败 -- msg: $t")
}
}
override suspend fun getUnConsumedRecords(): List<AutoPilotRecord> {
return try {
repository.getAllUnConsumedRecord() ?: emptyList()
} catch (t: Throwable) {
Log.d(BadCaseManager.TAG, "-- 获取所有未消费的数据失败 -- msg: $t")
emptyList()
}
}
override suspend fun deleteRecord(record: AutoPilotRecord) {
try {
repository.deleteRecord(record)
} catch (t: Throwable) {
Log.d(BadCaseManager.TAG, "-- 删除某条记录失败 -- msg: $t")
}
}
override suspend fun upload(map: Map<String, String>): UploadResult? = repository.upload(map)
override suspend fun updateLastModified(timestamp: Long) {
repository.uploadLastModified(timestamp)
}
override suspend fun getLastModified(): Long {
Log.d(BadCaseManager.TAG, " --- 2 ----")
return repository.getLastModified()
}
}

View File

@@ -0,0 +1,176 @@
package com.zhjt.mogo_core_function_devatools.badcase.mvp
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
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.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.utilcode.kotlin.*
import com.zhjt.mogo_core_function_devatools.R
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import kotlinx.android.synthetic.main.layout_badcase_collect.view.*
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
internal class BadCaseView: ConstraintLayout {
@Volatile
private var selectCase: Reason? = null
@Volatile
private var cases: List<Reason>? = null
private val presenter by lazy {
BadCasePresenter()
}
private var onDismiss: (() -> Unit)? = null
private var onSelect:(suspend (reason: Reason) -> Unit)? = null
private val scope by lazy {
lifecycleOwner.lifecycleScope
}
private var record: AutoPilotRecord? = null
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
@SuppressLint("SetTextI18n") 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 {
onDismiss?.invoke()
}
cancel?.also {
it.background = shape(solid = Color.parseColor("#3B4577"), radius = 16)
it.onClick {
onDismiss?.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 {
selectCase?.run {
scope.launch {
onSelect?.invoke(this@run)
}
}
}
}
scope.launchWhenCreated {
time_of_take_over?.text = "接管时间:${SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.getDefault()).format(record?.toLongTime() ?: System.currentTimeMillis())}"
showLoading()
presenter.loadBadCases().also {
cases = it
refresh(it)
}
hideLoading()
}
}
private fun refresh(causes: List<Reason>) {
cases = causes
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
}
}
}
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 register(record: AutoPilotRecord?, onDismiss: () -> Unit, onSelect:suspend (reason: Reason) -> Unit) {
this.record = record
this.onDismiss = onDismiss
this.onSelect = onSelect
}
}

View File

@@ -0,0 +1,24 @@
package com.zhjt.mogo_core_function_devatools.badcase.mvp.biz
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.UploadResult
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import kotlinx.coroutines.flow.Flow
internal interface IBadCasePresenter {
suspend fun loadBadCases(): List<Reason>
suspend fun updateLastModified(timestamp: Long)
suspend fun getLastModified(): Long
suspend fun upload(map: Map<String, String>): UploadResult?
suspend fun insertRecord(record: AutoPilotRecord)
suspend fun getUnConsumedRecords(): List<AutoPilotRecord>
suspend fun deleteRecord(record: AutoPilotRecord)
}

View File

@@ -0,0 +1,93 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository
import android.util.Log
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.UploadResult
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.BadCaseDbModel
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.BadCaseNetModel
import com.zhjt.mogo_core_function_devatools.badcase.repository.store.BadCaseStore
import kotlinx.coroutines.flow.Flow
internal class Repository {
private val net by lazy {
BadCaseNetModel()
}
private val db by lazy {
BadCaseDbModel()
}
private val store by lazy {
BadCaseStore
}
suspend fun loadBadCases(): List<Reason> {
return net.get()?.data?.takeIf { it.isNotEmpty() }?.also { store.updateRecords(it) } ?: store.records().takeIf { it.isNotEmpty() } ?: getBuildIn()
}
suspend fun uploadLastModified(timestamp: Long) {
store.updateLastModified(timestamp)
}
suspend fun getLastModified(): Long {
Log.d(BadCaseManager.TAG, " --- 3 ----")
return store.getLastModified()
}
private fun getBuildIn(): List<Reason> {
Log.d(BadCaseManager.TAG, "-- load cases from buildin -- 1 --")
val data = mutableListOf<Reason>()
data += Reason().also {
it.id = "1"
it.reason = "变道有干扰"
}
data += Reason().also {
it.id = "2"
it.reason = "遇红绿灯未停车"
}
data += Reason().also {
it.id = "3"
it.reason = "遇障碍物未停车"
}
data += Reason().also {
it.id = "4"
it.reason = "无法绕行"
}
data += Reason().also {
it.id = "5"
it.reason = "画龙"
}
data += Reason().also {
it.id = "6"
it.reason = "转弯过于靠近路侧"
}
data += Reason().also {
it.id = "7"
it.reason = "无故退出自动驾驶"
}
data += Reason().also {
it.id = "8"
it.reason = "其它"
}
return data
}
suspend fun upload(map: Map<String, String>): UploadResult? {
return net.upload(map)
}
suspend fun insert(record: AutoPilotRecord) {
db.dao().insertRecord(record)
}
suspend fun deleteRecord(record: AutoPilotRecord) {
db.dao().deleteRecord(record)
}
suspend fun getAllUnConsumedRecord(): List<AutoPilotRecord>? {
return db.dao().getAllUnConsumedRecords()
}
}

View File

@@ -0,0 +1,16 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.db
import androidx.room.Database
import androidx.room.RoomDatabase
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.dao.IBadCaseRecordDao
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
@Database(entities = [
AutoPilotRecord::class
],
version = 1,
exportSchema = false)
internal abstract class BadCaseDb : RoomDatabase() {
abstract fun dao(): IBadCaseRecordDao
}

View File

@@ -0,0 +1,12 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.db
import androidx.room.Room
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.dao.IBadCaseRecordDao
internal class BadCaseDbModel {
fun dao(): IBadCaseRecordDao {
return Room.databaseBuilder(Utils.getApp(), BadCaseDb::class.java, "bad-cases").build().dao()
}
}

View File

@@ -0,0 +1,19 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.db.dao
import androidx.room.*
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
@Dao
internal interface IBadCaseRecordDao {
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRecord(record: AutoPilotRecord): Long
@Transaction
@Delete
suspend fun deleteRecord(record: AutoPilotRecord): Int
@Query("SELECT * FROM record ORDER BY timestamp ASC")
suspend fun getAllUnConsumedRecords(): List<AutoPilotRecord>?
}

View File

@@ -0,0 +1,89 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.text.SimpleDateFormat
import java.util.*
@Entity(tableName = "record")
internal class AutoPilotRecord {
/**
* 磁盘可用空间(M)
*/
@ColumnInfo(name = "disk_free")
var diskFree: Long = 0
/**
* 采集时长
*/
var duration: Double = 0.0
/**
* 保存的文件名
*/
@ColumnInfo(name = "file_name")
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
*/
@PrimaryKey
var timestamp: String = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date())
/**
* 此次采集数据总大小M
*/
var total: Long? = 0
/**
* 记录此条数据是否已消费
*/
@Volatile
var consumed: Boolean = false
override fun toString(): String {
return "AutoPilotRecord(diskFree=$diskFree, duration=$duration, fileName=$fileName, note=$note, key=$key, stat=$stat, type=$type, id=$id, timestamp='$timestamp', total=$total, consumed=$consumed)"
}
fun toLongTime(): Long = try {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(this.timestamp)?.time ?: 0L
} catch (t: Throwable) {
0L
}
}

View File

@@ -0,0 +1,44 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.net
import android.util.Log
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.badcase.api.BadCaseApi
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.UploadResult
import com.zhjt.mogo_core_function_devatools.badcase.consts.BadCaseHost
internal class BadCaseNetModel {
suspend fun get(): BadCaseResponse? = try {
Log.d(BadCaseManager.TAG, "-- load cases from net -- 1 --")
MoGoRetrofitFactory
.getInstance(BadCaseHost.getHost())
.create(BadCaseApi::class.java)
.get()
.takeIf {
val body = it.body()
it.isSuccessful && body != null && (body.code == 0 || body.code == 200)
}
?.body()?.also {
Log.d(BadCaseManager.TAG, "-- load cases from net -- 2 --")
}
} catch (t: Throwable) {
Log.d(BadCaseManager.TAG, "-- load cases from net -- 3 --")
null
}
suspend fun upload(map: Map<String, String>): UploadResult? = try {
MoGoRetrofitFactory
.getInstance(BadCaseHost.getHost())
.create(BadCaseApi::class.java)
.post(map)
.takeIf {
val body = it.body()
return@takeIf it.isSuccessful && (body != null) && (body.code == 0 || body.code == 200)
}
?.body()
} catch (t: Throwable) {
null
}
}

View File

@@ -0,0 +1,109 @@
package com.zhjt.mogo_core_function_devatools.badcase.repository.store
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.Serializer
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse
import com.zhjt.mogo_core_function_devatools.badcase.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.generated.BadCauses
import com.zhjt.mogo_core_function_devatools.badcase.generated.Cause
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlin.math.log
internal object BadCaseStore {
private val serializer = object : Serializer<BadCauses> {
override val defaultValue: BadCauses
get() = BadCauses.getDefaultInstance()
override suspend fun readFrom(input: InputStream): BadCauses = suspendCancellableCoroutine {
Log.d(BadCaseManager.TAG, "--- readFrom ---")
it.invokeOnCancellation {
Thread.currentThread().interrupt()
}
try {
it.resumeWith(Result.success(BadCauses.parseFrom(input)))
} catch (t: Throwable) {
it.resumeWith(Result.failure(t))
}
}
override suspend fun writeTo(t: BadCauses, output: OutputStream) = suspendCancellableCoroutine<Unit> {
it.invokeOnCancellation {
Thread.currentThread().interrupt()
}
try {
t.writeTo(output)
it.resumeWith(Result.success(Unit))
} catch (t: Throwable) {
it.resumeWith(Result.failure(t))
}
}
}
private val store: DataStore<BadCauses> by lazy {
DataStoreFactory.create(serializer = serializer) { File(Utils.getApp().filesDir, "bad_cases.pb") }
}
suspend fun updateRecords(reasons: List<Reason>): BadCauses {
Log.d(BadCaseManager.TAG, "--- updateRecords ---")
val data = mutableListOf<Cause>()
reasons.forEach { itx ->
data += Cause.newBuilder().let {
it.id = itx.id
it.reason = itx.reason
it.build()
}
}
return store.updateData { itx ->
itx.toBuilder().clearData().addAllData(data).build()
}
}
suspend fun updateLastModified(timestamp: Long): BadCauses {
Log.d(BadCaseManager.TAG, "--- updateLastModified ---")
return store.updateData { itx ->
itx.toBuilder().setLastModified(timestamp).build()
}
}
suspend fun getLastModified(): Long {
Log.d(BadCaseManager.TAG, " --- 4 ----")
return store
.data
.catch {
if (it is IOException) {
emit(BadCauses.getDefaultInstance())
}
}
.map {
it.lastModified
}.firstOrNull() ?: 0L
}
@OptIn(FlowPreview::class)
suspend fun records(): List<Reason> {
Log.d(BadCaseManager.TAG, "-- load cases from pb -- 1 -- ")
val causes = store.data.firstOrNull()
return causes?.dataList?.map {
Reason().also { itx ->
itx.id = it.id
itx.reason = it.reason
}
}?.fold(mutableListOf()) { acc, reason ->
acc += reason
acc
} ?: emptyList()
}
}

View File

@@ -0,0 +1,17 @@
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.zhjt.mogo_core_function_devatools.badcase.generated";
option java_outer_classname = "BadCausesProto";
message BadCauses {
int64 lastModified = 1;
repeated Cause data = 2 ;
}
message Cause {
string id = 1;
string reason = 2;
}

View File

@@ -38,6 +38,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = "1.8"
}
} }
dependencies { dependencies {

View File

@@ -2,21 +2,14 @@ package com.mogo.eagle.core.function.hmi.ui
import android.animation.Animator import android.animation.Animator
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.OvershootInterpolator import android.view.animation.OvershootInterpolator
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.mvp.MvpFragment import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.voice.AIAssist import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.autopilot.AdUpgradeStateHelper import com.mogo.eagle.core.data.autopilot.AdUpgradeStateHelper
@@ -30,9 +23,9 @@ import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener 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.IMoGoWaringProvider
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener 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.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import com.mogo.eagle.core.function.call.map.CallerMapDataCollectorManager import com.mogo.eagle.core.function.call.map.CallerMapDataCollectorManager
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R import com.mogo.eagle.core.function.hmi.R
@@ -45,23 +38,15 @@ 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.notice.NoticeNormalBannerView
import com.mogo.eagle.core.function.hmi.ui.setting.DebugSettingView 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.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.function.hmi.ui.widget.V2XNotificationView
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.Logger import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.ThreadUtils import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.Utils
import com.mogo.module.common.enums.EventTypeEnum import com.mogo.module.common.enums.EventTypeEnum
import kotlinx.android.synthetic.main.fragment_hmi.* import kotlinx.android.synthetic.main.fragment_hmi.*
import kotlinx.coroutines.* 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.*
import java.util.concurrent.TimeUnit
/** /**
* @author xiaoyuzhou * @author xiaoyuzhou
@@ -94,132 +79,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
// 检测、自动驾驶速度设置 // 检测、自动驾驶速度设置
private var toolsViewFloat: WarningFloat.Builder? = null private var toolsViewFloat: WarningFloat.Builder? = null
@Volatile
private var autoPilotBadCaseEntrance: View? = null
private var autoPilotBadCaseView: AutoPilotBadCaseView? = null
private var onBadCaseShow: (() -> View)? = null
private var onBadCaseHide: (() -> Unit)? = null
private var upgradeTipsView: (() -> View)? = null private var upgradeTipsView: (() -> View)? = null
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val CASE_EXPIRE_DURATION = TimeUnit.HOURS.toMillis(4)
}
@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) {
Log.d("QQQ", "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", 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("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
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 {
Log.d("QQQ", "record: [$record] is displaying and consuming ~~~")
showBadCaseEntrance(it)
}
continue
}
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 {
record = it
}?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Log.d("QQQ", "record: [$record] is displaying for rest ...")
showBadCaseEntrance(it)
}
} else {
withContext(Dispatchers.Main) {
entrance.takeIf { it.visibility != View.VISIBLE }?.also {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
it.visibility = View.VISIBLE
}
}
Log.d("QQQ", "record: [$old] hasn't been consumed~~~~")
}
} finally {
delay(1000)
}
}
}
}
}
}
private val handler by lazy {
Handler(Handler.Callback { it ->
if (it.what == MSG_WHAT_DISMISS_BAD_CASE_ENTRY) {
val entrance = autoPilotBadCaseEntrance
if (entrance != null && entrance.visibility == View.VISIBLE) {
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()
if (entrance.visibility != View.GONE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
entrance.visibility = View.GONE
onBadCaseHide?.invoke()
}
}
return@Callback true
}
return@Callback false
})
}
override fun vipIdentification(visible: Boolean) { override fun vipIdentification(visible: Boolean) {
ThreadUtils.runOnUiThread { ThreadUtils.runOnUiThread {
Logger.d(TAG, "vipIdentification") Logger.d(TAG, "vipIdentification")
@@ -260,18 +121,6 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
CallerAutopilotIdentifyListenerManager.addListener(TAG, this) 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()
}
}
}
/*// TODO 这里后面需要改成独立进程通讯后台获取YUV /*// TODO 这里后面需要改成独立进程通讯后台获取YUV
view.postDelayed({ view.postDelayed({
@@ -282,21 +131,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) { override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
record ?: return record ?: return
Log.d("QQQ", "onAutopilotRecordResult:$record") if (HmiBuildConfig.isShowBadCaseView && record.type == 1 && record.stat == 100) {
if (record.type == 1 && record.stat == 100) { CallerDevaToolsManager.onReceiveBadCaseRecord(record)
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.IO) {
val dao = Repository.dao()
try {
dao.insertRecord(record)
} catch (t: Throwable) {
t.printStackTrace()
}
record.also {
channel.send(it)
}
}
}
} }
if (record.type == 2 && (record.stat == 101 || record.stat == 100)) { if (record.type == 2 && (record.stat == 101 || record.stat == 100)) {
CallerMapDataCollectorManager.finish(record.id, record.stat, "", record.fileName ?: "", record.note ?: "") CallerMapDataCollectorManager.finish(record.id, record.stat, "", record.fileName ?: "", record.note ?: "")
@@ -308,61 +144,6 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
CallerAutopilotIdentifyListenerManager.removeListener(TAG) CallerAutopilotIdentifyListenerManager.removeListener(TAG)
} }
private fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Log.d("QQQ", "showBadCaseEntrance:$record")
lifecycleScope.launch {
if (HmiBuildConfig.isShowBadCaseView) {
if (vsBadCaseToolsView?.parent != null) {
val inflateView = vsBadCaseToolsView.inflate()
autoPilotBadCaseEntrance = inflateView
}
}
val entrance = autoPilotBadCaseEntrance
Log.d("QQQ", "show --- 1 ----")
if (entrance != null) {
if (entrance.visibility != View.VISIBLE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
entrance.visibility = View.VISIBLE
}
entrance.setTag(R.id.autopilot_badcase_record, record)
entrance.onClick {
showBadCasesFloat {
if (it.visibility != View.GONE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
it.visibility = View.GONE
onBadCaseHide?.invoke()
}
}
}
dismissBadCaseEntryAfterSomeTime()
} else {
val view = onBadCaseShow?.invoke()
if (view != null) {
view.lifecycleOwner.lifecycle.addObserver(badCaseEntranceObserver)
view.visibility = View.GONE
autoPilotBadCaseEntrance = view
showBadCaseEntrance(record)
}
}
}
}
private val badCaseEntranceObserver = LifecycleEventObserver { _, event ->
if (event == ON_DESTROY) {
onBadCaseShow = null
onBadCaseHide = null
autoPilotBadCaseEntrance = null
}
}
override fun registerBadCaseCallback(onShow: () -> View, onHide: (() -> Unit)?) {
onBadCaseShow = onShow
onBadCaseHide = onHide
}
/** /**
*注册工控机升级提示圆点View的回调 *注册工控机升级提示圆点View的回调
@@ -391,116 +172,6 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
} }
} }
private fun showBadCasesFloat(dismiss: (() -> Unit)?) {
Log.d("QQQ", "showBadCaseToolsFloat")
context?.let { it ->
if (autoPilotToolsFloat == null) {
if (autoPilotBadCaseView == null) {
autoPilotBadCaseView = AutoPilotBadCaseView(it).also { itx ->
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
itx.tag = record
itx.onDismiss {
dismissBadCaseFloatView()
}
itx.onSelect {
lifecycleScope.launch {
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["duration"] = record?.duration?.toInt()?.toString()
?: ""
params["timestamp"] = record?.timestamp ?: ""
}
val response = post(params)
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Log.e("QQQ", "返回的body是空的~~~")
return@launch
}
if (body.code == 200) {
Logger.i(TAG, "ok:${body}")
dismissBadCaseFloatView()
dismiss?.invoke()
CallerAutoPilotManager.recordCause(
record?.key,
record?.fileName,
it.id, it.reason)
ToastUtils.showShort("接管反馈成功~")
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
}
Log.e("QQQ", "fail:${body}")
}
} catch (t: Throwable) {
t.printStackTrace()
ToastUtils.showShort("网络请求失败,请尝试联网~")
Log.e("QQQ", "exception:${t.message}")
}
}
}
}
}
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, CASE_EXPIRE_DURATION)
}
private fun showToolsFloat() { private fun showToolsFloat() {
Logger.d(TAG, "showToolsFloat") Logger.d(TAG, "showToolsFloat")
@@ -597,6 +268,10 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
} else { } else {
setToolsViewVisibility(View.GONE) setToolsViewVisibility(View.GONE)
} }
if (HmiBuildConfig.isShowBadCaseView) {
CallerDevaToolsManager.initBadCase(vsBadCaseToolsView)
}
} }
override fun getLayoutId(): Int { override fun getLayoutId(): Int {
@@ -1125,18 +800,54 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
} }
private fun dismissBadCaseFloatView() { override fun showBadCaseFloat(tag: String, floatView: View): () -> Unit {
autoPilotToolsFloat?.let { WarningFloat.with(context ?: Utils.getApp())
WarningFloat.dismiss(it.config.floatTag, false) .setTag(tag)
autoPilotToolsFloat = null .setLayout(floatView)
autoPilotBadCaseView = null .setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.START, 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
}
})
.also {
autoPilotToolsFloat = it
}
.show()
return {
autoPilotToolsFloat?.let {
WarningFloat.dismiss(it.config.floatTag, false)
autoPilotToolsFloat = null
}
} }
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
Log.d(TAG, "onDestroy") Log.d(TAG, "onDestroy")
handler.removeCallbacksAndMessages(null)
} }

View File

@@ -1,404 +0,0 @@
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 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.MoGoRetrofitFactory
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 java.text.SimpleDateFormat
import java.util.*
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
@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" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"
internal suspend fun post(map: Map<String, String>): Response<PostResult> {
return MoGoRetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).post(map)
}
private suspend fun get(): Response<BadCaseEntity>? {
return try { MoGoRetrofitFactory.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.id = "1"
it.reason = "变道有干扰"
}
data += Reason().also {
it.id = "2"
it.reason = "遇红绿灯未停车"
}
data += Reason().also {
it.id = "3"
it.reason = "遇障碍物未停车"
}
data += Reason().also {
it.id = "4"
it.reason = "无法绕行"
}
data += Reason().also {
it.id = "5"
it.reason = "画龙"
}
data += Reason().also {
it.id = "6"
it.reason = "转弯过于靠近路侧"
}
data += Reason().also {
it.id = "7"
it.reason = "无故退出自动驾驶"
}
data += Reason().also {
it.id = "8"
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
}
time_of_take_over?.text = "接管时间: ${(tag as? AutoPilotRecordResult)?.timestamp?.let {
try {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.let { itx ->
SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.getDefault()).format(itx)
}
} catch (e: Throwable) {
null
} ?: SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.getDefault()).format(Date())}}"
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
}
}

View File

@@ -98,14 +98,14 @@
app:layout_constraintStart_toEndOf="@+id/viewPerspectiveSwitch" app:layout_constraintStart_toEndOf="@+id/viewPerspectiveSwitch"
app:layout_goneMarginStart="50px" /> app:layout_goneMarginStart="50px" />
<ViewStub <ImageView
android:id="@+id/vsBadCaseToolsView" android:id="@+id/vsBadCaseToolsView"
android:inflatedId="@+id/badCaseToolsView"
android:layout_width="@dimen/module_hmi_check_size" android:layout_width="@dimen/module_hmi_check_size"
android:layout_height="@dimen/module_hmi_check_size" android:layout_height="@dimen/module_hmi_check_size"
android:layout_marginStart="25px" android:layout_marginStart="25px"
android:layout_marginBottom="40px" android:layout_marginBottom="40px"
android:layout="@layout/view_ap_badcase_entrance" android:visibility="gone"
android:background="@drawable/icon_car_ap_badcase_entrance"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivToolsIcon" app:layout_constraintStart_toEndOf="@+id/ivToolsIcon"
app:layout_goneMarginStart="50px"/> app:layout_goneMarginStart="50px"/>

View File

@@ -44,13 +44,16 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.kotlinstdlibjdk7 implementation rootProject.ext.dependencies.kotlinstdlibjdk7
implementation rootProject.ext.dependencies.arouter implementation rootProject.ext.dependencies.arouter
implementation project(path: ':libraries:mogo-adas')
kapt rootProject.ext.dependencies.aroutercompiler kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.coroutinescore implementation rootProject.ext.dependencies.coroutinescore
implementation rootProject.ext.dependencies.coroutinesandroid implementation rootProject.ext.dependencies.coroutinesandroid
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogo_core_data implementation rootProject.ext.dependencies.mogo_core_data
compileOnly rootProject.ext.dependencies.adasHigh
} else { } else {
implementation project(':core:mogo-core-data') implementation project(':core:mogo-core-data')
compileOnly project(':libraries:mogo-adas')
} }
} }

View File

@@ -1,5 +1,7 @@
package com.mogo.eagle.core.function.api.devatools package com.mogo.eagle.core.function.api.devatools
import android.view.View
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.chain.ChainLogParam import com.mogo.eagle.core.data.chain.ChainLogParam
import com.mogo.eagle.core.function.api.base.IMoGoFunctionServerProvider import com.mogo.eagle.core.function.api.base.IMoGoFunctionServerProvider
@@ -18,4 +20,17 @@ interface IDevaToolsProvider : IMoGoFunctionServerProvider {
fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>) fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>)
/**
* 初始化BadCase入口
* @param view: 展示入口
* @param onShow: BadCase入口展示时回调
* @param onHide: BadCase入口隐藏时回调
* 注: 此方法必须在[recoverBadCase]和[onReceiveBadCaseRecord]之前调用
*/
fun initBadCase(view: View, onShow: (() -> Unit)? = null, onHide: (() -> Unit)? = null)
/**
* 当工控机回调时调用
*/
fun onReceiveBadCaseRecord(record: AutoPilotRecordResult)
} }

View File

@@ -186,13 +186,6 @@ interface IMoGoWaringProvider {
fun showAdUpgradeStatus(upgradeMode : Int,downloadStatus : Int,currentProgress : Int,totalProgress : Int fun showAdUpgradeStatus(upgradeMode : Int,downloadStatus : Int,currentProgress : Int,totalProgress : Int
,downloadVersion : String,upgradeStatus : Int) ,downloadVersion : String,upgradeStatus : Int)
/**
* 注册badcase入口展示和隐藏的回调
* 当[onShow]被调用时,调用[showBadCaseEntrance]
* [onHide]回调不用关心,可以不注册
*/
fun registerBadCaseCallback(onShow:() -> View, onHide: (() -> Unit)?)
/** /**
*注册工控机升级提示圆点View的回调 *注册工控机升级提示圆点View的回调
* @param 提示圆点View * @param 提示圆点View
@@ -205,4 +198,11 @@ interface IMoGoWaringProvider {
* @param msg * @param msg
*/ */
fun showDockerRebootResult(code: Int,msg: String) fun showDockerRebootResult(code: Int,msg: String)
/**
* @param floatView: 要展示的View
* @param tag: 唯一标识
* @return 触发消失时回调
*/
fun showBadCaseFloat(tag: String = "BadCaseFloat", floatView: View): () -> Unit
} }

View File

@@ -1,5 +1,7 @@
package com.mogo.eagle.core.function.call.devatools package com.mogo.eagle.core.function.call.devatools
import android.view.View
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.chain.ChainLogParam import com.mogo.eagle.core.data.chain.ChainLogParam
import com.mogo.eagle.core.data.constants.MogoServicePaths import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
@@ -7,7 +9,7 @@ import com.mogo.eagle.core.function.call.base.CallerBase
object CallerDevaToolsManager { object CallerDevaToolsManager {
private val devaToolsProviderApi: IDevaToolsProvider private val devaToolsProviderApi: IDevaToolsProvider?
get() = CallerBase.getApiInstance( get() = CallerBase.getApiInstance(
IDevaToolsProvider::class.java, IDevaToolsProvider::class.java,
MogoServicePaths.PATH_DEVA_TOOLS MogoServicePaths.PATH_DEVA_TOOLS
@@ -17,7 +19,7 @@ object CallerDevaToolsManager {
* 开始抓取全量日志 * 开始抓取全量日志
*/ */
fun startCatchLog() { fun startCatchLog() {
devaToolsProviderApi.startLogCatch() devaToolsProviderApi?.startLogCatch()
} }
/** /**
@@ -25,27 +27,41 @@ object CallerDevaToolsManager {
* duration 分钟数 * duration 分钟数
*/ */
fun startCatchLog(duration: Int) { fun startCatchLog(duration: Int) {
devaToolsProviderApi.startLogCatch(duration) devaToolsProviderApi?.startLogCatch(duration)
} }
/** /**
* 停止抓取全量日志 * 停止抓取全量日志
*/ */
fun stopCatchLog() { fun stopCatchLog() {
devaToolsProviderApi.stopLogCatch() devaToolsProviderApi?.stopLogCatch()
} }
/** /**
* 更新链路节点信息,是否写入 * 更新链路节点信息,是否写入
*/ */
fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>) { fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>) {
devaToolsProviderApi.refreshTraceInfo(map) devaToolsProviderApi?.refreshTraceInfo(map)
} }
/** /**
* 获取链路节点信息 * 获取链路节点信息
*/ */
fun getTraceInfo(): HashMap<Int, ChainLogParam> { fun getTraceInfo(): HashMap<Int, ChainLogParam> {
return devaToolsProviderApi.getTraceInfo() return devaToolsProviderApi?.getTraceInfo() ?: HashMap()
}
/**
* 初始化BadCase相关配置
*/
fun initBadCase(view: View, onShow: (() -> Unit)? = null, onHide: (() -> Unit)? = null) {
devaToolsProviderApi?.initBadCase(view, onShow, onHide)
}
/**
* 收到工控机回调时触发
*/
fun onReceiveBadCaseRecord(record: AutoPilotRecordResult) {
devaToolsProviderApi?.onReceiveBadCaseRecord(record)
} }
} }

View File

@@ -264,15 +264,6 @@ object CallerHmiManager : CallerBase() {
waringProviderApi?.showAdUpgradeStatus(upgradeMode,downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus) waringProviderApi?.showAdUpgradeStatus(upgradeMode,downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus)
} }
/**
* 注册badcase入口展示和隐藏的回调
* 当[onShow]被调用时, 表示达到展示条件,返回展示的入口控件
* [onHide]回调不用关心,可以不注册
*/
fun registerBadCaseCallback(onShow:() -> View, onHide: (() -> Unit)?) {
waringProviderApi?.registerBadCaseCallback(onShow, onHide)
}
/** /**
*注册工控机升级提示圆点View的回调 *注册工控机升级提示圆点View的回调
* @param 提示圆点View * @param 提示圆点View
@@ -290,4 +281,10 @@ object CallerHmiManager : CallerBase() {
waringProviderApi?.showDockerRebootResult(code, msg) waringProviderApi?.showDockerRebootResult(code, msg)
} }
/**
* 展示BadCase浮层
*/
fun showBadCaseFloat(tag: String = "BadCaseFloat", floatView: View): (() -> Unit)? {
return waringProviderApi?.showBadCaseFloat(tag, floatView)
}
} }