syntax = "proto3";

package agv.calibration.sensor;

import "calibration_common.proto";
import "vehicle_profile.proto";

// =========================================================
// 文件作用：传感器标定执行代理协议
// 运行关系：
// Ubuntu 车间电脑(Linux) -> Windows 车端代理 / 采集服务
// 说明：
// 1) Linux 负责图像 / 点云 / 激光扫描 / IMU 数据分析与参数求解
// 2) 车端 / 采集服务负责订阅车端传感器、组织采集、回传原始数据和观测状态、写入结果
// 3) 支持相机内参、IMU 内参、传感器外参、手眼标定
// 4) 支持相机“只做内参 / 只做外参 / 内外参都做”
// =========================================================

// =========================================================
// 服务：传感器标定服务
// 运行位置：Windows 车端代理 或 Linux 采集桥接服务
// 调用方：Ubuntu 车间电脑
// =========================================================
service AgvCalibSensorService {
  // 心跳保活
  rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
      returns (.agv.calibration.common.HeartbeatResponse);

  // 查询传感器标定服务当前是否具备执行条件
  rpc GetSensorReadiness(.agv.calibration.common.AgentReadinessRequest)
      returns (SensorReadinessResponse);

  // 设置传感器标定工作模式
  rpc SetSensorCalibrationWorkMode(SensorCalibrationWorkModeRequest)
      returns (.agv.calibration.common.StandardResponse);

  // 查询传感器标定能力
  rpc GetSensorCalibrationCapability(SensorCalibrationCapabilityRequest)
      returns (SensorCalibrationCapabilityResponse);

  // 启动一个传感器标定任务
  rpc StartSensorCalibrationTask(SensorCalibrationTaskRequest)
      returns (.agv.calibration.common.JobAccepted);

  // 查询传感器标定任务状态
  rpc GetSensorCalibrationJobStatus(.agv.calibration.common.JobQuery)
      returns (.agv.calibration.common.JobStatus);

  // 查询传感器标定任务结果
  rpc GetSensorCalibrationJobResult(.agv.calibration.common.JobQuery)
      returns (SensorCalibrationJobResult);

  // 取消传感器标定任务
  rpc CancelSensorCalibrationJob(.agv.calibration.common.JobQuery)
      returns (.agv.calibration.common.StandardResponse);

  // 打开传感器标定遥测流
  rpc StreamSensorCalibrationTelemetry(StreamSensorCalibrationTelemetryRequest)
      returns (stream SensorCalibrationTelemetry);

  // 打开车端传感器原始数据流；用于 WiFi6 把车载相机、雷达、IMU 数据传给车间电脑
  rpc StreamVehicleSensorData(StreamVehicleSensorDataRequest)
      returns (stream VehicleSensorFrame);

  // 写入传感器标定参数
  rpc CommitSensorCalibrationParameters(CommitSensorCalibrationParametersRequest)
      returns (.agv.calibration.common.StandardResponse);

  // 查询当前已生效传感器参数
  rpc GetAppliedSensorCalibrationParameters(GetAppliedSensorCalibrationParametersRequest)
      returns (AppliedSensorCalibrationParametersResponse);

  // 紧急停止
  rpc EmergencyStop(.agv.calibration.common.Empty)
      returns (.agv.calibration.common.StandardResponse);
}

// =========================================================
// 服务：车间电脑传感器数据接收服务
// 运行位置：Ubuntu 车间电脑
// 调用方：Windows 车端代理 / 仿真车端代理
// 说明：
// 1) 当现场网络策略采用车端主动上报时使用
// 2) 与 StreamVehicleSensorData 使用同一帧结构
// =========================================================
service AgvCalibWorkshopSensorIngestService {
  rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
      returns (.agv.calibration.common.HeartbeatResponse);

  rpc PushVehicleSensorFrame(VehicleSensorFrame)
      returns (.agv.calibration.common.StandardResponse);
}

