From b585cb6601ac851e61d40a25d3b9a726d48c4e9f Mon Sep 17 00:00:00 2001 From: linbo <1034003879@qq.com> Date: Thu, 23 Jul 2026 09:10:39 +0800 Subject: [PATCH] add executeJsonCommand --- cmvr-es/devices/abstract_device.h | 4 + .../camera/hikvision_camera/CMakeLists.txt | 55 +++++- .../include/hikvision_camera.h | 1 + .../hikvision_camera/src/hikvision_camera.cpp | 165 ++++++++++++++++++ .../device_manager/include/device_manager.h | 1 + .../device_manager/src/device_manager.cpp | 10 ++ .../grpc/include/grpc_system_service.h | 1 + .../service/grpc/src/grpc_system_service.cpp | 31 ++++ protos/cmvr/api/common.proto | 12 ++ protos/cmvr/api/system_service.proto | 4 +- 10 files changed, 279 insertions(+), 5 deletions(-) diff --git a/cmvr-es/devices/abstract_device.h b/cmvr-es/devices/abstract_device.h index cba2eb23..f104a120 100644 --- a/cmvr-es/devices/abstract_device.h +++ b/cmvr-es/devices/abstract_device.h @@ -34,6 +34,10 @@ namespace cmvr::device { virtual bool start() { return true; } virtual bool stop() { return true; } virtual bool update() { return true; } + virtual bool executeJsonCommand(const std::string& request_json, std::string& response_json) { + response_json = R"({"success":false,"error_message":"JSON command unsupported"})"; + return false; + } protected: std::string id_; // 设备名称 diff --git a/cmvr-es/devices/camera/hikvision_camera/CMakeLists.txt b/cmvr-es/devices/camera/hikvision_camera/CMakeLists.txt index bef14e87..9308507b 100644 --- a/cmvr-es/devices/camera/hikvision_camera/CMakeLists.txt +++ b/cmvr-es/devices/camera/hikvision_camera/CMakeLists.txt @@ -4,9 +4,52 @@ target_include_directories(hikvision_camera PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set(HIKVISION_SDK_ROOT "${CMAKE_SOURCE_DIR}/dependency/${ARCH}/third_party/hikvision_sdk/v6.1.11.5") set(HIKVISION_SDK_LIB_DIR "${HIKVISION_SDK_ROOT}/lib") +set(HIKVISION_SDK_COM_DIR "${HIKVISION_SDK_LIB_DIR}/HCNetSDKCom") + +file(GLOB HIKVISION_SDK_LIBS CONFIGURE_DEPENDS + "${HIKVISION_SDK_LIB_DIR}/*.so" + "${HIKVISION_SDK_LIB_DIR}/*.so.*" +) +file(GLOB HIKVISION_SDK_COM_LIBS CONFIGURE_DEPENDS + "${HIKVISION_SDK_COM_DIR}/*.so" + "${HIKVISION_SDK_COM_DIR}/*.so.*" +) +set(HIKVISION_HCNETSDK_LIB "${HIKVISION_SDK_LIB_DIR}/libhcnetsdk.so") + +if(NOT HIKVISION_SDK_LIBS) + message(FATAL_ERROR "Hikvision SDK libraries not found: ${HIKVISION_SDK_LIB_DIR}") +endif() +if(NOT HIKVISION_SDK_COM_LIBS) + message(FATAL_ERROR "Hikvision SDK component libraries not found: ${HIKVISION_SDK_COM_DIR}") +endif() +if(NOT EXISTS "${HIKVISION_HCNETSDK_LIB}") + message(FATAL_ERROR "Hikvision HCNetSDK library not found: ${HIKVISION_HCNETSDK_LIB}") +endif() target_include_directories(hikvision_camera PRIVATE "${HIKVISION_SDK_ROOT}/include") -target_compile_definitions(hikvision_camera PRIVATE HIKVISION_SDK_LIB_DIR="${HIKVISION_SDK_LIB_DIR}/") +target_link_directories(hikvision_camera PRIVATE "${HIKVISION_SDK_LIB_DIR}" "${HIKVISION_SDK_COM_DIR}") + +set(HIKVISION_JSONCPP_LINK jsoncpp) +if(TARGET jsoncpp) + get_target_property(HIKVISION_JSONCPP_IMPORTED_LOCATION jsoncpp IMPORTED_LOCATION) + get_target_property(HIKVISION_JSONCPP_IMPORTED_LOCATION_RELEASE jsoncpp IMPORTED_LOCATION_RELEASE) + get_target_property(HIKVISION_JSONCPP_INTERFACE_INCLUDES jsoncpp INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "[hikvision_camera] jsoncpp target: jsoncpp") + message(STATUS "[hikvision_camera] jsoncpp IMPORTED_LOCATION: ${HIKVISION_JSONCPP_IMPORTED_LOCATION}") + message(STATUS "[hikvision_camera] jsoncpp IMPORTED_LOCATION_RELEASE: ${HIKVISION_JSONCPP_IMPORTED_LOCATION_RELEASE}") + message(STATUS "[hikvision_camera] jsoncpp INTERFACE_INCLUDE_DIRECTORIES: ${HIKVISION_JSONCPP_INTERFACE_INCLUDES}") +else() + find_library(HIKVISION_JSONCPP_LIBRARY NAMES jsoncpp) + if(HIKVISION_JSONCPP_LIBRARY) + set(HIKVISION_JSONCPP_LINK "${HIKVISION_JSONCPP_LIBRARY}") + message(STATUS "[hikvision_camera] jsoncpp library: ${HIKVISION_JSONCPP_LIBRARY}") + else() + message(WARNING "[hikvision_camera] jsoncpp library not found by CMake; linker will resolve -ljsoncpp") + endif() +endif() + +find_path(HIKVISION_JSONCPP_INCLUDE_DIR NAMES json/json.h PATH_SUFFIXES jsoncpp) +message(STATUS "[hikvision_camera] jsoncpp include dir: ${HIKVISION_JSONCPP_INCLUDE_DIR}") target_link_libraries(hikvision_camera PUBLIC @@ -14,13 +57,17 @@ target_link_libraries(hikvision_camera opencv_core cmvr_es::proto PRIVATE - -lhcnetsdk + "${HIKVISION_HCNETSDK_LIB}" + ${HIKVISION_JSONCPP_LINK} pthread ) + set_target_properties(hikvision_camera PROPERTIES - BUILD_RPATH "${HIKVISION_SDK_LIB_DIR};${HIKVISION_SDK_LIB_DIR}/HCNetSDKCom" + BUILD_RPATH "${HIKVISION_SDK_LIB_DIR};${HIKVISION_SDK_COM_DIR}" INSTALL_RPATH "$ORIGIN;$ORIGIN/HCNetSDKCom" ) + add_library(cmvr_es::device::hikvision_camera ALIAS hikvision_camera) install(TARGETS hikvision_camera LIBRARY DESTINATION lib) -install(DIRECTORY "${HIKVISION_SDK_LIB_DIR}/HCNetSDKCom" DESTINATION lib) +install(FILES ${HIKVISION_SDK_LIBS} DESTINATION lib) +install(DIRECTORY "${HIKVISION_SDK_COM_DIR}" DESTINATION lib) diff --git a/cmvr-es/devices/camera/hikvision_camera/include/hikvision_camera.h b/cmvr-es/devices/camera/hikvision_camera/include/hikvision_camera.h index a4140af4..dea0cb96 100644 --- a/cmvr-es/devices/camera/hikvision_camera/include/hikvision_camera.h +++ b/cmvr-es/devices/camera/hikvision_camera/include/hikvision_camera.h @@ -35,6 +35,7 @@ public: bool startStreaming() override; void stopStreaming() override; bool controlPtz(PtzCommand command, bool stop, int speed) override; + bool executeJsonCommand(const std::string& request_json, std::string& response_json) override; void onEsData(unsigned int packet_type, unsigned char* buffer, diff --git a/cmvr-es/devices/camera/hikvision_camera/src/hikvision_camera.cpp b/cmvr-es/devices/camera/hikvision_camera/src/hikvision_camera.cpp index 21a39fb9..d3f0afde 100644 --- a/cmvr-es/devices/camera/hikvision_camera/src/hikvision_camera.cpp +++ b/cmvr-es/devices/camera/hikvision_camera/src/hikvision_camera.cpp @@ -1,11 +1,14 @@ #include "../include/hikvision_camera.h" #include +#include +#include #include #include #include #include #include +#include #include #include @@ -15,6 +18,7 @@ #include "HCNetSDK.h" #include "common/base/logging/logger.h" +#include "json/json.h" namespace { @@ -104,6 +108,114 @@ DWORD normalizePtzSpeed(int speed) return static_cast(speed); } +std::string lowerString(std::string value) +{ + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return value; +} + +std::string jsonEscape(const std::string& value) +{ + std::ostringstream out; + for (const char c : value) { + switch (c) { + case '\\': + out << "\\\\"; + break; + case '"': + out << "\\\""; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + out << c; + break; + } + } + return out.str(); +} + +std::string jsonStringField(const Json::Value& value, const char* name) +{ + const Json::Value* member = value.find(name, name + std::strlen(name)); + return member && member->isString() ? member->asString() : ""; +} + +bool jsonBoolField(const Json::Value& value, const char* name, bool& out) +{ + const Json::Value* member = value.find(name, name + std::strlen(name)); + if (!member || !member->isBool()) { + return false; + } + out = member->asBool(); + return true; +} + +int jsonIntField(const Json::Value& value, const char* name, int default_value) +{ + const Json::Value* member = value.find(name, name + std::strlen(name)); + return member && member->isInt() ? member->asInt() : default_value; +} + +bool parseJsonCommand(const std::string& request_json, Json::Value& root, std::string& error) +{ + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + return reader->parse( + request_json.data(), request_json.data() + request_json.size(), &root, &error); +} + +bool parsePtzCommandName(const std::string& command, cmvr::device::PtzCommand& out) +{ + const std::string normalized = lowerString(command); + if (normalized == "tilt_up" || normalized == "up") { + out = cmvr::device::PtzCommand::TiltUp; + } else if (normalized == "tilt_down" || normalized == "down") { + out = cmvr::device::PtzCommand::TiltDown; + } else if (normalized == "pan_left" || normalized == "left") { + out = cmvr::device::PtzCommand::PanLeft; + } else if (normalized == "pan_right" || normalized == "right") { + out = cmvr::device::PtzCommand::PanRight; + } else if (normalized == "up_left") { + out = cmvr::device::PtzCommand::UpLeft; + } else if (normalized == "up_right") { + out = cmvr::device::PtzCommand::UpRight; + } else if (normalized == "down_left") { + out = cmvr::device::PtzCommand::DownLeft; + } else if (normalized == "down_right") { + out = cmvr::device::PtzCommand::DownRight; + } else if (normalized == "zoom_in") { + out = cmvr::device::PtzCommand::ZoomIn; + } else if (normalized == "zoom_out") { + out = cmvr::device::PtzCommand::ZoomOut; + } else if (normalized == "pan_auto" || normalized == "auto") { + out = cmvr::device::PtzCommand::PanAuto; + } else { + return false; + } + return true; +} + +std::string makeJsonResult(bool success, const std::string& error_message = "") +{ + std::ostringstream out; + out << "{\"success\":" << (success ? "true" : "false"); + if (!error_message.empty()) { + out << ",\"error_message\":\"" << jsonEscape(error_message) << "\""; + } + out << "}"; + return out.str(); +} + void CALLBACK hikvisionEsRealPlayCallback( LONG, NET_DVR_PACKET_INFO_EX* packet_info, void* user) { @@ -396,6 +508,59 @@ bool HikvisionCamera::controlPtz(PtzCommand command, bool stop, int speed) return true; } +bool HikvisionCamera::executeJsonCommand(const std::string& request_json, std::string& response_json) +{ + Json::Value root; + std::string parse_error; + if (!parseJsonCommand(request_json, root, parse_error) || !root.isObject()) { + response_json = makeJsonResult(false, "invalid json: " + parse_error); + return false; + } + + const std::string command_type = lowerString( + !jsonStringField(root, "command").empty() ? jsonStringField(root, "command") : + !jsonStringField(root, "type").empty() ? jsonStringField(root, "type") : + jsonStringField(root, "action")); + if (command_type != "ptz") { + response_json = makeJsonResult(false, "unsupported json command: " + command_type); + return false; + } + + const std::string ptz_command_name = + !jsonStringField(root, "direction").empty() ? jsonStringField(root, "direction") : + !jsonStringField(root, "ptz_command").empty() ? jsonStringField(root, "ptz_command") : + jsonStringField(root, "operation"); + + PtzCommand ptz_command{}; + if (!parsePtzCommandName(ptz_command_name, ptz_command)) { + response_json = makeJsonResult(false, "invalid ptz command: " + ptz_command_name); + return false; + } + + bool stop = false; + if (!jsonBoolField(root, "stop", stop)) { + const std::string ptz_action = lowerString(jsonStringField(root, "ptz_action")); + const std::string action = lowerString(jsonStringField(root, "action")); + stop = ptz_action == "stop" || action == "stop"; + } + + const int speed = jsonIntField(root, "speed", 0); + if (!controlPtz(ptz_command, stop, speed)) { + response_json = makeJsonResult(false, state_.error_message); + return false; + } + + std::ostringstream result; + result << "{\"success\":true" + << ",\"command\":\"ptz\"" + << ",\"direction\":\"" << jsonEscape(ptz_command_name) << "\"" + << ",\"stop\":" << (stop ? "true" : "false") + << ",\"speed\":" << static_cast(normalizePtzSpeed(speed)) + << "}"; + response_json = result.str(); + return true; +} + void HikvisionCamera::onEsData( unsigned int packet_type, unsigned char* buffer, diff --git a/cmvr-es/manager/device_manager/include/device_manager.h b/cmvr-es/manager/device_manager/include/device_manager.h index f80bc179..323a66b6 100644 --- a/cmvr-es/manager/device_manager/include/device_manager.h +++ b/cmvr-es/manager/device_manager/include/device_manager.h @@ -31,6 +31,7 @@ namespace cmvr::device { void getDeviceList(std::list> &device_list); void registerDevice(const std::shared_ptr& device); void registerDevice(const std::string& device_id, const std::shared_ptr& device); + std::shared_ptr getDeviceBase(const std::string& device_id); std::string version() const; std::string name() const; diff --git a/cmvr-es/manager/device_manager/src/device_manager.cpp b/cmvr-es/manager/device_manager/src/device_manager.cpp index ef889f6d..ffbac7fb 100644 --- a/cmvr-es/manager/device_manager/src/device_manager.cpp +++ b/cmvr-es/manager/device_manager/src/device_manager.cpp @@ -155,6 +155,16 @@ std::shared_ptr DeviceManager::getDevice(const std::string& device_i return ptr; } +std::shared_ptr DeviceManager::getDeviceBase(const std::string& device_id) +{ + auto it = devices_.find(device_id); + if (it == devices_.end()) { + CMVR_LOG(WARNING) << "[DeviceManager]: Device ID " << device_id << " not found."; + return nullptr; + } + return it->second.device; +} + void DeviceManager::getDeviceList(std::list>& device_list){ device_list.clear(); for (const auto& [device_id, record] : devices_) { diff --git a/cmvr-es/service/grpc/include/grpc_system_service.h b/cmvr-es/service/grpc/include/grpc_system_service.h index 17028a92..41fb2646 100644 --- a/cmvr-es/service/grpc/include/grpc_system_service.h +++ b/cmvr-es/service/grpc/include/grpc_system_service.h @@ -18,6 +18,7 @@ namespace cmvr::service 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 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_system_service.cpp b/cmvr-es/service/grpc/src/grpc_system_service.cpp index 44ee5286..8486fb2f 100644 --- a/cmvr-es/service/grpc/src/grpc_system_service.cpp +++ b/cmvr-es/service/grpc/src/grpc_system_service.cpp @@ -85,6 +85,37 @@ 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/protos/cmvr/api/common.proto b/protos/cmvr/api/common.proto index 53ecd55b..2ea6077c 100644 --- a/protos/cmvr/api/common.proto +++ b/protos/cmvr/api/common.proto @@ -57,3 +57,15 @@ message ConfigParam { bytes bytes_value = 6; // 二进制数据类型 } } + +message JsonDeviceCommand { + message Request { + CommandHeader.Request header = 1; + string request_json = 2; + } + + message Feedback { + CommandHeader.Feedback header = 1; + string response_json = 2; + } +} diff --git a/protos/cmvr/api/system_service.proto b/protos/cmvr/api/system_service.proto index c8aee52e..84d1c9bd 100644 --- a/protos/cmvr/api/system_service.proto +++ b/protos/cmvr/api/system_service.proto @@ -1,5 +1,6 @@ syntax = "proto3"; +import "cmvr/api/common.proto"; import "cmvr/api/system_command.proto"; package cmvr.api; @@ -10,6 +11,7 @@ service SystemService { rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {} rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {} + rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback) {} rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {} -} \ No newline at end of file +}