[672][device] 硬件设备模块 添加链路日志、添加语音设备物理按钮单独回调、调试窗硬件服务状态弹窗优化

This commit is contained in:
xinfengkun
2024-11-20 17:23:07 +08:00
parent d8b78fdb1d
commit 13755417cc
12 changed files with 330 additions and 128 deletions

View File

@@ -217,7 +217,7 @@ ext {
androidautoSize : 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1',
//========================= 串口设备 ======================
serialport : 'com.mogo.support.device.manager:serial_port:2.2.3',
serialport : 'com.mogo.support.device.manager:serial_port:2.2.4',
thread_opt : "com.mogo.thread.opt:lib:10.10.3",

View File

@@ -9,7 +9,7 @@ import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager
import com.mogo.eagle.core.function.datacenter.location.MoGoLocationDispatcher
import com.mogo.eagle.core.function.datacenter.v2x.SpeedLimitDispatcher
import com.mogo.eagle.core.function.datacenter.v2x.TrafficLightDispatcher
import com.mogo.eagle.core.function.datacenter.iot.IotInItManager
import com.mogo.eagle.core.function.datacenter.iot.IotManager
@Route(path = MogoServicePaths.PATH_DATA_CENTER_MODULE)
class DataCenterProvider : IDataCenterProvider {
@@ -27,12 +27,13 @@ class DataCenterProvider : IDataCenterProvider {
CallerMsgBoxManager.queryAllMessages(it)
TrafficLightDispatcher.INSTANCE.initServer(it)
SpeedLimitDispatcher.INSTANCE.initLimit(it)
IotInItManager.init()
IotManager.init()
}
}
override fun onDestroy() {
TrafficLightDispatcher.INSTANCE.destroy()
SpeedLimitDispatcher.INSTANCE.destroy()
IotManager.destroy()
}
}

View File

@@ -1,28 +0,0 @@
package com.mogo.eagle.core.function.datacenter.iot
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.storage.SharedPrefsMgr
import com.mogo.eagle.core.function.api.cloud.IMoGoCloudListener
import com.mogo.eagle.core.function.call.cloud.CallerCloudListenerManager
import com.mogo.support.device.DevicesManager
object IotInItManager : IMoGoCloudListener {
private const val TAG = "IotInIt"
fun init() {
CallerCloudListenerManager.addListener(TAG, this)
hardwareDeviceBind(SharedPrefsMgr.getInstance().sn)
}
override fun tokenGot(token: String, sn: String) {
super.tokenGot(token, sn)
hardwareDeviceBind(sn)
}
private fun hardwareDeviceBind(sn: String) {
if (sn.isNotEmpty()) {
DevicesManager.init(AbsMogoApplication.getApp(), sn)
}
}
}

View File