// =========================================================
// 传感器标定工作模式
// 作用：切换采集服务到不同模式
// =========================================================
message SensorCalibrationWorkModeRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头

  enum Mode {
    SENSOR_CALIBRATION_MODE_UNSPECIFIED = 0; // 未指定
    NORMAL_MODE = 1;                         // 正常模式
    CALIBRATION_READY_MODE = 2;              // 标定准备模式
    DATA_CAPTURE_MODE = 3;                   // 采集模式
    VALIDATION_MODE = 4;                     // 验证模式
  }

  Mode target_mode = 2; // 目标模式
  string reason = 3;    // 切换原因
}

// =========================================================
// 传感器服务就绪响应
// 作用：返回当前传感器服务是否满足执行条件
// =========================================================
message SensorReadinessResponse {
  bool success = 1;                                  // 是否成功
  .agv.calibration.common.ErrorCode error_code = 2; // 错误码
  string message = 3;                                // 说明

  bool agent_ready = 4;                // 服务本身是否就绪
  bool capture_pipeline_ready = 5;     // 采集链路是否就绪
  bool storage_ready = 6;              // 存储链路是否就绪
  bool telemetry_ready = 7;            // 遥测链路是否就绪
  bool vehicle_safe_to_move = 8;       // 若任务需移动车辆，当前是否允许移动
  bool arm_ready = 9;                  // 若任务涉及机械臂，机械臂是否就绪

  repeated string ready_sensor_ids = 10; // 已就绪的传感器 ID
  repeated .agv.calibration.common.ReadinessIssue issues = 11; // 不满足项
  int64 checked_timestamp_us = 12;      // 检查时间

  bool sensor_data_stream_ready = 13;   // 车端原始传感器数据流是否就绪
  repeated string streaming_sensor_ids = 14; // 当前可被转发到车间电脑的传感器 ID
  string sensor_data_transport = 15;    // 传输方式，例如 wifi6 / wifi6_sim_tcp
}

// =========================================================
// 查询传感器标定能力请求
// 作用：查询当前车辆和工位支持的传感器标定能力
// =========================================================
message SensorCalibrationCapabilityRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
}

// =========================================================
// 查询传感器标定能力响应
// 作用：返回支持的传感器标定类型
// =========================================================
message SensorCalibrationCapabilityResponse {
  bool success = 1;                                  // 是否成功
  .agv.calibration.common.ErrorCode error_code = 2; // 错误码
  string message = 3;                                // 说明

  bool supports_camera_intrinsic = 4;         // 是否支持相机内参标定
  bool supports_imu_intrinsic = 5;            // 是否支持 IMU 内参标定
  bool supports_sensor_to_base_extrinsic = 6; // 是否支持传感器到 base_link 外参
  bool supports_hand_eye = 7;                 // 是否支持手眼标定

  bool supports_3d_lidar_extrinsic = 8;       // 是否支持 3D 激光雷达外参
  bool supports_2d_lidar_extrinsic = 9;       // 是否支持 2D 激光雷达外参
  bool supports_camera_only_intrinsic = 10;   // 是否支持仅做相机内参
  bool supports_camera_only_extrinsic = 11;   // 是否支持仅做相机外参
  bool supports_vehicle_sensor_data_stream = 12; // 是否支持车端原始传感器数据流
  bool supports_sensor_frame_chunking = 13;      // 是否支持大帧分片传输
  bool supports_compressed_image_stream = 14;    // 是否支持压缩图像流
}

// =========================================================
// 采集意图类型
// 作用：告诉采集服务“这次数据是为哪类标定而采”
// =========================================================
enum CaptureIntentType {
  CAPTURE_INTENT_TYPE_UNSPECIFIED = 0; // 未指定
  CAMERA_INTRINSIC_CAPTURE = 1;        // 相机内参采集
  CAMERA_EXTRINSIC_CAPTURE = 2;        // 相机外参采集
  LIDAR_3D_EXTRINSIC_CAPTURE = 3;      // 3D 激光雷达外参采集
  LIDAR_2D_EXTRINSIC_CAPTURE = 4;      // 2D 激光雷达外参采集
  IMU_STATIC_CAPTURE = 5;              // IMU 静态采集
  IMU_DYNAMIC_CAPTURE = 6;             // IMU 动态采集
  HAND_EYE_CAPTURE = 7;                // 手眼标定采集
  VALIDATION_CAPTURE = 8;              // 验证采集
}

