/**
 * @file agv_command.proto
 * @brief AGV 控制相关的 Protobuf 消息定义。
 *        涵盖仙工智能（Seer）SRC 系列控制器的主要 API 命令，
 *        包括状态查询、电池、位置、地图管理、控制权、运动控制、导航任务等。
 * @note 所有命令均遵循仙工协议帧格式，通过 gRPC 服务（AgvService）对外暴露。
 */

syntax = "proto3";

import "cmvr/api/common.proto";

package cmvr.api;

// ============================================================================
// 1. 基本信息查询（命令码 1000, 0x03E8）
// ============================================================================

/**
 * @brief AGV 基本信息响应结构。
 * @note 对应仙工 API 1000（0x03E8）的响应体，包含设备标识、软硬件版本、网络状态等。
 */
message AgvStatusInfo {
  optional string id = 1;                      ///< AGV 设备唯一 ID
  optional string vehicle_id = 2;              ///< 车辆编号（如 "agv_001"）
  optional string version = 3;                 ///< 软件版本号
  optional string model = 4;                   ///< 车型型号（如 "SRC-1100"）
  optional string dsp_version = 5;             ///< DSP 固件版本
  optional string current_ip = 6;              ///< 当前 IP 地址
  optional string mac = 7;                     ///< MAC 地址
  optional int32 rssi = 8;                     ///< Wi-Fi 信号强度（0~100）
  optional int32 ret_code = 9;                 ///< 返回码，0 表示成功
  optional string err_msg = 10;                ///< 错误信息（失败时填充）
}

/**
 * @brief 获取 AGV 状态命令（请求/反馈）。
 * @note 请求携带设备 ID（header.device_id），反馈返回 AgvStatusInfo。
 */
message GetAgvStatusInfoCommand {
  message Request {
    CommandHeader.Request header = 1;        ///< 公共请求头，包含 device_id
  }

  message Feedback {
    CommandHeader.Feedback header = 1;       ///< 公共响应头（success/error_message/timestamp）
    AgvStatusInfo status = 2;                ///< AGV 状态信息
  }
}


// ============================================================================
// 2. 电池状态查询（命令码 1007, 0x03EF）
// ============================================================================

/**
 * @brief 电池状态响应结构。
 * @note 对应仙工 API 1007（0x03EF），支持简单/全量两种模式。
 */
message AgvBatteryStatus {
  optional double battery_level = 1;           ///< 电池电量（0~1，对应 0%~100%）
  optional double battery_temp = 2;            ///< 电池温度（摄氏度）
  optional bool charging = 3;                  ///< 是否正在充电
  optional double voltage = 4;                 ///< 当前电压（V）
  optional double current = 5;                 ///< 当前电流（A）
  optional double max_charge_voltage = 6;      ///< 最大充电电压（-1 表示不支持）
  optional double max_charge_current = 7;      ///< 最大充电电流（-1 表示不支持）
  optional bool manual_charge = 8;             ///< 是否连接手动充电器（仅 SRC-2000）
  optional bool auto_charge = 9;               ///< 是否连接自动充电桩（仅 SRC-2000）
  optional int32 battery_cycle = 10;           ///< 电池循环次数（从 BMS 读取，可能不准）
  optional string battery_user_data = 11;      ///< 电池用户自定义数据
  optional string extra = 12;                  ///< 扩展字段（预留）
  optional int32 ret_code = 13;                ///< 返回码，0 表示成功
  optional string create_on = 14;              ///< 数据创建时间戳（ISO 8601）
  optional string err_msg = 15;                ///< 错误信息
}

/**
 * @brief 电池状态请求数据。
 */
message RobotStatusBatteryRequestData {
  optional bool simple = 1;                    ///< true=仅关键数据，false=全量数据（默认 false）
}

/**
 * @brief 电池状态查询命令。
 */
message RobotStatusBatteryCommand {
  message Request {
    CommandHeader.Request header = 1;        ///< 公共请求头
    RobotStatusBatteryRequestData data = 2;  ///< 请求参数
  }
  message Feedback {
    CommandHeader.Feedback header = 1;       ///< 公共响应头
    AgvBatteryStatus status = 2;             ///< 电池状态信息
  }
}


