665 lines
24 KiB
C++
665 lines
24 KiB
C++
|
|
#include "DroneScrewZmqProtocol.h"
|
|||
|
|
#include "VrLog.h"
|
|||
|
|
#include "VrError.h"
|
|||
|
|
#include "IWDRemoteReceiver.h"
|
|||
|
|
#include "AuthManager.h"
|
|||
|
|
|
|||
|
|
// 复用 WDRemoteReceiver 头部协议(48 字节)
|
|||
|
|
#include "IWDRemoteReceiver.h"
|
|||
|
|
|
|||
|
|
#include <QJsonObject>
|
|||
|
|
#include <QJsonArray>
|
|||
|
|
#include <QJsonDocument>
|
|||
|
|
#include <QHostAddress>
|
|||
|
|
#include <QUdpSocket>
|
|||
|
|
|
|||
|
|
#include <algorithm>
|
|||
|
|
#include <cstring>
|
|||
|
|
#include <exception>
|
|||
|
|
#include <mutex>
|
|||
|
|
#include <new>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
namespace
|
|||
|
|
{
|
|||
|
|
void fillDetectionJson(const DroneScrewResult& result, QJsonObject& obj)
|
|||
|
|
{
|
|||
|
|
obj["frameId"] = static_cast<qint64>(result.frameId);
|
|||
|
|
obj["ts"] = static_cast<qint64>(result.timestampUs);
|
|||
|
|
obj["width"] = result.imageWidth;
|
|||
|
|
obj["height"] = result.imageHeight;
|
|||
|
|
obj["success"] = result.success;
|
|||
|
|
obj["errorCode"] = result.errorCode;
|
|||
|
|
obj["msg"] = QString::fromStdString(result.message);
|
|||
|
|
|
|||
|
|
QJsonArray boxes;
|
|||
|
|
for (const auto& b : result.boxes)
|
|||
|
|
{
|
|||
|
|
QJsonObject jb;
|
|||
|
|
jb["cls"] = b.classId;
|
|||
|
|
jb["score"] = b.score;
|
|||
|
|
jb["x"] = b.x;
|
|||
|
|
jb["y"] = b.y;
|
|||
|
|
jb["w"] = b.width;
|
|||
|
|
jb["h"] = b.height;
|
|||
|
|
boxes.append(jb);
|
|||
|
|
}
|
|||
|
|
obj["boxes"] = boxes;
|
|||
|
|
|
|||
|
|
QJsonArray distances;
|
|||
|
|
for (const auto& d : result.distances)
|
|||
|
|
{
|
|||
|
|
QJsonObject jd;
|
|||
|
|
jd["fromId"] = d.fromId;
|
|||
|
|
jd["toId"] = d.toId;
|
|||
|
|
jd["distanceMm"] = static_cast<double>(d.distanceMm);
|
|||
|
|
distances.append(jd);
|
|||
|
|
}
|
|||
|
|
obj["distances"] = distances;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void setErrorResp(QJsonObject& resp, int code, const QString& msg)
|
|||
|
|
{
|
|||
|
|
resp["ok"] = false;
|
|||
|
|
resp["code"] = code;
|
|||
|
|
resp["msg"] = msg;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void appendObject(QJsonObject& dst, const QJsonObject& src)
|
|||
|
|
{
|
|||
|
|
for (auto it = src.constBegin(); it != src.constEnd(); ++it)
|
|||
|
|
dst[it.key()] = it.value();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QString localMachineCode()
|
|||
|
|
{
|
|||
|
|
static QString code = QString::fromStdString(AuthManager::GetMachineCode());
|
|||
|
|
return code;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void fillServerInfoJson(const QString& cmd,
|
|||
|
|
const QString& rtspUrl,
|
|||
|
|
int controlPort,
|
|||
|
|
int resultPort,
|
|||
|
|
int rawImagePort,
|
|||
|
|
const QJsonObject& req,
|
|||
|
|
QJsonObject& resp)
|
|||
|
|
{
|
|||
|
|
resp["ok"] = true;
|
|||
|
|
resp["code"] = 0;
|
|||
|
|
resp["cmd"] = cmd;
|
|||
|
|
resp["machineCode"] = localMachineCode();
|
|||
|
|
resp["clientMachineCode"] = req["machineCode"].toString();
|
|||
|
|
resp["deviceName"] = "DroneScrewServer";
|
|||
|
|
resp["rtsp"] = rtspUrl;
|
|||
|
|
resp["controlPort"] = controlPort;
|
|||
|
|
resp["resultPort"] = resultPort;
|
|||
|
|
resp["rawImagePort"] = rawImagePort;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int fallbackHandlerError()
|
|||
|
|
{
|
|||
|
|
return ERR_CODE(DRONESCREW_ERR_ZMQ_HANDLER_NOT_SET);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DroneScrewZmqProtocol::DroneScrewZmqProtocol(QObject* parent)
|
|||
|
|
: QObject(parent)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DroneScrewZmqProtocol::~DroneScrewZmqProtocol()
|
|||
|
|
{
|
|||
|
|
stopServer();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewZmqProtocol::startServer(int controlPort, int resultPort, int rawImagePort)
|
|||
|
|
{
|
|||
|
|
if (m_bRunning.load()) return true;
|
|||
|
|
|
|||
|
|
m_controlPort = controlPort;
|
|||
|
|
m_resultPort = resultPort;
|
|||
|
|
m_rawImagePort = rawImagePort;
|
|||
|
|
|
|||
|
|
// 1) REP 控制
|
|||
|
|
if (!IVrZeroMQServer::CreateObject(&m_pZmqServer) || !m_pZmqServer)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqServer create fail\n");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
auto onEvent = [](MQEvent ev) {
|
|||
|
|
LOG_DEBUG("Zmq Event: %d\n", (int)ev);
|
|||
|
|
};
|
|||
|
|
auto onRecv = [this](const char* data, const size_t len) -> std::string {
|
|||
|
|
QByteArray req(data, static_cast<int>(len));
|
|||
|
|
QByteArray resp = this->handleControlMessage(req);
|
|||
|
|
return std::string(resp.constData(), static_cast<size_t>(resp.size()));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
int r = m_pZmqServer->Init(controlPort, onEvent, onRecv, false);
|
|||
|
|
if (r != 0)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqServer Init fail: %d\n", r);
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2) PUB 检测结果 (topic="result")
|
|||
|
|
if (!IVrZeroMQPublisher::CreateObject(&m_pZmqPublisher) || !m_pZmqPublisher)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqPublisher create fail\n");
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (m_pZmqPublisher->Init(resultPort) != 0)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqPublisher init fail\n");
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3) PUB 原始图像 (topic="raw"),可选
|
|||
|
|
if (rawImagePort > 0)
|
|||
|
|
{
|
|||
|
|
if (!IVrZeroMQPublisher::CreateObject(&m_pZmqRawPublisher) || !m_pZmqRawPublisher)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqRawPublisher create fail\n");
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (m_pZmqRawPublisher->Init(rawImagePort) != 0)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("ZmqRawPublisher init fail\n");
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!startDiscoveryServer())
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[UDP-DISCOVER] start fail port=%u\n",
|
|||
|
|
static_cast<unsigned>(WD_REMOTE_DISCOVERY_PORT));
|
|||
|
|
stopServer();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
m_bRunning = true;
|
|||
|
|
LOG_INFO("ZMQ control=%d, result=%d, rawImage=%d, discoverUdp=%u\n",
|
|||
|
|
controlPort, resultPort, rawImagePort,
|
|||
|
|
static_cast<unsigned>(WD_REMOTE_DISCOVERY_PORT));
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewZmqProtocol::stopServer()
|
|||
|
|
{
|
|||
|
|
stopDiscoveryServer();
|
|||
|
|
if (m_pZmqServer) { m_pZmqServer->UnInit(); delete m_pZmqServer; m_pZmqServer = nullptr; }
|
|||
|
|
if (m_pZmqPublisher) { m_pZmqPublisher->UnInit(); delete m_pZmqPublisher; m_pZmqPublisher = nullptr; }
|
|||
|
|
if (m_pZmqRawPublisher) { m_pZmqRawPublisher->UnInit(); delete m_pZmqRawPublisher; m_pZmqRawPublisher = nullptr; }
|
|||
|
|
m_bRunning = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool DroneScrewZmqProtocol::startDiscoveryServer()
|
|||
|
|
{
|
|||
|
|
if (m_pDiscoverySocket) return true;
|
|||
|
|
|
|||
|
|
m_pDiscoverySocket = new QUdpSocket(this);
|
|||
|
|
const bool ok = m_pDiscoverySocket->bind(
|
|||
|
|
QHostAddress::AnyIPv4,
|
|||
|
|
static_cast<quint16>(WD_REMOTE_DISCOVERY_PORT),
|
|||
|
|
QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
|
|||
|
|
if (!ok)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[UDP-DISCOVER] bind failed port=%u err=%s\n",
|
|||
|
|
static_cast<unsigned>(WD_REMOTE_DISCOVERY_PORT),
|
|||
|
|
m_pDiscoverySocket->errorString().toStdString().c_str());
|
|||
|
|
delete m_pDiscoverySocket;
|
|||
|
|
m_pDiscoverySocket = nullptr;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
connect(m_pDiscoverySocket, &QUdpSocket::readyRead,
|
|||
|
|
this, &DroneScrewZmqProtocol::handleDiscoveryDatagrams);
|
|||
|
|
LOG_INFO("[UDP-DISCOVER] listen port=%u\n",
|
|||
|
|
static_cast<unsigned>(WD_REMOTE_DISCOVERY_PORT));
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewZmqProtocol::stopDiscoveryServer()
|
|||
|
|
{
|
|||
|
|
if (!m_pDiscoverySocket) return;
|
|||
|
|
m_pDiscoverySocket->close();
|
|||
|
|
delete m_pDiscoverySocket;
|
|||
|
|
m_pDiscoverySocket = nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewZmqProtocol::handleDiscoveryDatagrams()
|
|||
|
|
{
|
|||
|
|
while (m_pDiscoverySocket && m_pDiscoverySocket->hasPendingDatagrams())
|
|||
|
|
{
|
|||
|
|
QByteArray req;
|
|||
|
|
req.resize(static_cast<int>(m_pDiscoverySocket->pendingDatagramSize()));
|
|||
|
|
|
|||
|
|
QHostAddress sender;
|
|||
|
|
quint16 senderPort = 0;
|
|||
|
|
const qint64 readBytes =
|
|||
|
|
m_pDiscoverySocket->readDatagram(req.data(), req.size(),
|
|||
|
|
&sender, &senderPort);
|
|||
|
|
if (readBytes <= 0)
|
|||
|
|
continue;
|
|||
|
|
req.resize(static_cast<int>(readBytes));
|
|||
|
|
|
|||
|
|
QJsonParseError err;
|
|||
|
|
const QJsonDocument doc = QJsonDocument::fromJson(req, &err);
|
|||
|
|
if (err.error != QJsonParseError::NoError || !doc.isObject())
|
|||
|
|
{
|
|||
|
|
LOG_DEBUG("[UDP-DISCOVER] ignore invalid json from=%s:%u err=%s\n",
|
|||
|
|
sender.toString().toStdString().c_str(),
|
|||
|
|
static_cast<unsigned>(senderPort),
|
|||
|
|
err.errorString().toStdString().c_str());
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const QJsonObject obj = doc.object();
|
|||
|
|
const QString cmd = obj["cmd"].toString();
|
|||
|
|
if (cmd != "discover")
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
QJsonObject resp;
|
|||
|
|
fillServerInfoJson("discover", m_rtspUrl, m_controlPort, m_resultPort,
|
|||
|
|
m_rawImagePort, obj, resp);
|
|||
|
|
if (m_serverInfoProvider)
|
|||
|
|
appendObject(resp, m_serverInfoProvider());
|
|||
|
|
|
|||
|
|
const QByteArray payload =
|
|||
|
|
QJsonDocument(resp).toJson(QJsonDocument::Compact);
|
|||
|
|
const qint64 sent =
|
|||
|
|
m_pDiscoverySocket->writeDatagram(payload, sender, senderPort);
|
|||
|
|
LOG_DEBUG("[UDP-DISCOVER] reply %s:%u bytes=%lld reqMachine=%s\n",
|
|||
|
|
sender.toString().toStdString().c_str(),
|
|||
|
|
static_cast<unsigned>(senderPort),
|
|||
|
|
static_cast<long long>(sent),
|
|||
|
|
obj["machineCode"].toString().toStdString().c_str());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QByteArray DroneScrewZmqProtocol::handleControlMessage(const QByteArray& reqData)
|
|||
|
|
{
|
|||
|
|
LOG_DEBUG("[ZMQ-REP] recv: %s\n", reqData.constData());
|
|||
|
|
|
|||
|
|
QJsonParseError err;
|
|||
|
|
QJsonDocument doc = QJsonDocument::fromJson(reqData, &err);
|
|||
|
|
if (err.error != QJsonParseError::NoError || !doc.isObject())
|
|||
|
|
{
|
|||
|
|
LOG_DEBUG("[ZMQ-REP] parse error: %s\n", err.errorString().toStdString().c_str());
|
|||
|
|
QJsonObject o;
|
|||
|
|
setErrorResp(o, ERR_CODE(DRONESCREW_ERR_INVALID_JSON), "invalid json");
|
|||
|
|
return QJsonDocument(o).toJson(QJsonDocument::Compact);
|
|||
|
|
}
|
|||
|
|
QJsonObject obj = doc.object();
|
|||
|
|
QString cmd = obj["cmd"].toString();
|
|||
|
|
LOG_DEBUG("[ZMQ-REP] cmd=%s\n", cmd.toStdString().c_str());
|
|||
|
|
|
|||
|
|
QJsonObject resp;
|
|||
|
|
resp["ok"] = true;
|
|||
|
|
resp["code"] = 0;
|
|||
|
|
resp["cmd"] = cmd;
|
|||
|
|
|
|||
|
|
if (cmd == "start")
|
|||
|
|
{
|
|||
|
|
// 解析检测模式(mono/binocular),设置后才启动检测
|
|||
|
|
const std::string detectMode = obj["detectMode"].toString("binocular").toStdString();
|
|||
|
|
emit detectModeRequested(detectMode);
|
|||
|
|
|
|||
|
|
const bool enableRaw = (m_rawImagePort > 0 &&
|
|||
|
|
m_pZmqRawPublisher != nullptr);
|
|||
|
|
emit rawImagePublishEnabledRequested(enableRaw);
|
|||
|
|
const int ret = m_startWorkHandler ? m_startWorkHandler()
|
|||
|
|
: fallbackHandlerError();
|
|||
|
|
resp["mode"] = "detection";
|
|||
|
|
resp["detectMode"] = QString::fromStdString(detectMode);
|
|||
|
|
resp["rawImagePublish"] = enableRaw;
|
|||
|
|
if (ret != 0)
|
|||
|
|
{
|
|||
|
|
emit rawImagePublishEnabledRequested(false);
|
|||
|
|
setErrorResp(resp, ret, "start detection failed");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (cmd == "set_detect_mode")
|
|||
|
|
{
|
|||
|
|
const QString mode = obj["detectMode"].toString(obj["mode"].toString());
|
|||
|
|
if (mode != "mono" && mode != "binocular")
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ERR_CODE(DEV_ARG_INVAILD), "invalid detectMode");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
emit detectModeRequested(mode.toStdString());
|
|||
|
|
resp["detectMode"] = mode;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (cmd == "stop")
|
|||
|
|
{
|
|||
|
|
emit rawImagePublishEnabledRequested(false);
|
|||
|
|
const int ret = m_stopWorkHandler ? m_stopWorkHandler()
|
|||
|
|
: fallbackHandlerError();
|
|||
|
|
if (ret != 0)
|
|||
|
|
setErrorResp(resp, ret, "stop detection failed");
|
|||
|
|
}
|
|||
|
|
else if (cmd == "start_stream")
|
|||
|
|
{
|
|||
|
|
emit rawImagePublishEnabledRequested(false);
|
|||
|
|
const int ret = m_startLiveStreamHandler ? m_startLiveStreamHandler()
|
|||
|
|
: fallbackHandlerError();
|
|||
|
|
resp["mode"] = "live_stream";
|
|||
|
|
resp["rawImagePublish"] = false;
|
|||
|
|
if (ret != 0)
|
|||
|
|
setErrorResp(resp, ret, "start live stream failed");
|
|||
|
|
}
|
|||
|
|
else if (cmd == "stop_stream")
|
|||
|
|
{
|
|||
|
|
const int ret = m_stopLiveStreamHandler ? m_stopLiveStreamHandler()
|
|||
|
|
: fallbackHandlerError();
|
|||
|
|
resp["mode"] = "idle";
|
|||
|
|
if (ret != 0)
|
|||
|
|
setErrorResp(resp, ret, "stop live stream failed");
|
|||
|
|
}
|
|||
|
|
else if (cmd == "swap_cameras" || cmd == "swap_left_right")
|
|||
|
|
{
|
|||
|
|
const int ret = m_swapCameraRolesHandler ? m_swapCameraRolesHandler()
|
|||
|
|
: fallbackHandlerError();
|
|||
|
|
if (ret != 0)
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ret, "swap camera roles failed");
|
|||
|
|
}
|
|||
|
|
else if (m_serverInfoProvider)
|
|||
|
|
{
|
|||
|
|
appendObject(resp, m_serverInfoProvider());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (cmd == "single")
|
|||
|
|
{
|
|||
|
|
if (!m_singleDetectionHandler)
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ERR_CODE(DRONESCREW_ERR_SINGLE_HANDLER_NOT_SET),
|
|||
|
|
"single handler not set");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
std::unique_lock<std::mutex> singleLock(m_singleMutex, std::try_to_lock);
|
|||
|
|
if (!singleLock.owns_lock())
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ERR_CODE(DRONESCREW_ERR_SINGLE_BUSY),
|
|||
|
|
"single detection busy");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
DroneScrewResult result = m_singleDetectionHandler();
|
|||
|
|
fillDetectionJson(result, resp);
|
|||
|
|
resp["ok"] = result.success;
|
|||
|
|
resp["code"] = result.success ? 0
|
|||
|
|
: (result.errorCode != 0
|
|||
|
|
? result.errorCode
|
|||
|
|
: ERR_CODE(DRONESCREW_ERR_RESPONSE_NOT_OK));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (cmd == "open")
|
|||
|
|
{
|
|||
|
|
const QString targetMachineCode = obj["targetMachineCode"].toString();
|
|||
|
|
const QString selfMachineCode = localMachineCode();
|
|||
|
|
if (!targetMachineCode.isEmpty() && targetMachineCode != selfMachineCode)
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ERR_CODE(DRONESCREW_ERR_MACHINE_CODE_MISMATCH),
|
|||
|
|
"machine code mismatch");
|
|||
|
|
resp["machineCode"] = selfMachineCode;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
fillServerInfoJson(cmd, m_rtspUrl, m_controlPort, m_resultPort,
|
|||
|
|
m_rawImagePort, obj, resp);
|
|||
|
|
if (m_serverInfoProvider)
|
|||
|
|
appendObject(resp, m_serverInfoProvider());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (cmd == "set_exposure")
|
|||
|
|
{
|
|||
|
|
emit setExposureRequested(obj["value"].toDouble());
|
|||
|
|
}
|
|||
|
|
else if (cmd == "set_gain")
|
|||
|
|
{
|
|||
|
|
emit setGainRequested(obj["value"].toDouble());
|
|||
|
|
}
|
|||
|
|
else if (cmd == "set_algo_params")
|
|||
|
|
{
|
|||
|
|
DroneScrewAlgoParams p;
|
|||
|
|
p.scoreThreshold = static_cast<float>(obj["score"].toDouble(0.5));
|
|||
|
|
p.nmsThreshold = static_cast<float>(obj["nms"].toDouble(0.45));
|
|||
|
|
p.inputWidth = obj["width"].toInt(4096);
|
|||
|
|
p.inputHeight = obj["height"].toInt(3000);
|
|||
|
|
const QString configPath = obj.contains("configPath")
|
|||
|
|
? obj["configPath"].toString()
|
|||
|
|
: (obj.contains("modelPath")
|
|||
|
|
? obj["modelPath"].toString()
|
|||
|
|
: obj["model"].toString());
|
|||
|
|
p.modelPath = configPath.toStdString();
|
|||
|
|
p.modelType = obj["modelType"].toInt(1);
|
|||
|
|
p.expectedBoltCount = obj.contains("expected")
|
|||
|
|
? obj["expected"].toInt(8)
|
|||
|
|
: obj["expectedBoltCount"].toInt(8);
|
|||
|
|
emit updateAlgoParamsRequested(p);
|
|||
|
|
}
|
|||
|
|
else if (cmd == "get_info")
|
|||
|
|
{
|
|||
|
|
fillServerInfoJson(cmd, m_rtspUrl, m_controlPort, m_resultPort,
|
|||
|
|
m_rawImagePort, obj, resp);
|
|||
|
|
if (m_serverInfoProvider)
|
|||
|
|
appendObject(resp, m_serverInfoProvider());
|
|||
|
|
if (m_calibrationInfoProvider)
|
|||
|
|
resp["calibration"] = m_calibrationInfoProvider();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
setErrorResp(resp, ERR_CODE(DRONESCREW_ERR_UNKNOWN_CMD), "unknown cmd");
|
|||
|
|
}
|
|||
|
|
QByteArray rspData = QJsonDocument(resp).toJson(QJsonDocument::Compact);
|
|||
|
|
const int previewSize = std::min(rspData.size(), 512);
|
|||
|
|
LOG_DEBUG("[ZMQ-REP] send resp bytes=%d preview=%.*s%s\n",
|
|||
|
|
rspData.size(), previewSize, rspData.constData(),
|
|||
|
|
rspData.size() > previewSize ? "..." : "");
|
|||
|
|
return rspData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewZmqProtocol::publishDetectionResult(const DroneScrewResult& result)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!m_pZmqPublisher) { LOG_DEBUG("[PUB-result] no publisher\n"); return; }
|
|||
|
|
|
|||
|
|
QJsonObject obj;
|
|||
|
|
fillDetectionJson(result, obj);
|
|||
|
|
|
|||
|
|
QByteArray payload = QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
|||
|
|
|
|||
|
|
std::lock_guard<std::mutex> lk(m_pubMutex);
|
|||
|
|
const int ret = m_pZmqPublisher->Publish("result", payload.constData(),
|
|||
|
|
static_cast<size_t>(payload.size()));
|
|||
|
|
static int pubCnt = 0;
|
|||
|
|
static int pubFailCnt = 0;
|
|||
|
|
if (ret != 0)
|
|||
|
|
{
|
|||
|
|
++pubFailCnt;
|
|||
|
|
LOG_ERROR("[PUB-result] publish failed ret=%d fail=%d frame=%llu payload=%d\n",
|
|||
|
|
ret, pubFailCnt, result.frameId, payload.size());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (++pubCnt % 100 == 0)
|
|||
|
|
LOG_DEBUG("[PUB-result] #%d frame=%llu boxes=%zu\n", pubCnt, result.frameId, result.boxes.size());
|
|||
|
|
}
|
|||
|
|
catch (const std::exception& e)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[PUB-result] exception frame=%llu boxes=%zu: %s\n",
|
|||
|
|
result.frameId, result.boxes.size(), e.what());
|
|||
|
|
}
|
|||
|
|
catch (...)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[PUB-result] unknown exception frame=%llu boxes=%zu\n",
|
|||
|
|
result.frameId, result.boxes.size());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MvsImageData.pixelFormat (Hik 原值,enMvGvspPixelType 透传) → WDRemotePixelFormat
|
|||
|
|
// 常见映射,未知归 UNKNOWN
|
|||
|
|
static uint32_t mvsPixelFormatToWDRemote(int mvsPixelFormat)
|
|||
|
|
{
|
|||
|
|
switch (mvsPixelFormat)
|
|||
|
|
{
|
|||
|
|
case 0x01080001: return static_cast<uint32_t>(WDRemotePixelFormat::MONO8); // PixelType_Gvsp_Mono8
|
|||
|
|
case 0x02180014: return static_cast<uint32_t>(WDRemotePixelFormat::BGR8); // PixelType_Gvsp_BGR8_Packed
|
|||
|
|
case 0x02180015: return static_cast<uint32_t>(WDRemotePixelFormat::RGB8); // PixelType_Gvsp_RGB8_Packed
|
|||
|
|
case 0x01080009: return static_cast<uint32_t>(WDRemotePixelFormat::BAYERRG8); // PixelType_Gvsp_BayerRG8
|
|||
|
|
default: return static_cast<uint32_t>(WDRemotePixelFormat::UNKNOWN);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void DroneScrewZmqProtocol::publishRawImage(const MvsImageData& left, const MvsImageData& right)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!m_pZmqRawPublisher) return;
|
|||
|
|
// 左目必须有效;右目允许缺失(缺失时右目字段保持 0,接收端按空右目处理)
|
|||
|
|
if (!left.pData || left.dataSize == 0) return;
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] enter frame=%llu left=%ux%u right=%ux%u\n",
|
|||
|
|
left.frameID, left.width, left.height, right.width, right.height);
|
|||
|
|
|
|||
|
|
WDRemoteBinocularRawImageHeader hdr{};
|
|||
|
|
hdr.magic = WD_REMOTE_BINOCULAR_RAW_IMAGE_MAGIC;
|
|||
|
|
hdr.version = WD_REMOTE_BINOCULAR_RAW_IMAGE_VERSION;
|
|||
|
|
hdr.frameId = left.frameID;
|
|||
|
|
hdr.timestampUs = static_cast<int64_t>(left.timestamp);
|
|||
|
|
|
|||
|
|
// 左目(多数 GVSP 单字节像素 stride==width)
|
|||
|
|
hdr.leftWidth = static_cast<uint32_t>(left.width);
|
|||
|
|
hdr.leftHeight = static_cast<uint32_t>(left.height);
|
|||
|
|
hdr.leftStride = static_cast<uint32_t>(left.width);
|
|||
|
|
hdr.leftPixelFormat = mvsPixelFormatToWDRemote(left.pixelFormat);
|
|||
|
|
|
|||
|
|
// 右目(无效时保持 0)
|
|||
|
|
const bool hasRight = (right.pData != nullptr && right.dataSize > 0);
|
|||
|
|
if (hasRight)
|
|||
|
|
{
|
|||
|
|
hdr.rightWidth = static_cast<uint32_t>(right.width);
|
|||
|
|
hdr.rightHeight = static_cast<uint32_t>(right.height);
|
|||
|
|
hdr.rightStride = static_cast<uint32_t>(right.width);
|
|||
|
|
hdr.rightPixelFormat = mvsPixelFormatToWDRemote(right.pixelFormat);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Precision raw image publishing must be lossless; mono frames use zlib/raw too.
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] before zlib encode frame=%llu\n", left.frameID);
|
|||
|
|
|
|||
|
|
// 真·无损:Qt qCompress(zlib/deflate 封装,bit-exact,跨平台、无需额外依赖)。
|
|||
|
|
// level=1 快速档:不在意压缩比,优先低 CPU 占用。下游算法可直接用还原字节。
|
|||
|
|
QByteArray leftZ = qCompress(reinterpret_cast<const uchar*>(left.pData),
|
|||
|
|
static_cast<int>(left.dataSize), 1);
|
|||
|
|
QByteArray rightZ;
|
|||
|
|
bool useZlib = !leftZ.isEmpty();
|
|||
|
|
if (useZlib && hasRight)
|
|||
|
|
{
|
|||
|
|
rightZ = qCompress(reinterpret_cast<const uchar*>(right.pData),
|
|||
|
|
static_cast<int>(right.dataSize), 1);
|
|||
|
|
useZlib = !rightZ.isEmpty();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] after zlib encode frame=%llu useZlib=%d leftSize=%d rightSize=%d\n",
|
|||
|
|
left.frameID, useZlib ? 1 : 0, leftZ.size(), rightZ.size());
|
|||
|
|
|
|||
|
|
if (useZlib)
|
|||
|
|
{
|
|||
|
|
hdr.reserved[0] = WD_REMOTE_IMAGE_COMPRESSION_ZLIB;
|
|||
|
|
hdr.reserved[1] = static_cast<uint32_t>(left.dataSize); // 解压后大小(左)
|
|||
|
|
hdr.reserved[2] = hasRight ? static_cast<uint32_t>(right.dataSize) : 0u; // 解压后大小(右)
|
|||
|
|
hdr.leftDataSize = static_cast<uint32_t>(leftZ.size());
|
|||
|
|
hdr.rightDataSize = hasRight ? static_cast<uint32_t>(rightZ.size()) : 0u;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
hdr.reserved[0] = WD_REMOTE_IMAGE_COMPRESSION_RAW;
|
|||
|
|
hdr.leftDataSize = static_cast<uint32_t>(left.dataSize);
|
|||
|
|
hdr.rightDataSize = hasRight ? static_cast<uint32_t>(right.dataSize) : 0u;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
QByteArray payload;
|
|||
|
|
payload.reserve(static_cast<int>(sizeof(hdr) + hdr.leftDataSize + hdr.rightDataSize));
|
|||
|
|
payload.append(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
|
|||
|
|
if (useZlib)
|
|||
|
|
{
|
|||
|
|
payload.append(leftZ);
|
|||
|
|
if (hasRight)
|
|||
|
|
payload.append(rightZ);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
payload.append(reinterpret_cast<const char*>(left.pData), static_cast<int>(left.dataSize));
|
|||
|
|
if (hasRight)
|
|||
|
|
payload.append(reinterpret_cast<const char*>(right.pData), static_cast<int>(right.dataSize));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] before ZMQ publish frame=%llu payloadSize=%d\n",
|
|||
|
|
left.frameID, payload.size());
|
|||
|
|
|
|||
|
|
std::lock_guard<std::mutex> lk(m_rawMutex);
|
|||
|
|
const int ret = m_pZmqRawPublisher->Publish("raw", payload.constData(),
|
|||
|
|
static_cast<size_t>(payload.size()));
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] after ZMQ publish frame=%llu ret=%d\n", left.frameID, ret);
|
|||
|
|
|
|||
|
|
static int rawPubCnt = 0;
|
|||
|
|
static int rawFailCnt = 0;
|
|||
|
|
if (ret != 0)
|
|||
|
|
{
|
|||
|
|
++rawFailCnt;
|
|||
|
|
LOG_ERROR("[PUB-raw] publish failed ret=%d fail=%d frame=%llu compression=%s payload=%d\n",
|
|||
|
|
ret, rawFailCnt, left.frameID, useZlib ? "zlib" : "raw", payload.size());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
++rawPubCnt;
|
|||
|
|
if (rawPubCnt == 1)
|
|||
|
|
LOG_INFO("[PUB-raw] first frame=%llu compression=%s left=%ux%u fmt=%u size=%u raw=%u right=%ux%u fmt=%u size=%u raw=%u payload=%d\n",
|
|||
|
|
left.frameID,
|
|||
|
|
useZlib ? "zlib" : "raw",
|
|||
|
|
hdr.leftWidth, hdr.leftHeight, hdr.leftPixelFormat, hdr.leftDataSize,
|
|||
|
|
useZlib ? hdr.reserved[1] : hdr.leftDataSize,
|
|||
|
|
hdr.rightWidth, hdr.rightHeight, hdr.rightPixelFormat, hdr.rightDataSize,
|
|||
|
|
useZlib ? hdr.reserved[2] : hdr.rightDataSize,
|
|||
|
|
payload.size());
|
|||
|
|
else if (rawPubCnt % 100 == 0)
|
|||
|
|
LOG_DEBUG("[PUB-raw] #%d frame=%llu compression=%s left=%ux%u right=%ux%u payload=%d\n",
|
|||
|
|
rawPubCnt, left.frameID, useZlib ? "zlib" : "raw", left.width, left.height,
|
|||
|
|
hasRight ? right.width : 0, hasRight ? right.height : 0,
|
|||
|
|
payload.size());
|
|||
|
|
|
|||
|
|
LOG_DEBUG("[PUB-raw] exit frame=%llu\n", left.frameID);
|
|||
|
|
}
|
|||
|
|
catch (const std::bad_alloc& e)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[PUB-raw] memory allocation failed frame=%llu left=%ux%u leftSize=%u right=%ux%u rightSize=%u: %s\n",
|
|||
|
|
left.frameID, left.width, left.height, left.dataSize,
|
|||
|
|
right.width, right.height, right.dataSize, e.what());
|
|||
|
|
}
|
|||
|
|
catch (const std::exception& e)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[PUB-raw] exception frame=%llu left=%ux%u leftSize=%u right=%ux%u rightSize=%u: %s\n",
|
|||
|
|
left.frameID, left.width, left.height, left.dataSize,
|
|||
|
|
right.width, right.height, right.dataSize, e.what());
|
|||
|
|
}
|
|||
|
|
catch (...)
|
|||
|
|
{
|
|||
|
|
LOG_ERROR("[PUB-raw] unknown exception frame=%llu left=%ux%u leftSize=%u right=%ux%u rightSize=%u\n",
|
|||
|
|
left.frameID, left.width, left.height, left.dataSize,
|
|||
|
|
right.width, right.height, right.dataSize);
|
|||
|
|
}
|
|||
|
|
}
|