添加注释

This commit is contained in:
tankaitao 2026-06-23 16:44:03 +08:00
parent 2e01a6035a
commit b4468809da
8 changed files with 5190 additions and 2456 deletions

View File

@ -1,12 +1,12 @@
src1100_agvs { src1100_agvs {
id: "agv_src1100" id: "agv_src1100"
ip: "192.168.192.5" ip: "192.168.192.5"
port_status: 19301 port_status: 19204
port_control: 19301 port_control: 19205
port_nav: 19301 port_nav: 19206
port_config: 19301 port_config: 19207
port_other: 19301 port_other: 19210
port_push: 19301 port_push: 19302

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,11 @@
// //
// Created by linbo on 2025/6/20. // Created by linbo on 2025/6/20.
// //
/**
* @file agv_src1100.h
* @brief 仙工智能 SRC-1100/2200 系列 AGV 控制器设备实现类的头文件。
* 继承自 AbstractAgv,实现了所有纯虚接口,并管理多端口 TCP 连接。
*/
#ifndef AGV_SRC1100_H #ifndef AGV_SRC1100_H
#define AGV_SRC1100_H #define AGV_SRC1100_H
#pragma once #pragma once
@ -10,77 +15,291 @@
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
using json = nlohmann::json; using json = nlohmann::json;
namespace cmvr::device { namespace cmvr::device {
/**
* @brief 仙工智能 SRC-1100/2200 系列 AGV 控制器设备实现类
*
* 继承自 AbstractAgv,实现了与仙工智能 SRC 系列控制器通信的具体协议。
* 支持多端口(状态、控制、导航、配置、推送)并发连接和指令交互。
*
* @note 该类不是线程安全的,外部调用需自行保证同一设备实例的串行访问。
*/
class AgvSrc1100 final : public AbstractAgv { class AgvSrc1100 final : public AbstractAgv {
public: public:
// ==================== 构造 / 析构 ====================
/**
* @brief 从 XML 配置节点构造设备实例(已弃用或未使用,保留兼容)
* @param cfg XML 配置节点,包含设备 ID、IP、端口、启用标志等
*/
explicit AgvSrc1100(const XmlNode& cfg); explicit AgvSrc1100(const XmlNode& cfg);
/**
* @brief 从 Protobuf 配置对象构造设备实例
* @param cfg AGVsrc1100Config 配置对象,包含设备 ID、IP、端口、启用标志等
*/
explicit AgvSrc1100(const config::AGVsrc1100Config& cfg); explicit AgvSrc1100(const config::AGVsrc1100Config& cfg);
/**
* @brief 析构函数,自动停止设备并释放资源
*/
~AgvSrc1100() override; ~AgvSrc1100() override;
// ==================== 生命周期管理(通用) ====================
/**
* @brief 获取当前设备运行状态
* @return Status 枚举值(CREATED / INITIALIZED / RUNNING / PAUSED / STOPPED / FAULT)
*/
Status state() const override; Status state() const override;
/**
* @brief 获取最后一次发生的错误信息
* @return 错误描述字符串,若无错误则返回空字符串
*/
std::string lastError() const override; std::string lastError() const override;
/**
* @brief 启动设备,标记为运行状态
* @note 所有 socket 连接应在构造时已完成,启动仅改变状态标志
*/
void start() override; void start() override;
/**
* @brief 停止设备,关闭所有 TCP 连接并释放资源
*/
void stop() override; void stop() override;
/**
* @brief 周期性更新设备状态
* @note 当前为空实现,可扩展心跳检测或重连逻辑
*/
void update() override; void update() override;
// ==================== 端口 19204 – 机器人状态 API(允许 10 个连接) ====================
// 功能:查询机器人各种状态信息(只读操作,不改变机器人状态)
/**
* @brief 查询机器人基本信息(命令码 1000, robot_status_info_req)
* @param info 输出参数,填充 AgvStatusInfo
* @note 返回信息包括:版本、型号、地图名称、网络 IP、MAC、Wi-Fi 信号等
*/
void getStatusInfo(AgvStatusInfo& info) override; void getStatusInfo(AgvStatusInfo& info) override;
/**
* @brief 查询电池状态(命令码 1007, robot_status_battery_req)
* @param info 输出参数,填充 BatteryStatus
* @param simple 若为 true,仅返回关键电量信息(当前实现未区分)
* @note 返回信息包括:电量百分比、温度、充放电状态、电压、电流等
*/
void getBatteryStatus(BatteryStatus& info, bool simple = false) override; void getBatteryStatus(BatteryStatus& info, bool simple = false) override;
/**
* @brief 查询机器人当前位置(命令码 1004, robot_status_loc_req)
* @param info 输出参数,填充 RobotLocation
* @note 返回信息包括:世界坐标系 X/Y 坐标、朝向角、定位置信度、当前站点
*/
void getRobotLocation(RobotLocation& info) override; void getRobotLocation(RobotLocation& info) override;
void downloadMap(DownloadMapResult& info, const std::string& map_name) override;
/**
* @brief 查询地图状态(命令码 1300, robot_status_map_req)
* @param info 输出参数,填充 MapStatus
* @note 返回信息包括:当前载入的地图名称、所有存储的地图列表、文件详情
*/
void getMapStatus(MapStatus& info) override; void getMapStatus(MapStatus& info) override;
void uploadMap(UploadMapResult& info, const std::string& map_json) override;
void lockRobotControl(LockResult& info, const std::string& nick_name) override; /**
* @brief 查询当前控制权持有者(命令码 1060, robot_status_current_lock_req)
* @param info 输出参数,填充 CurrentLockStatus
* @note 返回信息包括:是否被锁定、持有者 IP/端口/昵称、锁定时间等
*/
void getCurrentLockStatus(CurrentLockStatus& info) override; void getCurrentLockStatus(CurrentLockStatus& info) override;
void robotMotionControl(MotionCtrlRes& res, const MotionCtrlReq& req) override; /**
* @brief 查询地图载入状态(命令码 1022, robot_status_loadmap_req)
void robotLoadMap(LoadMapRes& res, const LoadMapReq& req) override; * @param res 输出参数,包含 loadmap_status
* @note loadmap_status: 0=失败, 1=成功, 2=载入中(载入中禁止重定位)
*/
void queryLoadMapStatus(QueryLoadMapStatusRes& res) override; void queryLoadMapStatus(QueryLoadMapStatusRes& res) override;
/**
* @brief 查询当前地图站点列表(命令码 1301, robot_status_station_req)
* @param res 输出参数,填充 QueryStationRes,包含所有站点信息
* @note 返回信息包括:站点 ID、类型、坐标、朝向角、属性等
*/
void queryStationList(QueryStationRes& res) override; void queryStationList(QueryStationRes& res) override;
/**
* @brief 查询当前导航状态(命令码 1020, robot_status_task_req)
* @param res 输出参数,填充 RobotStatusTaskCurrentRes
* @param req 输入参数,simple=true 时只返回 task_status
* @note 返回信息包括:任务状态、任务类型、目标站点/坐标、已走/未走路径
*/
void robotStatusTaskCurrent(RobotStatusTaskCurrentRes& res, const RobotStatusTaskCurrentReq& req) override;
/**
* @brief 批量查询任务状态(命令码 1110, robot_status_task_status_package_req)
* @param res 输出参数,填充 QueryTaskStatusPackageRes
* @param req 输入参数,task_ids 列表;若为空则查询所有未完成 + 最近一条已完成的任务
* @note 返回信息包括:任务列表、每个任务的 ID/状态/类型、进度百分比、剩余距离等
*/
void robotStatusTaskPackage(QueryTaskStatusPackageRes& res, const QueryTaskStatusPackageReq& req) override;
// ==================== 端口 19205 – 机器人控制 API(允许 5 个连接) ====================
// 功能:下发控制指令,改变机器人运动或状态(非导航类指令)
/**
* @brief 下发开环速度运动指令(命令码 2010, robot_control_motion_req)
* @param res 输出参数,执行结果
* @param req 输入参数,包含 vx/vy/w/steer/real_steer/duration
* @warning 下发此指令会立即取消当前正在执行的导航任务
* @note 多舵轮设备仅 vx/vy/w 生效,steer/real_steer 仅单舵轮设备有效
*/
void robotMotionControl(MotionCtrlRes& res, const MotionCtrlReq& req) override;
/**
* @brief 切换载入地图(命令码 2022, robot_control_loadmap_req)
* @param res 输出参数,执行结果
* @param req 输入参数,目标地图名称
* @note 目标地图必须已存在于机器人中,否则切换失败
*/
void robotLoadMap(LoadMapRes& res, const LoadMapReq& req) override;
// ==================== 端口 19206 – 机器人导航 API(允许 5 个连接) ====================
// 功能:下发导航任务
/**
* @brief 指定路径导航(命令码 3066, robot_task_gotargetlist_req)
* @param res 输出参数,下发结果(ret_code=0 仅表示指令被接收,不表示执行完成)
* @param req 输入参数,包含 move_task_list(站点序列)
* @attention
* - 每个任务必须含 task_id、source_id、id 三个必填字段
* - source_id 和 id 之间必须有直接相连的线路,不可跳点
* - 任务会排队执行,前一个任务失败时后续任务自动取消
* - 适合多车调度场景
*/
void robotGoTargetList(GoTargetListRes& res, const GoTargetListReq& req) override; void robotGoTargetList(GoTargetListRes& res, const GoTargetListReq& req) override;
/**
* @brief 单点站点自动规划导航(命令码 3051, robot_task_gotarget_req)
* @param res 输出参数,下发结果(ret_code=0 仅表示指令被接收)
* @param req 输入参数,支持自由导航(freeGo)和基于站点的路径导航两种模式
* @warning
* - **严禁用于多车调度场景**,仅限单车测试/任务链验证
* - 下发新任务会取消当前正在执行的任务(不排队)
* - 成功下发后会自动清除指定的 warning/error 报错码
* - 自由导航(freeGo)仅支持双轮差速底盘
*/
void robotGoTarget(RobotGoTargetRes& res, const RobotGoTargetReq& req) override;
void robotStatusTask(RobotStatusTaskRes& res, const RobotStatusTaskReq& req) override; // ==================== 端口 19207 – 机器人配置 API(允许 5 个连接) ====================
// 功能:配置类操作(地图上传/下载、控制权管理、参数修改等)
/**
* @brief 抢占机器人控制权(命令码 4005, robot_config_lock_req)
* @param info 输出参数,填充 LockResult
* @param nick_name 抢占者昵称,用于标识调用方
* @note 抢占成功后调用方获得独占控制权,其他方仅可查询状态
*/
void lockRobotControl(LockResult& info, const std::string& nick_name) override;
/**
* @brief 上传地图(命令码 4010, robot_config_uploadmap_req)
* @param info 输出参数,填充 UploadMapResult
* @param map_json 地图 JSON 字符串(完整的地图文件内容)
* @note 地图数据较大时会自动分片发送,需确保 JSON 格式正确
*/
void uploadMap(UploadMapResult& info, const std::string& map_json) override;
/**
* @brief 下载指定地图(命令码 4011, robot_config_downloadmap_req)
* @param info 输出参数,填充 DownloadMapResult(包含地图 JSON 内容)
* @param map_name 要下载的地图名称
* @note 下载的地图内容以 JSON 字符串形式存储在 info.map_content 中
*/
void downloadMap(DownloadMapResult& info, const std::string& map_name) override;
// ==================== 端口 19210 – 其他 API(允许 5 个连接) ====================
// 功能:外设控制(顶升/货叉/辊筒/音频/IO 等)
// 当前未实现具体方法,预留扩展
// ==================== 端口 19301 – 机器人推送 API(允许 10 个连接) ====================
// 功能:接收机器人主动推送的实时状态数据
// 相关配置方法(如 9300)可能在此,当前未实现
// ---------- 新增任务控制接口(3001/3002/3003)及停止运动(2000) ----------
void robotTaskPause(RobotTaskPauseRes& res) override;
void robotTaskResume(RobotTaskResumeRes& res) override;
void robotTaskCancel(RobotTaskCancelRes& res) override;
void robotControlStop(RobotControlStopRes& res) override;
private: private:
// ==================== 私有通信辅助函数 ====================
/**
* @brief 异步连接指定端口
* @param sock 输出参数,连接成功后存储 socket 文件描述符
* @param port 目标端口号(19204/19205/19206/19207/19210/19301)
* @note 在独立线程中执行,连接成功后将 sock 设置为有效值
*/
void asyncConnect(int& sock, int port); void asyncConnect(int& sock, int port);
/**
* @brief 发送请求并接收完整响应
* @param sock 已连接的 socket 文件描述符
* @param header 16 字节协议帧头
* @return true 表示收发成功,current_json_ 中存储响应 JSON;false 表示失败
* @note 内部先发送帧头,然后循环接收直到超时,最后提取 JSON 部分
*/
bool sendAndRecv(int sock, const uint8_t* header); bool sendAndRecv(int sock, const uint8_t* header);
/**
* @brief 清空 socket 接收缓冲区中的残留数据
* @param sock 目标 socket 文件描述符
* @note 采用非阻塞方式读取并丢弃所有可读数据,防止粘包干扰
*/
void flushSocket(int sock); void flushSocket(int sock);
/**
* @brief 设置 socket 接收超时时间
* @param sock 目标 socket 文件描述符
* @param timeout_ms 超时时间(毫秒),0 表示取消超时
*/
void setSocketTimeout(int sock, int timeout_ms); void setSocketTimeout(int sock, int timeout_ms);
/**
* @brief 解析 JSON 字符串并做异常捕获
// 辅助函数:将JSON字符串解析为Json::Value,同时做错误处理 * @param json_str 输入的 JSON 字符串
* @param root 输出参数,解析后的 json 对象
* @param err_msg 输出参数,解析失败时的错误信息
* @return true 表示解析成功,false 表示解析失败
*/
bool parseJson(const std::string& json_str, json& root, std::string& err_msg); bool parseJson(const std::string& json_str, json& root, std::string& err_msg);
mutable std::mutex mutex_;
std::atomic<Status> status_{Status::CREATED};
std::string last_error_;
std::string current_json_;
// ==================== 成员变量 ====================
mutable std::mutex mutex_; ///< 保护状态和错误信息的互斥锁
std::atomic<Status> status_{Status::CREATED}; ///< 当前设备运行状态(原子变量)
std::string last_error_; ///< 最后一次错误信息
std::string current_json_; ///< 最近一次响应的 JSON 内容(由 sendAndRecv 填充)
// 多socket // ---------- 各端口 Socket 文件描述符 ----------
int sock_status_ = -1; int sock_status_ = -1; ///< 19204 – 状态 API 端口
int sock_control_ = -1; int sock_control_ = -1; ///< 19205 – 控制 API 端口
int sock_nav_ = -1; int sock_nav_ = -1; ///< 19206 – 导航 API 端口
int sock_config_ = -1; int sock_config_ = -1; ///< 19207 – 配置 API 端口
int sock_other_ = -1; int sock_other_ = -1; ///< 19210 – 其他 API 端口
int sock_push_ = -1; int sock_push_ = -1; ///< 19301 – 推送 API 端口
std::vector<std::thread> connect_threads_; std::vector<std::thread> connect_threads_; ///< 各端口异步连接线程
mutable std::mutex connect_mutex_; mutable std::mutex connect_mutex_; ///< 保护 connect_threads_ 的互斥锁
}; };
} // namespace cmvr::device } // namespace cmvr::device

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,14 @@
// //
// Created by xtkuang on 2025/6/1. // Created by xtkuang on 2025/6/1.
// //
/**
* @file grpc_agv_service.h
* @brief gRPC AGV 服务实现类的头文件。
* 继承自自动生成的 api::AgvService::Service,提供所有 AGV 相关 RPC 接口的具体实现。
* 通过 DeviceManager 获取对应的 AGV 设备实例(如 AgvSrc1100),将请求转发至设备层。
* @author xtkuang
* @date 2025-06-01
*/
#ifndef GRPC_AGV_SERVICE_H #ifndef GRPC_AGV_SERVICE_H
#define GRPC_AGV_SERVICE_H #define GRPC_AGV_SERVICE_H
@ -11,121 +19,260 @@
namespace cmvr::service { namespace cmvr::service {
/**
* @brief gRPC AGV 服务实现类。
* 负责将 gRPC 请求转换为对具体 AGV 设备的调用,并统一处理异常、日志和时间戳。
*/
class gRPCAGVServiceImpl final : public api::AgvService::Service { class gRPCAGVServiceImpl final : public api::AgvService::Service {
public: public:
/**
* @brief 构造函数,获取 DeviceManager 单例引用。
*/
gRPCAGVServiceImpl(); gRPCAGVServiceImpl();
/**
* @brief 默认析构函数。
*/
~gRPCAGVServiceImpl() override = default; ~gRPCAGVServiceImpl() override = default;
// AGV服务接口 // ===================== 基本状态查询接口 =====================
/**
* @brief 获取 AGV 基本信息(命令码 1000)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID(header.device_id)。
* @param response 响应消息,填充 AGV 状态信息(版本、型号、IP、MAC 等)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status GetStatusInfo(grpc::ServerContext* context, grpc::Status GetStatusInfo(grpc::ServerContext* context,
const api::GetAgvStatusInfoCommand_Request* request, const api::GetAgvStatusInfoCommand_Request* request,
api::GetAgvStatusInfoCommand_Feedback* response) override; api::GetAgvStatusInfoCommand_Feedback* response) override;
/**
* @brief 查询电池状态(命令码 1007)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和可选的 simple 标志。
* @param response 响应消息,填充电池电量、温度、充放电状态等。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status GetBatteryStatus(grpc::ServerContext* context, grpc::Status GetBatteryStatus(grpc::ServerContext* context,
const api::RobotStatusBatteryCommand_Request* request, const api::RobotStatusBatteryCommand_Request* request,
api::RobotStatusBatteryCommand_Feedback* response) override; api::RobotStatusBatteryCommand_Feedback* response) override;
/**
* @brief 查询机器人当前位置(命令码 1004)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID。
* @param response 响应消息,填充坐标、朝向角、置信度、当前站点等。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status GetRobotLocation(grpc::ServerContext* context, grpc::Status GetRobotLocation(grpc::ServerContext* context,
const api::RobotStatusLocCommand_Request* request, const api::RobotStatusLocCommand_Request* request,
api::RobotStatusLocCommand_Feedback* response) override; api::RobotStatusLocCommand_Feedback* response) override;
// ===================== 地图管理接口 =====================
/**
* @brief 下载指定地图(命令码 4011)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和地图名称。
* @param response 响应消息,返回地图 JSON 内容或错误码。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotConfigDownloadMap(grpc::ServerContext* context, grpc::Status RobotConfigDownloadMap(grpc::ServerContext* context,
const api::RobotConfigDownloadMapCommand_Request* request, const api::RobotConfigDownloadMapCommand_Request* request,
api::RobotConfigDownloadMapCommand_Feedback* response) override; api::RobotConfigDownloadMapCommand_Feedback* response) override;
/**
* @brief 查询地图状态(命令码 1300)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID。
* @param response 响应消息,返回当前地图名称、所有地图列表及文件详情。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status GetMapStatus(grpc::ServerContext* context, grpc::Status GetMapStatus(grpc::ServerContext* context,
const api::RobotStatusMapCommand_Request* request, const api::RobotStatusMapCommand_Request* request,
api::RobotStatusMapCommand_Feedback* response) override; api::RobotStatusMapCommand_Feedback* response) override;
/**
* @brief 上传地图(命令码 4010)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和完整的地图 JSON 字符串。
* @param response 响应消息,返回上传结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotConfigUploadMap(grpc::ServerContext* context, grpc::Status RobotConfigUploadMap(grpc::ServerContext* context,
const api::RobotConfigUploadMapCommand_Request* request, const api::RobotConfigUploadMapCommand_Request* request,
api::RobotConfigUploadMapCommand_Feedback* response) override; api::RobotConfigUploadMapCommand_Feedback* response) override;
// ===================== 控制权管理接口 =====================
/** /**
* @brief gRPC接口:抢占机器人控制权 * @brief 抢占机器人控制权(命令码 4005)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和抢占者昵称。
* @param response 响应消息,返回抢占结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status RobotConfigLock(grpc::ServerContext* context, grpc::Status RobotConfigLock(grpc::ServerContext* context,
const api::RobotConfigLockCommand_Request* request, const api::RobotConfigLockCommand_Request* request,
api::RobotConfigLockCommand_Feedback* response) override; api::RobotConfigLockCommand_Feedback* response) override;
/** /**
* @brief gRPC接口 查询当前控制权持有者 * @brief 查询当前控制权持有者(命令码 1060)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID。
* @param response 响应消息,返回是否被锁定、持有者 IP/端口/昵称等信息。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status GetCurrentLockStatus(grpc::ServerContext* context, grpc::Status GetCurrentLockStatus(grpc::ServerContext* context,
const api::RobotStatusCurrentLockCommand_Request* request, const api::RobotStatusCurrentLockCommand_Request* request,
api::RobotStatusCurrentLockCommand_Feedback* response) override; api::RobotStatusCurrentLockCommand_Feedback* response) override;
// ===================== 运动控制接口 =====================
/** /**
* @brief AGV开环速度运动控制 * @brief 下发开环速度运动指令(命令码 2010)。
* @param context grpc上下文 * @param context gRPC 上下文(未使用)。
* @param request 请求体,header携带device_id区分多AGV * @param request 请求消息,包含设备 ID 和速度参数(vx, vy, w, steer, duration 等)。
* @param response 运动指令执行结果 * @param response 响应消息,返回指令下发结果(ret_code)。
* @note 此指令会强制取消当前自动导航任务,多舵轮设备仅 vx/vy/w 生效。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status RobotMotionControl(grpc::ServerContext* context, grpc::Status RobotMotionControl(grpc::ServerContext* context,
const api::RobotMotionControlCommand_Request* request, const api::RobotMotionControlCommand_Request* request,
api::RobotMotionControlCommand_Feedback* response) override; api::RobotMotionControlCommand_Feedback* response) override;
/** /**
* @brief 切换载入地图 * @brief 切换载入地图(命令码 2022)。
* @param context grpc上下文 * @param context gRPC 上下文(未使用)。
* @param request 请求体,携带目标地图名称 * @param request 请求消息,包含设备 ID 和目标地图名称。
* @param response 切换地图执行结果 * @param response 响应消息,返回切换结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status RobotLoadMap( grpc::Status RobotLoadMap(grpc::ServerContext* context,
grpc::ServerContext* context,
const api::RobotLoadMapCommand_Request* request, const api::RobotLoadMapCommand_Request* request,
api::RobotLoadMapCommand_Feedback* response) override; api::RobotLoadMapCommand_Feedback* response) override;
/** /**
* @brief 查询地图载入状态 * @brief 停止开环运动(命令码 2000)。
* @param context grpc上下文 * @param context gRPC 上下文(未使用)。
* @param request 请求头 * @param request 请求消息,包含设备 ID(无业务数据)。
* @param response 地图加载状态结果 * @param response 响应消息,返回停止结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status QueryLoadMapStatus( grpc::Status RobotControlStop(grpc::ServerContext* context,
grpc::ServerContext* context, const api::RobotControlStopCommand::Request* request,
api::RobotControlStopCommand::Feedback* response) override;
// ===================== 导航任务接口 =====================
/**
* @brief 查询地图载入状态(命令码 1022)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID。
* @param response 响应消息,返回 loadmap_status(0=失败, 1=成功, 2=载入中)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status QueryLoadMapStatus(grpc::ServerContext* context,
const api::RobotQueryLoadMapStatusCommand_Request* request, const api::RobotQueryLoadMapStatusCommand_Request* request,
api::RobotQueryLoadMapStatusCommand_Feedback* response) override; api::RobotQueryLoadMapStatusCommand_Feedback* response) override;
/** /**
* @brief 查询当前地图所有站点信息 * @brief 查询当前地图所有站点信息(命令码 1301)。
* @param context grpc上下文 * @param context gRPC 上下文(未使用)。
* @param request 请求头 * @param request 请求消息,包含设备 ID。
* @param response 站点列表结果 * @param response 响应消息,返回站点列表(ID、坐标、类型等)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status QueryStationList( grpc::Status QueryStationList(grpc::ServerContext* context,
grpc::ServerContext* context,
const api::QueryStationListCommand_Request* request, const api::QueryStationListCommand_Request* request,
api::QueryStationListCommand_Feedback* response) override; api::QueryStationListCommand_Feedback* response) override;
/** /**
* @brief 指定路径连续站点导航 * @brief 指定路径导航(命令码 3066)。
* @param context grpc上下文 * @param context gRPC 上下文(未使用)。
* @param request 导航任务列表请求 * @param request 请求消息,包含设备 ID 和多段导航任务列表(move_task_list)。
* @param response 下发任务返回结果 * @param response 响应消息,返回下发结果(ret_code=0 仅表示接收成功,不表示执行完成)。
* @attention 每个任务必须包含 task_id, source_id, id,且相邻站点间必须有直接路径。
* 任务会排队执行,适合多车调度场景。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/ */
grpc::Status RobotGoTargetList( grpc::Status RobotGoTargetList(grpc::ServerContext* context,
grpc::ServerContext* context,
const api::RobotGoTargetListCommand_Request* request, const api::RobotGoTargetListCommand_Request* request,
api::RobotGoTargetListCommand_Feedback* response) override; api::RobotGoTargetListCommand_Feedback* response) override;
/**
* @brief 查询当前实时导航状态(命令码 1020)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和 simple 标志。
* @param response 响应消息,返回任务状态、任务类型、目标、路径等信息。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotStatusTaskCurrent(grpc::ServerContext* context,
const api::RobotStatusTaskCurrentCommand_Request* request,
api::RobotStatusTaskCurrentCommand_Feedback* response) override;
grpc::Status RobotStatusTask( /**
grpc::ServerContext* context, * @brief 批量查询任务状态(命令码 1110)。
const api::RobotStatusTaskCommand_Request* request, * @param context gRPC 上下文(未使用)。
api::RobotStatusTaskCommand_Feedback* response) override; * @param request 请求消息,包含设备 ID 和 task_ids 列表(为空则查询所有未完成+最近完成)。
* @param response 响应消息,返回任务状态包(进度、距离、各任务状态等)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotStatusTaskPackage(grpc::ServerContext* context,
const api::RobotStatusTaskPackageCommand_Request* request,
api::RobotStatusTaskPackageCommand_Feedback* response) override;
/**
* @brief 单点站点自动规划导航(命令码 3051)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID 和完整的导航参数(支持自由导航、动作、PGV 等)。
* @param response 响应消息,返回下发结果(ret_code=0 仅表示接收成功)。
* @warning 严禁用于多车调度场景,仅限单车测试;新任务会取消当前任务(不排队)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotGoTarget(grpc::ServerContext* context,
const api::RobotGoTargetCommand_Request* request,
api::RobotGoTargetCommand_Feedback* response) override;
/**
* @brief 暂停当前导航任务(命令码 3001)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID(无业务数据)。
* @param response 响应消息,返回暂停结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotTaskPause(grpc::ServerContext* context,
const api::RobotTaskPauseCommand::Request* request,
api::RobotTaskPauseCommand::Feedback* response) override;
/**
* @brief 继续当前导航任务(命令码 3002)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID(无业务数据)。
* @param response 响应消息,返回继续结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotTaskResume(grpc::ServerContext* context,
const api::RobotTaskResumeCommand::Request* request,
api::RobotTaskResumeCommand::Feedback* response) override;
/**
* @brief 取消当前导航任务(命令码 3003)。
* @param context gRPC 上下文(未使用)。
* @param request 请求消息,包含设备 ID(无业务数据)。
* @param response 响应消息,返回取消结果(ret_code)。
* @return 始终返回 grpc::Status::OK,错误信息通过 response.header 返回。
*/
grpc::Status RobotTaskCancel(grpc::ServerContext* context,
const api::RobotTaskCancelCommand::Request* request,
api::RobotTaskCancelCommand::Feedback* response) override;
private: private:
device::DeviceManager& dmgr_; device::DeviceManager& dmgr_; ///< 设备管理器引用,用于根据 device_id 获取 AGV 设备实例。
}; };
}
} // namespace cmvr::service
#endif // GRPC_AGV_SERVICE_H #endif // GRPC_AGV_SERVICE_H

View File

@ -1,6 +1,15 @@
// //
// Created by xtkuang on 2025/6/1. // Created by xtkuang on 2025/6/1.
// //
/**
* @file grpc_agv_service.cpp
* @brief gRPC AGV 服务实现。
* 每个 RPC 方法从请求头中提取 device_id,通过 DeviceManager 获取对应的 AGV 设备,
* 调用设备层的具体方法,并将结果转换为 Protobuf 响应。
* 统一处理异常,设置响应头中的 success/error_message 和时间戳。
* @author xtkuang
* @date 2025-06-01
*/
#include "./service/grpc/include/grpc_agv_service.h" #include "./service/grpc/include/grpc_agv_service.h"
@ -12,9 +21,21 @@
namespace cmvr::service { namespace cmvr::service {
/**
* @brief 构造函数,获取 DeviceManager 单例。
*/
gRPCAGVServiceImpl::gRPCAGVServiceImpl() : dmgr_(device::DeviceManager::getInstance()) { gRPCAGVServiceImpl::gRPCAGVServiceImpl() : dmgr_(device::DeviceManager::getInstance()) {
} }
// ===================== GetStatusInfo =====================
/**
* @brief 获取 AGV 基本信息。
* @details 从请求中提取 device_id,调用设备层的 getStatusInfo,填充响应。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回状态信息和响应头。
* @return 始终返回 OK,错误通过 response.header 传递。
*/
grpc::Status gRPCAGVServiceImpl::GetStatusInfo( grpc::Status gRPCAGVServiceImpl::GetStatusInfo(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::GetAgvStatusInfoCommand_Request* request, const api::GetAgvStatusInfoCommand_Request* request,
@ -28,6 +49,7 @@ namespace cmvr::service {
device::AbstractAgv::AgvStatusInfo device_status; device::AbstractAgv::AgvStatusInfo device_status;
agv_device->getStatusInfo(device_status); agv_device->getStatusInfo(device_status);
// 映射到 Protobuf 消息
auto* proto_status = response->mutable_status(); auto* proto_status = response->mutable_status();
proto_status->set_id(device_status.id); proto_status->set_id(device_status.id);
proto_status->set_vehicle_id(device_status.vehicle_id); proto_status->set_vehicle_id(device_status.vehicle_id);
@ -58,7 +80,14 @@ namespace cmvr::service {
} }
} }
// ===================== GetBatteryStatus =====================
/**
* @brief 查询电池状态。
* @details 支持 simple 参数,调用设备层 getBatteryStatus。
* @param context 未使用。
* @param request 包含设备 ID 和 simple 标志。
* @param response 返回电池信息。
*/
grpc::Status gRPCAGVServiceImpl::GetBatteryStatus( grpc::Status gRPCAGVServiceImpl::GetBatteryStatus(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotStatusBatteryCommand_Request* request, const api::RobotStatusBatteryCommand_Request* request,
@ -113,7 +142,13 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== GetRobotLocation =====================
/**
* @brief 查询机器人当前位置。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回坐标、朝向、置信度等。
*/
grpc::Status gRPCAGVServiceImpl::GetRobotLocation( grpc::Status gRPCAGVServiceImpl::GetRobotLocation(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotStatusLocCommand_Request* request, const api::RobotStatusLocCommand_Request* request,
@ -157,7 +192,13 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== RobotConfigDownloadMap =====================
/**
* @brief 下载指定地图。
* @param context 未使用。
* @param request 包含设备 ID 和地图名称。
* @param response 返回地图 JSON 内容或错误码。
*/
grpc::Status gRPCAGVServiceImpl::RobotConfigDownloadMap( grpc::Status gRPCAGVServiceImpl::RobotConfigDownloadMap(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotConfigDownloadMapCommand_Request* request, const api::RobotConfigDownloadMapCommand_Request* request,
@ -196,8 +237,13 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== GetMapStatus =====================
/**
* @brief 查询地图状态。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回当前地图、地图列表及文件详情。
*/
grpc::Status gRPCAGVServiceImpl::GetMapStatus( grpc::Status gRPCAGVServiceImpl::GetMapStatus(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotStatusMapCommand_Request* request, const api::RobotStatusMapCommand_Request* request,
@ -249,7 +295,13 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== RobotConfigUploadMap =====================
/**
* @brief 上传地图。
* @param context 未使用。
* @param request 包含设备 ID 和地图 JSON 字符串。
* @param response 返回上传结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotConfigUploadMap( grpc::Status gRPCAGVServiceImpl::RobotConfigUploadMap(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotConfigUploadMapCommand_Request* request, const api::RobotConfigUploadMapCommand_Request* request,
@ -287,12 +339,12 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== RobotConfigLock =====================
/** /**
* @brief gRPC 抢占机器人控制权接口 * @brief 抢占机器人控制权。
* @param context grpc上下文 * @param context 未使用。
* @param request 前端请求参数,携带nick_name抢占者名称 * @param request 包含设备 ID 和抢占者昵称。
* @param response 抢占结果返回体 * @param response 返回抢占结果。
*/ */
grpc::Status gRPCAGVServiceImpl::RobotConfigLock( grpc::Status gRPCAGVServiceImpl::RobotConfigLock(
grpc::ServerContext* context, grpc::ServerContext* context,
@ -300,26 +352,20 @@ api::RobotStatusBatteryCommand_Feedback* response)
api::RobotConfigLockCommand_Feedback* response) api::RobotConfigLockCommand_Feedback* response)
{ {
try { try {
// 动态从header拿设备ID,和查询接口统一
std::string dev_id = request->header().device_id(); std::string dev_id = request->header().device_id();
LOG(INFO) << "[gRPCAGVServiceImpl] (RobotConfigLock): id=" << dev_id; LOG(INFO) << "[gRPCAGVServiceImpl] (RobotConfigLock): id=" << dev_id;
// 根据device_id获取对应AGV实例
const auto agv_device = dmgr_.getDevice<device::AbstractAgv>(dev_id); const auto agv_device = dmgr_.getDevice<device::AbstractAgv>(dev_id);
// 读取抢占者名称
std::string nick_name = request->data().nick_name(); std::string nick_name = request->data().nick_name();
device::AbstractAgv::LockResult lock_res; device::AbstractAgv::LockResult lock_res;
// 调用底层抢占接口
agv_device->lockRobotControl(lock_res, nick_name); agv_device->lockRobotControl(lock_res, nick_name);
// 填充protobuf返回数据
auto* proto_status = response->mutable_status(); auto* proto_status = response->mutable_status();
proto_status->set_ret_code(lock_res.ret_code); proto_status->set_ret_code(lock_res.ret_code);
proto_status->set_create_on(lock_res.create_on); proto_status->set_create_on(lock_res.create_on);
proto_status->set_err_msg(lock_res.err_msg); proto_status->set_err_msg(lock_res.err_msg);
// 响应头标记成功 + 时间戳对齐全局规范
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(true); header->set_success(true);
header->set_error_message(""); header->set_error_message("");
@ -330,7 +376,6 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
catch (std::exception &e) catch (std::exception &e)
{ {
// 统一异常捕获,和相机/查询接口风格完全一致
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(false); header->set_success(false);
header->set_error_message(e.what()); header->set_error_message(e.what());
@ -339,9 +384,12 @@ api::RobotStatusBatteryCommand_Feedback* response)
} }
} }
// ===================== GetCurrentLockStatus =====================
/** /**
* @brief 查询机器人当前控制权信息gRPC接口 * @brief 查询当前控制权持有者。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回锁定状态、持有者信息等。
*/ */
grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus( grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
grpc::ServerContext* context, grpc::ServerContext* context,
@ -349,18 +397,14 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
api::RobotStatusCurrentLockCommand_Feedback* response) api::RobotStatusCurrentLockCommand_Feedback* response)
{ {
try { try {
// 1. 读取请求device_id,打印日志(和相机代码保持一致)
std::string dev_id = request->header().device_id(); std::string dev_id = request->header().device_id();
LOG(INFO) << "[gRPCAGVServiceImpl] (GetCurrentLockStatus): id=" << dev_id; LOG(INFO) << "[gRPCAGVServiceImpl] (GetCurrentLockStatus): id=" << dev_id;
// 2. 按device_id获取AGV设备实例,和相机 dmgr_.getDevice<AbstractCamera>(dev_id) 统一写法
const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id); const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id);
// 3. 调用底层接口获取控制权信息
device::AbstractAgv::CurrentLockStatus lock_info; device::AbstractAgv::CurrentLockStatus lock_info;
agv_dev->getCurrentLockStatus(lock_info); agv_dev->getCurrentLockStatus(lock_info);
// 4. 填充protobuf返回结构体
auto* proto_status = response->mutable_status(); auto* proto_status = response->mutable_status();
proto_status->set_locked(lock_info.locked); proto_status->set_locked(lock_info.locked);
proto_status->set_ip(lock_info.ip); proto_status->set_ip(lock_info.ip);
@ -373,7 +417,6 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
proto_status->set_create_on(lock_info.create_on); proto_status->set_create_on(lock_info.create_on);
proto_status->set_err_msg(lock_info.err_msg); proto_status->set_err_msg(lock_info.err_msg);
// 5. 响应header标记成功 + 填充时间戳(对齐相机接口)
response->mutable_header()->set_success(true); response->mutable_header()->set_success(true);
setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); setCurrentTimestamp(response->mutable_header()->mutable_timestamp());
@ -383,7 +426,6 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
catch (std::exception &e) catch (std::exception &e)
{ {
// 异常统一捕获,失败header,填错误信息+时间戳,和相机完全对齐
response->mutable_header()->set_success(false); response->mutable_header()->set_success(false);
response->mutable_header()->set_error_message(e.what()); response->mutable_header()->set_error_message(e.what());
setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); setCurrentTimestamp(response->mutable_header()->mutable_timestamp());
@ -391,19 +433,23 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== RobotMotionControl =====================
/**
* @brief 开环速度运动控制。
* @param context 未使用。
* @param request 包含设备 ID 和速度参数。
* @param response 返回指令下发结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotMotionControl( grpc::Status gRPCAGVServiceImpl::RobotMotionControl(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotMotionControlCommand_Request* request, const api::RobotMotionControlCommand_Request* request,
api::RobotMotionControlCommand_Feedback* response) api::RobotMotionControlCommand_Feedback* response)
{ {
try { try {
// 从header读取device_id,区分多台AGV设备
std::string dev_id = request->header().device_id(); std::string dev_id = request->header().device_id();
LOG(INFO) << "[gRPCAGVServiceImpl] (RobotMotionControl): target device_id=" << dev_id; LOG(INFO) << "[gRPCAGVServiceImpl] (RobotMotionControl): target device_id=" << dev_id;
const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id); const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id);
// grpc入参转换到底层结构体
device::AbstractAgv::MotionCtrlReq req; device::AbstractAgv::MotionCtrlReq req;
auto& input_data = request->data(); auto& input_data = request->data();
req.vx = input_data.vx(); req.vx = input_data.vx();
@ -413,17 +459,14 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
req.real_steer = input_data.real_steer(); req.real_steer = input_data.real_steer();
req.duration = input_data.duration(); req.duration = input_data.duration();
// 调用底层运动控制接口
device::AbstractAgv::MotionCtrlRes res; device::AbstractAgv::MotionCtrlRes res;
agv_dev->robotMotionControl(res, req); agv_dev->robotMotionControl(res, req);
// 填充grpc返回状态
auto* resp_status = response->mutable_status(); auto* resp_status = response->mutable_status();
resp_status->set_ret_code(res.ret_code); resp_status->set_ret_code(res.ret_code);
resp_status->set_create_on(res.create_on); resp_status->set_create_on(res.create_on);
resp_status->set_err_msg(res.err_msg); resp_status->set_err_msg(res.err_msg);
// 统一规范header:成功标记+时间戳
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(true); header->set_success(true);
header->set_error_message(""); header->set_error_message("");
@ -436,7 +479,6 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
catch (std::exception& e) catch (std::exception& e)
{ {
// 全局统一异常捕获模板
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(false); header->set_success(false);
header->set_error_message(e.what()); header->set_error_message(e.what());
@ -445,6 +487,13 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== RobotLoadMap =====================
/**
* @brief 切换载入地图。
* @param context 未使用。
* @param request 包含设备 ID 和目标地图名称。
* @param response 返回切换结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotLoadMap( grpc::Status gRPCAGVServiceImpl::RobotLoadMap(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotLoadMapCommand_Request* request, const api::RobotLoadMapCommand_Request* request,
@ -470,7 +519,6 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(true); header->set_success(true);
header->set_error_message(""); header->set_error_message("");
// 修复:取timestamp子字段
setCurrentTimestamp(header->mutable_timestamp()); setCurrentTimestamp(header->mutable_timestamp());
LOG(INFO) << "RobotLoadMap finish, dev_id:" << dev_id LOG(INFO) << "RobotLoadMap finish, dev_id:" << dev_id
@ -483,13 +531,18 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
auto* header = response->mutable_header(); auto* header = response->mutable_header();
header->set_success(false); header->set_success(false);
header->set_error_message(e.what()); header->set_error_message(e.what());
// 修复:取timestamp子字段
setCurrentTimestamp(header->mutable_timestamp()); setCurrentTimestamp(header->mutable_timestamp());
return grpc::Status::OK; return grpc::Status::OK;
} }
} }
// ===================== QueryLoadMapStatus =====================
/**
* @brief 查询地图载入状态。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回 loadmap_status。
*/
grpc::Status gRPCAGVServiceImpl::QueryLoadMapStatus( grpc::Status gRPCAGVServiceImpl::QueryLoadMapStatus(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotQueryLoadMapStatusCommand_Request* request, const api::RobotQueryLoadMapStatusCommand_Request* request,
@ -529,6 +582,13 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== QueryStationList =====================
/**
* @brief 查询当前地图所有站点信息。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回站点列表。
*/
grpc::Status gRPCAGVServiceImpl::QueryStationList( grpc::Status gRPCAGVServiceImpl::QueryStationList(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::QueryStationListCommand_Request* request, const api::QueryStationListCommand_Request* request,
@ -547,7 +607,6 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
resp_status->set_create_on(res.create_on); resp_status->set_create_on(res.create_on);
resp_status->set_err_msg(res.err_msg); resp_status->set_err_msg(res.err_msg);
// 填充站点数组
for (auto& st : res.stations) for (auto& st : res.stations)
{ {
auto* pb_st = resp_status->add_stations(); auto* pb_st = resp_status->add_stations();
@ -584,7 +643,13 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== RobotGoTargetList =====================
/**
* @brief 指定路径导航(多点)。
* @param context 未使用。
* @param request 包含设备 ID 和 move_task_list。
* @param response 返回下发结果(仅表示指令被接收)。
*/
grpc::Status gRPCAGVServiceImpl::RobotGoTargetList( grpc::Status gRPCAGVServiceImpl::RobotGoTargetList(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotGoTargetListCommand_Request* request, const api::RobotGoTargetListCommand_Request* request,
@ -636,28 +701,31 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== RobotStatusTaskCurrent =====================
/**
* @brief 查询当前实时导航状态。
grpc::Status gRPCAGVServiceImpl::RobotStatusTask( * @param context 未使用。
* @param request 包含设备 ID 和 simple 标志。
* @param response 返回任务状态、类型、目标、路径等。
*/
grpc::Status gRPCAGVServiceImpl::RobotStatusTaskCurrent(
grpc::ServerContext* context, grpc::ServerContext* context,
const api::RobotStatusTaskCommand_Request* request, const api::RobotStatusTaskCurrentCommand_Request* request,
api::RobotStatusTaskCommand_Feedback* response) api::RobotStatusTaskCurrentCommand_Feedback* response)
{ {
try { try {
std::string dev_id = request->header().device_id(); std::string dev_id = request->header().device_id();
LOG(INFO) << "[gRPCAGVServiceImpl] (RobotStatusTask): id=" << dev_id; LOG(INFO) << "[gRPCAGVServiceImpl] (RobotStatusTaskCurrent): id=" << dev_id;
const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id); const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id);
// 嵌套结构体 必须加 AbstractAgv:: device::AbstractAgv::RobotStatusTaskCurrentReq req;
device::AbstractAgv::RobotStatusTaskReq req;
if (request->has_data()) if (request->has_data())
{ {
req.simple = request->data().simple(); req.simple = request->data().simple();
} }
device::AbstractAgv::RobotStatusTaskRes res; device::AbstractAgv::RobotStatusTaskCurrentRes res;
agv_dev->robotStatusTask(res, req); agv_dev->robotStatusTaskCurrent(res, req);
auto* out_data = response->mutable_data(); auto* out_data = response->mutable_data();
out_data->set_ret_code(res.ret_code); out_data->set_ret_code(res.ret_code);
@ -690,7 +758,7 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
header->set_error_message(""); header->set_error_message("");
setCurrentTimestamp(header->mutable_timestamp()); setCurrentTimestamp(header->mutable_timestamp());
LOG(INFO) << "RobotStatusTask device_id:" << dev_id << " task_status: " << res.task_status; LOG(INFO) << "RobotStatusTaskCurrent dev_id:" << dev_id << " task_status:" << res.task_status;
return grpc::Status::OK; return grpc::Status::OK;
} }
catch (std::exception &e) catch (std::exception &e)
@ -703,6 +771,438 @@ grpc::Status gRPCAGVServiceImpl::GetCurrentLockStatus(
} }
} }
// ===================== RobotStatusTaskPackage =====================
/**
* @brief 批量查询任务状态。
* @param context 未使用。
* @param request 包含设备 ID 和 task_ids 列表。
* @param response 返回任务状态包(进度、距离、每个任务状态等)。
*/
grpc::Status gRPCAGVServiceImpl::RobotStatusTaskPackage(
grpc::ServerContext* context,
const api::RobotStatusTaskPackageCommand_Request* request,
api::RobotStatusTaskPackageCommand_Feedback* response)
{
try {
std::string dev_id = request->header().device_id();
LOG(INFO) << "[gRPCAGVServiceImpl] (RobotStatusTaskPackage): id=" << dev_id;
const auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id);
device::AbstractAgv::QueryTaskStatusPackageReq req;
const auto& pb_ids = request->data().task_ids();
for (auto& tid : pb_ids)
{
req.task_ids.push_back(tid);
} }
device::AbstractAgv::QueryTaskStatusPackageRes res;
agv_dev->robotStatusTaskPackage(res, req);
auto* out_data = response->mutable_data();
out_data->set_ret_code(res.ret_code);
out_data->set_create_on(res.create_on);
out_data->set_err_msg(res.err_msg);
auto* pkg_pb = out_data->mutable_task_status_package();
auto& pkg_data = res.task_status_package;
pkg_pb->set_closest_target(pkg_data.closest_target);
pkg_pb->set_source_name(pkg_data.source_name);
pkg_pb->set_target_name(pkg_data.target_name);
pkg_pb->set_percentage(pkg_data.percentage);
pkg_pb->set_distance(pkg_data.distance);
pkg_pb->set_info(pkg_data.info);
for (auto& st_item : pkg_data.task_status_list)
{
auto* st_pb = pkg_pb->add_task_status_list();
st_pb->set_task_id(st_item.task_id);
st_pb->set_status(st_item.status);
st_pb->set_type(st_item.type);
}
auto* header = response->mutable_header();
header->set_success(true);
header->set_error_message("");
setCurrentTimestamp(header->mutable_timestamp());
LOG(INFO) << "RobotStatusTaskPackage dev_id:" << dev_id << " query task count:" << req.task_ids.size() << " ret_code:" << res.ret_code;
return grpc::Status::OK;
}
catch (std::exception &e)
{
auto* header = response->mutable_header();
header->set_success(false);
header->set_error_message(e.what());
setCurrentTimestamp(header->mutable_timestamp());
return grpc::Status::OK;
}
}
// ===================== RobotGoTarget =====================
/**
* @brief 单点站点自动规划导航。
* @param context 未使用。
* @param request 包含设备 ID 和完整的导航参数(支持自由导航、动作、PGV、脚本等)。
* @param response 返回下发结果。
* @warning 严禁用于多车调度场景,仅限单车测试。
*/
grpc::Status gRPCAGVServiceImpl::RobotGoTarget(
grpc::ServerContext* context,
const api::RobotGoTargetCommand_Request* request,
api::RobotGoTargetCommand_Feedback* response)
{
try
{
std::string dev_id = request->header().device_id();
LOG(INFO) << "[RobotGoTarget] device_id:" << dev_id;
auto agv_dev = dmgr_.getDevice<device::AbstractAgv>(dev_id);
if (!agv_dev)
{
auto* header = response->mutable_header();
header->set_success(false);
header->set_error_message("device not found");
setCurrentTimestamp(header->mutable_timestamp());
return grpc::Status::OK;
}
device::AbstractAgv::RobotGoTargetReq req;
auto& pb_data = request->data();
// -------- 必填字段 --------
req.source_id = pb_data.source_id();
req.id = pb_data.id();
if (pb_data.has_task_id()) req.task_id = pb_data.task_id();
// -------- 运动参数 --------
if (pb_data.has_angle()) req.angle = pb_data.angle();
if (pb_data.has_method()) req.method = pb_data.method();
if (pb_data.has_max_speed()) req.max_speed = pb_data.max_speed();
if (pb_data.has_max_wspeed()) req.max_wspeed = pb_data.max_wspeed();
if (pb_data.has_max_acc()) req.max_acc = pb_data.max_acc();
if (pb_data.has_max_wacc()) req.max_wacc = pb_data.max_wacc();
if (pb_data.has_duration()) req.duration = pb_data.duration();
if (pb_data.has_orientation()) req.orientation = pb_data.orientation();
if (pb_data.has_spin()) req.spin = pb_data.spin();
if (pb_data.has_delay()) req.delay = pb_data.delay();
if (pb_data.has_start_rot_dir()) req.start_rot_dir = pb_data.start_rot_dir();
if (pb_data.has_end_rot_dir()) req.end_rot_dir = pb_data.end_rot_dir();
if (pb_data.has_reach_dist()) req.reach_dist = pb_data.reach_dist();
if (pb_data.has_reach_angle()) req.reach_angle = pb_data.reach_angle();
if (pb_data.has_skill_name()) req.skill_name = pb_data.skill_name();
// -------- PGV 二次定位 --------
if (pb_data.has_pgv())
{
auto& pgv_pb = pb_data.pgv();
req.pgv.use_pgv = pgv_pb.use_pgv();
req.pgv.use_down_pgv = pgv_pb.use_down_pgv();
if (pgv_pb.has_pgv_adjust_dist()) req.pgv.pgv_adjust_dist = pgv_pb.pgv_adjust_dist();
if (pgv_pb.has_pgv_adjust_cx()) req.pgv.pgv_adjust_cx = pgv_pb.pgv_adjust_cx();
if (pgv_pb.has_pgv_adjust_cy()) req.pgv.pgv_adjust_cy = pgv_pb.pgv_adjust_cy();
if (pgv_pb.has_pgv_x_adjust()) req.pgv.pgv_x_adjust = pgv_pb.pgv_x_adjust();
}
// -------- 设备动作 --------
if (pb_data.has_operation()) req.operation = pb_data.operation();
if (pb_data.has_jack_height()) req.jack_height = pb_data.jack_height();
if (pb_data.has_start_height()) req.start_height = pb_data.start_height();
if (pb_data.has_end_height()) req.end_height = pb_data.end_height();
if (pb_data.has_fork_mid_height()) req.fork_mid_height = pb_data.fork_mid_height();
if (pb_data.has_fork_dist()) req.fork_dist = pb_data.fork_dist();
if (pb_data.has_direction()) req.direction = pb_data.direction();
if (pb_data.has_recognize()) req.recognize = pb_data.recognize();
if (pb_data.has_recfile()) req.recfile = pb_data.recfile();
// -------- 音频 --------
if (pb_data.has_sounds_args())
{
auto& s_pb = pb_data.sounds_args();
if (s_pb.has_name()) req.sounds_args.name = s_pb.name();
if (s_pb.has_loop()) req.sounds_args.loop = s_pb.loop();
if (s_pb.has_stop()) req.sounds_args.stop = s_pb.stop();
}
// -------- WaitDI --------
if (pb_data.has_wait_di_args())
{
auto& di_pb = pb_data.wait_di_args();
req.wait_di_args.timeout = di_pb.timeout();
for (auto& item : di_pb.di())
{
device::AbstractAgv::DIItem di;
di.id = item.id();
di.status = item.status();
req.wait_di_args.DI.push_back(di);
}
}
// -------- SetDO --------
if (pb_data.has_set_do_args())
{
auto& do_pb = pb_data.set_do_args();
for (auto& item : do_pb.do_list())
{
device::AbstractAgv::DOItem d;
d.id = item.id();
d.status = item.status();
req.set_do_args.DO.push_back(d);
}
}
// -------- 脚本 --------
if (pb_data.has_script_name()) req.script_name = pb_data.script_name();
if (pb_data.has_script_stage()) req.script_stage = pb_data.script_stage();
if (pb_data.has_script_args())
{
auto& s_arg_pb = pb_data.script_args();
for (auto& kv : s_arg_pb.str_kv()) req.script_args.str_kv[kv.first] = kv.second;
for (auto& kv : s_arg_pb.num_kv()) req.script_args.num_kv[kv.first] = kv.second;
for (auto& d : s_arg_pb.do_list())
{
device::AbstractAgv::DOItem di;
di.id = d.id();
di.status = d.status();
req.script_args.do_list.push_back(di);
}
for (auto& di : s_arg_pb.di_list())
{
device::AbstractAgv::DIItem d;
d.id = di.id();
d.status = di.status();
req.script_args.di_list.push_back(d);
}
}
// -------- 原地旋转 --------
if (pb_data.has_move_angle()) req.move_angle = pb_data.move_angle();
if (pb_data.has_speed_w()) req.speed_w = pb_data.speed_w();
if (pb_data.has_loc_mode()) req.loc_mode = pb_data.loc_mode();
// -------- 自由导航 --------
if (pb_data.has_freego())
{
auto& fg_pb = pb_data.freego();
req.freeGo.x = fg_pb.x();
req.freeGo.y = fg_pb.y();
req.freeGo.theta = fg_pb.theta();
}
// 调用设备层
device::AbstractAgv::RobotGoTargetRes res;
agv_dev->robotGoTarget(res, req);
auto* out_data = response->mutable_data();
out_data->set_ret_code(res.ret_code);
out_data->set_create_on(res.create_on);
out_data->set_err_msg(res.err_msg);
auto* header = response->mutable_header();
header->set_success(true);
header->set_error_message("");
setCurrentTimestamp(header->mutable_timestamp());
LOG(INFO) << "[RobotGoTarget] finish ret_code=" << res.ret_code;
return grpc::Status::OK;
}
catch (std::exception& e)
{
auto* header = response->mutable_header();
header->set_success(false);
header->set_error_message(std::string("exception:") + e.what());
setCurrentTimestamp(header->mutable_timestamp());
return grpc::Status::OK;
}
}
// ===================== RobotControlStop =====================
/**
* @brief 停止开环运动。
* @param context 未使用。
* @param request 包含设备 ID(无业务数据)。
* @param response 返回停止结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotControlStop(
grpc::ServerContext* context,
const api::RobotControlStopCommand::Request* request,
api::RobotControlStopCommand::Feedback* response) {
const auto& header = request->header();
std::string device_id = header.device_id();
auto* fb_header = response->mutable_header();
if (device_id.empty()) {
fb_header->set_error_message("Missing device_id");
return grpc::Status::OK;
}
auto agv = dmgr_.getDevice<cmvr::device::AbstractAgv>(device_id);
if (!agv) {
fb_header->set_error_message("Device not found or not an AGV");
return grpc::Status::OK;
}
cmvr::device::AbstractAgv::RobotControlStopRes res;
agv->robotControlStop(res);
auto* status = response->mutable_status();
status->set_ret_code(res.ret_code);
status->set_create_on(res.create_on);
status->set_err_msg(res.err_msg);
if (res.ret_code == 0) {
fb_header->set_error_message("");
} else {
fb_header->set_error_message(res.err_msg.empty() ? "Stop motion failed" : res.err_msg);
}
return grpc::Status::OK;
}
// ===================== RobotTaskPause =====================
/**
* @brief 暂停当前导航任务。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回暂停结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotTaskPause(
grpc::ServerContext* context,
const api::RobotTaskPauseCommand::Request* request,
api::RobotTaskPauseCommand::Feedback* response) {
const auto& header = request->header();
std::string device_id = header.device_id();
auto* fb_header = response->mutable_header();
if (device_id.empty()) {
fb_header->set_error_message("Missing device_id");
return grpc::Status::OK;
}
auto agv = dmgr_.getDevice<cmvr::device::AbstractAgv>(device_id);
if (!agv) {
fb_header->set_error_message("Device not found or not an AGV");
return grpc::Status::OK;
}
cmvr::device::AbstractAgv::RobotTaskPauseRes res;
try {
agv->robotTaskPause(res);
} catch (const std::exception& e) {
fb_header->set_error_message(std::string("Exception: ") + e.what());
return grpc::Status::OK;
}
auto* status = response->mutable_status();
status->set_ret_code(res.ret_code);
status->set_create_on(res.create_on);
status->set_err_msg(res.err_msg);
if (res.ret_code != 0) {
fb_header->set_error_message(res.err_msg.empty() ? "Pause task failed" : res.err_msg);
} else {
fb_header->set_error_message("");
}
return grpc::Status::OK;
}
// ===================== RobotTaskResume =====================
/**
* @brief 继续当前导航任务。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回继续结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotTaskResume(
grpc::ServerContext* context,
const api::RobotTaskResumeCommand::Request* request,
api::RobotTaskResumeCommand::Feedback* response) {
const auto& header = request->header();
std::string device_id = header.device_id();
auto* fb_header = response->mutable_header();
if (device_id.empty()) {
fb_header->set_error_message("Missing device_id");
return grpc::Status::OK;
}
auto agv = dmgr_.getDevice<cmvr::device::AbstractAgv>(device_id);
if (!agv) {
fb_header->set_error_message("Device not found or not an AGV");
return grpc::Status::OK;
}
cmvr::device::AbstractAgv::RobotTaskResumeRes res;
try {
agv->robotTaskResume(res);
} catch (const std::exception& e) {
fb_header->set_error_message(std::string("Exception: ") + e.what());
return grpc::Status::OK;
}
auto* status = response->mutable_status();
status->set_ret_code(res.ret_code);
status->set_create_on(res.create_on);
status->set_err_msg(res.err_msg);
if (res.ret_code != 0) {
fb_header->set_error_message(res.err_msg.empty() ? "Resume task failed" : res.err_msg);
} else {
fb_header->set_error_message("");
}
return grpc::Status::OK;
}
// ===================== RobotTaskCancel =====================
/**
* @brief 取消当前导航任务。
* @param context 未使用。
* @param request 包含设备 ID。
* @param response 返回取消结果。
*/
grpc::Status gRPCAGVServiceImpl::RobotTaskCancel(
grpc::ServerContext* context,
const api::RobotTaskCancelCommand::Request* request,
api::RobotTaskCancelCommand::Feedback* response) {
const auto& header = request->header();
std::string device_id = header.device_id();
auto* fb_header = response->mutable_header();
if (device_id.empty()) {
fb_header->set_error_message("Missing device_id");
return grpc::Status::OK;
}
auto agv = dmgr_.getDevice<cmvr::device::AbstractAgv>(device_id);
if (!agv) {
fb_header->set_error_message("Device not found or not an AGV");
return grpc::Status::OK;
}
cmvr::device::AbstractAgv::RobotTaskCancelRes res;
try {
agv->robotTaskCancel(res);
} catch (const std::exception& e) {
fb_header->set_error_message(std::string("Exception: ") + e.what());
return grpc::Status::OK;
}
auto* status = response->mutable_status();
status->set_ret_code(res.ret_code);
status->set_create_on(res.create_on);
status->set_err_msg(res.err_msg);
if (res.ret_code != 0) {
fb_header->set_error_message(res.err_msg.empty() ? "Cancel task failed" : res.err_msg);
} else {
fb_header->set_error_message("");
}
return grpc::Status::OK;
}
} // namespace cmvr::service

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
/**
* @file agv_service.proto
* @brief 定义AGV服务的gRPC接口,聚合所有AGV相关命令。
* 服务端实现类为 gRPCAGVServiceImpl。
*/
syntax = "proto3"; syntax = "proto3";
import "cmvr/api/agv_command.proto"; import "cmvr/api/agv_command.proto";
@ -45,16 +50,25 @@ service AgvService {
rpc RobotGoTargetList(RobotGoTargetListCommand.Request) returns (RobotGoTargetListCommand.Feedback); rpc RobotGoTargetList(RobotGoTargetListCommand.Request) returns (RobotGoTargetListCommand.Feedback);
// 查询当前导航状态 1020 robot_status_task_req // 查询当前导航状态 1020 robot_status_task_req
rpc RobotStatusTask(RobotStatusTaskCommand.Request) returns (RobotStatusTaskCommand.Feedback); // 1020 查询当前实时导航 robot_status_task_req
rpc RobotStatusTaskCurrent(RobotStatusTaskCurrentCommand.Request) returns (RobotStatusTaskCurrentCommand.Feedback);
// 批量查询任务状态 1110 robot_status_task_status_package_req
rpc RobotStatusTaskPackage(RobotStatusTaskPackageCommand.Request) returns (RobotStatusTaskPackageCommand.Feedback);
// 3051 单点站点自动规划导航 robot_task_gotarget_req 0x0BEB
rpc RobotGoTarget(RobotGoTargetCommand.Request) returns (RobotGoTargetCommand.Feedback);
// 0x07D0 停止开环运动
rpc RobotControlStop(RobotControlStopCommand.Request) returns (RobotControlStopCommand.Feedback);
// 暂停当前导航任务 命令码 3001 (0x0BB9)
rpc RobotTaskPause(RobotTaskPauseCommand.Request) returns (RobotTaskPauseCommand.Feedback);
// 继续当前导航任务 命令码 3002 (0x0BBA)
rpc RobotTaskResume(RobotTaskResumeCommand.Request) returns (RobotTaskResumeCommand.Feedback);
// 取消当前导航任务 命令码 3003 (0x0BBB)
rpc RobotTaskCancel(RobotTaskCancelCommand.Request) returns (RobotTaskCancelCommand.Feedback);
} }