message CalibrationReferenceTarget {
  string reference_target_id = 1;    // 参考目标唯一 ID
  string reference_target_type = 2;  // 类型，例如 board / pole / wall / ground_plane
  string description = 3;            // 说明
}

// =========================================================
// 相机内参标定任务
// 作用：采集图像并求解相机内参
// =========================================================
message CameraIntrinsicCalibrationTask {
  string sensor_id = 1;            // 目标相机 ID
  uint32 required_image_count = 2; // 所需图像数
  double timeout_sec = 3;          // 超时时间
  string target_board_id = 4;      // 标定板 ID
  CalibrationReferenceTarget reference_target = 5; // 统一参考目标
}

// =========================================================
// IMU 内参标定任务
// 作用：采集 IMU 静态 / 动态数据并求解偏置等参数
// =========================================================
message IMUIntrinsicCalibrationTask {
  string sensor_id = 1;                     // 目标 IMU ID
  uint32 required_static_segment_count = 2; // 静止段数量
  uint32 required_motion_segment_count = 3; // 运动段数量
  double timeout_sec = 4;                   // 超时时间
}

// =========================================================
// 传感器到 base_link 外参标定任务
// 作用：求解单个传感器与车辆 base_link 的位姿关系
// 说明：
// 1) 相机可只做外参
// 2) 3D / 2D 激光雷达通常只做外参
// 3) IMU 可做外参
// =========================================================
message SensorToBaseExtrinsicCalibrationTask {
  string sensor_id = 1;             // 目标传感器 ID
  string base_frame_id = 2;         // 车辆 base_link / 基准 frame
  uint32 required_sample_count = 3; // 所需样本数
  double timeout_sec = 4;           // 超时时间
  CaptureIntentType capture_intent = 5; // 采集意图
  CalibrationReferenceTarget reference_target = 6; // 统一参考目标
}

// =========================================================
// 手眼标定模式
// 作用：区分眼在手上与眼在手外
// =========================================================
enum HandEyeCalibrationMode {
  HAND_EYE_CALIBRATION_MODE_UNSPECIFIED = 0; // 未指定
  EYE_IN_HAND = 1;                           // 眼在手上
  EYE_TO_HAND = 2;                           // 眼在手外
}

// =========================================================
// 手眼标定任务
// 作用：求解机械臂相机与机械臂 / 车体的关系
// =========================================================
message HandEyeCalibrationTask {
  string sensor_id = 1;            // 目标相机 ID
  string arm_id = 2;               // 机械臂 ID
  HandEyeCalibrationMode mode = 3; // 手眼模式
  uint32 required_pose_count = 4;  // 所需位姿数
  double timeout_sec = 5;          // 超时时间
  CalibrationReferenceTarget reference_target = 6; // 统一参考目标
}

// =========================================================
// 传感器标定任务子类型
// 作用：在顶层 task_type 之下进一步区分具体算法分支
// =========================================================
enum SensorCalibrationTaskSubtype {
  SENSOR_CALIBRATION_TASK_SUBTYPE_UNSPECIFIED = 0;
  FRONT_CAMERA_INTRINSIC = 1;
  DOWNWARD_CAMERA_INTRINSIC = 2;
  IMU_INTRINSIC = 3;
  FRONT_CAMERA_EXTRINSIC = 4;
  DOWNWARD_CAMERA_EXTRINSIC = 5;
  IMU_EXTRINSIC = 6;
  LIDAR_2D_EXTRINSIC = 7;
  LIDAR_3D_EXTRINSIC = 8;
  HAND_EYE_IN_HAND = 9;
  HAND_EYE_TO_HAND = 10;
}

