From b4ec07c8512b2620534a961512fa5c896fe7d8e0 Mon Sep 17 00:00:00 2001 From: xtkuang <87661715@qq.com> Date: Tue, 4 Aug 2026 09:52:51 +0800 Subject: [PATCH] feat: add device inventory and move arm JSON RPC --- CMakeLists.txt | 5 +- cmvr-es/devices/arm/aubo_arm/README.md | 16 +- cmvr-es/service/CMakeLists.txt | 57 +++ cmvr-es/service/README.md | 24 +- .../service/grpc/include/grpc_arm_service.h | 3 + .../grpc/include/grpc_system_service.h | 2 +- cmvr-es/service/grpc/src/grpc_arm_service.cpp | 34 ++ .../service/grpc/src/grpc_system_service.cpp | 177 ++++++-- .../grpc/tests/grpc_arm_service_test.cpp | 378 ++++++++++++++++++ .../grpc/tests/grpc_system_service_test.cpp | 291 ++++++++++++++ protos/cmvr/api/arm_service.proto | 3 + protos/cmvr/api/system_command.proto | 73 +++- protos/cmvr/api/system_service.proto | 3 +- 13 files changed, 1019 insertions(+), 47 deletions(-) create mode 100644 cmvr-es/service/grpc/tests/grpc_arm_service_test.cpp create mode 100644 cmvr-es/service/grpc/tests/grpc_system_service_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bb267040..961b77e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,8 +70,9 @@ file(GLOB_RECURSE PROTO_FILES ${PROTO_IMPORT_DIR}/*.proto) set(Protobuf_PROTOC_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bin/protoc" CACHE FILEPATH "" FORCE) -set_property(TARGET gRPC::grpc_cpp_plugin - PROPERTY IMPORTED_LOCATION "${CMAKE_INSTALL_PREFIX}/bin/grpc_cpp_plugin" +set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES + IMPORTED_LOCATION "${CMAKE_INSTALL_PREFIX}/bin/grpc_cpp_plugin" + IMPORTED_LOCATION_RELEASE "${CMAKE_INSTALL_PREFIX}/bin/grpc_cpp_plugin" ) # 1) 先做 OBJECT:只负责生成/编译 pb.cc diff --git a/cmvr-es/devices/arm/aubo_arm/README.md b/cmvr-es/devices/arm/aubo_arm/README.md index 6d63e6b7..0045108b 100644 --- a/cmvr-es/devices/arm/aubo_arm/README.md +++ b/cmvr-es/devices/arm/aubo_arm/README.md @@ -2,8 +2,10 @@ `AuboArm` 是 AUBO SDK v0.27.1 的 `RobotArm` 后端。控制柜 Standard 数字 IO 通过设备通用的 `executeJsonCommand` 接口访问,远程调用复用 -`cmvr.api.SystemService/ExecuteJsonCommand`,不经过 `ArmService` 或 -`MotorService`。 +`cmvr.api.ArmService/ExecuteJsonCommand`,不经过 `SystemService` 或 +`MotorService`。该 RPC 只路由到 `RobotArm`,不会把 JSON 命令转发给其他设备类型。 +旧的 `cmvr.api.SystemService/ExecuteJsonCommand` 不再注册,调用方必须更新服务路径; +请求和响应消息结构保持不变。 返回 [Devices 模块指南](../../README.md) 或 [项目总览](../../../../README.md)。 @@ -14,9 +16,9 @@ - 设备配置:[`../../../config/devices/arm/aubo_arm.pb.txt`](../../../config/devices/arm/aubo_arm.pb.txt) - DeviceManager 配置: [`../../../config/manager/device_manager.pb.txt`](../../../config/manager/device_manager.pb.txt) -- SystemService 实现: - [`../../../service/grpc/src/grpc_system_service.cpp`](../../../service/grpc/src/grpc_system_service.cpp) -- Proto:[`../../../../protos/cmvr/api/system_service.proto`](../../../../protos/cmvr/api/system_service.proto) +- ArmService 实现: + [`../../../service/grpc/src/grpc_arm_service.cpp`](../../../service/grpc/src/grpc_arm_service.cpp) +- Proto:[`../../../../protos/cmvr/api/arm_service.proto`](../../../../protos/cmvr/api/arm_service.proto) 仓库配置使用 SDK RPC 端口 `30004`。现场部署必须填写真实控制器地址和凭据, 不要把生产密码提交到默认配置。 @@ -68,7 +70,7 @@ grpcurl -plaintext \ "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"get_di\",\"index\":0}" }' \ 127.0.0.1:50052 \ - cmvr.api.SystemService/ExecuteJsonCommand + cmvr.api.ArmService/ExecuteJsonCommand ``` 设置 DO0 为高电平: @@ -80,7 +82,7 @@ grpcurl -plaintext \ "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"set_do\",\"index\":0,\"value\":true}" }' \ 127.0.0.1:50052 \ - cmvr.api.SystemService/ExecuteJsonCommand + cmvr.api.ArmService/ExecuteJsonCommand ``` 使用源码默认配置时: diff --git a/cmvr-es/service/CMakeLists.txt b/cmvr-es/service/CMakeLists.txt index adccab30..9f54e4df 100644 --- a/cmvr-es/service/CMakeLists.txt +++ b/cmvr-es/service/CMakeLists.txt @@ -47,6 +47,63 @@ if(BUILD_TESTING) ) set_tests_properties(grpc_camera_stream_policy_test PROPERTIES TIMEOUT 10) + add_executable(grpc_system_service_test + grpc/tests/grpc_system_service_test.cpp + ) + target_include_directories(grpc_system_service_test + PRIVATE + ${CMAKE_SOURCE_DIR}/cmvr-es + ${CMAKE_SOURCE_DIR}/cmvr-es/manager/device_manager + ) + target_link_libraries(grpc_system_service_test + PRIVATE + service + cmvr_es::proto + gtest + gtest_main + pthread + ) + add_test( + NAME grpc_system_service_test + COMMAND grpc_system_service_test + ) + set(_grpc_system_test_environment + "LD_LIBRARY_PATH=${CMVR_TEST_EXTERNAL_LIBRARY_PATH}" + ) + if(CMVR_TEST_SYSTEM_LIBSTDCXX) + list(APPEND _grpc_system_test_environment + "LD_PRELOAD=${CMVR_TEST_SYSTEM_LIBSTDCXX}") + endif() + set_tests_properties(grpc_system_service_test PROPERTIES + TIMEOUT 10 + ENVIRONMENT "${_grpc_system_test_environment}" + ) + + add_executable(grpc_arm_service_test + grpc/tests/grpc_arm_service_test.cpp + ) + target_include_directories(grpc_arm_service_test + PRIVATE + ${CMAKE_SOURCE_DIR}/cmvr-es + ${CMAKE_SOURCE_DIR}/cmvr-es/manager/device_manager + ) + target_link_libraries(grpc_arm_service_test + PRIVATE + service + cmvr_es::proto + gtest + gtest_main + pthread + ) + add_test( + NAME grpc_arm_service_test + COMMAND grpc_arm_service_test + ) + set_tests_properties(grpc_arm_service_test PROPERTIES + TIMEOUT 10 + ENVIRONMENT "${_grpc_system_test_environment}" + ) + add_executable(grpc_arm_teleop_service_test grpc/tests/grpc_arm_teleop_service_test.cpp ) diff --git a/cmvr-es/service/README.md b/cmvr-es/service/README.md index 59959741..49e053cc 100644 --- a/cmvr-es/service/README.md +++ b/cmvr-es/service/README.md @@ -21,6 +21,24 @@ gRPC 和 QUIC 的职责边界: - 实时音视频使用 QUIC DATAGRAM; - `quic_edge/` 不是平台 Gateway,也不是浏览器服务器。 +## SystemService 设备清单 + +`SystemService/GetDeviceList` 返回 `DeviceManager` 的当前只读快照,只包含 +`enabled=true` 的设备。启用但创建、初始化、启动或健康检查失败的设备仍会返回, +并通过 `manager_state`、`health`、`has_error` 和 `error_message` 描述异常。 +接口同时返回稳定的 `device_type` 和仅用于展示/诊断的具体 `type_name`;调用方 +不得使用 `type_name` 做设备类别判断。 + +该 RPC 不修改配置、不动态注册设备,也不触发设备生命周期操作。启用 reflection +后可直接查询: + +```bash +grpcurl -plaintext \ + -d '{}' \ + 127.0.0.1:50052 \ + cmvr.api.SystemService/GetDeviceList +``` + ## MotorService `MotorService` 将 gRPC 电机命令适配到已经由 `DeviceManager` 创建的 @@ -53,9 +71,11 @@ PLC 后端的连接 epoch、stream epoch、寄存器、ACK 和 TIA Portal 要求 - [Modbus TCP PLC runtime](../devices/motor/bus_runtime/modbus_tcp/README.md) - [MotorService 与 CMVR PLC v1 完整协议](../../docs/motor_service_modbus_tcp.md) -AUBO 控制柜 IO 不经过 `MotorService` 或 `ArmService`,而是复用 -`SystemService/ExecuteJsonCommand`。厂商命令和安全约束见 +AUBO 控制柜 IO 不经过 `MotorService`,由 +`ArmService/ExecuteJsonCommand` 转发到目标 `RobotArm`。厂商命令和安全约束见 [AUBO 控制柜 IO](../devices/arm/aubo_arm/README.md)。 +旧的 `SystemService/ExecuteJsonCommand` 已移除;相机 PTZ 应使用类型化的 +`CameraService/ControlPtz`。 ## 新增 gRPC Service diff --git a/cmvr-es/service/grpc/include/grpc_arm_service.h b/cmvr-es/service/grpc/include/grpc_arm_service.h index 07a2dc30..230e021e 100644 --- a/cmvr-es/service/grpc/include/grpc_arm_service.h +++ b/cmvr-es/service/grpc/include/grpc_arm_service.h @@ -50,6 +50,9 @@ public: grpc::Status computeForwardKinematics(grpc::ServerContext* context, const api::ComputeForwardKinematics_Request* request, api::ComputeForwardKinematics_Response* response) override; + grpc::Status ExecuteJsonCommand(grpc::ServerContext* context, + const api::JsonDeviceCommand_Request* request, + api::JsonDeviceCommand_Feedback* response) override; grpc::Status clearFault(grpc::ServerContext *context, const cmvr::api::CommandHeader_Request *request, cmvr::api::CommandHeader_Feedback *response) override; diff --git a/cmvr-es/service/grpc/include/grpc_system_service.h b/cmvr-es/service/grpc/include/grpc_system_service.h index 41fb2646..978ca2f4 100644 --- a/cmvr-es/service/grpc/include/grpc_system_service.h +++ b/cmvr-es/service/grpc/include/grpc_system_service.h @@ -17,8 +17,8 @@ namespace cmvr::service ~gRPCSystemServiceImpl() override = default; grpc::Status GetSystemInfo(grpc::ServerContext* context, const api::GetSystemInfoCommand_Request* request, api::GetSystemInfoCommand_Feedback* response) override; grpc::Status GetSystemStatus(grpc::ServerContext* context, const api::GetSystemStatusCommand_Request* request, api::GetSystemStatusCommand_Feedback* response) override; + grpc::Status GetDeviceList(grpc::ServerContext* context, const api::GetDeviceListCommand_Request* request, api::GetDeviceListCommand_Feedback* response) override; grpc::Status UpdateParams(grpc::ServerContext* context, const cmvr::api::UpdateParamsCommand_Request* request, cmvr::api::UpdateParamsCommand_Feedback* response) override; - grpc::Status ExecuteJsonCommand(grpc::ServerContext* context, const cmvr::api::JsonDeviceCommand_Request* request, cmvr::api::JsonDeviceCommand_Feedback* response) override; grpc::Status StopAll(grpc::ServerContext* context, const cmvr::api::StopAllCommand_Request* request, cmvr::api::StopAllCommand_Feedback* response) override; private: device::DeviceManager& dmgr_; diff --git a/cmvr-es/service/grpc/src/grpc_arm_service.cpp b/cmvr-es/service/grpc/src/grpc_arm_service.cpp index 6c3ca566..b1567f00 100644 --- a/cmvr-es/service/grpc/src/grpc_arm_service.cpp +++ b/cmvr-es/service/grpc/src/grpc_arm_service.cpp @@ -510,6 +510,40 @@ grpc::Status gRPCArmServiceImpl::computeForwardKinematics(grpc::ServerContext*, return grpc::Status(grpc::StatusCode::UNIMPLEMENTED, "computeForwardKinematics is not implemented"); } +grpc::Status gRPCArmServiceImpl::ExecuteJsonCommand( + grpc::ServerContext*, + const api::JsonDeviceCommand_Request* request, + api::JsonDeviceCommand_Feedback* response) +{ + try { + const std::string device_id = request->header().device_id(); + auto arm = dmgr_.getDevice(device_id); + if (!arm) { + fillFeedback( + response->mutable_header(), + false, + "Device not found: " + device_id); + return grpc::Status::OK; + } + + std::string response_json; + const bool success = arm->executeJsonCommand( + request->request_json(), response_json); + fillFeedback( + response->mutable_header(), + success, + success ? "" : response_json); + response->set_response_json(response_json); + if (success) { + logRpcSuccess("ExecuteJsonCommand", device_id); + } + return grpc::Status::OK; + } catch (const std::exception& e) { + fillFeedback(response->mutable_header(), false, e.what()); + return grpc::Status::OK; + } +} + grpc::Status gRPCArmServiceImpl::clearFault(grpc::ServerContext *context, const cmvr::api::CommandHeader_Request *request, cmvr::api::CommandHeader_Feedback *response) diff --git a/cmvr-es/service/grpc/src/grpc_system_service.cpp b/cmvr-es/service/grpc/src/grpc_system_service.cpp index a9ac6a31..908f4c91 100644 --- a/cmvr-es/service/grpc/src/grpc_system_service.cpp +++ b/cmvr-es/service/grpc/src/grpc_system_service.cpp @@ -4,12 +4,107 @@ #include "../include/grpc_system_service.h" +#include +#include + #include "common/base/logging/logger.h" -using namespace cmvr::device; using namespace cmvr::device; using namespace cmvr::service; +namespace { + +std::uint64_t unixTimeMs() noexcept +{ + const auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + return elapsed.count() > 0 + ? static_cast(elapsed.count()) + : 0U; +} + +cmvr::api::SystemDeviceType toApiDeviceType( + const cmvr::device::DeviceKind kind) noexcept +{ + switch (kind) { + case cmvr::device::DeviceKind::AGV: + return cmvr::api::SYSTEM_DEVICE_TYPE_AGV; + case cmvr::device::DeviceKind::Arm: + return cmvr::api::SYSTEM_DEVICE_TYPE_ARM; + case cmvr::device::DeviceKind::Battery: + return cmvr::api::SYSTEM_DEVICE_TYPE_BATTERY; + case cmvr::device::DeviceKind::BioHead: + return cmvr::api::SYSTEM_DEVICE_TYPE_BIO_HEAD; + case cmvr::device::DeviceKind::Camera: + return cmvr::api::SYSTEM_DEVICE_TYPE_CAMERA; + case cmvr::device::DeviceKind::CanBus: + return cmvr::api::SYSTEM_DEVICE_TYPE_CAN_BUS; + case cmvr::device::DeviceKind::DexHand: + return cmvr::api::SYSTEM_DEVICE_TYPE_DEX_HAND; + case cmvr::device::DeviceKind::Gripper: + return cmvr::api::SYSTEM_DEVICE_TYPE_GRIPPER; + case cmvr::device::DeviceKind::Microphone: + return cmvr::api::SYSTEM_DEVICE_TYPE_MICROPHONE; + case cmvr::device::DeviceKind::Motor: + return cmvr::api::SYSTEM_DEVICE_TYPE_MOTOR; + case cmvr::device::DeviceKind::MotorSystem: + return cmvr::api::SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM; + case cmvr::device::DeviceKind::MujocoViewer: + return cmvr::api::SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER; + case cmvr::device::DeviceKind::MujocoWorld: + return cmvr::api::SYSTEM_DEVICE_TYPE_MUJOCO_WORLD; + case cmvr::device::DeviceKind::Robot: + return cmvr::api::SYSTEM_DEVICE_TYPE_ROBOT; + case cmvr::device::DeviceKind::Speaker: + return cmvr::api::SYSTEM_DEVICE_TYPE_SPEAKER; + case cmvr::device::DeviceKind::Unknown: + break; + } + return cmvr::api::SYSTEM_DEVICE_TYPE_UNSPECIFIED; +} + +cmvr::api::SystemDeviceState toApiDeviceState( + const cmvr::device::ManagedDeviceState state) noexcept +{ + switch (state) { + case cmvr::device::ManagedDeviceState::Disabled: + return cmvr::api::SYSTEM_DEVICE_STATE_DISABLED; + case cmvr::device::ManagedDeviceState::Initializing: + return cmvr::api::SYSTEM_DEVICE_STATE_INITIALIZING; + case cmvr::device::ManagedDeviceState::Registered: + return cmvr::api::SYSTEM_DEVICE_STATE_REGISTERED; + case cmvr::device::ManagedDeviceState::Ready: + return cmvr::api::SYSTEM_DEVICE_STATE_READY; + case cmvr::device::ManagedDeviceState::Running: + return cmvr::api::SYSTEM_DEVICE_STATE_RUNNING; + case cmvr::device::ManagedDeviceState::Stopped: + return cmvr::api::SYSTEM_DEVICE_STATE_STOPPED; + case cmvr::device::ManagedDeviceState::Error: + return cmvr::api::SYSTEM_DEVICE_STATE_ERROR; + case cmvr::device::ManagedDeviceState::Unknown: + break; + } + return cmvr::api::SYSTEM_DEVICE_STATE_UNSPECIFIED; +} + +cmvr::api::SystemDeviceHealth toApiDeviceHealth( + const cmvr::device::DeviceHealthState state) noexcept +{ + switch (state) { + case cmvr::device::DeviceHealthState::Healthy: + return cmvr::api::SYSTEM_DEVICE_HEALTH_HEALTHY; + case cmvr::device::DeviceHealthState::Degraded: + return cmvr::api::SYSTEM_DEVICE_HEALTH_DEGRADED; + case cmvr::device::DeviceHealthState::Fault: + return cmvr::api::SYSTEM_DEVICE_HEALTH_FAULT; + case cmvr::device::DeviceHealthState::Unknown: + break; + } + return cmvr::api::SYSTEM_DEVICE_HEALTH_UNSPECIFIED; +} + +} // namespace + gRPCSystemServiceImpl::gRPCSystemServiceImpl(): dmgr_(DeviceManager::getInstance()) {} grpc::Status gRPCSystemServiceImpl::GetSystemInfo(grpc::ServerContext* context, @@ -83,6 +178,55 @@ grpc::Status gRPCSystemServiceImpl::GetSystemStatus(grpc::ServerContext* context } } +grpc::Status gRPCSystemServiceImpl::GetDeviceList( + grpc::ServerContext* context, + const api::GetDeviceListCommand_Request* request, + api::GetDeviceListCommand_Feedback* response) +{ + (void)context; + (void)request; + try { + const auto snapshot = dmgr_.snapshot(); + response->set_manager_name(snapshot.name); + response->set_manager_version(snapshot.version); + response->set_manager_description(snapshot.description); + response->set_sampled_at_unix_ms(unixTimeMs()); + + for (const auto& source : snapshot.devices) { + // The public inventory contains enabled entries only. Keep enabled + // devices visible even when their lifecycle or health is in error. + if (!source.enabled) { + continue; + } + + auto* destination = response->add_device_list(); + destination->set_device_id(source.id); + destination->set_device_type(toApiDeviceType(source.kind)); + destination->set_type_name(source.type_name); + destination->set_enabled(true); + destination->set_manager_state(toApiDeviceState(source.state)); + destination->set_health(toApiDeviceHealth(source.health.state)); + destination->set_has_error(source.abnormal); + destination->set_error_message(source.error_message); + destination->set_status_updated_at_unix_ms( + source.status_updated_at_unix_ms); + } + + response->mutable_header()->set_success(true); + setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); + CMVR_LOG(DEBUG) << "[gRPCSystemServiceImpl] (GetDeviceList): success, devices=" + << response->device_list_size(); + return grpc::Status::OK; + } + catch (const std::exception& e) { + response->Clear(); + response->mutable_header()->set_success(false); + response->mutable_header()->set_error_message(e.what()); + setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); + return grpc::Status::OK; + } +} + grpc::Status gRPCSystemServiceImpl::UpdateParams(grpc::ServerContext* context, const cmvr::api::UpdateParamsCommand_Request* request, cmvr::api::UpdateParamsCommand_Feedback* response) { response->mutable_header()->set_success(false); @@ -91,37 +235,6 @@ grpc::Status gRPCSystemServiceImpl::UpdateParams(grpc::ServerContext* context, c return grpc::Status::OK; } -grpc::Status gRPCSystemServiceImpl::ExecuteJsonCommand(grpc::ServerContext* context, - const cmvr::api::JsonDeviceCommand_Request* request, cmvr::api::JsonDeviceCommand_Feedback* response) -{ - try { - const std::string& dev_id = request->header().device_id(); - auto dev = dmgr_.getDeviceBase(dev_id); - if (!dev) { - response->mutable_header()->set_success(false); - response->mutable_header()->set_error_message("Device not found: " + dev_id); - setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); - return grpc::Status::OK; - } - - std::string response_json; - const bool success = dev->executeJsonCommand(request->request_json(), response_json); - response->mutable_header()->set_success(success); - if (!success) { - response->mutable_header()->set_error_message(response_json); - } - response->set_response_json(response_json); - setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); - return grpc::Status::OK; - } - catch (std::exception& e) { - response->mutable_header()->set_success(false); - response->mutable_header()->set_error_message(e.what()); - setCurrentTimestamp(response->mutable_header()->mutable_timestamp()); - return grpc::Status::OK; - } -} - grpc::Status gRPCSystemServiceImpl::StopAll(grpc::ServerContext* context, const cmvr::api::StopAllCommand_Request* request, cmvr::api::StopAllCommand_Feedback* response) { diff --git a/cmvr-es/service/grpc/tests/grpc_arm_service_test.cpp b/cmvr-es/service/grpc/tests/grpc_arm_service_test.cpp new file mode 100644 index 00000000..57c9d060 --- /dev/null +++ b/cmvr-es/service/grpc/tests/grpc_arm_service_test.cpp @@ -0,0 +1,378 @@ +#include "service/grpc/include/grpc_arm_service.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cmvr/config/device_manager_config/device_manager_config.pb.h" +#include "manager/device_manager/include/device_manager.h" + +namespace cmvr::service { +namespace { + +class JsonCommandRobotArm final : public device::RobotArm { +public: + explicit JsonCommandRobotArm(std::string id) + { + id_ = std::move(id); + } + + std::string typeName() const override { return "JsonCommandRobotArm"; } + + bool executeJsonCommand(const std::string& request_json, + std::string& response_json) override + { + ++execute_calls; + last_request_json = request_json; + response_json = next_response_json; + return next_success; + } + + device::RobotModel getRobotModel() const override { return {}; } + std::size_t getDof() const override { return 0U; } + device::ArmState getRobotState() const override { return {}; } + device::JointGroupState getJointState() const override { return {}; } + device::CartesianPose getTcpPose( + device::FrameType = device::FrameType::Base) const override + { + return {}; + } + device::RobotMode getRobotMode() const override + { + return device::RobotMode::Unknown; + } + device::SafetyMode getSafetyMode() const override + { + return device::SafetyMode::Unknown; + } + device::ControlMode getControlMode() const override + { + return device::ControlMode::None; + } + + device::Result torqueOn() override { return device::Result::success(); } + device::Result torqueOff() override { return device::Result::success(); } + device::Result calibrateZeroQ(const std::string&) override + { + return device::Result::success(); + } + device::Result emergencyStop() override + { + return device::Result::success(); + } + device::Result protectiveStop() override + { + return device::Result::success(); + } + device::Result setSpeedScaling(double) override + { + return device::Result::success(); + } + double getSpeedScaling() const override { return 1.0; } + bool isProtectiveStopped() const override { return false; } + bool isEmergencyStopped() const override { return false; } + bool isFault() const override { return false; } + + device::Result moveJ(const device::JointPositionCommand&, + const device::MotionOptions&) override + { + return device::Result::success(); + } + device::Result speedJ(const device::JointVelocityCommand&, + double, + double) override + { + return device::Result::success(); + } + device::Result stopJ(double) override + { + return device::Result::success(); + } + device::Result moveL( + const device::CartesianPose&, + const device::MotionOptions&, + device::FrameType = device::FrameType::Base) override + { + return device::Result::success(); + } + device::Result speedL( + const device::CartesianVelocity&, + double, + double, + device::FrameType = device::FrameType::Base) override + { + return device::Result::success(); + } + device::Result stopL(std::optional = std::nullopt) override + { + return device::Result::success(); + } + device::Result stopMotion() override + { + return device::Result::success(); + } + + device::Result startServoMode(const device::ServoOptions&) override + { + return device::Result::success(); + } + device::Result servoJ(const device::JointPositionCommand&) override + { + return device::Result::success(); + } + device::Result servoL( + const device::CartesianPose&, + device::FrameType = device::FrameType::Base) override + { + return device::Result::success(); + } + device::Result servoSpeedJ(const device::JointVelocityCommand&) override + { + return device::Result::success(); + } + device::Result servoSpeedL( + const device::CartesianVelocity&, + device::FrameType = device::FrameType::Base) override + { + return device::Result::success(); + } + device::Result stopServoMode() override + { + return device::Result::success(); + } + + device::Result connect(const std::string&, int) override + { + return device::Result::success(); + } + device::Result disconnect() override + { + return device::Result::success(); + } + bool isConnected() const override { return true; } + device::Result powerOn() override { return device::Result::success(); } + device::Result powerOff() override { return device::Result::success(); } + device::Result brakeRelease() override + { + return device::Result::success(); + } + device::Result shutdown() override + { + return device::Result::success(); + } + device::Result clearFault() override + { + return device::Result::success(); + } + device::Result unlockProtectiveStop() override + { + return device::Result::success(); + } + device::Result loadProgram(const std::string&) override + { + return device::Result::success(); + } + device::Result playProgram() override + { + return device::Result::success(); + } + device::Result pauseProgram() override + { + return device::Result::success(); + } + device::Result stopProgram() override + { + return device::Result::success(); + } + + std::vector ik(const std::string&, + const std::string&, + const device::CartesianPose&) override + { + return {}; + } + std::shared_ptr kinematicsSolver() const override + { + return nullptr; + } + device::CartesianPose fk(const std::string&, + const std::string&) override + { + return {}; + } + device::CartesianPose fk(bool = true) override { return {}; } + device::CartesianVelocity getSpeedLCommandTwistBase() const override + { + return {}; + } + bool busy() const override { return false; } + + int execute_calls{0}; + bool next_success{true}; + std::string next_response_json; + std::string last_request_json; +}; + +class JsonCommandNonArmDevice final : public device::AbstractDevice { +public: + explicit JsonCommandNonArmDevice(std::string id) + : AbstractDevice(std::move(id)) + { + } + + std::string typeName() const override { return "JsonCommandNonArmDevice"; } + + bool executeJsonCommand(const std::string&, + std::string& response_json) override + { + ++execute_calls; + response_json = R"({"success":true})"; + return true; + } + + int execute_calls{0}; +}; + +class GrpcArmServiceTest : public ::testing::Test { +protected: + void SetUp() override + { + device::DeviceManager::destroyInstance(); + config::DeviceManagerConfig config; + auto& manager = device::DeviceManager::getInstance(config); + + left_arm_ = std::make_shared("left_arm"); + aubo_arm_ = std::make_shared("aubo_arm"); + non_arm_ = std::make_shared("camera"); + manager.registerDevice(left_arm_); + manager.registerDevice(aubo_arm_); + manager.registerDevice(non_arm_); + service_ = std::make_unique(); + } + + void TearDown() override + { + service_.reset(); + non_arm_.reset(); + aubo_arm_.reset(); + left_arm_.reset(); + device::DeviceManager::destroyInstance(); + } + + grpc::Status execute(const std::string& device_id, + const std::string& request_json, + api::JsonDeviceCommand_Feedback& response) + { + api::JsonDeviceCommand_Request request; + request.mutable_header()->set_device_id(device_id); + request.set_request_json(request_json); + grpc::ServerContext context; + return service_->ExecuteJsonCommand(&context, &request, &response); + } + + std::shared_ptr left_arm_; + std::shared_ptr aubo_arm_; + std::shared_ptr non_arm_; + std::unique_ptr service_; +}; + +TEST(GrpcArmServiceDescriptorTest, + ExecuteJsonCommandBelongsOnlyToArmService) +{ + const auto* pool = google::protobuf::DescriptorPool::generated_pool(); + const auto* arm_service = + pool->FindServiceByName("cmvr.api.ArmService"); + const auto* system_service = + pool->FindServiceByName("cmvr.api.SystemService"); + + ASSERT_NE(arm_service, nullptr); + ASSERT_NE(system_service, nullptr); + EXPECT_NE(arm_service->FindMethodByName("ExecuteJsonCommand"), nullptr); + EXPECT_EQ(system_service->FindMethodByName("ExecuteJsonCommand"), nullptr); +} + +TEST_F(GrpcArmServiceTest, RoutesByHeaderDeviceIdAndForwardsSuccessfulJson) +{ + const std::string request_json = + R"({"command":"cabinet_io","operation":"get_di","index":0})"; + const std::string response_json = + R"({"success":true,"operation":"get_di","index":0,"value":false})"; + aubo_arm_->next_response_json = response_json; + + api::JsonDeviceCommand_Feedback response; + const auto status = execute("aubo_arm", request_json, response); + + ASSERT_TRUE(status.ok()) << status.error_message(); + EXPECT_TRUE(response.header().success()); + EXPECT_TRUE(response.header().error_message().empty()); + EXPECT_TRUE(response.header().has_timestamp()); + EXPECT_GT(response.header().timestamp().seconds(), 0); + EXPECT_EQ(response.response_json(), response_json); + EXPECT_EQ(aubo_arm_->execute_calls, 1); + EXPECT_EQ(aubo_arm_->last_request_json, request_json); + EXPECT_EQ(left_arm_->execute_calls, 0); +} + +TEST_F(GrpcArmServiceTest, ForwardsDeviceJsonFailureWithLegacyGrpcOkSemantics) +{ + const std::string response_json = + R"({"success":false,"error_code":"not_connected"})"; + aubo_arm_->next_success = false; + aubo_arm_->next_response_json = response_json; + + api::JsonDeviceCommand_Feedback response; + const auto status = execute( + "aubo_arm", + R"({"command":"cabinet_io","operation":"get_do","index":0})", + response); + + ASSERT_TRUE(status.ok()) << status.error_message(); + EXPECT_FALSE(response.header().success()); + EXPECT_EQ(response.header().error_message(), response_json); + EXPECT_TRUE(response.header().has_timestamp()); + EXPECT_GT(response.header().timestamp().seconds(), 0); + EXPECT_EQ(response.response_json(), response_json); + EXPECT_EQ(aubo_arm_->execute_calls, 1); + EXPECT_EQ(left_arm_->execute_calls, 0); +} + +TEST_F(GrpcArmServiceTest, + MissingOrNonArmIdReturnsBusinessFailureWithoutBackendDispatch) +{ + api::JsonDeviceCommand_Feedback non_arm_response; + const auto non_arm_status = execute( + "camera", R"({"command":"cabinet_io"})", non_arm_response); + + ASSERT_TRUE(non_arm_status.ok()) << non_arm_status.error_message(); + EXPECT_FALSE(non_arm_response.header().success()); + EXPECT_EQ(non_arm_response.header().error_message(), + "Device not found: camera"); + EXPECT_TRUE(non_arm_response.header().has_timestamp()); + EXPECT_TRUE(non_arm_response.response_json().empty()); + EXPECT_EQ(non_arm_->execute_calls, 0); + EXPECT_EQ(aubo_arm_->execute_calls, 0); + EXPECT_EQ(left_arm_->execute_calls, 0); + + api::JsonDeviceCommand_Feedback missing_response; + const auto missing_status = execute( + "missing_arm", R"({"command":"cabinet_io"})", missing_response); + + ASSERT_TRUE(missing_status.ok()) << missing_status.error_message(); + EXPECT_FALSE(missing_response.header().success()); + EXPECT_EQ(missing_response.header().error_message(), + "Device not found: missing_arm"); + EXPECT_TRUE(missing_response.header().has_timestamp()); + EXPECT_TRUE(missing_response.response_json().empty()); + EXPECT_EQ(non_arm_->execute_calls, 0); + EXPECT_EQ(aubo_arm_->execute_calls, 0); + EXPECT_EQ(left_arm_->execute_calls, 0); +} + +} // namespace +} // namespace cmvr::service diff --git a/cmvr-es/service/grpc/tests/grpc_system_service_test.cpp b/cmvr-es/service/grpc/tests/grpc_system_service_test.cpp new file mode 100644 index 00000000..8642a5b4 --- /dev/null +++ b/cmvr-es/service/grpc/tests/grpc_system_service_test.cpp @@ -0,0 +1,291 @@ +#include "service/grpc/include/grpc_system_service.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmvr/config/device_manager_config/device_manager_config.pb.h" +#include "manager/device_manager/include/device_manager.h" + +namespace cmvr::service { +namespace { + +class SnapshotDevice final : public device::AbstractDevice { +public: + SnapshotDevice(std::string id, + device::DeviceKind kind, + std::string type_name, + device::DeviceHealthSnapshot health = {}) + : AbstractDevice(std::move(id)), + kind_(kind), + type_name_(std::move(type_name)), + health_(std::move(health)) + { + } + + device::DeviceKind kind() const noexcept override { return kind_; } + std::string typeName() const override { return type_name_; } + device::DeviceHealthSnapshot healthSnapshot() override + { + return health_; + } + +private: + device::DeviceKind kind_; + std::string type_name_; + device::DeviceHealthSnapshot health_; +}; + +std::uint64_t currentUnixTimeMs() +{ + const auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + return static_cast(elapsed.count()); +} + +const api::SystemDeviceInfo* findDevice( + const api::GetDeviceListCommand_Feedback& response, + const std::string& id) +{ + for (const auto& device : response.device_list()) { + if (device.device_id() == id) { + return &device; + } + } + return nullptr; +} + +class GrpcSystemServiceTest : public ::testing::Test { +protected: + void SetUp() override + { + device::DeviceManager::destroyInstance(); + } + + void TearDown() override + { + service_.reset(); + owned_devices_.clear(); + device::DeviceManager::destroyInstance(); + } + + api::GetDeviceListCommand_Feedback getDeviceList() + { + api::GetDeviceListCommand_Request request; + api::GetDeviceListCommand_Feedback response; + grpc::ServerContext context; + + const auto status = service_->GetDeviceList( + &context, &request, &response); + EXPECT_TRUE(status.ok()) << status.error_message(); + return response; + } + + void registerDevice(device::DeviceManager& manager, + std::shared_ptr device) + { + manager.registerDevice(device); + owned_devices_.push_back(std::move(device)); + } + + std::unique_ptr service_; + std::vector> owned_devices_; +}; + +TEST_F(GrpcSystemServiceTest, + ReturnsOnlyEnabledDevicesAndPreservesEnabledErrors) +{ + config::DeviceManagerConfig config; + config.set_name("system-service-test"); + config.set_version("9.2"); + config.set_description("GetDeviceList snapshot test"); + + auto* disabled = config.add_devices(); + disabled->set_id("b_disabled_camera"); + disabled->set_type(config::DeviceConfigEntry::DEVICE_TYPE_CAMERA); + disabled->set_enable(false); + + // An enabled unsupported entry remains in the manager snapshot as ERROR. + // GetDeviceList must expose it rather than filtering by lifecycle state. + auto* enabled_error = config.add_devices(); + enabled_error->set_id("c_enabled_error"); + enabled_error->set_type(config::DeviceConfigEntry::DEVICE_TYPE_UNKNOWN); + enabled_error->set_enable(true); + + auto& manager = device::DeviceManager::getInstance(config); + registerDevice( + manager, + std::make_shared( + "z_camera", + device::DeviceKind::Camera, + "VendorCamera", + device::DeviceHealthSnapshot{ + device::DeviceHealthState::Healthy, {}})); + registerDevice( + manager, + std::make_shared( + "a_unknown", + device::DeviceKind::Unknown, + "MysteryDriver", + device::DeviceHealthSnapshot{ + device::DeviceHealthState::Degraded, + "device health is degraded"})); + service_ = std::make_unique(); + + const auto response = getDeviceList(); + + ASSERT_TRUE(response.header().success()) + << response.header().error_message(); + EXPECT_TRUE(response.header().has_timestamp()); + EXPECT_GT(response.header().timestamp().seconds(), 0); + EXPECT_EQ(response.manager_name(), "system-service-test"); + EXPECT_EQ(response.manager_version(), "9.2"); + EXPECT_EQ(response.manager_description(), "GetDeviceList snapshot test"); + ASSERT_EQ(response.device_list_size(), 3); + + // DeviceManager::snapshot() supplies stable device-id ordering, and the + // service must preserve it while filtering disabled entries. + EXPECT_EQ(response.device_list(0).device_id(), "a_unknown"); + EXPECT_EQ(response.device_list(1).device_id(), "c_enabled_error"); + EXPECT_EQ(response.device_list(2).device_id(), "z_camera"); + EXPECT_EQ(findDevice(response, "b_disabled_camera"), nullptr); + + const auto* unknown = findDevice(response, "a_unknown"); + ASSERT_NE(unknown, nullptr); + EXPECT_TRUE(unknown->enabled()); + EXPECT_EQ(unknown->type_name(), "MysteryDriver"); + EXPECT_EQ(unknown->device_type(), + api::SYSTEM_DEVICE_TYPE_UNSPECIFIED); + EXPECT_NE(unknown->device_type(), api::SYSTEM_DEVICE_TYPE_AGV); + EXPECT_EQ(unknown->manager_state(), + api::SYSTEM_DEVICE_STATE_REGISTERED); + EXPECT_EQ(unknown->health(), api::SYSTEM_DEVICE_HEALTH_DEGRADED); + EXPECT_TRUE(unknown->has_error()); + EXPECT_EQ(unknown->error_message(), "device health is degraded"); + EXPECT_GT(unknown->status_updated_at_unix_ms(), 0U); + + const auto* error = findDevice(response, "c_enabled_error"); + ASSERT_NE(error, nullptr); + EXPECT_TRUE(error->enabled()); + EXPECT_EQ(error->type_name(), "Unknown"); + EXPECT_EQ(error->device_type(), api::SYSTEM_DEVICE_TYPE_UNSPECIFIED); + EXPECT_EQ(error->manager_state(), api::SYSTEM_DEVICE_STATE_ERROR); + EXPECT_EQ(error->health(), api::SYSTEM_DEVICE_HEALTH_UNSPECIFIED); + EXPECT_TRUE(error->has_error()); + EXPECT_FALSE(error->error_message().empty()); + EXPECT_GT(error->status_updated_at_unix_ms(), 0U); + + const auto* camera = findDevice(response, "z_camera"); + ASSERT_NE(camera, nullptr); + EXPECT_TRUE(camera->enabled()); + EXPECT_EQ(camera->type_name(), "VendorCamera"); + EXPECT_EQ(camera->device_type(), api::SYSTEM_DEVICE_TYPE_CAMERA); + EXPECT_EQ(camera->manager_state(), + api::SYSTEM_DEVICE_STATE_REGISTERED); + EXPECT_EQ(camera->health(), api::SYSTEM_DEVICE_HEALTH_HEALTHY); + EXPECT_FALSE(camera->has_error()); + EXPECT_TRUE(camera->error_message().empty()); + EXPECT_GT(camera->status_updated_at_unix_ms(), 0U); +} + +TEST_F(GrpcSystemServiceTest, EmptyListReturnsMetadataAndTimestamps) +{ + config::DeviceManagerConfig config; + config.set_name("empty-manager"); + config.set_version("1.2.3"); + config.set_description("manager without devices"); + device::DeviceManager::getInstance(config); + service_ = std::make_unique(); + + const auto before_ms = currentUnixTimeMs(); + const auto response = getDeviceList(); + const auto after_ms = currentUnixTimeMs(); + + ASSERT_TRUE(response.header().success()) + << response.header().error_message(); + EXPECT_TRUE(response.header().has_timestamp()); + EXPECT_GT(response.header().timestamp().seconds(), 0); + EXPECT_EQ(response.device_list_size(), 0); + EXPECT_EQ(response.manager_name(), "empty-manager"); + EXPECT_EQ(response.manager_version(), "1.2.3"); + EXPECT_EQ(response.manager_description(), "manager without devices"); + EXPECT_GE(response.sampled_at_unix_ms(), before_ms); + EXPECT_LE(response.sampled_at_unix_ms(), after_ms); +} + +TEST_F(GrpcSystemServiceTest, MapsEveryKnownDeviceKind) +{ + struct ExpectedMapping { + const char* id; + device::DeviceKind source; + api::SystemDeviceType destination; + }; + constexpr std::array mappings{{ + {"01_agv", device::DeviceKind::AGV, + api::SYSTEM_DEVICE_TYPE_AGV}, + {"02_arm", device::DeviceKind::Arm, + api::SYSTEM_DEVICE_TYPE_ARM}, + {"03_battery", device::DeviceKind::Battery, + api::SYSTEM_DEVICE_TYPE_BATTERY}, + {"04_bio_head", device::DeviceKind::BioHead, + api::SYSTEM_DEVICE_TYPE_BIO_HEAD}, + {"05_camera", device::DeviceKind::Camera, + api::SYSTEM_DEVICE_TYPE_CAMERA}, + {"06_can_bus", device::DeviceKind::CanBus, + api::SYSTEM_DEVICE_TYPE_CAN_BUS}, + {"07_dex_hand", device::DeviceKind::DexHand, + api::SYSTEM_DEVICE_TYPE_DEX_HAND}, + {"08_gripper", device::DeviceKind::Gripper, + api::SYSTEM_DEVICE_TYPE_GRIPPER}, + {"09_microphone", device::DeviceKind::Microphone, + api::SYSTEM_DEVICE_TYPE_MICROPHONE}, + {"10_motor", device::DeviceKind::Motor, + api::SYSTEM_DEVICE_TYPE_MOTOR}, + {"11_motor_system", device::DeviceKind::MotorSystem, + api::SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM}, + {"12_mujoco_viewer", device::DeviceKind::MujocoViewer, + api::SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER}, + {"13_mujoco_world", device::DeviceKind::MujocoWorld, + api::SYSTEM_DEVICE_TYPE_MUJOCO_WORLD}, + {"14_robot", device::DeviceKind::Robot, + api::SYSTEM_DEVICE_TYPE_ROBOT}, + {"15_speaker", device::DeviceKind::Speaker, + api::SYSTEM_DEVICE_TYPE_SPEAKER}, + }}; + + config::DeviceManagerConfig config; + auto& manager = device::DeviceManager::getInstance(config); + for (const auto& mapping : mappings) { + registerDevice( + manager, + std::make_shared( + mapping.id, mapping.source, "MappedBackend")); + } + service_ = std::make_unique(); + + const auto response = getDeviceList(); + + ASSERT_TRUE(response.header().success()) + << response.header().error_message(); + ASSERT_EQ(response.device_list_size(), + static_cast(mappings.size())); + for (std::size_t index = 0; index < mappings.size(); ++index) { + SCOPED_TRACE(mappings[index].id); + const auto& actual = response.device_list( + static_cast(index)); + EXPECT_EQ(actual.device_id(), mappings[index].id); + EXPECT_EQ(actual.device_type(), mappings[index].destination); + EXPECT_NE(actual.device_type(), + api::SYSTEM_DEVICE_TYPE_UNSPECIFIED); + } +} + +} // namespace +} // namespace cmvr::service diff --git a/protos/cmvr/api/arm_service.proto b/protos/cmvr/api/arm_service.proto index 92562f4a..a050532a 100644 --- a/protos/cmvr/api/arm_service.proto +++ b/protos/cmvr/api/arm_service.proto @@ -20,4 +20,7 @@ service ArmService { rpc calibrateZeroQ(CalibrateZeroQ.Request) returns (CalibrateZeroQ.Response); rpc getPoseMatrix(GetPoseMatrix.Request) returns (GetPoseMatrix.Response); rpc computeForwardKinematics(ComputeForwardKinematics.Request) returns (ComputeForwardKinematics.Response); + + // Vendor-specific arm extension currently used for AUBO cabinet IO. + rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback); } diff --git a/protos/cmvr/api/system_command.proto b/protos/cmvr/api/system_command.proto index 52b99221..c61fa36d 100644 --- a/protos/cmvr/api/system_command.proto +++ b/protos/cmvr/api/system_command.proto @@ -21,6 +21,77 @@ message DeviceList { DeviceType device_type = 2; } +// Stable device categories used by GetDeviceList. This intentionally does not +// reuse the legacy DeviceType enum above: its zero value is AGV and it does not +// cover all DeviceManager categories. +enum SystemDeviceType { + SYSTEM_DEVICE_TYPE_UNSPECIFIED = 0; + SYSTEM_DEVICE_TYPE_AGV = 1; + SYSTEM_DEVICE_TYPE_ARM = 2; + SYSTEM_DEVICE_TYPE_BATTERY = 3; + SYSTEM_DEVICE_TYPE_BIO_HEAD = 4; + SYSTEM_DEVICE_TYPE_CAMERA = 5; + SYSTEM_DEVICE_TYPE_CAN_BUS = 6; + SYSTEM_DEVICE_TYPE_DEX_HAND = 7; + SYSTEM_DEVICE_TYPE_GRIPPER = 8; + SYSTEM_DEVICE_TYPE_MICROPHONE = 9; + SYSTEM_DEVICE_TYPE_MOTOR = 10; + SYSTEM_DEVICE_TYPE_MOTOR_SYSTEM = 11; + SYSTEM_DEVICE_TYPE_MUJOCO_VIEWER = 12; + SYSTEM_DEVICE_TYPE_MUJOCO_WORLD = 13; + SYSTEM_DEVICE_TYPE_ROBOT = 14; + SYSTEM_DEVICE_TYPE_SPEAKER = 15; +} + +enum SystemDeviceState { + SYSTEM_DEVICE_STATE_UNSPECIFIED = 0; + SYSTEM_DEVICE_STATE_DISABLED = 1; + SYSTEM_DEVICE_STATE_INITIALIZING = 2; + SYSTEM_DEVICE_STATE_REGISTERED = 3; + SYSTEM_DEVICE_STATE_READY = 4; + SYSTEM_DEVICE_STATE_RUNNING = 5; + SYSTEM_DEVICE_STATE_STOPPED = 6; + SYSTEM_DEVICE_STATE_ERROR = 7; +} + +enum SystemDeviceHealth { + SYSTEM_DEVICE_HEALTH_UNSPECIFIED = 0; + SYSTEM_DEVICE_HEALTH_HEALTHY = 1; + SYSTEM_DEVICE_HEALTH_DEGRADED = 2; + SYSTEM_DEVICE_HEALTH_FAULT = 3; +} + +message SystemDeviceInfo { + string device_id = 1; + SystemDeviceType device_type = 2; + + // Concrete backend name for display and diagnostics only. Consumers must + // use device_type, rather than this free-form string, for decisions. + string type_name = 3; + + // GetDeviceList currently publishes only enabled entries. Keep this field + // explicit so each row remains self-describing and future-compatible. + bool enabled = 4; + SystemDeviceState manager_state = 5; + SystemDeviceHealth health = 6; + bool has_error = 7; + string error_message = 8; + uint64 status_updated_at_unix_ms = 9; +} + +message GetDeviceListCommand { + message Request {} + + message Feedback { + CommandHeader.Feedback header = 1; + string manager_name = 2; + string manager_version = 3; + string manager_description = 4; + repeated SystemDeviceInfo device_list = 5; + uint64 sampled_at_unix_ms = 6; + } +} + message GetSystemInfoCommand { message Request {} @@ -69,4 +140,4 @@ message StopAllCommand { message Feedback { CommandHeader.Feedback header = 1; } -} \ No newline at end of file +} diff --git a/protos/cmvr/api/system_service.proto b/protos/cmvr/api/system_service.proto index 84d1c9bd..83f19afd 100644 --- a/protos/cmvr/api/system_service.proto +++ b/protos/cmvr/api/system_service.proto @@ -1,6 +1,5 @@ syntax = "proto3"; -import "cmvr/api/common.proto"; import "cmvr/api/system_command.proto"; package cmvr.api; @@ -9,9 +8,9 @@ package cmvr.api; service SystemService { rpc GetSystemInfo(GetSystemInfoCommand.Request) returns (GetSystemInfoCommand.Feedback) {} rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {} + rpc GetDeviceList(GetDeviceListCommand.Request) returns (GetDeviceListCommand.Feedback) {} rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {} - rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback) {} rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {} }