syntax = "proto3";

// 规范包名，防止与其他业务（如底盘 chassis 或 运控 control）的接口冲突
// 设计原则：严格遵循“走-停-拍”防延迟策略与大文件分块流式传输
package agv.calibration.sensor;

// =========================================================
// 核心服务：多传感器自动化外参标定代理服务 
// [部署端 Server]：Windows车端 (充当带轮子的三脚架与文件下载服务器)
// [调用端 Client]：Linux标定服务器 (掌控状态机、拉取大文件、算 Ceres 矩阵)
// =========================================================
service SensorCalibrationService {
  
  // ---------------------------------------------------------
  // 第一步：物理走位 (走)
  // ---------------------------------------------------------
  // 💻 [Linux 发送 -> Windows]：调度车辆开到指定的标定观测点，到达后【绝对刹车静止】
  // 🚙 [Windows 返回 -> Linux]：物理到位抱死刹车后，返回成功回执
  // 🚨 业务潜台词：Linux 收到回执后，必须在代码里强制 sleep(0.5s) 等待避震悬挂平息，冻结物理空间！
  rpc MoveToObservationPose (PoseRequest) returns (StandardResponse);

  // ---------------------------------------------------------
  // 第二步：防延迟同步锁存 (停与拍)
  // ---------------------------------------------------------
  // 💻 [Linux 发送 -> Windows]：命令车辆瞬间将底层相机的显存和雷达的点云冻结到后备内存池
  // 🚙 [Windows 返回 -> Linux]：立刻锁存，并返回高精度硬件时间戳，作为后续拉取大文件的唯一“取件码”
  rpc TriggerSyncCapture (CaptureRequest) returns (CaptureResponse);

  // ---------------------------------------------------------
  // 第三步：大文件流式下载 (传 —— 破解 Windows 网络延迟的绝杀)
  // ---------------------------------------------------------
  // 💻 [Linux 发送 -> Windows]：凭“取件码”请求下载巨大的图片/点云原文件
  // 🚙 [Windows 持续流式返回 -> Linux]：将 5MB+ 的无损文件切成小块，像流水一样源源不断传回 Linux
  // 🚨 业务潜台词：必须使用 stream 关键字！否则 gRPC 会因为单包超过 4MB 瞬间崩溃！
  rpc DownloadImage (DataFetchRequest) returns (stream FileChunk);
  rpc DownloadPointCloud (DataFetchRequest) returns (stream FileChunk);

  // ---------------------------------------------------------
  // 第四步：标定闭环定稿 (写)
  // ---------------------------------------------------------
  // 💻 [Linux 发送 -> Windows]：Linux 攒够数据算完复杂的 4x4 外参矩阵后，下发给车端持久化保存
  // 🚙 [Windows 返回 -> Linux]：车端收到后直接覆写 sensor_config.yaml 或注册表，返回成功
  rpc CommitCalibrationResults (CalibrationPayload) returns (StandardResponse);
}

// =========================================================
// 基础响应
// =========================================================

// 🚙 [流向]：Windows 返回 -> Linux (通用应答)
message StandardResponse {
  bool success = 1;
  string message = 2; // 成功提示或具体的报错原因（如：标定点坐标越界导致碰撞防线触发）
}

// =========================================================
// 1. 物理走位请求 (走)
// =========================================================

// 💻 [流向]：Linux 发送 -> Windows
message PoseRequest {
  double target_x_m = 1;     // 目标 X 坐标 (米)
  double target_y_m = 2;     // 目标 Y 坐标 (米)
  double target_yaw_deg = 3; // 目标偏航角 (度)
  bool is_relative = 4;      // true: 相对当前位置移动; false: 绝对世界坐标
}

// =========================================================
// 2. 触发同步抓拍请求与响应 (停与拍)
// =========================================================