// =========================================================
// 传感器标定任务请求
// 作用：启动一次传感器标定任务
// 发送方：Ubuntu 车间电脑
// 接收方：Windows 车端代理 / Linux 采集服务
// =========================================================
message SensorCalibrationTaskRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
  string test_case_id = 2;                          // 测试用例 ID

  enum TaskPurpose {
    SENSOR_TASK_PURPOSE_UNSPECIFIED = 0; // 未指定
    DATA_COLLECTION = 1;                 // 数据采集
    SOLVING = 2;                         // 求解
    VALIDATION = 3;                      // 验证
  }

  TaskPurpose task_purpose = 3; // 任务目的
  SensorCalibrationTaskSubtype task_subtype = 4; // 任务子类型

  oneof task {
    CameraIntrinsicCalibrationTask camera_intrinsic = 5;           // 相机内参
    IMUIntrinsicCalibrationTask imu_intrinsic = 6;                 // IMU 内参
    SensorToBaseExtrinsicCalibrationTask sensor_to_base_extrinsic = 7; // 外参
    HandEyeCalibrationTask hand_eye = 8;                           // 手眼标定
  }

  string source_iteration_id = 9; // 来源迭代号
}

// =========================================================
// 传感器遥测流请求
// 作用：配置传感器标定过程中的状态上报
// =========================================================
message StreamSensorCalibrationTelemetryRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
  uint32 expected_hz = 2;                           // 期望上报频率
  bool include_observation_progress = 3;            // 是否包含采集进度
  bool include_quality_metrics = 4;                 // 是否包含质量指标
  bool include_capture_intent = 5;                  // 是否包含采集意图
}

// =========================================================
// 传感器标定遥测
// 作用：回传采集进度、观测质量和任务执行状态
// =========================================================
message SensorCalibrationTelemetry {
  int64 hardware_timestamp_us = 1;  // 硬件时间戳
  string sensor_id = 2;             // 传感器 ID
  .agv.calibration.vehicle.profile.SensorType sensor_type = 3; // 传感器类型
  uint32 collected_sample_count = 4; // 已采集样本数
  uint32 target_sample_count = 5;    // 目标样本数
  bool target_detected = 6;          // 当前是否检测到标定目标
  double quality_score = 7;          // 当前观测质量评分
  string active_job_id = 8;          // 当前任务 ID
  CaptureIntentType capture_intent = 9; // 当前采集意图
}

// =========================================================
// 车端传感器原始数据流请求
// 作用：车间电脑指定要接收哪些车端传感器数据
// =========================================================
message StreamVehicleSensorDataRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
  repeated string sensor_ids = 2;                   // 要接收的传感器 ID；为空表示由任务决定
  repeated .agv.calibration.vehicle.profile.SensorType sensor_types = 3; // 要接收的传感器类型
  uint32 expected_hz = 4;                           // 期望上报频率，0 表示车端默认
  uint32 max_frame_payload_bytes = 5;               // 单个分片最大 payload，0 表示车端默认
  bool allow_compressed = 6;                        // 是否允许压缩图像 / 点云
  bool include_camera_info = 7;                     // 是否随图像携带相机内参初值
  CaptureIntentType capture_intent = 8;             // 数据采集意图
  string active_job_id = 9;                         // 关联的标定任务 ID
}

// =========================================================
// 传感器原始帧负载类型
// =========================================================
enum SensorFramePayloadType {
  SENSOR_FRAME_PAYLOAD_TYPE_UNSPECIFIED = 0;
  IMAGE_FRAME = 1;        // 相机图像
  POINT_CLOUD_FRAME = 2;  // 3D 激光雷达点云
  LASER_SCAN_FRAME = 3;   // 2D 激光雷达扫描
  IMU_FRAME = 4;          // IMU 数据
  CAMERA_INFO_FRAME = 5;  // 相机内参 / camera_info
  RAW_BYTES_FRAME = 6;    // 预留给厂商私有二进制格式
}