// ============================================================================
// 3. 机器人位置查询（命令码 1004, 0x03EC）
// ============================================================================

/**
 * @brief 机器人位置响应结构。
 * @note 对应仙工 API 1004（0x03EC），返回世界坐标系下的位姿。
 */
message AgvRobotLocation {
  optional double x = 1;                       ///< X 坐标（米）
  optional double y = 2;                       ///< Y 坐标（米）
  optional double angle = 3;                   ///< 朝向角（弧度），逆时针为正
  optional double confidence = 4;              ///< 定位置信度（0~1）
  optional string current_station = 5;         ///< 当前所在站点 ID（若在站点上）
  optional string last_station = 6;            ///< 上一个经过的站点 ID
  optional int32 loc_method = 7;               ///< 定位方式（0=自然轮廓, 1=反光柱, 2=二维码, 3=里程计...）
  optional int32 ret_code = 8;                 ///< 返回码，0 表示成功
  optional string create_on = 9;               ///< 时间戳
  optional string err_msg = 10;                ///< 错误信息
}

/**
 * @brief 位置查询命令（无请求数据）。
 */
message RobotStatusLocCommand {
  message Request {
    CommandHeader.Request header = 1;        ///< 公共请求头
  }
  message Feedback {
    CommandHeader.Feedback header = 1;       ///< 公共响应头
    AgvRobotLocation status = 2;             ///< 位置信息
  }
}


// ============================================================================
// 4. 地图下载（命令码 4011, 0x0FAB）
// ============================================================================

/**
 * @brief 地图下载请求数据。
 */
message RobotConfigDownloadMapRequestData {
  optional string map_name = 1;                ///< 要下载的地图名称（必填）
}

/**
 * @brief 地图下载响应结果。
 */
message AgvDownloadMapResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
  optional string map_content = 4;             ///< 地图 JSON 文件内容（成功时填充）
}

/**
 * @brief 地图下载命令。
 */
message RobotConfigDownloadMapCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotConfigDownloadMapRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    AgvDownloadMapResult status = 2;
  }
}


// ============================================================================
// 5. 地图状态查询（命令码 1300, 0x0514）
// ============================================================================

/**
 * @brief 单个地图文件信息。
 */
message MapFileInfo {
  optional string name = 1;                    ///< 文件名
  optional string modified = 2;                ///< 最后修改时间
  optional int64 size = 3;                     ///< 文件大小（字节）
}

/**
 * @brief 地图状态响应结构。
 * @note 对应仙工 API 1300（0x0514），返回当前地图及所有地图列表。
 */
message AgvMapStatus {
  optional string current_map = 1;             ///< 当前加载的地图名称
  optional string current_map_md5 = 2;         ///< 当前地图的 MD5 校验值
  repeated string maps = 3;                    ///< 所有地图名称列表
  repeated MapFileInfo map_files_info = 4;     ///< 每个地图文件的详细信息
  optional int32 ret_code = 5;                 ///< 返回码，0 表示成功
  optional string create_on = 6;               ///< 时间戳
  optional string err_msg = 7;                 ///< 错误信息
}

/**
 * @brief 地图状态查询命令（无请求数据）。
 */
message RobotStatusMapCommand {
  message Request {
    CommandHeader.Request header = 1;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    AgvMapStatus status = 2;
  }
}


// ============================================================================
// 6. 上传地图（命令码 4010, 0x0FAA）
// ============================================================================

/**
 * @brief 上传地图请求数据。
 */
message RobotConfigUploadMapRequestData {
  optional string map_content = 1;             ///< 完整的地图 JSON 字符串（必填）
}

/**
 * @brief 上传地图响应结果。
 */
message AgvUploadMapResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 上传地图命令。
 */
message RobotConfigUploadMapCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotConfigUploadMapRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    AgvUploadMapResult status = 2;
  }
}


// ============================================================================
// 7. 抢占控制权（命令码 4005, 0x0FA5）
// ============================================================================

/**
 * @brief 抢占控制权请求数据。
 */
