[6.7.0][FSM] feat:完善 FSM 相关状态逻辑;
This commit is contained in:
@@ -459,7 +459,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
|
||||
<com.mogo.eagle.core.function.hmi.ui.bone.BoneContainerView
|
||||
<com.mogo.eagle.core.function.hmi.bone.BoneContainerView
|
||||
android:id="@+id/boneContainerView"
|
||||
android:layout_width="@dimen/dp_1046"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -166,6 +166,8 @@ class FSMStatus(var state: FSMStateCode, var desc: String = ""): Status(), IAuto
|
||||
}
|
||||
|
||||
override fun isException(): Boolean = state == FSMStateCode.ExistError
|
||||
|
||||
fun hasFSMModule(): Boolean = (state == FSMStateCode.ExistNormal) || (state == FSMStateCode.ExistError)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,20 +4,48 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.eagle.core.function.hmi.bone.status.StartAutoPilotStatusView
|
||||
import kotlinx.android.synthetic.main.view_bone_top_status.view.topStatusContainer
|
||||
|
||||
class BoneTopStatusLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr),
|
||||
StartAutoPilotStatusView.IStartAutoPilotStatusChanged {
|
||||
|
||||
companion object {
|
||||
const val TAG = "BoneTopStatusLayout"
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_bone_top_status, this, true)
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
StartAutoPilotStatusView.addStatusChangedListener(TAG, this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
StartAutoPilotStatusView.removeStatusChangedListener(TAG)
|
||||
}
|
||||
|
||||
override fun onStatusError() {
|
||||
super.onStatusError()
|
||||
context?.also {
|
||||
topStatusContainer.background = ContextCompat.getDrawable(
|
||||
it, R.drawable.bg_top_status_layout_error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStatusNormal() {
|
||||
super.onStatusNormal()
|
||||
context?.also {
|
||||
topStatusContainer.background =
|
||||
ContextCompat.getDrawable(it, R.drawable.bg_top_status_layout_normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package com.mogo.eagle.core.function.hmi.bone.status
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import chassis.Chassis
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.zhjt.mogo_core_function_devatools.status.StatusManager
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.AcceleratorStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.BrakeStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.DoubleFlashStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.FSMStateCode
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.FSMStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.GearStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.IAutopilotPreLaunchStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.SpeedStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.SteerStatus
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.fSMStatusLayout
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.iv_accelerator
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.iv_brake
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.iv_double_flash
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.iv_steer
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.tv_gear
|
||||
import kotlinx.android.synthetic.main.view_start_autopilot_status.view.withoutFSMStatusLayout
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* 自动驾驶相关的状态展示view
|
||||
* 1, 有 FSM 模块:展示 FSM 的当前状态,启动自驾时人工干预主要由 FSM 拦截,非自驾/自驾 状态都展示异常
|
||||
* 2,无 FSM 模块:展示 启动前 档位/方向盘/油门/刹车/双闪 等状态,人工干预由鹰眼判断拦截,鹰眼数据源为 CanAdapter,
|
||||
* 如果当前在自动驾驶状态 则不显示人工干预异常状态
|
||||
*/
|
||||
class StartAutoPilotStatusView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), StatusManager.IStatusListener {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "StartAutoPilotStatusView"
|
||||
private val statusChangedListeners by lazy { ConcurrentHashMap<String, IStartAutoPilotStatusChanged>() }
|
||||
fun addStatusChangedListener(tag: String, listener: IStartAutoPilotStatusChanged) {
|
||||
if (statusChangedListeners.containsKey(tag)) {
|
||||
statusChangedListeners.remove(tag)
|
||||
}
|
||||
statusChangedListeners[tag] = listener
|
||||
}
|
||||
|
||||
fun removeStatusChangedListener(tag: String) {
|
||||
statusChangedListeners.remove(tag)
|
||||
}
|
||||
}
|
||||
|
||||
private val hasFSM by lazy { AtomicBoolean(false) }
|
||||
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_start_autopilot_status, this, true)
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
// 默认展示 有 FSM 的情况,未知状态
|
||||
changeStatusContainer(true)
|
||||
handleFSM(FSMStatus(FSMStateCode.UnKnown, "未知"))
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否有 FSM 切换展示的容器
|
||||
*/
|
||||
private fun changeStatusContainer(hasFSMModule: Boolean) {
|
||||
CallerLogger.i(TAG, "changeStatusContainer 切换展示,hasFSM=$hasFSMModule")
|
||||
if (hasFSMModule) {
|
||||
fSMStatusLayout?.visibility = View.VISIBLE
|
||||
withoutFSMStatusLayout?.visibility = View.GONE
|
||||
} else {
|
||||
fSMStatusLayout?.visibility = View.GONE
|
||||
withoutFSMStatusLayout?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFSM(status: FSMStatus) {
|
||||
Logger.d(TAG, "--- handleFSM ---:${status.isException()}")
|
||||
val lastHasFSM = hasFSM.get()
|
||||
val newHasFSM = status.hasFSMModule()
|
||||
hasFSM.set(newHasFSM)
|
||||
if (lastHasFSM != newHasFSM) {
|
||||
changeStatusContainer(status.hasFSMModule())
|
||||
}
|
||||
|
||||
when (status.state) {
|
||||
FSMStateCode.UnKnown -> {
|
||||
fSMStatusLayout?.setOnClickListener(null)
|
||||
fSMStatusLayout?.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.icon_fsm_status_bg_unknown
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FSMStateCode.NotExist -> {
|
||||
fSMStatusLayout?.setOnClickListener(null)
|
||||
fSMStatusLayout?.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.icon_fsm_status_bg_unknown
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FSMStateCode.ExistNormal -> {
|
||||
fSMStatusLayout?.setOnClickListener(null)
|
||||
fSMStatusLayout?.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.icon_fsm_status_bg_normal
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FSMStateCode.ExistError -> {
|
||||
// TODO
|
||||
fSMStatusLayout?.onClick {
|
||||
ToastUtils.showLong("onClick")
|
||||
}
|
||||
fSMStatusLayout?.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.icon_fsm_status_bg_error
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
notifyStatus(status.isException())
|
||||
}
|
||||
|
||||
private fun handleWithoutFSM(status: Status) {
|
||||
val isError =
|
||||
status.isException() && CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().state != IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
|
||||
Logger.d(TAG, "--- handleWithoutFSM ---: $isError")
|
||||
when (status) {
|
||||
is GearStatus -> {
|
||||
val position = try {
|
||||
Chassis.GearPosition.valueOf(status.value)
|
||||
} catch (ignore: Throwable) {
|
||||
Chassis.GearPosition.GEAR_NONE
|
||||
}
|
||||
if (tv_gear?.isEnabled == true) {
|
||||
tv_gear?.isEnabled = false
|
||||
}
|
||||
|
||||
when (position) {
|
||||
Chassis.GearPosition.GEAR_N -> {
|
||||
tv_gear.isEnabled = isError
|
||||
tv_gear.text = "N"
|
||||
}
|
||||
|
||||
Chassis.GearPosition.GEAR_R -> {
|
||||
tv_gear.isEnabled = isError
|
||||
tv_gear.text = "R"
|
||||
}
|
||||
|
||||
Chassis.GearPosition.GEAR_P -> {
|
||||
tv_gear.isEnabled = isError
|
||||
tv_gear.text = "P"
|
||||
}
|
||||
|
||||
Chassis.GearPosition.GEAR_D -> {
|
||||
tv_gear.isEnabled = isError
|
||||
tv_gear.text = "D"
|
||||
}
|
||||
|
||||
Chassis.GearPosition.GEAR_NONE -> {
|
||||
tv_gear.isEnabled = false
|
||||
tv_gear.text = ""
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
is AcceleratorStatus -> {
|
||||
iv_accelerator?.isSelected = isError
|
||||
}
|
||||
|
||||
is BrakeStatus -> {
|
||||
iv_brake?.isSelected = isError
|
||||
}
|
||||
|
||||
is DoubleFlashStatus -> {
|
||||
iv_double_flash?.isSelected = isError
|
||||
}
|
||||
|
||||
is SteerStatus -> {
|
||||
iv_steer?.isSelected = isError
|
||||
}
|
||||
|
||||
is SpeedStatus -> {
|
||||
// TODO
|
||||
}
|
||||
|
||||
else -> {
|
||||
Logger.d(TAG, "other state: $status")
|
||||
}
|
||||
}
|
||||
notifyStatus(isError)
|
||||
}
|
||||
|
||||
private fun notifyStatus(isError: Boolean) {
|
||||
statusChangedListeners.values.forEach { itx ->
|
||||
if (isError) {
|
||||
itx.onStatusError()
|
||||
} else {
|
||||
itx.onStatusNormal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
StatusManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
StatusManager.removeListener(TAG)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param changed: 变化的数据
|
||||
* @param all: 所有状态数据
|
||||
*/
|
||||
override fun onStatusChanged(changed: List<Status>, all: List<Status>) {
|
||||
changed.filter { it is IAutopilotPreLaunchStatus }.forEach { status ->
|
||||
when (status) {
|
||||
is FSMStatus -> {
|
||||
handleFSM(status)
|
||||
}
|
||||
|
||||
else -> {
|
||||
handleWithoutFSM(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IStartAutoPilotStatusChanged {
|
||||
fun onStatusNormal() {}
|
||||
fun onStatusError() {}
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,4 @@ class CarInfoTabView @JvmOverloads constructor(
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_car_info_tab, this, true)
|
||||
}
|
||||
}x
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 381 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 384 KiB |
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true" android:drawable="@drawable/icon_status_gear_bg_error" />
|
||||
<item android:drawable="@drawable/icon_status_gear_bg_normal" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/icon_status_gear_bg_error" />
|
||||
<item android:state_enabled="false" android:drawable="@drawable/icon_status_gear_bg_normal" />
|
||||
</selector>
|
||||
@@ -12,6 +12,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginStart="@dimen/dp_19"
|
||||
android:elevation="10dp"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/topStatusContainer"
|
||||
android:layout_width="@dimen/dp_964"
|
||||
android:layout_height="@dimen/dp_357">
|
||||
android:layout_height="@dimen/dp_357"
|
||||
android:background="@drawable/bg_top_status_layout_normal">
|
||||
|
||||
|
||||
<com.mogo.eagle.core.function.hmi.ui.bone.status.StartAutoPilotStatusView
|
||||
android:layout_width="wrap_content"
|
||||
<com.mogo.eagle.core.function.hmi.bone.status.StartAutoPilotStatusView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/dp_59"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -6,15 +6,15 @@
|
||||
android:layout_height="@dimen/dp_102">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/clFSMStatusLayout"
|
||||
android:id="@+id/fSMStatusLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="fitXY"
|
||||
android:background="@drawable/icon_fsm_status_bg_normal"
|
||||
android:src="@drawable/icon_fsm_status_bg_normal"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llBeforeLaunchStatusLayout"
|
||||
android:id="@+id/withoutFSMStatusLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="@dimen/dp_34"
|
||||
|
||||
Reference in New Issue
Block a user