// =========================================================
// 传感器原始帧公共头
// =========================================================
message VehicleSensorFrameHeader {
  string sensor_id = 1;    // 传感器 ID，例如 demo_front_camera
  .agv.calibration.vehicle.profile.SensorType sensor_type = 2; // 传感器类型
  string frame_id = 3;     // ROS / 车端坐标系 frame_id
  int64 hardware_timestamp_us = 4; // 硬件时间戳
  uint64 sequence_id = 5;  // 单传感器递增帧号
  string active_job_id = 6; // 关联任务 ID
  CaptureIntentType capture_intent = 7; // 采集意图
}

// =========================================================
// 图像帧
// 对齐 ROS sensor_msgs/Image 的核心字段
// =========================================================
message VehicleImageFrame {
  uint32 width = 1;        // 图像宽度
  uint32 height = 2;       // 图像高度
  string encoding = 3;     // 例如 rgb8 / bgr8 / mono8 / jpeg / png
  bool is_bigendian = 4;   // 字节序
  uint32 step = 5;         // 每行字节数
  bytes data = 6;          // 图像数据或分片数据
  string compression = 7;  // none / jpeg / png
  CameraIntrinsics camera_intrinsics_hint = 8; // 可选内参初值或当前参数
}

// =========================================================
// PointCloud2 字段描述
// =========================================================
enum PointCloudFieldDataType {
  POINT_CLOUD_FIELD_DATA_TYPE_UNSPECIFIED = 0;
  POINT_CLOUD_FIELD_INT8 = 1;
  POINT_CLOUD_FIELD_UINT8 = 2;
  POINT_CLOUD_FIELD_INT16 = 3;
  POINT_CLOUD_FIELD_UINT16 = 4;
  POINT_CLOUD_FIELD_INT32 = 5;
  POINT_CLOUD_FIELD_UINT32 = 6;
  POINT_CLOUD_FIELD_FLOAT32 = 7;
  POINT_CLOUD_FIELD_FLOAT64 = 8;
}

message PointCloudField {
  string name = 1;                    // 字段名，例如 x / y / z / intensity
  uint32 offset = 2;                   // 字节偏移
  PointCloudFieldDataType datatype = 3; // 数据类型
  uint32 count = 4;                    // 元素数量
}

// =========================================================
// 3D 点云帧
// 对齐 ROS sensor_msgs/PointCloud2 的核心字段
// =========================================================
message VehiclePointCloudFrame {
  uint32 height = 1;
  uint32 width = 2;
  repeated PointCloudField fields = 3;
  bool is_bigendian = 4;
  uint32 point_step = 5;
  uint32 row_step = 6;
  bytes data = 7;        // 点云数据或分片数据
  bool is_dense = 8;
  string compression = 9; // none / zstd / lz4 / vendor
}

// =========================================================
// 2D 激光雷达扫描帧
// 对齐 ROS sensor_msgs/LaserScan 的核心字段
// =========================================================
message VehicleLaserScanFrame {
  double angle_min_rad = 1;
  double angle_max_rad = 2;
  double angle_increment_rad = 3;
  double time_increment_sec = 4;
  double scan_time_sec = 5;
  double range_min_m = 6;
  double range_max_m = 7;
  repeated float ranges_m = 8;
  repeated float intensities = 9;
}

// =========================================================
// IMU 帧
// 对齐 ROS sensor_msgs/Imu 的核心字段
// =========================================================
message VehicleImuFrame {
  double orientation_x = 1;
  double orientation_y = 2;
  double orientation_z = 3;
  double orientation_w = 4;
  repeated double orientation_covariance = 5;
  .agv.calibration.common.Vector3D angular_velocity = 6;
  repeated double angular_velocity_covariance = 7;
  .agv.calibration.common.Vector3D linear_acceleration = 8;
  repeated double linear_acceleration_covariance = 9;
}

