
# R2C协议扩展字段方式
R2C协议的Observations / Actions只有标准字段（joint_states、end_effector_poses等）。要传输深度图、语义指令、自定义信号这类**标准字段无法承载**的数据,不修改.proto（避免破坏协议兼容），通过extensions字段旁路扩展。
核心思想：**自描述盒子** ------ 每个扩展值自带dtype（类型）+ shape（维度），消费端拿到数据即可解析，零握手、无需外部schema。
```
Observations / Actions
├── joint_states / end_effector_poses / ...   ← 固定字段(proto 定义)
└── extensions:                               ← 自贴标签的盒子
      "depth_image"  → {dtype: FLOAT32, shape: [480,640], data: <bytes>}
      "language_goal"→ {dtype: STRING,  shape: [],       data: "pick cube"}
```
#### 协议定义
**DType枚举（common.proto）**
```
enum DType {
  DTYPE_UNSPECIFIED = 0;
  FLOAT32 = 1; FLOAT64 = 2; INT32 = 3; INT64 = 4;
  UINT8 = 5; BOOL = 6; STRING = 7; BYTES = 8;
}
```
**ExtensionValue消息（common.proto）**
```
message ExtensionValue {
  DType dtype = 1;           // 元素类型(必填)
  repeated int32 shape = 2;  // 维度,[] = 标量,[H,W,C] = 图像
  bytes data = 3;            // flat row-major 原始数据
  string mime_type = 4;      // 可选,压缩/编码标注(如 "image/jpeg")
}
```
**在消息中的位置**
```
message Observations {  map<string, ExtensionValue> extensions = 12; }   // 观测
message Actions     {  map<string, ExtensionValue> extensions = 8;  }   // 动作
// ObservationsH264 同样携带 extensions = 12,行为一致
```
#### 配置驱动用法
在mapper rule中添加extension块：
```
device_translator:
  mappings:
    # ── 完整示例:深度图(ndarray → bytes → 包装) ──
    - source: "camera.depth_frame"
      target: "extensions.depth_image"        # 必须 extensions. 前缀
      transforms:
        - ndarray_to_bytes                    # 可选:前置变换链
      extension:
        dtype: FLOAT32                        # 必填
        shape: [480, 640]                     # 可选,默认 [] = 标量
        mime_type: ""                         # 可选
    # ── 标量示例:语言指令 ──
    - source: "task.language_goal"
      target: "extensions.language_goal"
      extension:
        dtype: STRING
    # ── 常量示例:无 source,直接写 default ──
    - default: [1, 1, 1]
      target: "extensions.home_pose"
      extension:
        dtype: FLOAT64
        shape: [3]
```
**执行流程（源码：config_mapper.py）**
```
1. 从 source 读取原始值(或取 default)
2. 依次应用 transforms 变换
3. 应用 slice(如有)
4. _wrap_extension_value():按 dtype/shape/mime_type 打包为 ExtensionValue dict
5. 写入 output["extensions"]["<字段名>"]
6. 序列化时由 _parse_extensions() 转为 protobuf ExtensionValue
```
**打包细节**
表1打包方式说明 
| 输入值类型                | 打包方式                                                |
|:---|:---|
| bytes / bytearray    | 直接作为data（配合ndarray_to_bytes等transform）。             |
| str(dtype=STRING)    | UTF-8编码进data。                                       |
| 标量/嵌套 list(数值 dtype) | 递归flatten后逐元素struct.pack小端打包。                       |
| default 常量           | 与数值dtype同规则（如\[1,1,1\] + shape:\[3\] → 3 元素buffer）。 |
   
#### 编程接口（写自定义translator/adapter时）
r2c_sdk.common.models.wrappers.common.ExtensionValue:
```
from r2c_sdk.common.models.wrappers.common import ExtensionValue
import numpy as np
# ── 写入端:工厂方法 ──
ExtensionValue.from_ndarray(np.zeros((480, 640), np.float32))
#   → dtype="FLOAT32", shape=[480,640], data=arr.tobytes()
ExtensionValue.from_string("pick cube")     # dtype=STRING
ExtensionValue.from_scalar(3.14)            # FLOAT64,struct "<d" 小端
ExtensionValue.from_scalar(True)            # BOOL,\x01/\x00
ExtensionValue.from_scalar(42)              # INT64,8 字节小端有符号
# 直接构造(等效)
ExtensionValue(dtype="UINT8", shape=[224,224,3], data=jpeg_bytes, mime_type="image/jpeg")
# ── 消费端:解析方法 ──
ev.to_ndarray()   # np.frombuffer(data, dtype).reshape(shape);标量 shape=[] 返回标量
ev.to_string()    # 仅 STRING,UTF-8 解码
ev.to_scalar()    # 仅标量数值/BOOL
# STRING/BYTES 也可直接读 ev.data
```
#### 消费端解析（云端/任意第三方）
```
import numpy as np
ext = msg.extensions["depth_image"]
if ext.mime_type:                       # 先按 mime 解码(如 JPEG)
    arr = cv2.imdecode(np.frombuffer(ext.data, np.uint8), cv2.IMREAD_UNCHANGED)
else:
    dtype = {"FLOAT32": np.float32, "FLOAT64": np.float64, "INT32": np.int32,
             "INT64": np.int64, "UINT8": np.uint8, "BOOL": np.bool_}[ext.dtype]
    arr = np.frombuffer(ext.data, dtype=dtype).reshape(ext.shape)
```
下发方向（r2c_to_device）同理：云端把扩展字段写进Actions.extensions边端mapper从extensions.\<名字\>读取。
