2025-08-27 23:10:36 +08:00
|
|
|
|
#include "BeltTearingPresenter.h"
|
2025-09-10 00:31:27 +08:00
|
|
|
|
#include "PathManager.h"
|
|
|
|
|
|
#include "version.h"
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include "PointCloudImageUtils.h"
|
|
|
|
|
|
#include "ImageProcessingWorker.h"
|
2025-08-27 23:10:36 +08:00
|
|
|
|
#include <QUuid>
|
|
|
|
|
|
#include <QDataStream>
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QJsonArray>
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include <deque>
|
2025-08-27 23:10:36 +08:00
|
|
|
|
#include <QDateTime>
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include <QBuffer>
|
|
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
|
#include <QFile>
|
|
|
|
|
|
#include <QDir>
|
2025-08-27 23:10:36 +08:00
|
|
|
|
#include <iostream>
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
#include "VrLog.h"
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include "VrTimeUtils.h"
|
|
|
|
|
|
|
|
|
|
|
|
// 静态实例指针
|
|
|
|
|
|
BeltTearingPresenter* BeltTearingPresenter::s_instance = nullptr;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
|
|
|
|
|
|
BeltTearingPresenter::BeltTearingPresenter(QObject *parent)
|
|
|
|
|
|
: QObject(parent)
|
2025-09-21 22:20:24 +08:00
|
|
|
|
, m_tcpServer(nullptr)
|
2025-09-10 00:31:27 +08:00
|
|
|
|
, m_cameraInitTimer(new QTimer(this))
|
2025-08-27 23:10:36 +08:00
|
|
|
|
, m_port(0)
|
2025-09-10 00:31:27 +08:00
|
|
|
|
, m_config(nullptr)
|
|
|
|
|
|
, m_eyeDevice(nullptr)
|
|
|
|
|
|
, m_cameraInitialized(false)
|
|
|
|
|
|
, m_cameraDetecting(false)
|
2025-09-21 22:20:24 +08:00
|
|
|
|
, m_lineCounter(0)
|
|
|
|
|
|
, m_imageWorker(new ImageProcessingWorker(this))
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 设置静态实例
|
|
|
|
|
|
s_instance = this;
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 打印版本信息
|
|
|
|
|
|
LOG_INFO("Initializing %s %s\n", getProductName().toStdString().c_str(), getVersionString().toStdString().c_str());
|
|
|
|
|
|
LOG_INFO("Build: %s\n", getBuildInfo().toStdString().c_str());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 连接定时器信号
|
2025-09-10 00:31:27 +08:00
|
|
|
|
connect(m_cameraInitTimer, &QTimer::timeout, this, &BeltTearingPresenter::onCameraInitTimer);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 连接工作线程信号(只连接图像生成信号)
|
|
|
|
|
|
connect(m_imageWorker, &ImageProcessingWorker::imageGenerated, this, &BeltTearingPresenter::onImageGenerated);
|
|
|
|
|
|
|
|
|
|
|
|
// 工作线程在构造函数中自动启动,无需手动调用start()
|
|
|
|
|
|
|
|
|
|
|
|
// 创建TCP服务器实例
|
|
|
|
|
|
if (!VrCreatYTCPServer(&m_tcpServer)) {
|
|
|
|
|
|
LOG_ERROR("Failed to create TCP server\n");
|
|
|
|
|
|
m_tcpServer = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 创建配置实例
|
|
|
|
|
|
IVrBeltTearingConfig::CreateInstance(&m_config);
|
|
|
|
|
|
if (m_config) {
|
|
|
|
|
|
m_config->SetConfigChangeNotify(this);
|
|
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化SDK算法参数 - 使用配置系统的默认值
|
|
|
|
|
|
m_algorithmParam = configToSDKParam(BeltTearingConfigResult());
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 初始化相机
|
|
|
|
|
|
initializeCamera();
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BeltTearingPresenter::~BeltTearingPresenter()
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 清除静态实例
|
|
|
|
|
|
s_instance = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
// 停止工作线程
|
|
|
|
|
|
if (m_imageWorker) {
|
|
|
|
|
|
m_imageWorker->stop();
|
|
|
|
|
|
// 析构函数会自动等待线程结束,无需手动调用wait()
|
|
|
|
|
|
delete m_imageWorker;
|
|
|
|
|
|
m_imageWorker = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 23:10:36 +08:00
|
|
|
|
stopServer();
|
2025-09-10 00:31:27 +08:00
|
|
|
|
stopCamera();
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 清理激光线队列中的内存
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_queueMutex);
|
|
|
|
|
|
for (auto& line : m_laserLineQueue) {
|
|
|
|
|
|
if (line.p3DPoint) {
|
|
|
|
|
|
free(line.p3DPoint);
|
|
|
|
|
|
line.p3DPoint = nullptr;
|
|
|
|
|
|
}
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_laserLineQueue.clear();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_tcpServer) {
|
|
|
|
|
|
delete m_tcpServer;
|
|
|
|
|
|
m_tcpServer = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
if (m_config) {
|
|
|
|
|
|
delete m_config;
|
|
|
|
|
|
m_config = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::loadConfiguration(const QString& configFilePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_config) {
|
|
|
|
|
|
LOG_WARNING("Config instance not created\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_configFilePath = configFilePath;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 加载配置文件
|
|
|
|
|
|
m_configResult = m_config->LoadConfig(configFilePath.toStdString());
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Configuration loaded successfully:\n");
|
|
|
|
|
|
LOG_INFO(" Server Port: %d\n", m_configResult.serverPort);
|
|
|
|
|
|
LOG_INFO(" Project Type: %d\n", static_cast<int>(m_configResult.projectType));
|
|
|
|
|
|
LOG_INFO(" Servers count: %d\n", m_configResult.servers.size());
|
|
|
|
|
|
|
|
|
|
|
|
// 应用算法参数
|
|
|
|
|
|
applyAlgorithmParameters(m_configResult.algorithmParams);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Error loading configuration: %s\n", e.what());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::applyAlgorithmParameters(const BeltTearingAlgorithmParams& params)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 使用配置结构转换为SDK参数
|
|
|
|
|
|
BeltTearingConfigResult tempConfig;
|
|
|
|
|
|
tempConfig.algorithmParams = params;
|
|
|
|
|
|
m_algorithmParam = configToSDKParam(tempConfig);
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 应用算法参数
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_DEBUG("Applying SDK algorithm parameters...\n");
|
|
|
|
|
|
LOG_DEBUG(" Scan X Scale: %f\n", m_algorithmParam.scanXScale);
|
|
|
|
|
|
LOG_DEBUG(" Scan Y Scale: %f\n", m_algorithmParam.scanYScale);
|
|
|
|
|
|
LOG_DEBUG(" Difference Bin Threshold: %f\n", m_algorithmParam.differnceBinTh);
|
|
|
|
|
|
LOG_DEBUG(" Min Tear Length: %f\n", m_algorithmParam.tearingMinLen);
|
|
|
|
|
|
LOG_DEBUG(" Min Tear Gap: %f\n", m_algorithmParam.tearingMinGap);
|
|
|
|
|
|
LOG_DEBUG(" Same Gap Threshold: %f\n", m_algorithmParam.extractPara.sameGapTh);
|
|
|
|
|
|
LOG_DEBUG(" Gap Check Window: %d\n", m_algorithmParam.extractPara.gapChkWin);
|
|
|
|
|
|
|
|
|
|
|
|
// 监控参数
|
2025-09-10 00:31:27 +08:00
|
|
|
|
LOG_DEBUG(" Check Interval: %d ms\n", params.monitoringParam.checkInterval);
|
|
|
|
|
|
LOG_DEBUG(" Alert Threshold: %f\n", params.monitoringParam.alertThreshold);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 调试参数
|
|
|
|
|
|
if (m_configResult.debugParam.enableDebug) {
|
|
|
|
|
|
LOG_DEBUG("Debug mode enabled:\n");
|
|
|
|
|
|
LOG_DEBUG(" Save debug images: %s\n", (m_configResult.debugParam.saveDebugImage ? "Yes" : "No"));
|
|
|
|
|
|
LOG_DEBUG(" Print detail log: %s\n", (m_configResult.debugParam.printDetailLog ? "Yes" : "No"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::OnConfigChanged(const BeltTearingConfigResult& configResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("Configuration changed notification received\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 更新配置结果
|
|
|
|
|
|
m_configResult = configResult;
|
|
|
|
|
|
|
|
|
|
|
|
// 重新应用算法参数
|
|
|
|
|
|
applyAlgorithmParameters(configResult.algorithmParams);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果服务器端口改变,可能需要重启服务器
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_INFO("Server port changed, restarting server...\n");
|
|
|
|
|
|
stopServer();
|
|
|
|
|
|
startServer(configResult.serverPort);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::sendTestData(std::string fileName){
|
|
|
|
|
|
if (!m_tcpServer) {
|
|
|
|
|
|
LOG_WARNING("TCP server not initialized, cannot send test data\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断文件类型
|
|
|
|
|
|
std::ifstream inputFile(fileName);
|
|
|
|
|
|
if (!inputFile.is_open()) {
|
|
|
|
|
|
LOG_WARN("UN open file \n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_DEBUG("------------------------ \n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
std::vector<SVzNL3DPosition> sVzNLPostion;
|
|
|
|
|
|
sVzNLPostion.clear();
|
|
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
int nIndex = 0;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
SVzLaserLineData pLaserLine;
|
2025-09-24 22:36:13 +08:00
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
while (std::getline(inputFile, line)) {
|
|
|
|
|
|
if (line.find("Line_") == 0) {
|
|
|
|
|
|
|
|
|
|
|
|
if(!sVzNLPostion.empty()){
|
|
|
|
|
|
pLaserLine.p3DPoint = sVzNLPostion.data();
|
|
|
|
|
|
pLaserLine.nPointCount = sVzNLPostion.size();
|
|
|
|
|
|
processPointCloudData(&pLaserLine);
|
|
|
|
|
|
}
|
2025-09-24 22:36:13 +08:00
|
|
|
|
sscanf(line.c_str(), "Line_%lld_%lld", &pLaserLine.llFrameIdx, &pLaserLine.llTimeStamp);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
sVzNLPostion.clear();
|
|
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
nIndex++;
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
} else if (line.find("{") == 0) {
|
|
|
|
|
|
float lx, ly, rx, ry;
|
|
|
|
|
|
SVzNL3DPosition pos;
|
|
|
|
|
|
sscanf(line.c_str(), "{ %lf, %lf, %lf }-{ %f, %f}-{ %f, %f }",
|
|
|
|
|
|
&pos.pt3D.x, &pos.pt3D.y, &pos.pt3D.z,
|
|
|
|
|
|
&lx, &ly, &rx, &ry);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sVzNLPostion.push_back(pos);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
// if(nIndex > 500){
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
inputFile.close();
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::initializeCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_eyeDevice) {
|
|
|
|
|
|
return true; // 已经初始化过
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int result = IVrEyeDevice::CreateObject(&m_eyeDevice);
|
|
|
|
|
|
if (result != 0 || !m_eyeDevice) {
|
|
|
|
|
|
LOG_ERROR("Failed to create VrEyeDevice object, error code: %d\n", result);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result = m_eyeDevice->InitDevice();
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
|
LOG_ERROR("Failed to initialize VrEyeDevice, error code: %d\n", result);
|
|
|
|
|
|
delete m_eyeDevice;
|
|
|
|
|
|
m_eyeDevice = nullptr;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置状态回调
|
|
|
|
|
|
result = m_eyeDevice->SetStatusCallback(OnStatusCallback, this);
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
|
LOG_WARNING("Failed to set status callback, error code: %d\n", result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("VrEyeDevice initialized successfully\n");
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::startCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_eyeDevice) {
|
|
|
|
|
|
if (!initializeCamera()) {
|
|
|
|
|
|
// 启动定时器持续重试初始化
|
|
|
|
|
|
LOG_WARNING("Camera initialization failed, starting retry timer...\n");
|
|
|
|
|
|
m_cameraInitTimer->start(5000); // 每5秒重试一次
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试打开设备
|
2025-09-21 22:20:24 +08:00
|
|
|
|
int result = m_eyeDevice->OpenDevice(nullptr, false, false, true);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
if (result != 0) {
|
|
|
|
|
|
LOG_WARNING("Failed to open camera device, error code: %d, retrying...\n", result);
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定时器持续重试连接
|
|
|
|
|
|
m_cameraInitTimer->start(3000); // 每3秒重试一次
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Camera started successfully \n");
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 停止重试定时器
|
|
|
|
|
|
m_cameraInitTimer->stop();
|
|
|
|
|
|
m_cameraInitialized = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 开始检测
|
|
|
|
|
|
result = m_eyeDevice->StartDetect(OnPointCloudCallback, keResultDataType_Position, this);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_DEBUG("Camera detection started, result: %d\n", result);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
if (result != 0) {
|
|
|
|
|
|
LOG_ERROR("Failed to start detection, error code: %d\n", result);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_cameraDetecting = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::stopCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_cameraInitTimer->stop();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_eyeDevice) {
|
|
|
|
|
|
if (m_cameraDetecting) {
|
|
|
|
|
|
m_eyeDevice->StopDetect();
|
|
|
|
|
|
m_cameraDetecting = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_cameraInitialized) {
|
|
|
|
|
|
m_eyeDevice->CloseDevice();
|
|
|
|
|
|
m_cameraInitialized = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delete m_eyeDevice;
|
|
|
|
|
|
m_eyeDevice = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Camera stopped\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::onCameraInitTimer()
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_DEBUG("Retrying camera initialization...\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试重新初始化和连接相机
|
|
|
|
|
|
if (!m_eyeDevice) {
|
|
|
|
|
|
if (!initializeCamera()) {
|
|
|
|
|
|
return; // 继续重试
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试连接相机
|
2025-09-21 22:20:24 +08:00
|
|
|
|
int result = m_eyeDevice->OpenDevice(nullptr, false, false, true);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
if (result == 0) {
|
|
|
|
|
|
// 连接成功,开始检测
|
|
|
|
|
|
m_cameraInitTimer->stop();
|
|
|
|
|
|
m_cameraInitialized = true;
|
|
|
|
|
|
|
|
|
|
|
|
result = m_eyeDevice->StartDetect(OnPointCloudCallback, keResultDataType_Position, this);
|
|
|
|
|
|
if (result == 0) {
|
|
|
|
|
|
m_cameraDetecting = true;
|
|
|
|
|
|
LOG_INFO("Camera connection restored successfully!\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_ERROR("Failed to start detection after reconnection, error code: %d\n", result);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_WARNING("Still unable to connect to camera, continuing retry...\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::OnStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam)
|
|
|
|
|
|
{
|
|
|
|
|
|
BeltTearingPresenter* presenter = static_cast<BeltTearingPresenter*>(pInfoParam);
|
|
|
|
|
|
if (!presenter) return;
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Camera status changed: %d\n", static_cast<int>(eStatus));
|
|
|
|
|
|
|
|
|
|
|
|
// 处理相机状态变化
|
|
|
|
|
|
switch (eStatus) {
|
|
|
|
|
|
case keDeviceWorkStatus_Eye_Comming:
|
|
|
|
|
|
LOG_INFO("Camera connected\n");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case keDeviceWorkStatus_Offline:
|
|
|
|
|
|
LOG_WARNING("Camera disconnected, attempting to reconnect...\n");
|
|
|
|
|
|
presenter->m_cameraInitialized = false;
|
|
|
|
|
|
presenter->m_cameraDetecting = false;
|
|
|
|
|
|
// 开始重连尝试
|
|
|
|
|
|
if (!presenter->m_cameraInitTimer->isActive()) {
|
|
|
|
|
|
presenter->m_cameraInitTimer->start(3000);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::OnPointCloudCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pParam)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(keResultDataType_Position != eDataType) return;
|
|
|
|
|
|
|
|
|
|
|
|
BeltTearingPresenter* presenter = static_cast<BeltTearingPresenter*>(pParam);
|
|
|
|
|
|
if (!presenter || !pLaserLinePoint) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 处理点云数据
|
|
|
|
|
|
presenter->processPointCloudData(pLaserLinePoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::processPointCloudData(const SVzLaserLineData* pLaserLine)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (!pLaserLine) return;
|
|
|
|
|
|
if(pLaserLine->nPointCount <= 0) return;
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 将激光线数据添加到队列(用于图像生成)
|
|
|
|
|
|
addLaserLineToQueue(pLaserLine);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 调用SDK算法
|
|
|
|
|
|
int errorCode = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_bInitAlgo){
|
|
|
|
|
|
m_hLineWorkers.resize(pLaserLine->nPointCount);
|
|
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
m_beltTearings_new.clear();
|
|
|
|
|
|
m_beltTearings_new.shrink_to_fit();
|
|
|
|
|
|
m_beltTearings_growing.clear();
|
|
|
|
|
|
m_beltTearings_growing.shrink_to_fit();
|
|
|
|
|
|
m_beltTearings_ended.clear();
|
|
|
|
|
|
m_beltTearings_ended.shrink_to_fit();
|
|
|
|
|
|
m_beltTearings_unknown.clear();
|
|
|
|
|
|
m_beltTearings_unknown.shrink_to_fit();
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
sg_detectBeltTearing(
|
|
|
|
|
|
NULL, //空扫描线,用于复位内部静态变量
|
|
|
|
|
|
0,
|
|
|
|
|
|
0,
|
|
|
|
|
|
&errorCode,
|
|
|
|
|
|
m_hLineWorkers,
|
2025-09-24 22:36:13 +08:00
|
|
|
|
m_beltTearings_new,
|
|
|
|
|
|
m_beltTearings_growing,
|
|
|
|
|
|
m_beltTearings_ended,
|
|
|
|
|
|
m_beltTearings_unknown, //未判明,应用无需处理。
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_algorithmParam);
|
2025-09-24 22:36:13 +08:00
|
|
|
|
m_bInitAlgo = true;
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 使用SDK算法进行撕裂检测
|
|
|
|
|
|
SVzNL3DLaserLine algorithmData;
|
|
|
|
|
|
algorithmData.nTimeStamp = pLaserLine->llTimeStamp;
|
|
|
|
|
|
algorithmData.p3DPosition = static_cast<SVzNL3DPosition*>(pLaserLine->p3DPoint);
|
|
|
|
|
|
algorithmData.nPositionCnt = pLaserLine->nPointCount;
|
|
|
|
|
|
|
|
|
|
|
|
sg_detectBeltTearing(
|
|
|
|
|
|
&algorithmData,
|
|
|
|
|
|
static_cast<int>(pLaserLine->llFrameIdx),
|
|
|
|
|
|
algorithmData.nPositionCnt,
|
|
|
|
|
|
&errorCode,
|
|
|
|
|
|
m_hLineWorkers,
|
2025-09-24 22:36:13 +08:00
|
|
|
|
m_beltTearings_new,
|
|
|
|
|
|
m_beltTearings_growing,
|
|
|
|
|
|
m_beltTearings_ended,
|
|
|
|
|
|
m_beltTearings_unknown,
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_algorithmParam
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 合并所有检测结果
|
|
|
|
|
|
std::vector<SSG_beltTearingInfo> allResults;
|
2025-09-24 22:36:13 +08:00
|
|
|
|
allResults.reserve(m_beltTearings_new.size() + m_beltTearings_growing.size() + m_beltTearings_ended.size());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
allResults.insert(allResults.end(), m_beltTearings_new.begin(), m_beltTearings_new.end());
|
|
|
|
|
|
allResults.insert(allResults.end(), m_beltTearings_growing.begin(), m_beltTearings_growing.end());
|
|
|
|
|
|
allResults.insert(allResults.end(), m_beltTearings_ended.begin(), m_beltTearings_ended.end());
|
|
|
|
|
|
// allResults.insert(allResults.end(), m_beltTearings_unknown.begin(), m_beltTearings_unknown.end());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("line count : %d algo detect count: %d (new:%d, growing:%d, ended:%d, unknown:%d)[%d]\n",
|
2025-09-24 22:36:13 +08:00
|
|
|
|
algorithmData.nPositionCnt,
|
|
|
|
|
|
allResults.size(), m_beltTearings_new.size(), m_beltTearings_growing.size(),
|
|
|
|
|
|
m_beltTearings_ended.size(), m_beltTearings_unknown.size(), errorCode);
|
|
|
|
|
|
|
|
|
|
|
|
for(auto& item : m_beltTearings_new){
|
|
|
|
|
|
LOG_DEBUG("-----new %lld ID: %d st: %d depth: %f \n", pLaserLine->llFrameIdx, item.tearID, item.tearStatus, item.tearDepth);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
|
|
for(auto& item : m_beltTearings_ended){
|
|
|
|
|
|
LOG_DEBUG("-----end %lld ID: %d st: %d depth: %f \n", pLaserLine->llFrameIdx, item.tearID, item.tearStatus, item.tearDepth);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 发送检测结果
|
|
|
|
|
|
if (!allResults.empty()) {
|
|
|
|
|
|
sendTearingResults(allResults);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
} catch (const std::exception& e) {
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_ERROR("Error in point cloud data processing: %s\n", e.what());
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::sendTearingResults(const std::vector<SSG_beltTearingInfo>& results)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (results.empty() || m_clients.isEmpty() || !m_tcpServer) {
|
2025-09-10 00:31:27 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 将检测结果转换为JSON格式
|
|
|
|
|
|
QJsonArray tearingArray;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
for (const auto& result : results) {
|
|
|
|
|
|
QJsonObject tearingObj;
|
|
|
|
|
|
// tearingObj["level"] = QString::number(result.level);
|
|
|
|
|
|
tearingObj["id"] = QString::number(result.tearID);
|
|
|
|
|
|
tearingObj["tearStatus"] = QString::number(static_cast<int>(result.tearStatus));
|
|
|
|
|
|
tearingObj["tearType"] = QString::number(result.tearType);
|
|
|
|
|
|
tearingObj["statLineIdx"] = QString::number(result.statLineIdx);
|
|
|
|
|
|
tearingObj["endLineIdx"] = QString::number(result.endLineIdx);
|
|
|
|
|
|
tearingObj["tearDepth"] = QString::number(result.tearDepth);
|
|
|
|
|
|
tearingObj["tearWidth"] = QString::number(result.tearWidth);
|
|
|
|
|
|
// tearingObj["tearLength"] = QString::number(result.tearLength);
|
|
|
|
|
|
tearingObj["roiLeft"] = QString::number(result.roi.left);
|
|
|
|
|
|
tearingObj["roiRight"] = QString::number(result.roi.right);
|
|
|
|
|
|
tearingObj["roiTop"] = QString::number(result.roi.top);
|
|
|
|
|
|
tearingObj["roiBottom"] = QString::number(result.roi.bottom);
|
|
|
|
|
|
// tearingObj["imageFile"] = QString::fromStdString(result.imageFile);
|
|
|
|
|
|
// tearingObj["older"] = QString::fromStdString(result.older);
|
|
|
|
|
|
tearingObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
tearingArray.append(tearingObj);
|
|
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 转换为JSON字符串
|
|
|
|
|
|
QJsonDocument doc(tearingArray);
|
|
|
|
|
|
QByteArray message = doc.toJson(QJsonDocument::Compact);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 创建数据包
|
|
|
|
|
|
QByteArray package;
|
|
|
|
|
|
QDataStream stream(&package, QIODevice::WriteOnly);
|
|
|
|
|
|
stream.setByteOrder(QDataStream::BigEndian);
|
|
|
|
|
|
stream << quint8(static_cast<quint8>(ByteDataType::Text)) << quint32(message.size());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
stream.writeRawData(message.constData(), message.size());
|
2025-09-10 00:31:27 +08:00
|
|
|
|
package.append("___END___\r\n");
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 发送到所有连接的客户端
|
2025-09-21 22:20:24 +08:00
|
|
|
|
bool success = m_tcpServer->SendAllData(package.constData(), package.size());
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
LOG_DEBUG("Sent tearing results (%d bytes) to %d clients\n", package.size(), m_clients.size());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_WARNING("Failed to send tearing results to all clients\n");
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::startServer(quint16 port)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (!m_tcpServer) {
|
|
|
|
|
|
LOG_ERROR("TCP server not created\n");
|
|
|
|
|
|
return false;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 先停止现有服务器
|
|
|
|
|
|
stopServer();
|
|
|
|
|
|
|
2025-08-27 23:10:36 +08:00
|
|
|
|
m_port = port;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化TCP服务器
|
|
|
|
|
|
if (!m_tcpServer->Init(port, true)) { // 启用Nagle算法优化
|
|
|
|
|
|
LOG_ERROR("Failed to initialize TCP server on port %d\n", port);
|
|
|
|
|
|
return false;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置事件回调
|
|
|
|
|
|
m_tcpServer->SetEventCallback(OnServerEvent);
|
|
|
|
|
|
|
|
|
|
|
|
// 启动TCP服务器
|
|
|
|
|
|
if (!m_tcpServer->Start(OnServerRecv, false)) { // 不使用自定义协议
|
|
|
|
|
|
LOG_ERROR("Failed to start TCP server on port %d\n", port);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("TCP server started on port %d\n", port);
|
|
|
|
|
|
return true;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::stopServer()
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (!m_tcpServer) {
|
|
|
|
|
|
return;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 清空客户端映射
|
2025-08-27 23:10:36 +08:00
|
|
|
|
m_clients.clear();
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 停止TCP服务器
|
|
|
|
|
|
m_tcpServer->Stop();
|
|
|
|
|
|
m_tcpServer->Close();
|
|
|
|
|
|
|
2025-08-27 23:10:36 +08:00
|
|
|
|
m_port = 0;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_INFO("TCP server stopped\n");
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 静态回调函数实现
|
|
|
|
|
|
void BeltTearingPresenter::OnServerRecv(const TCPClient* pClient, const char* pData, const unsigned int nLen)
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (s_instance) {
|
|
|
|
|
|
s_instance->handleServerRecv(pClient, pData, nLen);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
void BeltTearingPresenter::OnServerEvent(const TCPClient* pClient, TCPServerEventType eventType)
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (s_instance) {
|
|
|
|
|
|
s_instance->handleServerEvent(pClient, eventType);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 实例方法实现
|
|
|
|
|
|
void BeltTearingPresenter::handleServerRecv(const TCPClient* pClient, const char* pData, const unsigned int nLen)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 处理接收到的数据 - 这里暂时只记录日志
|
|
|
|
|
|
QString clientId = generateClientId(pClient);
|
|
|
|
|
|
LOG_DEBUG("Received %d bytes from client %s\n", nLen, clientId.toStdString().c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::handleServerEvent(const TCPClient* pClient, TCPServerEventType eventType)
|
|
|
|
|
|
{
|
|
|
|
|
|
QString clientId = generateClientId(pClient);
|
|
|
|
|
|
|
|
|
|
|
|
switch (eventType) {
|
|
|
|
|
|
case TCP_EVENT_CLIENT_CONNECTED:
|
|
|
|
|
|
m_clients[clientId] = pClient;
|
|
|
|
|
|
LOG_INFO("Client connected: %s\n", clientId.toStdString().c_str());
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TCP_EVENT_CLIENT_DISCONNECTED:
|
2025-08-27 23:10:36 +08:00
|
|
|
|
m_clients.remove(clientId);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
LOG_INFO("Client disconnected: %s\n", clientId.toStdString().c_str());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TCP_EVENT_CLIENT_EXCEPTION:
|
|
|
|
|
|
m_clients.remove(clientId);
|
|
|
|
|
|
LOG_WARNING("Client exception: %s\n", clientId.toStdString().c_str());
|
|
|
|
|
|
break;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
QString BeltTearingPresenter::generateClientId(const TCPClient* client)
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
return QString("Client_%1").arg(client->m_nFD);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
QString BeltTearingPresenter::getVersionString()
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-10 00:31:27 +08:00
|
|
|
|
return QString(BELT_TEARING_SERVER_VERSION_STRING);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
QString BeltTearingPresenter::getProductName()
|
2025-08-31 21:08:28 +08:00
|
|
|
|
{
|
2025-09-10 00:31:27 +08:00
|
|
|
|
return QString(BELT_TEARING_SERVER_PRODUCT_NAME);
|
2025-08-31 21:08:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
QString BeltTearingPresenter::getBuildInfo()
|
2025-08-27 23:10:36 +08:00
|
|
|
|
{
|
2025-09-10 00:31:27 +08:00
|
|
|
|
return QString("%1 %2 built on %3 %4 for %5")
|
|
|
|
|
|
.arg(BELT_TEARING_SERVER_BUILD_TYPE)
|
|
|
|
|
|
.arg(BELT_TEARING_SERVER_VERSION_STRING)
|
|
|
|
|
|
.arg(BELT_TEARING_SERVER_BUILD_DATE)
|
|
|
|
|
|
.arg(BELT_TEARING_SERVER_BUILD_TIME)
|
|
|
|
|
|
.arg(BELT_TEARING_SERVER_PLATFORM);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 激光线队列管理方法实现
|
|
|
|
|
|
void BeltTearingPresenter::addLaserLineToQueue(const SVzLaserLineData* laserLine)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(laserLine == nullptr || laserLine->nPointCount <= 0){
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建激光线数据的深拷贝
|
|
|
|
|
|
SVzLaserLineData copiedLine = *laserLine;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 深拷贝点云数据
|
|
|
|
|
|
size_t pointDataSize = laserLine->nPointCount * sizeof(SVzNL3DPosition);
|
|
|
|
|
|
copiedLine.p3DPoint = static_cast<SVzNL3DPosition*>(malloc(pointDataSize));
|
|
|
|
|
|
if (copiedLine.p3DPoint) {
|
|
|
|
|
|
memcpy(copiedLine.p3DPoint, laserLine->p3DPoint, pointDataSize);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加数据到队列的锁作用域
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_queueMutex);
|
|
|
|
|
|
// 添加到队列
|
|
|
|
|
|
m_laserLineQueue.push_back(copiedLine);
|
|
|
|
|
|
m_lineCounter++;
|
|
|
|
|
|
|
|
|
|
|
|
// 管理队列大小
|
|
|
|
|
|
while (m_laserLineQueue.size() > MAX_QUEUE_SIZE) {
|
|
|
|
|
|
SVzLaserLineData oldLine = m_laserLineQueue.front();
|
|
|
|
|
|
m_laserLineQueue.pop_front();
|
|
|
|
|
|
|
|
|
|
|
|
// 释放深拷贝的点云数据内存
|
|
|
|
|
|
if (oldLine.p3DPoint) {
|
|
|
|
|
|
free(oldLine.p3DPoint);
|
|
|
|
|
|
oldLine.p3DPoint = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(m_imageWorker && m_imageWorker->isProcessing()){
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 每到图像生成间隔,提交图像生成任务给工作线程
|
|
|
|
|
|
if (m_lineCounter % IMAGE_GENERATION_INTERVAL == 0) {
|
|
|
|
|
|
// 准备扫描线数据
|
|
|
|
|
|
std::vector<std::vector<SVzNL3DPosition>> scanLines;
|
|
|
|
|
|
|
|
|
|
|
|
// 在锁保护下复制数据
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_queueMutex);
|
|
|
|
|
|
scanLines.reserve(m_laserLineQueue.size()); // 预分配内存提高效率
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& line : m_laserLineQueue) {
|
|
|
|
|
|
// 增加安全检查,确保指针有效
|
|
|
|
|
|
if (line.p3DPoint && line.nPointCount > 0) {
|
|
|
|
|
|
std::vector<SVzNL3DPosition> linePoints;
|
|
|
|
|
|
linePoints.reserve(line.nPointCount); // 预分配内存提高效率
|
|
|
|
|
|
|
|
|
|
|
|
SVzNL3DPosition* p3DPoints = static_cast<SVzNL3DPosition*>(line.p3DPoint);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用更高效的批量复制
|
|
|
|
|
|
linePoints.assign(p3DPoints, p3DPoints + line.nPointCount);
|
|
|
|
|
|
scanLines.emplace_back(std::move(linePoints)); // 使用move避免拷贝
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("addLaserLineToQueue, line counter: %d, scanLines size: %d \n", m_lineCounter, scanLines.size());
|
|
|
|
|
|
// 只提交图像生成任务给工作线程(不包含算法检测)
|
|
|
|
|
|
if (m_imageWorker && !scanLines.empty()) {
|
|
|
|
|
|
m_imageWorker->requestImageGeneration(scanLines);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::sendImageToClients(const QImage& image)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (image.isNull() || m_clients.isEmpty() || !m_tcpServer) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 将图像转换为字节数组
|
|
|
|
|
|
QByteArray imageData;
|
|
|
|
|
|
QBuffer buffer(&imageData);
|
|
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存为JPEG格式以减少数据大小
|
|
|
|
|
|
if (!image.save(&buffer, "JPEG", 85)) {
|
|
|
|
|
|
LOG_ERROR("Failed to convert image to JPEG format\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构造数据包
|
|
|
|
|
|
QByteArray packet;
|
|
|
|
|
|
QDataStream stream(&packet, QIODevice::WriteOnly);
|
|
|
|
|
|
stream.setByteOrder(QDataStream::BigEndian);
|
|
|
|
|
|
|
|
|
|
|
|
// 写入数据类型标识(图像类型)
|
|
|
|
|
|
stream << static_cast<quint8>(ByteDataType::Image);
|
|
|
|
|
|
|
|
|
|
|
|
// 写入图像数据大小
|
|
|
|
|
|
stream << static_cast<quint32>(imageData.size());
|
|
|
|
|
|
|
|
|
|
|
|
// 写入图像数据
|
|
|
|
|
|
stream.writeRawData(imageData.constData(), imageData.size());
|
|
|
|
|
|
packet.append("___END___\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 发送给所有连接的客户端
|
|
|
|
|
|
bool success = m_tcpServer->SendAllData(packet.constData(), packet.size());
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
LOG_DEBUG("Sent point cloud image (%d bytes) to %d clients\n", packet.size(), m_clients.size());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_WARNING("Failed to send image data to all clients\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Error sending image to clients: %s\n", e.what());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::onImageGenerated(const QImage& image)
|
|
|
|
|
|
{
|
|
|
|
|
|
sendImageToClients(image);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SSG_beltTearingParam BeltTearingPresenter::configToSDKParam(const BeltTearingConfigResult& config) const
|
|
|
|
|
|
{
|
|
|
|
|
|
SSG_beltTearingParam sdkParam;
|
|
|
|
|
|
|
|
|
|
|
|
// 基本参数
|
|
|
|
|
|
sdkParam.scanXScale = config.algorithmParams.beltTearingParam.scanXScale;
|
|
|
|
|
|
sdkParam.scanYScale = config.algorithmParams.beltTearingParam.scanYScale;
|
|
|
|
|
|
sdkParam.differnceBinTh = config.algorithmParams.beltTearingParam.differnceBinTh;
|
|
|
|
|
|
sdkParam.tearingMinLen = config.algorithmParams.beltTearingParam.tearingMinLen;
|
|
|
|
|
|
sdkParam.tearingMinGap = config.algorithmParams.beltTearingParam.tearingMinGap;
|
|
|
|
|
|
|
|
|
|
|
|
// 特征提取参数
|
|
|
|
|
|
sdkParam.extractPara.sameGapTh = config.algorithmParams.beltTearingParam.sameGapTh;
|
|
|
|
|
|
sdkParam.extractPara.gapChkWin = config.algorithmParams.beltTearingParam.gapChkWin;
|
|
|
|
|
|
|
|
|
|
|
|
return sdkParam;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BeltTearingConfigResult BeltTearingPresenter::sdkToConfigParam(const SSG_beltTearingParam& sdkParam) const
|
|
|
|
|
|
{
|
|
|
|
|
|
BeltTearingConfigResult config;
|
|
|
|
|
|
|
|
|
|
|
|
// 基本参数
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.scanXScale = sdkParam.scanXScale;
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.scanYScale = sdkParam.scanYScale;
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.differnceBinTh = sdkParam.differnceBinTh;
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.tearingMinLen = sdkParam.tearingMinLen;
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.tearingMinGap = sdkParam.tearingMinGap;
|
|
|
|
|
|
|
|
|
|
|
|
// 特征提取参数
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.sameGapTh = sdkParam.extractPara.sameGapTh;
|
|
|
|
|
|
config.algorithmParams.beltTearingParam.gapChkWin = sdkParam.extractPara.gapChkWin;
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
|
}
|