// =========================================================
// 车端传感器原始帧
// 作用：通过 WiFi6 在车端和车间电脑之间传输相机 / 雷达 / IMU 数据
// 说明：
// 1) fragment_index / total_fragments 用于大图像或点云分片
// 2) total_fragments 为 0 或 1 表示本消息携带完整帧
// 3) oneof 中的数据字段可携带完整数据，也可携带当前分片数据
// =========================================================
message VehicleSensorFrame {
  VehicleSensorFrameHeader header = 1;  // 公共帧头
  SensorFramePayloadType payload_type = 2; // 负载类型
  string stream_id = 3;                 // 数据流 ID
  uint32 fragment_index = 4;            // 当前分片序号，从 0 开始
  uint32 total_fragments = 5;           // 总分片数；0 或 1 表示完整帧
  bool is_final_fragment = 6;           // 当前是否最后一个分片
  uint32 uncompressed_size_bytes = 7;   // 解压后总大小，用于校验
  string payload_digest = 8;            // 完整帧摘要，例如 sha256:...

  oneof payload {
    VehicleImageFrame image = 20;
    VehiclePointCloudFrame point_cloud = 21;
    VehicleLaserScanFrame laser_scan = 22;
    VehicleImuFrame imu = 23;
    CameraIntrinsics camera_info = 24;
    bytes raw_payload = 25;
  }
}

// =========================================================
// 相机模型类型
// 作用：表达相机内参使用的模型
// =========================================================
enum CameraModelType {
  CAMERA_MODEL_TYPE_UNSPECIFIED = 0; // 未指定
  PINHOLE = 1;                       // 针孔模型
  FISHEYE = 2;                       // 鱼眼模型
}

// =========================================================
// 相机内参
// 作用：表达相机标定后的内参结果
// =========================================================
message CameraIntrinsics {
  uint32 image_width = 1;              // 图像宽度
  uint32 image_height = 2;             // 图像高度
  double fx = 3;                       // 焦距 fx
  double fy = 4;                       // 焦距 fy
  double cx = 5;                       // 主点 cx
  double cy = 6;                       // 主点 cy
  repeated double distortion_coeffs = 7; // 畸变系数
  string distortion_model = 8;         // 畸变模型名称
  CameraModelType camera_model_type = 9; // 相机模型类型
}

// =========================================================
// IMU 内参
// 作用：表达 IMU 偏置等内参结果
// 说明：
// 1) accel_bias 为加速度计三轴偏置
// 2) gyro_bias 为陀螺仪三轴偏置
// 3) 采用强约束三维向量，不再使用 repeated double
// =========================================================
message IMUIntrinsics {
  .agv.calibration.common.Vector3D accel_bias = 1; // 加速度计偏置
  .agv.calibration.common.Vector3D gyro_bias = 2;  // 陀螺仪偏置
}

// =========================================================
// 传感器外参
// 作用：表达传感器与车辆 base_link 的位姿关系
// 说明：旋转统一使用弧度
// =========================================================
message SensorExtrinsics {
  string parent_frame_id = 1;                      // 父坐标系，一般为 base_link
  string child_frame_id = 2;                       // 子坐标系，一般为 sensor frame
  .agv.calibration.common.Pose3D parent_to_child = 3; // 父到子的位姿
}

// =========================================================
// 单个传感器标定参数
// 作用：统一表达一个传感器的内参和 / 或外参
// 说明：
// 1) 对相机可只填 camera_intrinsics
// 2) 对相机也可只填 extrinsics
// 3) 对 IMU 可填 imu_intrinsics 和 / 或 extrinsics
// 4) 对激光雷达通常只填 extrinsics
// =========================================================
message SensorCalibrationParameter {
  string sensor_id = 1;                          // 传感器 ID
  .agv.calibration.vehicle.profile.SensorType sensor_type = 2; // 传感器类型
  CameraIntrinsics camera_intrinsics = 3;        // 相机内参；仅相机适用
  IMUIntrinsics imu_intrinsics = 4;              // IMU 内参；仅 IMU 适用
  SensorExtrinsics extrinsics = 5;               // 传感器外参
}