message RobotConfigLockRequestData {
  optional string nick_name = 1;               ///< 抢占者昵称/标识（必填）
}

/**
 * @brief 抢占控制权响应结果。
 */
message AgvLockResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 抢占控制权命令。
 */
message RobotConfigLockCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotConfigLockRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    AgvLockResult status = 2;
  }
}


// ============================================================================
// 8. 查询当前控制权所有者（命令码 1060, 0x0424）
// ============================================================================

/**
 * @brief 当前控制权所有者响应结构。
 * @note 对应仙工 API 1060（0x0424）。
 */
message AgvCurrentLockStatus {
  optional bool locked = 1;                    ///< 是否被锁定（有持有者）
  optional string ip = 2;                      ///< 持有者 IP 地址
  optional int32 port = 3;                     ///< 持有者端口
  optional uint32 type = 4;                    ///< 持有者类型（0=default, 2=roboshop, 0xDD=srd）
  optional string nick_name = 5;               ///< 持有者昵称
  optional int64 time_t = 6;                   ///< 锁定时间戳（Unix 秒）
  optional string desc = 7;                    ///< 描述信息
  optional int32 ret_code = 8;                 ///< 返回码，0 表示成功
  optional string create_on = 9;               ///< 时间戳
  optional string err_msg = 10;                ///< 错误信息
}

/**
 * @brief 查询控制权命令（无请求数据）。
 */
message RobotStatusCurrentLockCommand {
  message Request {
    CommandHeader.Request header = 1;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    AgvCurrentLockStatus status = 2;
  }
}


// ============================================================================
// 9. 开环速度运动控制（命令码 2010, 0x07DA）
// ============================================================================

/**
 * @brief 开环速度运动控制请求数据。
 * @note 命令码 2010（0x07DA）。下发后会强制取消当前自动导航任务。
 *       多舵轮设备仅 vx/vy/w 生效；steer/real_steer 仅单舵轮设备有效。
 *       duration = -1 表示持续运行直到下一次指令。
 */
message RobotMotionControlRequestData {
  optional double vx = 1;                      ///< X 方向线速度（m/s）
  optional double vy = 2;                      ///< Y 方向线速度（m/s）
  optional double w = 3;                       ///< 角速度（rad/s），逆时针为正
  optional double steer = 4;                   ///< 舵轮转向角（单舵轮，单位 rad）
  optional double real_steer = 5;              ///< 实际舵轮角度（单舵轮，优先级高于 steer）
  optional int64 duration = 6;                 ///< 持续时间（ms），-1 表示一直运行
}

/**
 * @brief 开环运动控制响应结果。
 */
message RobotMotionControlResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 开环运动控制命令。
 */
message RobotMotionControlCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotMotionControlRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotMotionControlResult status = 2;
  }
}


// ============================================================================
// 10. 切换载入地图（命令码 2022, 0x07E6）
// ============================================================================

/**
 * @brief 切换地图请求数据。
 * @note 命令码 2022（0x07E6）。目标地图必须已存在于机器人中。
 */
message RobotLoadMapRequestData {
  optional string map_name = 1;                ///< 目标地图名称（必填，仅字母数字-_）
}

/**
 * @brief 切换地图响应结果。
 */
message RobotLoadMapResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 切换地图命令。
 */
message RobotLoadMapCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotLoadMapRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotLoadMapResult status = 2;
  }
}


// ============================================================================
// 11. 查询地图载入状态（命令码 1022, 0x03FE）
// ============================================================================

/**
 * @brief 地图载入状态响应。
 * @note 命令码 1022（0x03FE）。loadmap_status: 0=失败, 1=成功, 2=载入中。
 *       载入中（2）时禁止执行重定位操作。
 */
message RobotQueryLoadMapStatusResult {
  optional int32 loadmap_status = 1;           ///< 0=失败, 1=成功, 2=载入中
  optional int32 ret_code = 2;                 ///< 返回码，0 表示成功
  optional string create_on = 3;               ///< 时间戳
  optional string err_msg = 4;                 ///< 错误信息
}

/**
 * @brief 查询地图载入状态命令（无请求数据）。
 */
