add executeJsonCommand

This commit is contained in:
linbo 2026-07-23 09:10:39 +08:00
parent a0fbeccc19
commit b585cb6601
10 changed files with 279 additions and 5 deletions

View File

@ -34,6 +34,10 @@ namespace cmvr::device {
virtual bool start() { return true; } virtual bool start() { return true; }
virtual bool stop() { return true; } virtual bool stop() { return true; }
virtual bool update() { 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: protected:
std::string id_; // 设备名称 std::string id_; // 设备名称

View File

@ -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_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_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_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 target_link_libraries(hikvision_camera
PUBLIC PUBLIC
@ -14,13 +57,17 @@ target_link_libraries(hikvision_camera
opencv_core opencv_core
cmvr_es::proto cmvr_es::proto
PRIVATE PRIVATE
-lhcnetsdk "${HIKVISION_HCNETSDK_LIB}"
${HIKVISION_JSONCPP_LINK}
pthread pthread
) )
set_target_properties(hikvision_camera PROPERTIES 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" INSTALL_RPATH "$ORIGIN;$ORIGIN/HCNetSDKCom"
) )
add_library(cmvr_es::device::hikvision_camera ALIAS hikvision_camera) add_library(cmvr_es::device::hikvision_camera ALIAS hikvision_camera)
install(TARGETS hikvision_camera LIBRARY DESTINATION lib) 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)

View File

@ -35,6 +35,7 @@ public:
bool startStreaming() override; bool startStreaming() override;
void stopStreaming() override; void stopStreaming() override;
bool controlPtz(PtzCommand command, bool stop, int speed) 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, void onEsData(unsigned int packet_type,
unsigned char* buffer, unsigned char* buffer,

View File

@ -1,11 +1,14 @@
#include "../include/hikvision_camera.h" #include "../include/hikvision_camera.h"
#include <atomic> #include <atomic>
#include <algorithm>
#include <cctype>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <filesystem> #include <filesystem>
#include <limits.h> #include <limits.h>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -15,6 +18,7 @@
#include "HCNetSDK.h" #include "HCNetSDK.h"
#include "common/base/logging/logger.h" #include "common/base/logging/logger.h"
#include "json/json.h"
namespace { namespace {
@ -104,6 +108,114 @@ DWORD normalizePtzSpeed(int speed)
return static_cast<DWORD>(speed); return static_cast<DWORD>(speed);
} }
std::string lowerString(std::string value)
{
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
return static_cast<char>(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<Json::CharReader> 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( void CALLBACK hikvisionEsRealPlayCallback(
LONG, NET_DVR_PACKET_INFO_EX* packet_info, void* user) 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; 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<int>(normalizePtzSpeed(speed))
<< "}";
response_json = result.str();
return true;
}
void HikvisionCamera::onEsData( void HikvisionCamera::onEsData(
unsigned int packet_type, unsigned int packet_type,
unsigned char* buffer, unsigned char* buffer,

View File

@ -31,6 +31,7 @@ namespace cmvr::device {
void getDeviceList(std::list<std::pair<std::string, std::string>> &device_list); void getDeviceList(std::list<std::pair<std::string, std::string>> &device_list);
void registerDevice(const std::shared_ptr<AbstractDevice>& device); void registerDevice(const std::shared_ptr<AbstractDevice>& device);
void registerDevice(const std::string& device_id, const std::shared_ptr<AbstractDevice>& device); void registerDevice(const std::string& device_id, const std::shared_ptr<AbstractDevice>& device);
std::shared_ptr<AbstractDevice> getDeviceBase(const std::string& device_id);
std::string version() const; std::string version() const;
std::string name() const; std::string name() const;

View File

@ -155,6 +155,16 @@ std::shared_ptr<DeviceType> DeviceManager::getDevice(const std::string& device_i
return ptr; return ptr;
} }
std::shared_ptr<AbstractDevice> 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<std::pair<std::string, std::string>>& device_list){ void DeviceManager::getDeviceList(std::list<std::pair<std::string, std::string>>& device_list){
device_list.clear(); device_list.clear();
for (const auto& [device_id, record] : devices_) { for (const auto& [device_id, record] : devices_) {

View File

@ -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 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 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 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; grpc::Status StopAll(grpc::ServerContext* context, const cmvr::api::StopAllCommand_Request* request, cmvr::api::StopAllCommand_Feedback* response) override;
private: private:
device::DeviceManager& dmgr_; device::DeviceManager& dmgr_;

View File

@ -85,6 +85,37 @@ grpc::Status gRPCSystemServiceImpl::UpdateParams(grpc::ServerContext* context, c
return grpc::Status::OK; 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, grpc::Status gRPCSystemServiceImpl::StopAll(grpc::ServerContext* context,
const cmvr::api::StopAllCommand_Request* request, cmvr::api::StopAllCommand_Feedback* response) const cmvr::api::StopAllCommand_Request* request, cmvr::api::StopAllCommand_Feedback* response)
{ {

View File

@ -57,3 +57,15 @@ message ConfigParam {
bytes bytes_value = 6; // 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;
}
}

View File

@ -1,5 +1,6 @@
syntax = "proto3"; syntax = "proto3";
import "cmvr/api/common.proto";
import "cmvr/api/system_command.proto"; import "cmvr/api/system_command.proto";
package cmvr.api; package cmvr.api;
@ -10,6 +11,7 @@ service SystemService {
rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {} rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {}
rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {} rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {}
rpc ExecuteJsonCommand(JsonDeviceCommand.Request) returns (JsonDeviceCommand.Feedback) {}
rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {} rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {}
} }