// =========================================================
// 传感器标定参数总包
// 作用：统一表达整车所有传感器的标定结果
// =========================================================
message SensorCalibrationParameterSet {
  repeated SensorCalibrationParameter items = 1; // 参数项列表
}

// =========================================================
// 标定产物类型
// 作用：用于区分不同采集任务产生的文件类型
// =========================================================
enum ArtifactType {
  ARTIFACT_TYPE_UNSPECIFIED = 0; // 未指定
  IMAGE_FILE = 1;                // 图像文件
  POINT_CLOUD_FILE = 2;          // 点云文件
  LASER_SCAN_FILE = 3;           // 2D 激光雷达扫描文件
  IMU_SEQUENCE_FILE = 4;         // IMU 序列文件
  POSE_SEQUENCE_FILE = 5;        // 位姿序列文件
  REPORT_FILE = 6;               // 报告文件
}

// =========================================================
// 标定产物
// 作用：记录一次任务关联的文件产物
// =========================================================
message CalibrationArtifact {
  ArtifactType artifact_type = 1;                // 产物类型
  .agv.calibration.common.FileReference file = 2; // 文件引用
  string sensor_id = 3;                          // 对应传感器 ID
}

// =========================================================
// 传感器验证摘要
// 作用：返回传感器专项自动验收的关键指标
// =========================================================
message SensorValidationSummary {
  double reprojection_error_px = 1;    // 相机重投影误差
  double translation_residual_m = 2;   // 外参平移残差
  double rotation_residual_rad = 3;    // 外参旋转残差
  double plane_residual_m = 4;         // 平面 / 配准残差
  double repeatability_error_m = 5;    // 重复性误差
  bool auto_acceptance_passed = 6;     // 自动验收是否通过
}

// =========================================================
// 写入传感器标定参数请求
// 作用：将 Linux 求解出的传感器参数写入系统
// =========================================================
message CommitSensorCalibrationParametersRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
  string parameter_version = 2;                     // 参数版本号
  SensorCalibrationParameterSet params = 3;         // 参数总包
  string commit_reason = 4;                         // 写入原因
  .agv.calibration.common.FileDigest digest = 5;   // 摘要
  bool persistent_write = 6;                        // 是否持久化
}

// =========================================================
// 查询当前已生效传感器参数请求
// =========================================================
message GetAppliedSensorCalibrationParametersRequest {
  .agv.calibration.common.RequestHeader header = 1; // 请求头
}

// =========================================================
// 查询当前已生效传感器参数响应
// =========================================================
message AppliedSensorCalibrationParametersResponse {
  bool success = 1;                                  // 是否成功
  .agv.calibration.common.ErrorCode error_code = 2; // 错误码
  string message = 3;                                // 说明
  string parameter_version = 4;                      // 当前版本
  SensorCalibrationParameterSet params = 5;          // 当前生效参数
  int64 applied_timestamp_us = 6;                    // 生效时间
}

// =========================================================
// 传感器标定任务结果
// 作用：返回某次传感器标定 / 验证的结果摘要
// =========================================================
message SensorCalibrationJobResult {
  bool success = 1;                                  // 是否成功
  .agv.calibration.common.ErrorCode error_code = 2; // 错误码
  string message = 3;                                // 说明
  string job_id = 4;                                 // 任务 ID

  bool data_quality_passed = 5;            // 数据质量是否达标
  bool suitable_for_commit = 6;            // 是否适合写入
  string recommended_parameter_version = 7; // 推荐参数版本

  SensorValidationSummary validation_summary = 8; // 验证摘要
  SensorCalibrationParameterSet estimated_params = 9; // 本轮估计参数
  repeated CalibrationArtifact artifacts = 10;    // 关联产物
}