// 💻 [流向]：Linux 发送 -> Windows
message CaptureRequest {
  // 业务潜台词：告诉车端这次要同时拍哪些传感器，例如 ["cam_front", "lidar_top"]
  // 收到指令的这一微秒，车端底层必须同时将名单里传感器的数据 copy 冻结出来！未点名的不管，节约内存。
  repeated string sensor_ids = 1; 
}

// 🚙 [流向]：Windows 返回 -> Linux
message CaptureResponse {
  bool success = 1;
  // 🚨 极度关键：“取件码”！车端打上的高精度硬件时间戳(微秒)。
  // 业务潜台词：这是提取大文件的唯一凭证，无论一会儿 Wi-Fi 传得有多慢，只要凭证一致，保证拿回来的图片和点云在物理空间上是绝对严丝合缝对齐的！
  int64 capture_timestamp_us = 2; 
  string error_message = 3;
}

// =========================================================
// 3. 大文件下载请求与文件流块 (传)
// =========================================================

// 💻 [流向]：Linux 发送 -> Windows (凭码提货)
message DataFetchRequest {
  int64 capture_timestamp_us = 1; // 阶段2拿到的唯一“取件码”
  string sensor_id = 2;           // 具体要拉取哪个传感器的数据，例如 "cam_front"
}

// 🚙 [流向]：Windows 持续流式返回 -> Linux (流水线发货)
message FileChunk {
  // 业务潜台词：规避 gRPC 单条消息默认 4MB 的内存限制，防止传大图片时程序崩溃。
  bytes chunk_data = 1;   // 文件的二进制分块（建议车端 C++ 每次切 512KB - 1MB 发送）
  bool is_last_chunk = 2; // 标记是否为最后一块，告诉 Linux 停止拼接并保存文件
  
  // 🚨 致命防坑潜台词：绝对禁止传 "jpg" 或 "jpeg"！
  // 有损压缩的伪影会导致亚像素角点提取出现好几个像素的偏差，毁掉整个 3D 标定精度。
  // 必须是 "png", "bmp", "raw" 或 "pcd"！
  string format_ext = 3;  
}

// =========================================================
// 4. 标定结果载荷（支持内参、外参灵活组合发回车端） (写)
// =========================================================

// 💻 [流向]：Linux 发送 -> Windows (下发算好的内参，多为离线预标定备用)
message CameraIntrinsics {
  string camera_id = 1;
  double fx = 2; double fy = 3;
  double cx = 4; double cy = 5;
  repeated double dist_coeffs = 6; // 畸变系数阵列 [k1, k2, p1, p2, k3]
}

// 💻 [流向]：Linux 发送 -> Windows (下发算好的 6-DOF 外参矩阵)
message SensorExtrinsics {
  // 业务潜台词：告诉车端，source 坐标系 相对 target 坐标系，平移和旋转了多少。
  string source_frame = 1; // 源坐标系，如 "lidar_top" 或 "cam_left"
  string target_frame = 2; // 目标坐标系，如 "cam_front" 或 "base_link" (底盘质心)
  
  // 平移向量 (强制规定工业制式单位为毫米 mm，消除浮点数歧义)
  double trans_x_mm = 3; 
  double trans_y_mm = 4; 
  double trans_z_mm = 5;
  
  // 空间旋转姿态 (强制规定工业制式单位为度 degrees，方便人工排错，绝不混用弧度)
  double roll_deg = 6; 
  double pitch_deg = 7; 
  double yaw_deg = 8;
}

// 💻 [流向]：Linux 发送 -> Windows (出厂全量包)
message CalibrationPayload {
  string task_id = 1; // 标定任务流水号，用于 MES 系统的云端溯源
  
  // 业务潜台词：采用 repeated 数组，Linux 可以一次性把全车所有的内参、外参一股脑发过去。
  // 车端只需一个 for 循环，把这些矩阵依次写进硬盘即可。
  repeated CameraIntrinsics updated_intrinsics = 2;
  repeated SensorExtrinsics updated_extrinsics = 3;
}