[6.7.0][FSM] feat:完善 FSM 相关状态逻辑;

This commit is contained in:
aibingbing
2024-09-12 19:12:44 +08:00
parent 2a80dca6fd
commit b40b74dbf4
11 changed files with 316 additions and 19 deletions

View File

@@ -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)
}
/**

View File

@@ -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)
}
}
}

View File

@@ -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() {}
}
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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"