> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wuji.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# 诊断与日志

## joint\_diagnostics（SUB）

**关节诊断流**：订阅逐关节的状态字 / 电流 / 母线电压 / 温度 / 错误码。帧变长，仅含在线关节，用每条的 `nid` 定位关节。本流与 `joint_states` 共用同一条设备流，调低其中一个的推送频率，另一个会同步降低，见 [joint\_states（SUB）](/docs/wuji-hand/latest/sdk-reference/joint-control/#joint_states（sub）)。

```python theme={null}
sub = hand.joint_diagnostics().subscribe()   # → Subscription[JointDiagnosticsFrame]
frame = await sub.recv_async()
for j in frame.joints:
    print(j.nid, j.vbus_v_fb, j.mcu_temp_c_fb, f"0x{j.error_code_current:04X}")
sub.close()
```

`error_code_current` 用静态方法 `WujiHand2.describe_error(code)` 解码，返回对象含 `code`、错误名、`desc`、`cause`、`resolution`、`severity`（`Warning` / `DeferredStop` / `ImmediateStop` / `Fatal`）与 `clear_policy`（`AutoClear` / `ManualClear` / `NonClearable`）。用 `cause` 与 `resolution` 确认故障原因和处理方式。未知错误码返回 `None`。直接打印诊断条目对象时，错误码按 16 进制显示。

<Info>
  关节角请用 `joint_states` 流的 `position`，诊断流不含关节角。
</Info>

## comm\_diag（GET，1 Hz）

**通信诊断**：每秒返回 5 根手指的吞吐 / 错误率。

```python theme={null}
diag = hand.comm_diag().get()           # → HandCommunicationDiagnostics
for finger in diag.fingers:             # 5 根手指各自的吞吐 / 错误率
    print(finger.tx_kbps, finger.rx_kbps, finger.error_per_sec)
```

用于排查关节与控制板之间的通信问题。

## Flash 日志导出（诊断）

```python theme={null}
# 该调用会阻塞，直至导出完成
result = hand.export_flash_logs("./logs")
print(result["path"])      # str：导出 JSONL 文件的绝对路径
print(result["frames"])    # int：导出的日志记录数
```

SDK 会在 `out_dir` 中写入一个 `flash_<serial>_<YYYY-MM-DD_HHMMSS>.jsonl` 文件。省略 `out_dir` 时，文件保存到 `~/.wuji/logs`。

SDK 先写入临时文件，导出完成后再将其保存为 JSONL 文件。导出失败时，SDK 会自动清理临时文件，输出目录中不会出现残缺的导出结果。

<Info>
  固件 v2.6.0 及更高版本支持同一设备的并发导出。更早的固件请依次导出。

  多个会话同时导出同一设备的日志时，SDK 会自动重试。若重试后仍无法完成，本次导出会报设备忙。SDK 会清除临时文件，不会保留未完成的日志。
</Info>

<Warning>
  日志导出要求设备固件和 Wuji SDK 使用兼容版本。版本不匹配时，导出会报错且不会生成日志文件。请同步升级设备固件和 Wuji SDK 后重试。
</Warning>

## 数据类型

### JointDiagnosticsFrame / JointDiagnosticsEntry

**关节诊断帧**：`hand.joint_diagnostics().subscribe()` 流的帧类型。变长，仅含在线关节。

```python theme={null}
class JointDiagnosticsFrame:
    header: FrameHeader                    # seq / timestamp_us / frame_id
    num_joints: int
    joints: list[JointDiagnosticsEntry]
    comm: Hand2CommSummary                 # 帧级通信健康摘要，1 Hz 刷新（见 Hand2CommSummary）

class JointDiagnosticsEntry:
    nid: int                               # 节点编号，跨帧定位关节
    status_word: StatusWord                # 状态字
    current: float                         # 电流 (A)
    vbus_v_fb: float                       # 母线电压 (V)
    mcu_temp_c_fb: float                   # MCU 温度 (°C)
    error_code_current: int                # 当前错误码（整数值，打印条目对象时按 16 进制显示），用 describe_error() 解码
    comm_response_rate_pct: int            # 该关节最近 1 秒的总线响应率，0–100
    comm_timeout_total: int                # 该关节累计总线超时次数
```

### HandCommunicationDiagnostics

**通信诊断数据**。

```python theme={null}
class HandCommunicationDiagnostics:
    fingers: list[FingerCommunicationDiagnostics]   # 长度 5

class FingerCommunicationDiagnostics:
    tx_frame_total: int
    rx_frame_total: int
    tx_kbps: int
    rx_kbps: int
    error_per_sec: int
    crc_error_total: int
    frame_format_error_total: int
    uart_hw_error_total: int
    transfer_stats: list[TransferStats]
    nodes: list[NodeDiagnostics]    # per-node：online / ms_since_last_response / response_rate_pct
```

### StatusWord

**状态字**：[JointDiagnosticsEntry](#jointdiagnosticsframe-/-jointdiagnosticsentry) 的 `status_word` 字段类型，SDK 暴露的公开状态视图。

| 字段 | 类型 | 说明 |
| - | - | - |
| `ext_state` | `int` | 扩展状态值（如 Init / Ready / Enabled / Stopped） |
| `ext_state_name` | `str` | `ext_state` 的语义名 |
| `position_limit_active` | `bool` | 位置限幅触发 |
| `velocity_limit_active` | `bool` | 速度限幅触发 |
| `current_limit_active` | `bool` | 电流限幅触发 |

### Hand2CommSummary

**帧级通信健康摘要**：[JointDiagnosticsFrame](#jointdiagnosticsframe-/-jointdiagnosticsentry) 的 `comm` 字段类型，同时覆盖设备内部总线与 SDK 本地端到端（以太网）两段。设备内部快照由 SDK 常驻任务以 1 Hz 自动刷新，无需手动轮询。

| 字段 | 类型 | 说明 |
| - | - | - |
| `age_ms` | `int` | 设备内部快照距今毫秒数。`65535` = 从未成功采样（设备内部字段均为 0）。`65534` = 已饱和，快照距今 ≥ 65.5 秒 |
| `e2e_received` | `int` | 全部订阅流累计收帧数 |
| `e2e_lost` | `int` | 以太网段经序号缺口检测出的累计丢帧数 |
| `e2e_reordered` | `int` | 累计乱序/迟到帧数 |
| `e2e_duplicates` | `int` | 累计重复帧数 |
| `e2e_window_loss_x100` | `int` | 最近 1 秒窗口丢包率，0.01% 单位 |
| `rpc_total` | `int` | 累计请求数。**`0` 表示当前传输后端不提供 RPC 统计**（仅 wuji-proto 后端提供，存活的 wuji-proto 连接必然已发过至少一次请求），因此此处为 0 不等于「没有发生重传」 |
| `rpc_retries` | `int` | 累计请求重传次数 |
| `rpc_timeouts` | `int` | 累计最终超时的请求数 |
| `comm_get_failures` | `int` | 累计内部快照刷新失败次数 |
| `sdk_dropped` | `int` | SDK 内部某一跳落后而丢弃的帧数（累计）——可能是应用侧的订阅消费端，也可能是 SDK 内部的流 handler。与网络丢帧 `e2e_lost` 语义不同 |

<Info>
  **设备内部字段的判读**：`age_ms` 以及每关节的 `comm_*` 字段同属一份快照，由 SDK 以 1 Hz 刷新。固件升级进行期间会**有意跳过**这次刷新，因此短暂的陈旧值属预期状态而非故障。用 `comm_get_failures` 区分两种情况：`age_ms` 增大而 `comm_get_failures` 不变 = 正在有意跳过，`comm_get_failures` 持续增加 = 刷新本身在失败。
</Info>

<Warning>
  **计数器饱和，不回绕**：本表所有计数达到上限后即停在上限（16 位的 `e2e_reordered`、`e2e_duplicates`、`rpc_retries`、`rpc_timeouts`、`comm_get_failures` 停在 65535，32 位的停在 4294967295），不会归零重新计数。1 kHz 流长时间运行时确实可能触顶，因此应按**两次采样之间的增量**判读，而非当作全生命周期的绝对累计值。
</Warning>

## 异常处理

所有 SDK 操作错误统一抛 `WujiException`，消息中携带错误类型前缀（`Disconnected`、`Timeout`、`PathNotFound`、`SchemaMismatch`、`SerializeError`、…）。业务侧按需 `try / except` 即可。

```python theme={null}
from wuji_sdk import WujiException

try:
    hand.joint_states().subscribe()
except WujiException as e:
    print(f"SDK error: {e}")
```

完整异常类型清单见 [Wuji SDK 附录 — Python 异常类型](/docs/wuji-sdk/appendix/#python-异常类型)，断开后的重连写法见 [Wuji SDK 最佳实践 — 重连策略](/docs/wuji-sdk/advanced/#重连策略)。

### 设备故障码

关节诊断的 `error_code_current` 与错误历史记录的 `error_code` 都是 `u16` 设备故障码，用 `WujiHand2.describe_error()` 解码。

设备故障码按 `0xSCNN` 布局，但不要自行拆解 hex 位——`describe_error()` 返回的 `severity` 与 `clear_policy` 字段以字符串形式携带同样信息，布局变化时仍然正确。把码值当作不透明标识：记录日志、展示给用户、写进故障报告。解码成功时用返回的 `severity` 与 `clear_policy` 判断处置方式。`describe_error()` 返回 `None`（未知码）时不要访问这些字段，保留原始数值用于记录与上报。

<Tabs>
  <Tab title="Python">
    Python：本页前文已展示 Python 接口。
  </Tab>

  <Tab title="C">
    C 侧接口与本页 Python 接口语义一致，函数前缀 `wuji_hand_2_`。初始化约定、内存管理、错误码与工程构建见 [C 接口参考](/docs/wuji-sdk/data-reference/)。

    ### 订阅关节诊断

    `wuji_hand_2_subscribe_joint_diagnostics` 的回调签名与 `wuji_hand_2_subscribe_joint_states` 相似，每条 `WujiJointDiagnosticsEntry` 含 `status_word` / `current` / `vbus_v_fb` / `mcu_temp_c_fb` / `error_code_current` 字段，以及每关节总线通信质量（`comm_response_rate_pct` / `comm_timeout_total`）。帧级 `comm` 字段（`WujiHand2CommSummary`）附带端到端流丢帧、RPC 重传与超时计数，以及触觉在线位图。

    ### 解码关节状态字

    `wuji_hand_2_decode_joint_status` 将 `WujiJointDiagnosticsEntry.status_word` 解码为 `WujiJointStatus`。`ext_state` 的取值含义如下：`0` 表示初始化，`1` 表示就绪，`2` 表示启用，`3` 表示停止。`enabled` 仅在 `ext_state == 2` 时为 `true`。其余字段分别表示位置、速度和电流限制是否激活。该函数无需设备连接。

    ```c theme={null}
    void print_joint_status(uint32_t status_word) {
        WujiJointStatus status = {0};
        if (wuji_hand_2_decode_joint_status(status_word, &status) != WUJI_STATUS_OK) {
            return;
        }

        printf("state=%u enabled=%d limits=%d/%d/%d\n",
               status.ext_state,
               status.enabled,
               status.position_limit_active,
               status.velocity_limit_active,
               status.current_limit_active);
    }
    ```

    `out` 为 `NULL` 时，函数返回 `WUJI_STATUS_ERR_INVALID_ARG`。

    ### Flash 日志导出

    C SDK v2026.8.31 及以上提供 Flash 历史日志导出接口。调用 `wuji_hand_2_export_flash_logs()` 后，当前线程会一直等待，直到 SDK 读取设备日志并写入文件，函数才会返回包含 `path` 和 `frame_count` 的结果结构体：

    ```c theme={null}
    WujiFlashLogExport result = {0};
    if (wuji_hand_2_export_flash_logs(dev, NULL, &result) == WUJI_STATUS_OK) {
        printf("%u frames -> %s\n", result.frame_count, result.path);
        wuji_hand_2_flash_log_export_free(&result);
    } else {
        fprintf(stderr, "export failed: %s\n", wuji_last_error());
    }
    ```

    `out_dir` 传 `NULL` 时，文件保存为 `~/.wuji/logs/flash_<serial>_<date>_<time>.jsonl`。也可传入目录路径指定保存位置。每次调用生成独立文件。同一秒内生成的文件会增加编号后缀，不会覆盖已有文件。

    设备日志环为空时，返回的帧数可以是 `0`。此时空文件仍表示导出成功。

    常见导出错误状态如下：

    * `dev` 或 `out` 为 `NULL`，或 `out_dir` 不是有效的 UTF-8 字符串时，C 返回 `WUJI_STATUS_ERR_INVALID_ARG`。
    * 设备不是 Wuji Hand 2，或设备固件不支持当前协议时，C 返回 `WUJI_STATUS_ERR_UNSUPPORTED`。
    * 已有导出任务正在运行，或导出过程中日志被覆盖并导致多次重试仍无法完成时，C 返回 `WUJI_STATUS_ERR_TIMEOUT`。通过 `wuji_last_error()` 获取详细原因。
    * 导出过程中设备断开连接时，C 返回 `WUJI_STATUS_ERR_DISCONNECTED`。
    * 设备报告 flash-log 协议错误或返回的数据格式无效时，C 返回 `WUJI_STATUS_ERR_PROTOCOL`。
    * 文件系统 I/O 操作失败时，C 返回 `WUJI_STATUS_ERR_INTERNAL`。通过 `wuji_last_error()` 获取详细原因。

    收到其他状态值时，通过 `wuji_last_error()` 获取具体原因。导出失败时不会生成结果文件。

    仅在导出成功后调用 `wuji_hand_2_flash_log_export_free`。
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.