message RobotQueryLoadMapStatusCommand {
  message Request {
    CommandHeader.Request header = 1;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotQueryLoadMapStatusResult status = 2;
  }
}


// ============================================================================
// 12. 查询站点列表（命令码 1301, 0x0515）
// ============================================================================

/**
 * @brief 单个站点信息。
 */
message StationItem {
  optional string id = 1;                      ///< 站点唯一 ID
  optional string type = 2;                    ///< 站点类型（如 "LocationMark", "ChargePoint"）
  optional double x = 3;                       ///< X 坐标（米）
  optional double y = 4;                       ///< Y 坐标（米）
  optional double r = 5;                       ///< 朝向角（弧度）
  optional string desc = 6;                    ///< 描述信息
  optional string executor = 7;                ///< 执行器信息
  optional string prepoint = 8;                ///< 前置点 ID
  optional string recfile = 9;                 ///< 识别文件路径
  optional bool spin = 10;                     ///< 是否允许自旋
  optional bool use_down_pgv = 11;             ///< 是否使用下视 PGV
}

/**
 * @brief 站点列表响应结果。
 */
message QueryStationListResult {
  repeated StationItem stations = 1;           ///< 站点列表
  optional int32 ret_code = 2;                 ///< 返回码，0 表示成功
  optional string create_on = 3;               ///< 时间戳
  optional string err_msg = 4;                 ///< 错误信息
}

/**
 * @brief 查询站点列表命令（无请求数据）。
 */
message QueryStationListCommand {
  message Request {
    CommandHeader.Request header = 1;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    QueryStationListResult status = 2;
  }
}


// ============================================================================
// 13. 指定路径导航（命令码 3066, 0x0BFA）
// ============================================================================

/**
 * @brief 单段导航任务单元。
 * @note 用于 3066 命令的 move_task_list 数组。
 *       source_id 和 id 之间必须有直接路径，不可跳点。
 */
message MoveTaskItem {
  optional string task_id = 1;                 ///< 任务唯一 ID（必填）
  optional string source_id = 2;               ///< 起始站点 ID（必填）
  optional string id = 3;                      ///< 目标站点 ID（必填）
  optional string operation = 4;               ///< 到达后执行的动作（如 "JackLoad"）
  optional double jack_height = 5;             ///< 顶升高度（米）
}

/**
 * @brief 指定路径导航请求数据。
 */
message RobotGoTargetListRequestData {
  repeated MoveTaskItem move_task_list = 1;    ///< 导航任务序列（顺序执行）
}

/**
 * @brief 指定路径导航响应结果。
 * @note ret_code=0 仅表示指令被接收，不表示执行完成。
 */
message RobotGoTargetListResult {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功接收
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 指定路径导航命令。
 */
message RobotGoTargetListCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotGoTargetListRequestData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotGoTargetListResult status = 2;
  }
}


// ============================================================================
// 14. 查询当前实时导航状态（命令码 1020, 0x03FC）
// ============================================================================

/**
 * @brief 1020 命令请求数据。
 */
message RobotStatusTaskReqData {
  optional bool simple = 1;                    ///< true=仅返回 task_status，false=全量
}

/**
 * @brief 导航任务中的料箱信息。
 */
message NavContainerItem {
  optional string container_name = 1;          ///< 料箱名称
  optional string desc = 2;                    ///< 描述
  optional string goods_id = 3;                ///< 绑定的货物 ID
  optional bool has_goods = 4;                 ///< 是否已绑定货物
}

/**
 * @brief 1020 命令响应数据。
 */
