Skip to content

Latest commit

 

History

History
342 lines (261 loc) · 11.1 KB

File metadata and controls

342 lines (261 loc) · 11.1 KB

RobotSDK 异常系统技术文档

本文档详细说明 M20 SDK 异常子系统的完整数据流、枚举设计、UI 展示逻辑以及国际化注意事项。


整体架构

机器人(运控系统)
    │  UDP/TCP 上报 Type=1002, Command=3
    ▼
RobotSdkManager (接收原始数据)
    │  回调 connection.readData()
    ▼
RobotStatesManager.parseResponse()
    │  匹配 CommandId.ROBOT_ERROR_STATE_MSG_ID
    │  提取 PatrolDevice.Items → JSON 字符串
    ▼
errorStatusInfoLive.postValue(json)
    │  FilterMutableLiveData(自动去重)
    ▼
RobotErrorManager.parseErrorStates()          ← IO 协程
    │  解析 {"ErrorList": [{errorCode, component}, ...]}
    │  逐个通过 整数 code 匹配 RobotErrorCode 枚举
    ▼
errorListLive: LiveData<MutableList<RobotErrorCode>>
    │
    ├──→ MainActivity.updateErrorIndicator()   ← 顶部状态栏指示器
    └──→ ErrorListDialogFragment               ← 异常列表弹窗
              │  调用 getErrorTitle() / getErrorContent()
              │  获取用户可见文本(@StringRes → 动态资源 → description 兜底)
              ▼
         UI 显示

数据格式

机器人上报的异常 JSON

机器人运控系统以 2Hz 频率主动上报异常状态,消息格式:

{
  "PatrolDevice": {
    "Type": 1002,
    "Command": 3,
    "Time": "2026-06-15 10:30:00",
    "Items": {
      "ErrorList": [
        { "errorCode": 32769, "component": 1 },
        { "errorCode": 33026, "component": 0 }
      ]
    }
  }
}

关键点:errorCodecomponent 均为整数,不包含任何中文文本。

字段 类型 说明
errorCode Int 故障码,如 0x8001(32769)= 电机温度预警
component Int 故障部件位掩码,bit0~bit15 对应 16 个关节(1=异常,0=正常)。非关节类异常此值为 0

Items 为空字符串时

表示机器人当前无异常,RobotErrorManager 会清空内部异常列表。


枚举匹配机制

匹配原理:整数 code 查找

枚举匹配 完全基于整数 code 字段,与枚举标识符名称无关:

// RobotErrorCode.getError()
fun getError(errorCode: Int, component: Int): RobotErrorCode? {
    return RobotErrorCode.values().firstOrNull { errorCode == it.code }
        ?.apply {
            this.component = component
            this.show = true
        }
}

这意味着枚举标识符改名(中文→英文)不影响故障匹配逻辑。 机器人上报 errorCode: 32769,SDK 遍历所有枚举值找到 code == 0x8001 的那一项,无论它叫 电机温度预警 还是 MotorTempWarning

parseErrorList() 处理流程

遍历 ErrorList 数组
  │
  ├─ 本轮已出现相同 code → 合并 component(按位或)
  ├─ 上一轮已存在相同 code → 更新 component
  └─ 全新 code → getError() 查找枚举,加入列表
  │
  ▼
清空旧列表 → 替换为本轮结果 → postValue(errorListLive)

每 500ms(2Hz)收到一次上报,完整替换异常列表。上一轮存在但本轮不存在的异常码自动消失(表示故障已恢复)。


枚举设计

四个枚举类的职责

枚举类 职责 标识符语言
RobotErrorCode ~113 个具体故障码,每个对应一种异常 英文 PascalCase
RobotErrorLevel 6 个告警等级(正常/恢复/次要/重要/紧急/关键) 英文 PascalCase
RobotErrorPart 9 个运行域(关节/电池/主板/手动操控/外设/充电/自主/综合/采集) 英文 PascalCase
RobotErrorPartDetail ~40 个异常对象(电机温度/电池电量/CPU占用率等) 英文 PascalCase

description 属性

每个枚举条目均携带 val description: String 属性,存储原始中文文本:

enum class RobotErrorCode(
    val part: RobotErrorPart,
    val partDetail: RobotErrorPartDetail,
    val level: RobotErrorLevel,
    val code: Int,
    // ... @StringRes 和其他字段 ...
    val description: String = ""
) {
    MotorTempWarning(
        RobotErrorPart.JointMonitor,
        RobotErrorPartDetail.MotorTemperature,
        RobotErrorLevel.MinorWarning,
        0x8001,
        R.string.error_code_title_8001,
        R.string.error_code_content_single_8001,
        R.string.error_code_content_multiole_8001,
        description = "电机温度预警"
    ),
    // ...
}

description 的用途:

  • getErrorTitle() / getErrorContent() 在无字符串资源时作为兜底显示文本
  • toFormatString() 输出格式:"电机温度预警(MotorTempWarning)[0x8001,0b1]"
  • 日志输出:desc=${error.description}

枚举标识符命名规范

场景 命名规则 示例
通用故障 英文描述 MotorTempWarning
同域相似故障 英文描述 + 数字后缀 GaitSwitchFailed / GaitSwitchFailed2
缩写保留 保留行业通用缩写 IMUDataNaN, RTKPositioningInaccurate, BMSDataTimeout
5V 控制 数字前缀加下划线 Control5V

UI 展示文本获取

getErrorTitle() 文本解析链

titleRes (@StringRes) 存在?
  ├─ 是 → context.resources.getString(titleRes!!)
  └─ 否 → 动态查找 error_code_title_{HEX} 资源
              ├─ 找到 → context.resources.getString(resId)
              └─ 未找到 → description(中文兜底文本)
  │
  ▼