@@ -0,0 +1,63 @@
package com.mogo.eagle.core.function.datacenter.iot
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.storage.SharedPrefsMgr
import com.mogo.commons.utils.MogoAnalyticUtils
import com.mogo.eagle.core.data.deva.chain.ChainConstant
import com.mogo.eagle.core.function.api.cloud.IMoGoCloudListener
import com.mogo.eagle.core.function.call.cloud.CallerCloudListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.i
import com.mogo.support.device.DevicesManager
import com.mogo.support.device.IWriteChainLogListener
import com.zhjt.service.chain.ChainLog
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object IotManager : IMoGoCloudListener, IWriteChainLogListener {
private val sdf: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ", Locale.CHINA)
private val TAG = IotManager::class.java.simpleName
private const val EVENT_KEY_HARDWARE_DEVICES = "event_key_hardware_devices"
fun init() {
CallerCloudListenerManager.addListener(TAG, this)
hardwareDeviceBind(SharedPrefsMgr.getInstance().sn)
DevicesManager.registerWriteChainLogListener(this)
}
override fun tokenGot(token: String, sn: String) {
super.tokenGot(token, sn)
hardwareDeviceBind(sn)
}
private fun hardwareDeviceBind(sn: String) {
if (sn.isNotEmpty()) {
DevicesManager.init(AbsMogoApplication.getApp(), sn)
}
}
fun destroy() {
DevicesManager.destroy()
DevicesManager.unregisterWriteChainLogListener()
}
@ChainLog(
linkChainLog = ChainConstant.CHAIN_TYPE_STATUS,
linkCode = ChainConstant.CHAIN_SOURCE_INIT,
nodeAliasCode = ChainConstant.CHAIN_CODE_HARDWARE_DEVICES,
paramIndexes = [0, 1]
)
override fun onWriteChainLog(title: String, info: String) {
try {
i("${TAG}_${EVENT_KEY_HARDWARE_DEVICES}", "${title}_${info}")
val params = HashMap<String, Any>()
params["time"] = sdf.format(Date())
params["title"] = title
params["info"] = info
MogoAnalyticUtils.track(EVENT_KEY_HARDWARE_DEVICES, params)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -9,7 +9,6 @@ import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Environment
import android.os.Environment.*
import android.os.Process
import android.text.Html
@@ -135,9 +134,9 @@ import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_300
import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_CROSS
import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_TOP
import com.mogo.map.uicontroller.VisualAngleMode.MODE_LONG_SIGHT
import com.mogo.tts.base.IMogoTTSCallback
import com.mogo.support.device.DevicesManager
import com.mogo.support.device.IBindStateChangeListener
import com.mogo.tts.base.IMogoTTSCallback
import com.zhjt.mogo.adas.data.AdasConstants
import com.zhjt.service.chain.ChainLog
import kotlinx.android.synthetic.main.view_debug_setting.view.appVersionInfoLayout
@@ -337,6 +336,7 @@ import kotlin.collections.component4
import kotlin.collections.set
import kotlin.math.abs
import kotlin.system.exitProcess
import com.mogo.eagle.core.function.hmi.ui.tools.ShowVersionDialog
/**
@@ -1336,37 +1336,16 @@ internal class DebugSettingView @JvmOverloads constructor(
}
//硬件服务绑定状态以及版本
tvHardwareDeviceBindState.text =
getBindDeviceServiceState(DevicesManager.getServiceBindState())
tvHardwareDeviceBindState.setOnClickListener {
val state = when (DevicesManager.getServiceBindState()) {
0 -> {
"绑定成功"
}
1 -> {
"绑定失败未安装“硬件服务”APP"
}
2 -> {
"绑定失败:没有绑定权限或找不到服务"
}
3 -> {
"服务被异常销毁"
}
else -> {
"未知"
}
}
var msg = "客户端SDK版本${DevicesManager.getSDKVersion() ?: "未知"}\n"
var msg =
"${getBindDeviceServiceState(DevicesManager.getServiceBindState(), false)}\n"
msg += "客户端SDK版本${DevicesManager.getSDKVersion() ?: "未知"}\n"
msg += "客户端SDK AIDL版本${DevicesManager.getSDKAIDLVersion() ?: "未知"}\n"
msg += "硬件服务端APP版本${DevicesManager.getServerVersion() ?: "未知"}\n"
msg += "硬件服务端APP AIDL版本${DevicesManager.getServerAIDLVersion() ?: ""}"
AlertDialog.Builder(context)
.setTitle("硬件服务绑定状态: $state")
.setMessage(msg)
.setPositiveButton("确定", null)
.create().show()
msg += "硬件服务端APP AIDL版本${DevicesManager.getServerAIDLVersion() ?: "未知"}"
ShowVersionDialog(context, "硬件服务绑定", msg).show()
}
}
@@ -2766,29 +2745,42 @@ internal class DebugSettingView @JvmOverloads constructor(
}
override fun onServiceState(state: Int) {
val stateMsg = when (state) {
UiThreadHandler.post {
tvHardwareDeviceBindState.text = getBindDeviceServiceState(state)
}
}
private fun getBindDeviceServiceState(state: Int, isUseTitle: Boolean = true): CharSequence {
val msg = when (state) {
0 -> {
"绑定成功"
"<font color='green'>绑定成功</font>"
}
1 -> {
"绑定失败未安装“硬件服务”APP"
"<font color='red'>绑定失败未安装“硬件服务”APP</font>"
}
2 -> {
"绑定失败没有绑定权限或找不到服务"
"<font color='red'>绑定失败没有绑定权限或找不到服务</font>"
}
3 -> {
"服务被异常销毁"
"<font color='red'>绑定失败,服务被异常销毁</font>"
}
else -> {
"未知"
"<font color='red'>未知</font>"
}
}
UiThreadHandler.post {
tvHardwareDeviceBindState.text = "硬件服务绑定状态: $stateMsg"
val title = if (isUseTitle) {
"硬件服务绑定"
} else {
""
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml("${title}状态:${msg}", Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml("${title}状态:${msg}")
}
}
}

View File

@@ -0,0 +1,50 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.content.Context
import android.widget.TextView
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.support.device.DevicesManager
/**
* 离线地图缓存
*/
class ShowVersionDialog(context: Context, val title: String, val msg: String) :
BaseFloatDialog(context, TAG) {
companion object {
private const val TAG = "ShowVersionDialog"
}
private var tvTitle: TextView? = null
private var tvMsg: TextView? = null
private var okView: TextView? = null
init {
setContentView(R.layout.dialog_show_version)
setCanceledOnTouchOutside(true)
setCancelable(true)
initView()
}
var i=3;
private fun initView() {
tvTitle = findViewById(R.id.tv_title)
tvMsg = findViewById(R.id.tv_msg)
okView = findViewById(R.id.tv_ok)
tvTitle?.text = title
tvMsg?.text = msg
okView?.setOnClickListener {
DevicesManager.speechCx830seBroadcast("你好${i++}")
dismiss()
}
}
override fun dismiss() {
super.dismiss()
}
}

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<com.mogo.eagle.core.widget.RoundConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/roundRootLayout"
android:layout_width="@dimen/dp_900"
android:layout_height="wrap_content"
android:background="@drawable/bg_bone_dialog"
app:roundLayoutRadius="@dimen/dp_50">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_40"
android:textColor="@color/white"
android:textSize="@dimen/dp_45"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_30"
android:textColor="@color/white"
android:textSize="@dimen/dp_36"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<TextView
android:id="@+id/tv_ok"
android:layout_width="@dimen/dp_356"
android:layout_height="@dimen/dp_120"
android:layout_marginTop="@dimen/dp_40"
android:layout_marginBottom="@dimen/dp_40"
android:background="@drawable/bg_dialog_btn"
android:gravity="center"
android:text="@string/ok_tip"
android:textColor="@color/white"
android:textSize="@dimen/dp_40"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_msg" />
</com.mogo.eagle.core.widget.RoundConstraintLayout>

View File

@@ -152,5 +152,10 @@ class ChainConstant {
// Biz
const val CHAIN_CODE_MAP_ROAD_CROSS_ERROR = "CHAIN_CODE_MAP_ROAD_CROSS_ERROR"
//硬件设备相关
const val CHAIN_CODE_HARDWARE_DEVICES = "CHAIN_CODE_HARDWARE_DEVICES"
//
}
}

View File

@@ -28,11 +28,29 @@ object DevicesManager {
ConcurrentHashMap()
private val speechCx830seListeners: ConcurrentHashMap<String, ISpeechCx830seListener> =
ConcurrentHashMap()
private val physicalButtonListeners: ConcurrentHashMap<String, IPhysicalButtonListener> =
ConcurrentHashMap()
private var writeChainLogListener: IWriteChainLogListener? = null
fun init(context: Context, sn: String) {
serialPortManager.bindService(context, sn, bindStateChangeListener)
}
fun destroy() {
serialPortManager.close(DefaultDevices.VERIFICATION.path)
serialPortManager.close(DefaultDevices.SPEECH_CX830SE.path)
serialPortManager.unbindService()
}
fun registerWriteChainLogListener(writeChainLogListener: IWriteChainLogListener) {
this.writeChainLogListener = writeChainLogListener
}
fun unregisterWriteChainLogListener() {
this.writeChainLogListener = null
}
/**
* 注册服务端APP绑定状态
*/
@@ -53,15 +71,15 @@ object DevicesManager {
}
/**
* 注册核销设备回调接口 注册时自动连接核销设备
* 注册核销设备回调接口 注册时如果未连接将进行自动连接
*/
fun addVerificationListener(tag: String, listener: IVerificationAutoListener) {
if (verificationAutoListeners.containsKey(tag)) {
return
}
verificationAutoListeners[tag] = listener
if (verificationAutoListeners.isNotEmpty()) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
if (!serialPortManager.isOpen(DefaultDevices.VERIFICATION.path)) {
serialPortManager.open(DefaultDevices.VERIFICATION, verificationListener)
}
}
@@ -75,20 +93,22 @@ object DevicesManager {
verificationAutoListeners.remove(tag)
}
if (verificationAutoListeners.isEmpty()) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
serialPortManager.close(DefaultDevices.VERIFICATION.path)
}
}
}
/**
* 注册语音设备回调接口 注册时自动连接语音设备
* 注册语音设备回调接口 注册时如果未连接将进行自动连接
*/
fun addSpeechCx830seListener(tag: String, listener: ISpeechCx830seListener) {
if (speechCx830seListeners.containsKey(tag)) {
return
}
speechCx830seListeners[tag] = listener
if (speechCx830seListeners.isNotEmpty()) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
if (!serialPortManager.isOpen(DefaultDevices.SPEECH_CX830SE.path)) {
serialPortManager.open(DefaultDevices.SPEECH_CX830SE, speechCx830seListener)
}
}
@@ -101,6 +121,41 @@ object DevicesManager {
if (speechCx830seListeners.containsKey(tag)) {
speechCx830seListeners.remove(tag)
}
if (speechCx830seListeners.isEmpty() && physicalButtonListeners.isEmpty()) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
serialPortManager.close(DefaultDevices.SPEECH_CX830SE.path)
}
}
}
/**
* 注册语音设备物理按键回调接口 注册时如果未连接将进行自动连接
*/
fun addPhysicalButtonListener(tag: String, listener: IPhysicalButtonListener) {
if (physicalButtonListeners.containsKey(tag)) {
return
}
physicalButtonListeners[tag] = listener
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
if (!serialPortManager.isOpen(DefaultDevices.SPEECH_CX830SE.path)) {
serialPortManager.open(DefaultDevices.SPEECH_CX830SE, speechCx830seListener)
}
}
}
/**
* 取消注册语音设备回调接口
*/
fun removePhysicalButtonListener(tag: String) {
if (physicalButtonListeners.containsKey(tag)) {
physicalButtonListeners.remove(tag)
}
if (physicalButtonListeners.isEmpty() && speechCx830seListeners.isEmpty()) {
if (serviceBindState == SERVICE_STATE.BIND_SUCCEED) {
serialPortManager.close(DefaultDevices.SPEECH_CX830SE.path)
}
}
}
@@ -108,44 +163,39 @@ object DevicesManager {
override fun onServiceState(serviceState: Int) {
serviceBindState = serviceState
if (bindStateChangeListeners.isNotEmpty()) {
bindStateChangeListeners.forEach {
it.value.onServiceState(serviceState)
}
}
//TODO 链路日志和埋点
// OchChainLogManager.writeChainLogScanner(
// TAG + "bindStatus",
// "绑定服务结果:serviceState:${serviceState}"
// )
var msg = ""
when (serviceState) {
SERVICE_STATE.BIND_SUCCEED -> {
Log.d(TAG, "服务绑定成功")
if (verificationAutoListeners.isNotEmpty()) {
msg = "服务绑定成功"
if (!serialPortManager.isOpen(DefaultDevices.VERIFICATION.path)) {
serialPortManager.open(DefaultDevices.VERIFICATION, verificationListener)
}
if (speechCx830seListeners.isNotEmpty()) {
if (!serialPortManager.isOpen(DefaultDevices.SPEECH_CX830SE.path)) {
serialPortManager.open(DefaultDevices.SPEECH_CX830SE, speechCx830seListener)
}
}
SERVICE_STATE.BIND_FAILURE_UNINSTALLED -> {
Log.d(TAG, "服务绑定失败未安装串口服务端APP")
msg = "服务绑定失败未安装串口服务端APP"
}
SERVICE_STATE.BIND_FAILURE_NO_PERMISSION_NOT_FOUND -> {
Log.d(
TAG,
msg =
"服务绑定失败:没有绑定权限或找不到服务(如果是此状态,基本上安装后就可以找到,主要就是权限问题)"
)
}
SERVICE_STATE.EXCEPTION -> {
Log.d(TAG, "服务被异常销毁")
msg = "服务被异常销毁"
}
else -> {}
}
if (bindStateChangeListeners.isNotEmpty()) {
bindStateChangeListeners.forEach {
it.value.onServiceState(serviceState)
}
}
writeChainLogListener?.onWriteChainLog("硬件服务", "绑定状态=${msg}")
}
}
@@ -162,14 +212,20 @@ object DevicesManager {
it.value.onDeviceState(path, deviceType, isOpen, message)
}
}
writeChainLogListener?.onWriteChainLog(
"核销设备",
"状态=地址:${path} 设备类型:${deviceType} 是否打开:${isOpen} 消息:${message}"
)
}
override fun onReceive(data: VerificationData) {
Log.i(TAG, "核销数据=${data.toString()}")
if (verificationAutoListeners.isNotEmpty()) {
verificationAutoListeners.forEach {
it.value.onReceive(data)
}
}
writeChainLogListener?.onWriteChainLog("核销设备", "核销数据接收=$data")
}
}
@@ -186,24 +242,10 @@ object DevicesManager {
it.value.onOpenState(path, isOpen, throwableMessage)
}
}
// if (isOpen) {
// openBtnEnable(!serialPortManager.isOpen(selectDevice.path))
// updateUi(path, true)
// if (serialPortManager.getDeviceType(path) == DeviceType.SPEECH_CX830SE) {
// val device = serialPortManager.getOpenedSerialPort(path)
// serialPortManager.speechCx830seSendCommand(
// device,
// SpeechCommand.READ_DEVICE_ADDRESS
// )
// serialPortManager.speechCx830seSendCommand(
// device,
// SpeechCommand.READ_SWITCHING_VALUE_STATE
// )
// }
// } else {
// showOpenFailed(path)
// }
writeChainLogListener?.onWriteChainLog(
"车外语音设备",
"状态=地址:${path} 是否打开:${isOpen} 消息:${throwableMessage}"
)
}
override fun onSpeechState(path: String, state: Int) {
@@ -232,13 +274,14 @@ object DevicesManager {
else -> ""
}
writeChainLogListener?.onWriteChainLog("车外语音设备", "播报状态=${msg}")
Log.d(TAG, msg)
}
override fun onSwitchingValue(path: String, state: Int, data: SwitchingValueData?) {
super.onSwitchingValue(path, state, data)
if (speechCx830seListeners.isNotEmpty()) {
speechCx830seListeners.forEach {
if (physicalButtonListeners.isNotEmpty()) {
physicalButtonListeners.forEach {
it.value.onSwitchingValue(path, state, data)
}
}
@@ -258,8 +301,11 @@ object DevicesManager {
else -> null
}
if (!msg.isNullOrEmpty()) {
Log.d(TAG, msg)
if (!msg.isNullOrEmpty() && data != null) {
writeChainLogListener?.onWriteChainLog(
"车外语音设备",
"物理按钮状态=$msg $data"
)
}
}
@@ -321,10 +367,16 @@ object DevicesManager {
*/
@Define.Code
fun speechCx830seBroadcast(content: String?): Int {
return serialPortManager.speechCx830seBroadcastSpeech(
val code = serialPortManager.speechCx830seBroadcastSpeech(
DefaultDevices.SPEECH_CX830SE,
content
)
writeChainLogListener?.onWriteChainLog(
"车外语音设备",
"播报内容=${content ?: " 调用结果=${code}"}"
)
return code
}
/**

View File

@@ -0,0 +1,19 @@
package com.mogo.support.device
import com.mogo.support.serialport.common.core.Define
import com.mogo.support.serialport.common.devices.speech.cx830se.data.SwitchingValueData
/**
* 语音设备(科星互联 CX-830S-E物理按钮状态监听
*/
interface IPhysicalButtonListener {
/**
* 开关量状态
* 设备主动触发开关量时 state = Code.PASSIVITY_RECEIVE
*
* @param path 串口
* @param state 状态 查询成功:{@link Code#SUCCESS};查询命令下发失败:{@link Code#FAILED};查询超时:{@link Code#TIMEOUT};设备主动触发开关量:{@link Code#PASSIVITY_RECEIVE}
* @param data 开关量数据
*/
fun onSwitchingValue(path: String, @Define.Code state: Int, data: SwitchingValueData?)
}

View File

@@ -1,7 +1,6 @@
package com.mogo.support.device;
import com.mogo.support.serialport.common.core.Define
import com.mogo.support.serialport.common.devices.speech.cx830se.data.SwitchingValueData
/**
* 语音设备(科星互联 CX-830S-E监听
@@ -25,16 +24,6 @@ interface ISpeechCx830seListener {
*/
fun onSpeechState(path: String, @Define.Code state: Int) {}
/**
* 开关量状态
* 设备主动触发开关量时 state = Code.PASSIVITY_RECEIVE
*
* @param path 串口
* @param state 状态 查询成功:{@link Code#SUCCESS};查询命令下发失败:{@link Code#FAILED};查询超时:{@link Code#TIMEOUT};设备主动触发开关量:{@link Code#PASSIVITY_RECEIVE}
* @param data 开关量数据
*/
fun onSwitchingValue(path: String, @Define.Code state: Int, data: SwitchingValueData?) {}
/**
* 设备地址
*

View File

@@ -0,0 +1,9 @@
package com.mogo.support.device
/**
* 日志
*/
interface IWriteChainLogListener {
fun onWriteChainLog(title: String, info: String)
}