message RobotStatusTaskResData {
  optional int32 task_status = 1;              ///< 0=NONE, 1=WAITING, 2=RUNNING, 3=SUSPENDED, 4=COMPLETED, 5=FAILED, 6=CANCELED
  optional int32 task_type = 2;                ///< 0=无, 1=自由导航到坐标, 2=自由导航到站点, 3=路径导航到站点, 7=平动转动
  optional string target_id = 3;               ///< 目标站点 ID（task_type 为 2/3 时）
  repeated double target_point = 4;            ///< 目标坐标 [x, y, r]（task_type 为 1 时）
  repeated string finished_path = 5;           ///< 已经过的站点列表
  repeated string unfinished_path = 6;         ///< 尚未经过的站点列表
  optional string move_status_info = 7;        ///< 附加信息（如错误描述）
  repeated NavContainerItem containers = 8;    ///< 料箱信息
  optional int32 ret_code = 9;                 ///< 返回码，0 表示成功
  optional string create_on = 10;              ///< 时间戳
  optional string err_msg = 11;                ///< 错误信息
}

/**
 * @brief 查询当前导航状态命令。
 */
message RobotStatusTaskCurrentCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotStatusTaskReqData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotStatusTaskResData data = 2;
  }
}


// ============================================================================
// 15. 批量查询任务状态（命令码 1110, 0x0456）
// ============================================================================

/**
 * @brief 1110 命令请求数据。
 */
message QueryTaskStatusPackageReqData {
  repeated string task_ids = 1;                ///< 要查询的任务 ID 列表；为空则查所有未完成 + 最近一条完成
}

/**
 * @brief 单条任务状态项。
 */
message SingleTaskStatusItem {
  optional string task_id = 1;                 ///< 任务 ID
  optional int32 status = 2;                   ///< 状态码（同 task_status）
  optional int32 type = 3;                     ///< 任务类型（同 task_type）
}

/**
 * @brief 任务状态包（包含最近完成和所有未完成的任务）。
 */
message TaskStatusPackage {
  optional string closest_target = 1;          ///< 最近站点 ID
  optional string source_name = 2;             ///< 起点站点名称
  optional string target_name = 3;             ///< 终点站点名称
  optional double percentage = 4;              ///< 任务进度百分比（0~100）
  optional double distance = 5;                ///< 剩余距离（米）
  optional string info = 6;                    ///< 附加信息
  repeated SingleTaskStatusItem task_status_list = 7; ///< 所有任务状态列表
}

/**
 * @brief 1110 命令响应数据。
 */
message QueryTaskStatusPackageResData {
  optional TaskStatusPackage task_status_package = 1; ///< 任务状态包
  optional int32 ret_code = 2;                 ///< 返回码，0 表示成功
  optional string create_on = 3;               ///< 时间戳
  optional string err_msg = 4;                 ///< 错误信息
}

/**
 * @brief 批量查询任务状态命令。
 */
message RobotStatusTaskPackageCommand {
  message Request {
    CommandHeader.Request header = 1;
    QueryTaskStatusPackageReqData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    QueryTaskStatusPackageResData data = 2;
  }
}


// ============================================================================
// 16. 单点站点自动规划导航（命令码 3051, 0x0BEB）
// ============================================================================

/**
 * @brief DI（数字输入）点位。
 */
message DIItem {
  int32 id = 1;                                ///< DI 编号
  bool status = 2;                             ///< 状态（true=高电平, false=低电平）
}

/**
 * @brief DO（数字输出）点位。
 */
message DOItem {
  int32 id = 1;                                ///< DO 编号
  bool status = 2;                             ///< 状态（true=高电平, false=低电平）
}

/**
 * @brief 音频播放参数。
 */
message SoundArgs {
  optional string name = 1;                    ///< 音频文件名
  optional int32 loop = 2;                     ///< 循环次数（0=播放一次, 1=循环）
  optional int32 stop = 3;                     ///< 1=停止播放
}

/**
 * @brief WaitDI 动作参数。
 */
message WaitDIArgs {
  repeated DIItem DI = 1;                      ///< 需要等待的 DI 条件列表
  optional double timeout = 2;                 ///< 超时时间（秒），0 表示无限等待
}

/**
 * @brief SetDO 动作参数。
 */
message SetDOArgs {
  repeated DOItem do_list = 1;                 ///< 要设置的 DO 列表
}

/**
 * @brief PGV（二维码）二次定位参数。
 */