拼接格式:"$title (0xXXXX)"

getErrorContent() 文本解析链

多部件异常(component 含多个 bit=1)且 contentResMult 存在?
  └─ 是 → getString(contentResMult!!)

单部件 且 contentResSingle 存在?
  └─ 是 → getString(contentResSingle!!)
            └─ 含 %s 占位符?
                ├─ 关节异常 → 替换为关节名称(R.array.joint_list)
                └─ 电池异常 → 替换为电池名称(R.array.battery_list)

以上均不满足 → 动态查找 error_code_content_{HEX} 资源
  ├─ 找到 → getString(resId)
  └─ 未找到 → description(中文兜底文本)

字符串资源命名约定

资源名 用途
error_code_title_{HEX} 异常标题,如 error_code_title_8001 = "电机温度预警"
error_code_content_single_{HEX} 单部件异常描述,可含 %s 占位符
error_code_content_multiole_{HEX} 多部件异常描述(注意:代码中拼写为 multiole
error_code_title_joint_base 关节类通用标题
error_code_content_single_joint_base 关节类单部件通用描述
error_code_content_multiple_joint_base 关节类多部件通用描述
error_code_title_battery_base 电池类通用标题
error_code_title_auto_charge_base 充电类通用标题

未配置专用字符串资源的枚举条目,将 fallback 到 description 属性值。


告警分级与业务影响

等级划分

等级 枚举名 level 含义
正常 Normal 0 无异常
恢复 Recovery 1 故障消除
次要告警 MinorWarning 2 基础功能不受影响,部分功能受限
重要告警 MajorWarning 3 可自主运动,但无法自主作业
紧急告警 CriticalWarning 4 无法自主运动,可现场恢复
关键告警 FatalWarning 5 硬件故障,现场不可恢复

等级对控制逻辑的影响

controlEnable() 判断链:
  └─ errorList.hasError() → 存在 level ≥ 3 的异常时,禁止控制机器人

hasError() = isWarn() || isError() || isProtect()
  其中:
  isWarn()    → level == MinorWarning (2)        ← 仅预警,不阻断控制
  isError()   → level ∈ {MajorWarning(3), CriticalWarning(4), FatalWarning(5)}
  isProtect() → level == FatalWarning (5)

运行域筛选函数

函数 筛选条件 典型用途
isJointError() part == JointMonitor && partDetail != IMUData 关节故障,触发关节名称替换
isBatteryError() part == BatteryMonitor 电池故障,触发电池名称替换
isAutoChargeError() part == AutoCharging 充电故障,影响充电流程判断
jointTemperatureErrorList() 关节域 + 6 个温度相关枚举 温度异常专项筛选

部件位掩码(component)

component 是一个 32 位整数,bit0~bit15 对应 16 个关节:

bit 0  = 左前髋X关节(索引 0)
bit 1  = 左前髋Y关节(索引 1)
bit 2  = 左前膝关节(索引 2)
bit 3  = 左前轮关节(索引 3)
bit 4  = 右前髋X关节(索引 4)
...
bit 15 = 右后轮关节(索引 15)
bit 16~31 = 预留

多个关节同时异常时,component 为各位的按位或。例如左前髋X + 右前髋X 异常:component = 0b0000000000010001 = 0x0011

SDK 内部通过 getJointErrorComponents() 提取所有置 1 的位索引,用于 UI 显示具体故障关节名称。


弹窗显示控制

RobotErrorCode.show 字段控制同一异常是否重复弹窗:

  1. 首次出现的异常:show = truegetNextShowError() 会返回该异常
  2. 用户确认后调用 dissmiss()show = false
  3. 异常消失后再次出现:getError() 重新设置 show = true
// 依次获取待弹窗的异常
fun getNextShowError(): RobotErrorCode? =
    errorList.filterErrorList().firstOrNull { it.isShow() }

断连特殊处理

RobotErrorManager.hasDisConnectError() 不依赖枚举匹配,而是直接检查连接状态:

fun hasDisConnectError(): Boolean = !RobotStatesManager.isConnect()

断连时 controlEnable() 直接返回 false,无需等待异常上报。重连后 RobotStatesManager.resetStates() 会清空异常列表,避免残留旧异常。


SimpleDemo 中的使用示例

观察异常列表

// MainViewModel.kt
val errorListLive = RobotErrorManager.errorListLive

// MainActivity.kt
viewModel.errorListLive.observe(this) { errorList ->
    updateErrorIndicator(errorList)  // 更新顶部状态指示器
}

异常指示器颜色规则

when {
    errors.isNotEmpty() → 红色 "N项异常"
    warns.isNotEmpty()  → 橙色 "N项预警"
    else                → 绿色 "正常"
}

测试异常功能

SimpleDemo 提供长按触发测试异常的功能,构造模拟 JSON 注入 errorStatusInfoLive

val json = """{"ErrorList":[{"errorCode":32769,"component":1}]}"""
RobotStatesManager.errorStatusInfoLive.postValue(json)

关键设计总结

设计决策 原因
枚举匹配用整数 code,不用 name 机器人协议规定故障码为整数,name 仅是代码可读性工具
description 属性承载中文文本 枚举标识符国际化后,仍需保留中文作为 UI 兜底显示文本
@StringRes 优先于 description 字符串资源支持多语言(中/英),description 仅作无资源时的 fallback
FilterMutableLiveData 去重 相同异常 JSON 不重复触发解析,减少不必要的 UI 刷新
每轮完整替换异常列表 机器人每 500ms 上报完整异常快照,消失的故障码自动恢复