feat: 优化双相机布局和调试数据存储
fix(螺杆定位): 修改TCP协议只输出一个目标点
This commit is contained in:
parent
0331f3f319
commit
ad5a7f40b5
@ -71,15 +71,15 @@ RX = 89.211
|
||||
格式:
|
||||
|
||||
```text
|
||||
PointNum_AX1_AY1_AZ1_A1_B1_C1_X1_Y1_Z1_A1_B1_C1/AX2_AY2_AZ2_A2_B2_C2_X2_Y2_Z2_A2_B2_C2/\r\n
|
||||
PointNum_AX_AY_AZ_A_B_C_X_Y_Z_A_B_C\r\n
|
||||
```
|
||||
|
||||
每个目标由 12 个浮点组成:先 **接近点** (`AX/AY/AZ + A/B/C`),再 **目标点** (`X/Y/Z + A/B/C`),两者姿态完全相同。
|
||||
每次只发送一个目标(第一个),由 12 个浮点组成:先 **接近点** (`AX/AY/AZ + A/B/C`),再 **目标点** (`X/Y/Z + A/B/C`),两者姿态完全相同。
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
2_90.500_200.300_50.100_15.500_-2.300_45.000_100.500_200.300_50.100_15.500_-2.300_45.000/110.000_210.500_48.000_-5.000_1.200_30.000_120.000_210.500_48.000_-5.000_1.200_30.000/\r\n
|
||||
1_90.500_200.300_50.100_15.500_-2.300_45.000_100.500_200.300_50.100_15.500_-2.300_45.000\r\n
|
||||
0\r\n
|
||||
```
|
||||
|
||||
|
||||
@ -41,8 +41,9 @@ public:
|
||||
|
||||
// Protocol:
|
||||
// request : T1_X_Y_Z_A_B_C\r\n
|
||||
// response: PointNum_AX1_AY1_AZ1_A1_B1_C1_X1_Y1_Z1_A1_B1_C1/.../\r\n
|
||||
// 每个目标 12 个浮点:先接近点(AX/AY/AZ + A/B/C),后目标点(X/Y/Z + A/B/C),两者姿态相同
|
||||
// response: PointNum_AX_AY_AZ_A_B_C_X_Y_Z_A_B_C\r\n
|
||||
// PointNum 为检测到的目标数量,每次只发送第一个目标的 12 个浮点
|
||||
// 先接近点(AX/AY/AZ + A/B/C),后目标点(X/Y/Z + A/B/C),两者姿态相同
|
||||
int SendTextResult(const std::string& text, const TCPClient* pClient = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
@ -132,16 +132,18 @@ void ScrewPositionPresenter::_SendDetectionResultToTCP(const DetectionResult& re
|
||||
int count = static_cast<int>(result.positions.size());
|
||||
std::string resultText = std::to_string(count);
|
||||
|
||||
for (const auto& pos : result.positions) {
|
||||
// 每次只发送一个目标,取第一个
|
||||
if (!result.positions.empty()) {
|
||||
const auto& pos = result.positions.front();
|
||||
double angle1 = 0.0;
|
||||
double angle2 = 0.0;
|
||||
double angle3 = 0.0;
|
||||
ReorderPoseAngles(pos.roll, pos.pitch, pos.yaw, poseOutputOrder, angle1, angle2, angle3);
|
||||
|
||||
// 每组输出 12 个浮点:先接近点(X/Y/Z/A/B/C),后目标点(X/Y/Z/A/B/C),姿态相同
|
||||
// 输出 12 个浮点:先接近点(AX/AY/AZ + A/B/C),后目标点(X/Y/Z + A/B/C),姿态相同
|
||||
char buf[512];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f/",
|
||||
"_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f_%.3f",
|
||||
pos.approachX, pos.approachY, pos.approachZ, angle1, angle2, angle3,
|
||||
pos.x, pos.y, pos.z, angle1, angle2, angle3);
|
||||
resultText += buf;
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
|
||||
|
||||
#define SCREWPOSITION_APP_NAME "螺杆定位"
|
||||
#define SCREWPOSITION_VERSION_STRING "1.1.5"
|
||||
#define SCREWPOSITION_BUILD_STRING "3"
|
||||
#define SCREWPOSITION_VERSION_STRING "1.1.6"
|
||||
#define SCREWPOSITION_BUILD_STRING "1"
|
||||
#define SCREWPOSITION_FULL_VERSION_STRING "V" SCREWPOSITION_VERSION_STRING "_" SCREWPOSITION_BUILD_STRING
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
# 1.1.5 2026-04-19
|
||||
# 1.1.6 2026-04-25
|
||||
## build_1
|
||||
1. 双相机
|
||||
|
||||
# 1.1.5 2026-04-19
|
||||
## build_1-5
|
||||
1. 增加目标点的偏移并且协议更新
|
||||
2. 目标只输出一个目标
|
||||
3. 调试姿态转换
|
||||
|
||||
# 1.1.4 2026-04-19
|
||||
## build_1
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include <QGraphicsView>
|
||||
#include <QPushButton>
|
||||
#include "ScrewPositionPresenter.h"
|
||||
#include "ResultListLayoutHelper.h"
|
||||
|
||||
#include "Version.h"
|
||||
#include "IVrUtils.h"
|
||||
@ -399,7 +400,7 @@ void MainWindow::OnCameraCountChanged(int cameraCount)
|
||||
{
|
||||
// 设置设备状态widget中的相机数量
|
||||
if (m_deviceStatusWidget) {
|
||||
m_deviceStatusWidget->setCameraCount(cameraCount);
|
||||
m_deviceStatusWidget->setCameraCount(cameraCount, true);
|
||||
|
||||
// 设置相机名称(从配置文件中读取)
|
||||
if (m_presenter) {
|
||||
@ -415,6 +416,7 @@ void MainWindow::OnCameraCountChanged(int cameraCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果只有一个相机,更新状态消息
|
||||
if (cameraCount < 2) {
|
||||
updateStatusLog(tr("系统使用单相机模式"));
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
#define WHEELMEASURE_APP_NAME "轮眉高度测量"
|
||||
#define WHEELMEASURE_VERSION_STRING "1.1.5"
|
||||
#define WHEELMEASURE_VERSION_STRING "1.1.6"
|
||||
#define WHEELMEASURE_BUILD_STRING "1"
|
||||
#define WHEELMEASURE_FULL_VERSION_STRING "V" WHEELMEASURE_VERSION_STRING "_" WHEELMEASURE_BUILD_STRING
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
# 1.1.6
|
||||
## build_1 2026-04-25
|
||||
1. 优化存储数据
|
||||
|
||||
# 1.1.5
|
||||
## build_1 2026-04-20
|
||||
1. 补偿系数
|
||||
|
||||
@ -152,3 +152,6 @@ else:unix:!macx: {
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
DISTFILES += \
|
||||
Version.md
|
||||
|
||||
Binary file not shown.
@ -4,6 +4,11 @@
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
//向量叉乘
|
||||
SG_APISHARED_EXPORT SVzNL3DPoint vec3_cross(const SVzNL3DPoint& a, const SVzNL3DPoint& b);
|
||||
|
||||
//逆时针旋转时 θ > 0 ;顺时针旋转时 θ < 0
|
||||
SG_APISHARED_EXPORT SVzNL3DPoint wd_rotate2D(const SVzNL3DPoint& pt, const double angle);
|
||||
|
||||
//滤除离群点:z跳变门限方法(大于门限视为不连续,根据连续段点数量判断噪声)
|
||||
SG_APISHARED_EXPORT void sg_lineDataRemoveOutlier(
|
||||
@ -872,3 +877,15 @@ SG_APISHARED_EXPORT void scanLinesSmooting3x3(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& gridDataInput,
|
||||
std::vector< std::vector<SVzNL3DPosition>>& smoothingData
|
||||
);
|
||||
|
||||
|
||||
SG_APISHARED_EXPORT void WD_getHoleInfo(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
|
||||
const SSG_lineSegParam lineSegPara,
|
||||
const SSG_outlierFilterParam filterParam,
|
||||
const SSG_treeGrowParam growParam,
|
||||
const double valieCommonNumRatio,
|
||||
std::vector<SWD_segFeatureTree>& segTrees_v,
|
||||
std::vector<SWD_segFeatureTree>& segTrees_h,
|
||||
std::vector<SSG_intPair>& validObjects
|
||||
);
|
||||
|
||||
@ -44,3 +44,6 @@
|
||||
//拓普发工件定位
|
||||
#define SX_ERR_UNKNOWN_OBJECT -2501
|
||||
|
||||
//配天工件定位
|
||||
#define SX_ERR_UNKNOWN_PLATE_DIR -2601
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -35,13 +35,15 @@ HEADERS += \
|
||||
Inc/BasePresenter.h \
|
||||
Inc/VrCommonConfig.h \
|
||||
Inc/ProtocolCommon.h \
|
||||
Inc/TCPServerProtocol.h
|
||||
Inc/TCPServerProtocol.h \
|
||||
Inc/DebugDataSaver.h
|
||||
|
||||
# 源文件
|
||||
SOURCES += \
|
||||
Src/SingleInstanceManager.cpp \
|
||||
Src/BasePresenter.cpp \
|
||||
Src/TCPServerProtocol.cpp
|
||||
Src/TCPServerProtocol.cpp \
|
||||
Src/DebugDataSaver.cpp
|
||||
|
||||
# Windows平台库链接
|
||||
win32:CONFIG(release, debug|release): {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "VrCommonConfig.h" // 包含公共配置结构体
|
||||
#include "LaserDataLoader.h" // 激光数据加载器
|
||||
#include "IVisionApplicationStatus.h" // 工作状态枚举
|
||||
#include "DebugDataSaver.h" // 调试数据异步存储
|
||||
|
||||
// 前向声明
|
||||
class IYModbusTCPServer;
|
||||
@ -573,6 +574,9 @@ protected:
|
||||
// 调试参数
|
||||
VrDebugParam m_debugParam;
|
||||
|
||||
// 调试数据异步存储
|
||||
DebugDataSaver m_debugDataSaver;
|
||||
|
||||
private:
|
||||
// 启动ModbusTCP服务(在Init中调用)
|
||||
int StartModbusServer(int port = 5020);
|
||||
|
||||
60
AppUtils/AppCommon/Inc/DebugDataSaver.h
Normal file
60
AppUtils/AppCommon/Inc/DebugDataSaver.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef DEBUG_DATA_SAVER_H
|
||||
#define DEBUG_DATA_SAVER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
#include "VZNL_Types.h"
|
||||
|
||||
class DebugDataSaver
|
||||
{
|
||||
public:
|
||||
explicit DebugDataSaver(int maxThreads = 4);
|
||||
~DebugDataSaver();
|
||||
|
||||
// 异步保存点云数据(内部深拷贝数据,不阻塞调用线程)
|
||||
// timestamp: 检测时刻的时间戳,格式 YYYYMMDD_HHmmss
|
||||
// 调用方需在持有数据锁时调用,DeepCopyData 会在调用线程完成深拷贝
|
||||
void SaveAsync(const std::string& basePath,
|
||||
const std::string& timestamp,
|
||||
const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& data,
|
||||
const std::string& deviceName = "");
|
||||
|
||||
// 停止所有工作线程(等待队列中任务完成后退出)
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
struct SaveTask {
|
||||
std::string filePath;
|
||||
std::string deviceName;
|
||||
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> data;
|
||||
};
|
||||
|
||||
void WorkerThread();
|
||||
|
||||
// 深拷贝检测数据(含原始指针数组的完整拷贝)
|
||||
static std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> DeepCopyData(
|
||||
const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& src);
|
||||
|
||||
// 释放深拷贝的数据
|
||||
static void FreeData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& data);
|
||||
|
||||
// 生成带日期目录的完整保存路径: basePath/YYYYMMDD/pointcloud[_deviceName]_YYYYMMDD_HHmmss_NNN.txt
|
||||
// timestamp: 检测时刻的时间戳,格式 YYYYMMDD_HHmmss
|
||||
// deviceName: 设备名称(为空时不包含在文件名中)
|
||||
std::string GenerateSavePath(const std::string& basePath, const std::string& timestamp, const std::string& deviceName = "");
|
||||
|
||||
int m_maxThreads;
|
||||
std::vector<std::thread> m_workers;
|
||||
std::queue<SaveTask> m_taskQueue;
|
||||
std::mutex m_queueMutex;
|
||||
std::condition_variable m_queueCondition;
|
||||
std::atomic<bool> m_running;
|
||||
std::atomic<int> m_taskCounter{0};
|
||||
};
|
||||
|
||||
#endif // DEBUG_DATA_SAVER_H
|
||||
@ -39,6 +39,9 @@ BasePresenter::~BasePresenter()
|
||||
// 停止检测线程
|
||||
StopAlgoDetectThread();
|
||||
|
||||
// 停止调试数据异步存储
|
||||
m_debugDataSaver.Stop();
|
||||
|
||||
// 释放检测数据缓存中的动态内存
|
||||
ClearDetectionDataCache();
|
||||
|
||||
@ -282,12 +285,13 @@ int BasePresenter::InitCamera(std::vector<DeviceInfo>& cameraList, bool bRGB, bo
|
||||
LOG_INFO("[BasePresenter] init eyedevice list\n");
|
||||
// 初始化相机列表,预分配空间
|
||||
m_vrEyeDeviceList.resize(cameraCount, std::make_pair("", nullptr));
|
||||
LOG_INFO("[BasePresenter] camera count : %d\n", cameraCount);
|
||||
for(int i = 0; i < cameraCount; i++)
|
||||
{
|
||||
m_vrEyeDeviceList[i] = std::make_pair(cameraList[i].name, nullptr);
|
||||
LOG_INFO("[BasePresenter] camera %d name : %s, ip : %s\n", i, cameraList[i].name.c_str(), cameraList[i].ip.c_str());
|
||||
}
|
||||
|
||||
LOG_INFO("[BasePresenter] camera count : %d\n", cameraCount);
|
||||
|
||||
// 尝试初始化所有相机
|
||||
bool allCamerasConnected = true;
|
||||
@ -496,37 +500,41 @@ int BasePresenter::DetectTask()
|
||||
LOG_INFO("[BasePresenter] 检测数据缓存大小: %zu\n", m_detectionDataCache.size());
|
||||
}
|
||||
|
||||
// 2. 调试模式 - 保存点云数据
|
||||
// 2. 调试模式 - 异步保存点云数据
|
||||
if (debugParam.enableDebug && debugParam.savePointCloud) {
|
||||
// 确定输出路径
|
||||
QString outputPath;
|
||||
if (debugParam.debugOutputPath.empty()) {
|
||||
// 默认使用应用程序目录下的 debug 子目录
|
||||
outputPath = QCoreApplication::applicationDirPath() + "/debug";
|
||||
} else {
|
||||
outputPath = QString::fromStdString(debugParam.debugOutputPath);
|
||||
}
|
||||
|
||||
// 确保输出目录存在
|
||||
// 确保输出根目录存在
|
||||
QDir dir(outputPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
// 生成带时间戳的文件名
|
||||
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss");
|
||||
QString fileName = QString("%1/pointcloud_%2.txt").arg(outputPath).arg(timestamp);
|
||||
// 在检测时刻生成时间戳,确保文件名反映实际检测时间
|
||||
std::string timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss").toStdString();
|
||||
|
||||
if (debugParam.printDetailLog) {
|
||||
LOG_INFO("[BasePresenter] 保存点云数据到: %s\n", fileName.toStdString().c_str());
|
||||
// 获取当前相机名称,用于生成更直观的文件名
|
||||
std::string deviceName;
|
||||
int arrayIndex = m_currentCameraIndex - 1;
|
||||
LOG_INFO("[BasePresenter] currentCameraIndex: %d, arrayIndex: %d\n", m_currentCameraIndex, arrayIndex);
|
||||
if (arrayIndex >= 0 && arrayIndex < static_cast<int>(m_vrEyeDeviceList.size())) {
|
||||
deviceName = m_vrEyeDeviceList[arrayIndex].first;
|
||||
}
|
||||
|
||||
// 保存点云数据
|
||||
int saveRet = SaveDetectionDataToFile(fileName.toStdString());
|
||||
if (saveRet != SUCCESS) {
|
||||
LOG_WARNING("[BasePresenter] 保存点云数据失败,错误码: %d\n", saveRet);
|
||||
} else {
|
||||
LOG_INFO("[BasePresenter] 点云数据保存成功\n");
|
||||
if (debugParam.printDetailLog) {
|
||||
LOG_INFO("[BasePresenter] 提交点云数据异步保存任务,路径: %s,时间: %s,设备: %s\n", outputPath.toStdString().c_str(), timestamp.c_str(), deviceName.c_str());
|
||||
}
|
||||
|
||||
// 深拷贝数据后异步保存,不阻塞检测流程
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_detectionDataMutex);
|
||||
m_debugDataSaver.SaveAsync(outputPath.toStdString(), timestamp, m_detectionDataCache, deviceName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
185
AppUtils/AppCommon/Src/DebugDataSaver.cpp
Normal file
185
AppUtils/AppCommon/Src/DebugDataSaver.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
#include "DebugDataSaver.h"
|
||||
#include "LaserDataLoader.h"
|
||||
#include "VrLog.h"
|
||||
#include "VrError.h"
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <cstring>
|
||||
|
||||
DebugDataSaver::DebugDataSaver(int maxThreads)
|
||||
: m_maxThreads(std::max(1, std::min(maxThreads, 4)))
|
||||
, m_running(true)
|
||||
{
|
||||
for (int i = 0; i < m_maxThreads; ++i) {
|
||||
m_workers.emplace_back(&DebugDataSaver::WorkerThread, this);
|
||||
}
|
||||
LOG_INFO("[DebugDataSaver] 已启动 %d 个存储线程\n", m_maxThreads);
|
||||
}
|
||||
|
||||
DebugDataSaver::~DebugDataSaver()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void DebugDataSaver::Stop()
|
||||
{
|
||||
if (!m_running.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_queueCondition.notify_all();
|
||||
|
||||
for (auto& worker : m_workers) {
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
m_workers.clear();
|
||||
|
||||
// 释放队列中未处理的任务
|
||||
while (!m_taskQueue.empty()) {
|
||||
FreeData(m_taskQueue.front().data);
|
||||
m_taskQueue.pop();
|
||||
}
|
||||
|
||||
LOG_INFO("[DebugDataSaver] 已停止\n");
|
||||
}
|
||||
|
||||
void DebugDataSaver::SaveAsync(const std::string& basePath, const std::string& timestamp, const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& data, const std::string& deviceName)
|
||||
{
|
||||
if (data.empty() || !m_running) return;
|
||||
|
||||
SaveTask task;
|
||||
task.deviceName = deviceName;
|
||||
task.filePath = GenerateSavePath(basePath, timestamp, deviceName);
|
||||
task.data = DeepCopyData(data);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_queueMutex);
|
||||
m_taskQueue.push(std::move(task));
|
||||
}
|
||||
m_queueCondition.notify_one();
|
||||
}
|
||||
|
||||
void DebugDataSaver::WorkerThread()
|
||||
{
|
||||
LaserDataLoader dataLoader;
|
||||
|
||||
while (true) {
|
||||
SaveTask task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_queueMutex);
|
||||
m_queueCondition.wait(lock, [this] {
|
||||
return !m_taskQueue.empty() || !m_running;
|
||||
});
|
||||
|
||||
if (!m_running && m_taskQueue.empty()) {
|
||||
break;
|
||||
}
|
||||
if (m_taskQueue.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
task = std::move(m_taskQueue.front());
|
||||
m_taskQueue.pop();
|
||||
}
|
||||
|
||||
int lineNum = static_cast<int>(task.data.size());
|
||||
float scanSpeed = 0.0f;
|
||||
int maxTimeStamp = 0;
|
||||
int clockPerSecond = 0;
|
||||
|
||||
int result = dataLoader.SaveLaserScanData(task.filePath, task.data, lineNum, scanSpeed, maxTimeStamp, clockPerSecond);
|
||||
|
||||
if (result == SUCCESS) {
|
||||
LOG_INFO("[DebugDataSaver] 保存成功: %s (%d行)\n", task.filePath.c_str(), lineNum);
|
||||
} else {
|
||||
LOG_ERROR("[DebugDataSaver] 保存失败: %s, 错误: %s\n", task.filePath.c_str(), dataLoader.GetLastError().c_str());
|
||||
}
|
||||
|
||||
FreeData(task.data);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> DebugDataSaver::DeepCopyData(
|
||||
const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& src)
|
||||
{
|
||||
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> dst;
|
||||
dst.reserve(src.size());
|
||||
|
||||
for (const auto& item : src) {
|
||||
EVzResultDataType dataType = item.first;
|
||||
const SVzLaserLineData& srcLine = item.second;
|
||||
|
||||
SVzLaserLineData dstLine;
|
||||
memset(&dstLine, 0, sizeof(SVzLaserLineData));
|
||||
|
||||
if (srcLine.nPointCount > 0) {
|
||||
if (dataType == keResultDataType_Position) {
|
||||
dstLine.p3DPoint = new SVzNL3DPosition[srcLine.nPointCount];
|
||||
if (srcLine.p3DPoint) {
|
||||
memcpy(dstLine.p3DPoint, srcLine.p3DPoint, sizeof(SVzNL3DPosition) * srcLine.nPointCount);
|
||||
}
|
||||
dstLine.p2DPoint = new SVzNL2DPosition[srcLine.nPointCount];
|
||||
if (srcLine.p2DPoint) {
|
||||
memcpy(dstLine.p2DPoint, srcLine.p2DPoint, sizeof(SVzNL2DPosition) * srcLine.nPointCount);
|
||||
}
|
||||
} else if (dataType == keResultDataType_PointXYZRGBA) {
|
||||
dstLine.p3DPoint = new SVzNLPointXYZRGBA[srcLine.nPointCount];
|
||||
if (srcLine.p3DPoint) {
|
||||
memcpy(dstLine.p3DPoint, srcLine.p3DPoint, sizeof(SVzNLPointXYZRGBA) * srcLine.nPointCount);
|
||||
}
|
||||
dstLine.p2DPoint = new SVzNL2DLRPoint[srcLine.nPointCount];
|
||||
if (srcLine.p2DPoint) {
|
||||
memcpy(dstLine.p2DPoint, srcLine.p2DPoint, sizeof(SVzNL2DLRPoint) * srcLine.nPointCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dstLine.nPointCount = srcLine.nPointCount;
|
||||
dstLine.llTimeStamp = srcLine.llTimeStamp;
|
||||
dstLine.llFrameIdx = srcLine.llFrameIdx;
|
||||
dstLine.nEncodeNo = srcLine.nEncodeNo;
|
||||
dstLine.fSwingAngle = srcLine.fSwingAngle;
|
||||
dstLine.bEndOnceScan = srcLine.bEndOnceScan;
|
||||
|
||||
dst.push_back(std::make_pair(dataType, dstLine));
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void DebugDataSaver::FreeData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& data)
|
||||
{
|
||||
LaserDataLoader loader;
|
||||
loader.FreeLaserScanData(data);
|
||||
data.clear();
|
||||
}
|
||||
|
||||
std::string DebugDataSaver::GenerateSavePath(const std::string& basePath, const std::string& timestamp, const std::string& deviceName)
|
||||
{
|
||||
// 从时间戳提取日期部分作为子文件夹名(前8位:YYYYMMDD)
|
||||
std::string dateFolder = timestamp.substr(0, 8);
|
||||
|
||||
QString fullPath = QString::fromStdString(basePath) + "/" + QString::fromStdString(dateFolder);
|
||||
|
||||
QDir dir(fullPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
int taskId = m_taskCounter.fetch_add(1);
|
||||
QString fileName;
|
||||
if (!deviceName.empty()) {
|
||||
fileName = QString("%1/%2_%3_%4.txt").arg(fullPath)
|
||||
.arg(taskId, 4, 10, QChar('0'))
|
||||
.arg(QString::fromStdString(deviceName))
|
||||
.arg(QString::fromStdString(timestamp));
|
||||
} else {
|
||||
fileName = QString("%1/pointcloud_%2_%3.txt").arg(fullPath)
|
||||
.arg(QString::fromStdString(timestamp))
|
||||
.arg(taskId, 4, 10, QChar('0'));
|
||||
}
|
||||
|
||||
return fileName.toStdString();
|
||||
}
|
||||
@ -40,7 +40,8 @@ public:
|
||||
void setCamera2Name(const QString& name);
|
||||
|
||||
// 设置相机数量(用于控制相机二的显示和布局方向)
|
||||
void setCameraCount(int cameraCount);
|
||||
// cameraCount>=2 时 horizontal 有效:true=横向不调整布局,false=纵向(默认)
|
||||
void setCameraCount(int cameraCount, bool horizontal = false);
|
||||
|
||||
// 设置机械臂是否可见(默认可见)
|
||||
void setRobotVisible(bool visible);
|
||||
@ -76,6 +77,9 @@ private:
|
||||
// 相机数量
|
||||
int m_cameraCount;
|
||||
|
||||
// 双相机布局方向(true=横向,false=纵向)
|
||||
bool m_horizontalLayout;
|
||||
|
||||
// 相机是否可点击
|
||||
bool m_cameraClickable;
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ DeviceStatusWidget::DeviceStatusWidget(QWidget *parent)
|
||||
, m_camera2Name("")
|
||||
, m_robotVisible(true)
|
||||
, m_cameraCount(1)
|
||||
, m_horizontalLayout(false)
|
||||
, m_cameraClickable(true)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@ -235,13 +236,14 @@ void DeviceStatusWidget::updateSerialStatus(bool isConnected)
|
||||
}
|
||||
|
||||
// 设置相机数量(用于控制相机二的显示和布局方向)
|
||||
void DeviceStatusWidget::setCameraCount(int cameraCount)
|
||||
void DeviceStatusWidget::setCameraCount(int cameraCount, bool horizontal)
|
||||
{
|
||||
LOG_DEBUG("setCameraCount cameraCount: %d \n", cameraCount);
|
||||
LOG_DEBUG("setCameraCount cameraCount: %d, horizontal: %d\n", cameraCount, horizontal);
|
||||
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
m_cameraCount = cameraCount;
|
||||
m_horizontalLayout = horizontal;
|
||||
|
||||
// 如果相机数量小于2,隐藏相机二相关的UI元素
|
||||
bool showCamera2 = (cameraCount >= 2);
|
||||
@ -261,12 +263,15 @@ void DeviceStatusWidget::setCameraCount(int cameraCount)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新布局
|
||||
updateLayout();
|
||||
// 横向布局时不需要调整布局
|
||||
if (!horizontal) {
|
||||
updateLayout();
|
||||
}
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, cameraCount]() {
|
||||
QMetaObject::invokeMethod(this, [this, cameraCount, horizontal]() {
|
||||
m_cameraCount = cameraCount;
|
||||
m_horizontalLayout = horizontal;
|
||||
|
||||
// 如果相机数量小于2,隐藏相机二相关的UI元素
|
||||
bool showCamera2 = (cameraCount >= 2);
|
||||
@ -286,8 +291,10 @@ void DeviceStatusWidget::setCameraCount(int cameraCount)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新布局
|
||||
updateLayout();
|
||||
// 横向布局时不需要调整布局
|
||||
if (!horizontal) {
|
||||
updateLayout();
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,15 +8,15 @@
|
||||
| 2 | 皮带撕裂 | BeltTearingServer | 2.0.8.1 |
|
||||
| 3 | 孔洞检测 | HoleDetection | 1.1.6.2 |
|
||||
| 4 | 棒材定位 | RodAndBarPosition | 1.1.0.1 |
|
||||
| 5 | 车轮拱高测量 | WheelMeasure | 1.1.5.1 |
|
||||
| 5 | 车轮拱高测量 | WheelMeasure | 1.1.6.1 |
|
||||
| 6 | 定子定位 | StatorPosition | 1.0.0.1 |
|
||||
| 7 | 焊接 | LapWeld | 1.0.0.1 |
|
||||
| 8 | 工件定位 | Workpiece | 1.0.3.0 |
|
||||
| 9 | 颗粒尺寸检测 | ParticleSize | 1.0.0.0 |
|
||||
| 10 | 双目标记检测 | BinocularMarkServer | 1.0.0.4 |
|
||||
| 11 | 铁路隧道槽道测量 | TunnelChannel | 1.0.0.3 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.1.5.3 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.1.6.1 |
|
||||
| 13 | 包裹拆线位置定位 | BagThreadPosition | 1.0.0.4 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.1.3.1 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.1.3.2 |
|
||||
| 16 | 坑孔定位 | HolePitPosition | 无 |
|
||||
|
||||
|
||||
2
Utils
2
Utils
@ -1 +1 @@
|
||||
Subproject commit 598cd176f5390520aa400dbe8cb7cc7b7a213b91
|
||||
Subproject commit c088ad39c4b0ee5d41d9a6ec2ca5f1efdaea9bf6
|
||||
Loading…
x
Reference in New Issue
Block a user