message PgvParam {
  optional bool use_pgv = 1;                   ///< 是否使用上视 PGV
  optional bool use_down_pgv = 2;              ///< 是否使用下视 PGV
  optional double pgv_adjust_dist = 3;         ///< 最大调整半径（米）
  optional double pgv_adjust_cx = 4;           ///< 二维码坐标系 X 偏移（米）
  optional double pgv_adjust_cy = 5;           ///< 二维码坐标系 Y 偏移（米）
  optional double pgv_x_adjust = 6;            ///< 仅调整 X 方向误差（米）
}

/**
 * @brief 自由导航目标点（坐标 + 朝向）。
 */
message FreeGoPoint {
  double x = 1;                                ///< X 坐标（米）
  double y = 2;                                ///< Y 坐标（米）
  double theta = 3;                            ///< 朝向角（弧度）
}

/**
 * @brief 脚本自定义参数。
 */
message ScriptArgs {
  map<string, string> str_kv = 1;              ///< 字符串键值对
  map<string, double> num_kv = 2;              ///< 数字键值对
  repeated DOItem do_list = 3;                 ///< DO 列表
  repeated DIItem di_list = 4;                 ///< DI 列表
}

/**
 * @brief 3051 命令请求数据（单点站点自动规划导航）。
 * @warning 严禁用于多车调度场景，仅限单车测试/验证。
 *          下发此任务会取消当前正在执行的任务（不排队）。
 *          成功下发后会自动清除指定的 warning/error 报错码。
 *          自由导航（freego）仅支持双轮差速底盘。
 */
message RobotGoTargetReqData {
  // -------- 必填字段 --------
  string source_id = 1;                        ///< 起始站点 ID（"SELF_POSITION" 表示当前位置）
  string id = 2;                               ///< 目标站点 ID（"SELF_POSITION" 表示原地执行 operation）
  optional string task_id = 3;                 ///< 任务 ID（建议提供）

  // -------- 运动通用参数 --------
  optional double angle = 4;                   ///< 目标点朝向角（弧度）
  optional string method = 5;                  ///< "forward"（正走）或 "backward"（倒走）
  optional double max_speed = 6;               ///< 最大线速度（m/s），0 使用默认
  optional double max_wspeed = 7;              ///< 最大角速度（rad/s）
  optional double max_acc = 8;                 ///< 最大加速度（m/s²）
  optional double max_wacc = 9;                ///< 最大角加速度（rad/s²）
  optional int64 duration = 10;                ///< 到达后等待时间（毫秒）
  optional int32 orientation = 11;             ///< 全向车保持的角度（暂不使用）
  optional bool spin = 12;                     ///< 是否随动（托盘旋转）
  optional int64 delay = 13;                   ///< 延迟结束导航状态（毫秒），缺省 0
  optional int32 start_rot_dir = 14;           ///< 起步旋转方向：-1=顺时针, 0=就近, 1=逆时针
  optional int32 end_rot_dir = 15;             ///< 到点旋转方向：-1=顺时针, 0=就近, 1=逆时针
  optional double reach_dist = 16;             ///< 到点位置精度（米）
  optional double reach_angle = 17;            ///< 到点角度精度（弧度）
  optional string skill_name = 18;             ///< 技能名称："Action" 或 "GotoSpecifiedPose"

  // -------- PGV --------
  optional PgvParam pgv = 19;                  ///< 二次定位参数

  // -------- 设备动作 --------
  optional string operation = 20;              ///< 操作类型（JackLoad/ForkUnload/RollerLoad/HookLoad/WaitDI/SetDO/sound/Script 等）
  optional double jack_height = 21;            ///< 顶升高度（米）
  optional double start_height = 22;           ///< 货叉起步前举升高度（米）
  optional double end_height = 23;             ///< 货叉到点后举升高度（米）
  optional double fork_mid_height = 24;        ///< 货叉行走过程中举升高度（米）
  optional double fork_dist = 25;              ///< 货叉前移距离（米）
  optional string direction = 26;              ///< 方向："left"/"right"/"front"/"back"
  optional bool recognize = 27;                ///< 是否启用视觉识别
  optional string recfile = 28;                ///< 识别文件路径（如 "shelf/s0002.shelf"）

  // -------- 音频 --------
  optional SoundArgs sounds_args = 29;         ///< 音频参数

  // -------- WaitDI / SetDO --------
  optional WaitDIArgs wait_di_args = 30;       ///< WaitDI 参数
  optional SetDOArgs set_do_args = 31;         ///< SetDO 参数

  // -------- 脚本 --------
  optional string script_name = 32;            ///< 脚本文件名
  optional ScriptArgs script_args = 33;        ///< 脚本参数
  optional int32 script_stage = 34;            ///< 执行阶段：0=导航前, 1=导航中, 2=导航后, 3=脚本控制导航

  // -------- 原地旋转（GoByOdometer） --------
  optional double move_angle = 35;             ///< 旋转角度（弧度）
  optional double speed_w = 36;                ///< 旋转角速度（rad/s）
  optional int32 loc_mode = 37;                ///< 定位模式：1=激光定位, 0=里程定位

  // -------- 自由导航（仅双轮差速） --------
  optional FreeGoPoint freego = 38;            ///< 自由导航目标（若提供则忽略 id 字段）
}

/**
 * @brief 3051 命令响应数据。
 */
message RobotGoTargetResData {
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功接收
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 单点导航命令。
 */
message RobotGoTargetCommand {
  message Request {
    CommandHeader.Request header = 1;
    RobotGoTargetReqData data = 2;
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    RobotGoTargetResData data = 2;
  }
}


// ============================================================================
// 17. 停止开环运动（命令码 2000, 0x07D0）
// ============================================================================

/**
 * @brief 停止开环运动请求数据（无业务字段）。
 * @note 命令码 2000（0x07D0）。下发零速度指令，终止当前持续运动。
 */
message RobotControlStopRequestData
{
}

/**
 * @brief 停止开环运动响应结果。
 */
message RobotControlStopResult
{
  optional int32 ret_code = 1;                 ///< 返回码，0 表示成功
  optional string create_on = 2;               ///< 时间戳
  optional string err_msg = 3;                 ///< 错误信息
}

/**
 * @brief 停止开环运动命令。
 */
message RobotControlStopCommand
{
  message Request
  {
    CommandHeader.Request header = 1;
    RobotControlStopRequestData data = 2;
  }
  message Feedback
  {
    CommandHeader.Feedback header = 1;
    RobotControlStopResult status = 2;
  }
}


// ============================================================================
// 18. 暂停/继续/取消导航任务（命令码 3001/3002/3003）
// ============================================================================

/**
 * @brief 暂停当前导航任务（命令码 3001, 0x0BB9）。
 * @note 请求无业务数据。
 */
message RobotTaskPauseCommand {
  message Request {
    CommandHeader.Request header = 1;
    // 无 data 字段
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    /**
     * @brief 暂停结果。
     */
    message Result {
      optional int32 ret_code = 1;         ///< 返回码，0 表示成功
      optional string create_on = 2;       ///< 时间戳
      optional string err_msg = 3;         ///< 错误信息
    }
    Result status = 2;
  }
}

/**
 * @brief 继续当前导航任务（命令码 3002, 0x0BBA）。
 * @note 请求无业务数据。
 */
message RobotTaskResumeCommand {
  message Request {
    CommandHeader.Request header = 1;
    // 无 data 字段
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    /**
     * @brief 继续结果。
     */
    message Result {
      optional int32 ret_code = 1;         ///< 返回码，0 表示成功
      optional string create_on = 2;       ///< 时间戳
      optional string err_msg = 3;         ///< 错误信息
    }
    Result status = 2;
  }
}

/**
 * @brief 取消当前导航任务（命令码 3003, 0x0BBB）。
 * @note 请求无业务数据。
 */
message RobotTaskCancelCommand {
  message Request {
    CommandHeader.Request header = 1;
    // 无 data 字段
  }
  message Feedback {
    CommandHeader.Feedback header = 1;
    /**
     * @brief 取消结果。
     */
    message Result {
      optional int32 ret_code = 1;         ///< 返回码，0 表示成功
      optional string create_on = 2;       ///< 时间戳
      optional string err_msg = 3;         ///< 错误信息
    }
    Result status = 2;
  }
}