Compare commits
2 Commits
716fd5089f
...
04a4adf34f
| Author | SHA1 | Date | |
|---|---|---|---|
| 04a4adf34f | |||
| a355fa904c |
@ -10,6 +10,11 @@ win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# For Windows crash dump generation and MessageBox functions (only for MSVC)
|
||||
win32-msvc {
|
||||
LIBS += -lDbgHelp -luser32
|
||||
}
|
||||
|
||||
# Include paths
|
||||
INCLUDEPATH += $$PWD/Presenter/Inc
|
||||
INCLUDEPATH += ../BeltTearingConfig/Inc
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QByteArray>
|
||||
|
||||
|
||||
class BeltTearingPresenter : public QWidget
|
||||
@ -19,6 +20,7 @@ class BeltTearingPresenter : public QWidget
|
||||
signals:
|
||||
void tearingDataReceived(const QJsonObject &data);
|
||||
void serverDataReceived(const QString &serverName, const QJsonObject &data);
|
||||
void tcpDataReceivedAsync(const QString &aliasName, const QByteArray &data);
|
||||
|
||||
|
||||
public:
|
||||
@ -61,6 +63,7 @@ private slots:
|
||||
void onConnected(const QString &serverName);
|
||||
void onDisconnected(const QString &serverName);
|
||||
void onTcpError(const QString &serverName, const QString &error);
|
||||
void onTcpDataReceivedAsync(const QString &aliasName, const QByteArray &data);
|
||||
|
||||
private:
|
||||
// 静态回调函数(用于C接口)- 已弃用,使用lambda替代
|
||||
|
||||
@ -24,6 +24,11 @@ BeltTearingPresenter::BeltTearingPresenter(QWidget* parent)
|
||||
{
|
||||
// 创建配置实例
|
||||
IVrBeltTearingConfig::CreateInstance(&m_config);
|
||||
|
||||
// 连接异步数据处理信号和槽
|
||||
connect(this, &BeltTearingPresenter::tcpDataReceivedAsync,
|
||||
this, &BeltTearingPresenter::onTcpDataReceivedAsync,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
BeltTearingPresenter::~BeltTearingPresenter()
|
||||
@ -167,11 +172,15 @@ void BeltTearingPresenter::disconnectFromServer(const QString &serverName)
|
||||
it.value()->CloseDevice();
|
||||
m_connectionStatus[it.key()] = false;
|
||||
}
|
||||
// 清空所有数据缓存
|
||||
m_dataBuffers.clear();
|
||||
} else {
|
||||
// 断开指定服务器连接
|
||||
if (m_tcpClients.contains(serverName)) {
|
||||
m_tcpClients[serverName]->CloseDevice();
|
||||
m_connectionStatus[serverName] = false;
|
||||
// 清空该服务器的数据缓存
|
||||
m_dataBuffers.remove(serverName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -277,34 +286,11 @@ void BeltTearingPresenter::handleTcpDataReceived(const QString &aliasName, const
|
||||
{
|
||||
if (!pData || nLen <= 0) return;
|
||||
|
||||
// 将新数据添加到缓存
|
||||
m_dataBuffers[aliasName].append(pData, nLen);
|
||||
// LOG_DEBUG("Received data from %s: size=%d\n", aliasName.toStdString().c_str(), nLen);
|
||||
|
||||
// 检查是否有完整的数据包
|
||||
QByteArray &buffer = m_dataBuffers[aliasName];
|
||||
const QByteArray endMarker = "___END___\r\n";
|
||||
|
||||
int endPos = buffer.indexOf(endMarker);
|
||||
while (endPos != -1) {
|
||||
// 找到完整数据包
|
||||
QByteArray completePacket = buffer.left(endPos);
|
||||
// LOG_DEBUG("Found complete packet for %s: size=%d\n", serverName.toStdString().c_str(), completePacket.size());
|
||||
|
||||
// 处理完整数据包
|
||||
processCompletePacket(aliasName, completePacket);
|
||||
|
||||
// 从缓存中移除已处理的数据(包括结束标记)
|
||||
buffer.remove(0, endPos + endMarker.length());
|
||||
|
||||
// 检查缓存中是否还有其他完整数据包
|
||||
endPos = buffer.indexOf(endMarker);
|
||||
}
|
||||
|
||||
// 如果缓存过大,清空以避免内存问题
|
||||
if (buffer.size() > 1024 * 1024 * 2) { // 2MB 限制
|
||||
LOG_WARNING("Buffer too large for %s, clearing buffer\n", aliasName.toStdString().c_str());
|
||||
buffer.clear();
|
||||
}
|
||||
// 将接收到的数据转换为QByteArray并通过信号发射到主线程异步处理
|
||||
QByteArray dataBuffer(pData, nLen);
|
||||
emit tcpDataReceivedAsync(aliasName, dataBuffer);
|
||||
}
|
||||
|
||||
void BeltTearingPresenter::processCompletePacket(const QString &aliasName, const QByteArray &completeData)
|
||||
@ -575,3 +561,39 @@ void BeltTearingPresenter::ResetDetect(const QString& targetServerAlias)
|
||||
}
|
||||
}
|
||||
|
||||
void BeltTearingPresenter::onTcpDataReceivedAsync(const QString &aliasName, const QByteArray &data)
|
||||
{
|
||||
// 此槽函数在主线程中执行,避免阻塞TCP接收线程
|
||||
if (data.isEmpty()) return;
|
||||
|
||||
// LOG_DEBUG("Processing async data from %s: size=%d\n", aliasName.toStdString().c_str(), data.size());
|
||||
|
||||
// 将新数据添加到缓存
|
||||
m_dataBuffers[aliasName].append(data);
|
||||
|
||||
// 检查是否有完整的数据包
|
||||
QByteArray &buffer = m_dataBuffers[aliasName];
|
||||
const QByteArray endMarker = "___END___\r\n";
|
||||
|
||||
int endPos = buffer.indexOf(endMarker);
|
||||
while (endPos != -1) {
|
||||
// 找到完整数据包
|
||||
QByteArray completePacket = buffer.left(endPos);
|
||||
// LOG_DEBUG("Found complete packet for %s: size=%d\n", aliasName.toStdString().c_str(), completePacket.size());
|
||||
|
||||
// 处理完整数据包
|
||||
processCompletePacket(aliasName, completePacket);
|
||||
|
||||
// 从缓存中移除已处理的数据(包括结束标记)
|
||||
buffer.remove(0, endPos + endMarker.length());
|
||||
|
||||
// 检查缓存中是否还有其他完整数据包
|
||||
endPos = buffer.indexOf(endMarker);
|
||||
}
|
||||
|
||||
// 如果缓存过大,清空以避免内存问题
|
||||
if (buffer.size() > 1024 * 1024 * 2) { // 2MB 限制
|
||||
LOG_WARNING("Buffer too large for %s, clearing buffer\n", aliasName.toStdString().c_str());
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "IStatusUpdate.h"
|
||||
#include "IVrBeltTearingConfig.h"
|
||||
#include "widgets/TearingDataTableWidget.h"
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMetaType>
|
||||
@ -9,9 +10,18 @@
|
||||
#include <QList>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 初始化崩溃处理程序
|
||||
// CrashHandler::setDumpPath("c:/crash_dumps");
|
||||
// CrashHandler::registerCrashHandler();
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// Register meta types for signal-slot connections
|
||||
@ -29,6 +39,43 @@ int main(int argc, char *argv[])
|
||||
qRegisterMetaType<BeltTearingConfigResult>("BeltTearingConfigResult");
|
||||
qRegisterMetaType<NumericTableWidgetItem>("NumericTableWidgetItem");
|
||||
|
||||
|
||||
// 使用应用程序名称作为唯一标识符
|
||||
const QString appKey = "BeltTearingApp_SingleInstance_Key";
|
||||
|
||||
// 创建系统信号量,用于同步访问共享内存
|
||||
QSystemSemaphore semaphore(appKey + "_semaphore", 1);
|
||||
semaphore.acquire(); // 获取信号量
|
||||
|
||||
// 创建共享内存对象
|
||||
QSharedMemory sharedMemory(appKey + "_memory");
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
// 尝试附加到现有的共享内存
|
||||
if (sharedMemory.attach()) {
|
||||
// 如果能够附加,说明已有实例在运行
|
||||
isRunning = true;
|
||||
} else {
|
||||
// 尝试创建新的共享内存
|
||||
if (!sharedMemory.create(1)) {
|
||||
// 创建失败,可能是因为已经存在
|
||||
qDebug() << "Unable to create shared memory segment:" << sharedMemory.errorString();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
semaphore.release(); // 释放信号量
|
||||
|
||||
if (isRunning) {
|
||||
// 已有实例在运行,显示提示信息并退出
|
||||
QMessageBox::information(nullptr,
|
||||
QObject::tr("应用程序已运行"),
|
||||
QObject::tr("撕裂应用程序已经在运行中,请勿重复启动!"),
|
||||
QMessageBox::Ok);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
|
||||
@ -82,7 +82,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
// 设置表格最大行数限制,防止内存占用过大
|
||||
if (m_tearingDataTableWidget) {
|
||||
m_tearingDataTableWidget->setMaximumRows(500);
|
||||
m_tearingDataTableWidget->setMaximumRows(100);
|
||||
}
|
||||
|
||||
m_presenter->Init();
|
||||
|
||||
@ -264,6 +264,9 @@ void TearingDataTableWidget::addDataBatch(const QString devName, const std::vect
|
||||
|
||||
// 手动触发一次按ID列倒序排序
|
||||
m_tableWidget->sortItems(1, Qt::DescendingOrder);
|
||||
|
||||
// 检查是否需要限制行数
|
||||
limitRowsIfNeeded();
|
||||
}
|
||||
|
||||
void TearingDataTableWidget::clearData()
|
||||
@ -289,7 +292,7 @@ void TearingDataTableWidget::clearData()
|
||||
void TearingDataTableWidget::setMaximumRows(int maxRows)
|
||||
{
|
||||
// 设置表格最大行数,防止内存占用过大
|
||||
// m_maxRows = maxRows;
|
||||
m_maxRows = maxRows;
|
||||
}
|
||||
|
||||
void TearingDataTableWidget::removeRowFromSet(int row)
|
||||
@ -319,11 +322,12 @@ void TearingDataTableWidget::limitRowsIfNeeded()
|
||||
// 禁用更新以提高性能
|
||||
m_tableWidget->setUpdatesEnabled(false);
|
||||
|
||||
// 删除最旧的行(假设新数据在后面)
|
||||
// 从后往前删除多余的行(假设新数据在后面)
|
||||
for (int i = 0; i < rowsToDelete; i++) {
|
||||
int rowToRemove = m_tableWidget->rowCount() - 1 - i;
|
||||
// 从集合中移除
|
||||
removeRowFromSet(0);
|
||||
m_tableWidget->removeRow(0);
|
||||
removeRowFromSet(rowToRemove);
|
||||
m_tableWidget->removeRow(rowToRemove);
|
||||
}
|
||||
|
||||
// 重新启用更新
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
|
||||
#include "VrLog.h"
|
||||
#include "VrSimpleLog.h"
|
||||
@ -252,24 +254,16 @@ void BeltTearingPresenter::sendTestData(std::string fileName){
|
||||
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);
|
||||
|
||||
&pos.pt3D.x, &pos.pt3D.y, &pos.pt3D.z, &lx, &ly, &rx, &ry);
|
||||
|
||||
sVzNLPostion.push_back(pos);
|
||||
}
|
||||
|
||||
// if(nIndex > 500){
|
||||
// break;
|
||||
// }
|
||||
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
}
|
||||
|
||||
// 发送模拟数据
|
||||
// 发送模拟撕裂结果数据
|
||||
void BeltTearingPresenter::sendSimulationData()
|
||||
{
|
||||
LOG_INFO("Sending simulation data\n");
|
||||
@ -278,7 +272,7 @@ void BeltTearingPresenter::sendSimulationData()
|
||||
m_pRobotProtocol->SetWorkStatus(RobotProtocol::WORK_STATUS_WORKING);
|
||||
}
|
||||
|
||||
// 创建模拟的皮带撕裂检测结果
|
||||
// 创建模拟的撕裂结果数据
|
||||
std::vector<SSG_beltTearingInfo> simulationResults;
|
||||
|
||||
// 创建第一个模拟撕裂结果
|
||||
@ -301,40 +295,31 @@ void BeltTearingPresenter::sendSimulationData()
|
||||
|
||||
simulationResults.push_back(tear2);
|
||||
|
||||
// 创建第三个模拟撕裂结果
|
||||
SSG_beltTearingInfo tear3;
|
||||
tear3.tearID = 13;
|
||||
tear3.tearStatus = keSG_tearStatus_Ended; // 假设3表示无效撕裂
|
||||
tear3.tearWidth = 18.7f; // 撕裂宽度 18.7mm
|
||||
tear3.tearDepth = 9.5f; // 撕裂深度 9.5mm
|
||||
// 其他字段由于结构定义不明确,暂时不填充
|
||||
// 发送撕裂结果到所有客户端
|
||||
sendTearingResults(simulationResults);
|
||||
SendDetectionResultToRobot(simulationResults); // 发送检测结果到机械臂
|
||||
|
||||
simulationResults.push_back(tear3);
|
||||
LOG_INFO("Simulation tearing data sent successfully\n");
|
||||
}
|
||||
|
||||
// 创建第四个模拟撕裂结果
|
||||
SSG_beltTearingInfo tear4;
|
||||
tear4.tearID = 14;
|
||||
tear4.tearStatus = keSG_tearStatus_Ended; // 假设1表示有效撕裂
|
||||
tear4.tearWidth = 30.1f; // 撕裂宽度 30.1mm
|
||||
tear4.tearDepth = 15.8f; // 撕裂深度 15.8mm
|
||||
// 其他字段由于结构定义不明确,暂时不填充
|
||||
// 发送模拟图像数据
|
||||
void BeltTearingPresenter::sendSimulationImageData()
|
||||
{
|
||||
LOG_INFO("Sending simulation image data\n");
|
||||
|
||||
simulationResults.push_back(tear4);
|
||||
// 创建一个模拟的图像
|
||||
QImage simulationImage(800, 600, QImage::Format_RGB32);
|
||||
|
||||
// 创建第五个模拟撕裂结果
|
||||
SSG_beltTearingInfo tear5;
|
||||
tear5.tearID = 15;
|
||||
tear5.tearStatus = keSG_tearStatus_Ended; // 假设1表示有效撕裂
|
||||
tear5.tearWidth = 25.4f; // 撕裂宽度 25.4mm
|
||||
tear5.tearDepth = 11.3f; // 撕裂深度 11.3mm
|
||||
// 其他字段由于结构定义不明确,暂时不填充
|
||||
// 填充背景色
|
||||
simulationImage.fill(Qt::white);
|
||||
|
||||
simulationResults.push_back(tear5);
|
||||
// 创建 QPainter 对象用于绘制
|
||||
QPainter painter(&simulationImage);
|
||||
|
||||
// 发送检测结果到机械臂
|
||||
SendDetectionResultToRobot(simulationResults);
|
||||
// 发送图像到所有客户端
|
||||
sendImageToClients(simulationImage);
|
||||
|
||||
LOG_INFO("Simulation data sent successfully\n");
|
||||
LOG_INFO("Simulation image data sent successfully\n");
|
||||
}
|
||||
|
||||
bool BeltTearingPresenter::initializeCamera()
|
||||
@ -783,7 +768,7 @@ void BeltTearingPresenter::sendImageToClients(const QImage& image)
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
// 保存为JPEG格式以减少数据大小
|
||||
if (!image.save(&buffer, "JPEG", 75)) {
|
||||
if (!image.save(&buffer, "JPEG", 50)) {
|
||||
LOG_ERROR("Failed to convert image to JPEG format\n");
|
||||
return;
|
||||
}
|
||||
@ -809,6 +794,7 @@ void BeltTearingPresenter::sendImageToClients(const QImage& image)
|
||||
if (!success) {
|
||||
LOG_WARNING("Failed to send image data to all clients\n");
|
||||
}
|
||||
// LOG_DEBUG("Sent image data to %d clients datalen %d\n", m_clients.size(), imageData.size());
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Error sending image to clients: %s\n", e.what());
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include <atomic>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include "IVrEyeDevice.h"
|
||||
#include "../../SDK/beltTearing/Inc/beltTearingDetection_Export.h"
|
||||
#include "beltTearingDetection_Export.h"
|
||||
#include "IVrBeltTearingConfig.h"
|
||||
#include "VZNL_Common.h"
|
||||
#include "VZNL_Types.h"
|
||||
@ -55,6 +55,9 @@ public:
|
||||
void sendTestData(std::string fileName);
|
||||
void sendSimulationData();
|
||||
|
||||
// 发送模拟图像数据
|
||||
void sendSimulationImageData();
|
||||
|
||||
private slots:
|
||||
void onCameraInitTimer();
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ int main(int argc, char *argv[])
|
||||
presenter.startCamera();
|
||||
|
||||
// 输出系统信息
|
||||
LOG_INFO("=== BeltTearing Server Started ===\n");
|
||||
LOG_INFO("=== BeltTearing Server Started %s===\n", BELT_TEARING_SERVER_VERSION_STRING);
|
||||
LOG_DEBUG("===================================\n");
|
||||
LOG_INFO("Server is running on port %d\n", serverPort);
|
||||
LOG_INFO("Press Ctrl+C to exit.\n");
|
||||
@ -73,14 +73,16 @@ int main(int argc, char *argv[])
|
||||
LOG_INFO("Simulation thread started - sending test data every 5 seconds\n");
|
||||
// 处理应用程序退出
|
||||
std::thread testThread([&presenter]() {
|
||||
QThread::msleep(5000); // 等待5秒
|
||||
QThread::msleep(10); // 等待5秒
|
||||
while (true)
|
||||
{
|
||||
presenter.sendTestData("C:\\tmp\\8_LazerData_Je08A052.txt");
|
||||
// presenter.sendSimulationData();
|
||||
// presenter.sendTestData("C:\\tmp\\8_LazerData_Je08A052.txt");
|
||||
presenter.sendSimulationData(); // 启用发送模拟撕裂数据
|
||||
presenter.sendSimulationImageData();
|
||||
}
|
||||
});
|
||||
testThread.detach();
|
||||
|
||||
#endif
|
||||
|
||||
return app.exec();
|
||||
|
||||
@ -655,6 +655,9 @@ int GrabBagPresenter::InitAlgorithmParams()
|
||||
m_algoParam.growParam.minLTypeTreeLen = xmlParams.growParam.minLTypeTreeLen;
|
||||
m_algoParam.growParam.minVTypeTreeLen = xmlParams.growParam.minVTypeTreeLen;
|
||||
|
||||
// 设置支持旋转参数
|
||||
m_algoParam.supportRotate = xmlParams.supportRotate;
|
||||
|
||||
// 设置颜色参数
|
||||
m_hsvCmpParam.hueTh = xmlParams.hsvCmpParam.hueTh ;
|
||||
m_hsvCmpParam.saturateTh = xmlParams.hsvCmpParam.saturateTh;
|
||||
@ -686,6 +689,8 @@ int GrabBagPresenter::InitAlgorithmParams()
|
||||
m_algoParam.growParam.yDeviation_max, m_algoParam.growParam.zDeviation_max,
|
||||
m_algoParam.growParam.maxLineSkipNum, m_algoParam.growParam.maxSkipDistance);
|
||||
|
||||
LOG_INFO(" SupportRotate: %d\n", m_algoParam.supportRotate);
|
||||
|
||||
// 循环打印所有相机的调平参数
|
||||
LOG_INFO("Loading plane calibration parameters for all cameras:\n");
|
||||
for (const auto& cameraParam : m_cameraCalibParams) {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
#define GRABBAG_VERSION_STRING "1.2.0"
|
||||
#define GRABBAG_BUILD_STRING "1"
|
||||
#define GRABBAG_FULL_VERSION_STRING "V1.2.0_1"
|
||||
#define GRABBAG_VERSION_STRING "1.2.1"
|
||||
#define GRABBAG_BUILD_STRING "0"
|
||||
#define GRABBAG_FULL_VERSION_STRING "V1.2.1_0"
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
inline const char* GetGrabBagVersion() {
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
# 1.2.1 2025-10-8
|
||||
## build_0
|
||||
1. 更新算法。增加支持旋转参数。
|
||||
|
||||
# 1.2.0 2025-9-15
|
||||
## build_1
|
||||
1. 增加相机持续连接功能
|
||||
|
||||
@ -276,6 +276,7 @@ struct VrAlgorithmParams
|
||||
VrHsvCmpParam hsvCmpParam; // HSV颜色比较参数
|
||||
VrRgbColorPattern rgbColorPattern; // RGB颜色模式
|
||||
VrColorTemplateParam colorTemplateParam; // 颜色模板参数
|
||||
int supportRotate; // 是否支持旋转功能
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -172,6 +172,19 @@ ConfigResult CVrConfig::LoadConfig(const std::string& filePath)
|
||||
result.algorithmParams.growParam.minVTypeTreeLen = growParamElement->DoubleAttribute("minVTypeTreeLen");
|
||||
}
|
||||
|
||||
// 解析支持旋转参数
|
||||
XMLElement* supportRotateElement = algoParamsElement->FirstChildElement("SupportRotate");
|
||||
if (supportRotateElement)
|
||||
{
|
||||
if (supportRotateElement->Attribute("enabled"))
|
||||
result.algorithmParams.supportRotate = supportRotateElement->IntAttribute("enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认值为0,表示不支持旋转
|
||||
result.algorithmParams.supportRotate = 0;
|
||||
}
|
||||
|
||||
// 解析HSV颜色比较参数
|
||||
XMLElement* hsvCmpParamElement = algoParamsElement->FirstChildElement("HsvCmpParam");
|
||||
if (hsvCmpParamElement)
|
||||
@ -506,6 +519,11 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
growParamElement->SetAttribute("minVTypeTreeLen", configResult.algorithmParams.growParam.minVTypeTreeLen);
|
||||
algoParamsElement->InsertEndChild(growParamElement);
|
||||
|
||||
// 添加支持旋转参数
|
||||
XMLElement* supportRotateElement = doc.NewElement("SupportRotate");
|
||||
supportRotateElement->SetAttribute("enabled", configResult.algorithmParams.supportRotate);
|
||||
algoParamsElement->InsertEndChild(supportRotateElement);
|
||||
|
||||
// 添加多相机平面校准参数
|
||||
XMLElement* planeCalibParamsElement = doc.NewElement("PlaneCalibParams");
|
||||
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
<GrowParam maxLineSkipNum="5" yDeviation_max="20.0" maxSkipDistance="20.0"
|
||||
zDeviation_max="80.0" minLTypeTreeLen="50.0" minVTypeTreeLen="50.0" />
|
||||
|
||||
<!-- 支持旋转参数 -->
|
||||
<SupportRotate enabled="0" />
|
||||
|
||||
<!-- HSV颜色比较参数 -->
|
||||
<HsvCmpParam hueTh="15.0" saturateTh="120.0" FBVldPtRatioTh="0.075"
|
||||
frontVldPtGreater="true" front_upVldPtGreater="false" back_upVldPtGreater="true" />
|
||||
|
||||
@ -25,3 +25,6 @@ SUBDIRS += ../App/BeltTearing/BeltTearing.pro
|
||||
#焊接
|
||||
SUBDIRS += ../App/LapWeld/LapWeld.pro
|
||||
|
||||
# Test 测试
|
||||
# SUBDIRS += ../Test/Test.pro
|
||||
|
||||
|
||||
29
QtUtils/Inc/CrashHandler.h
Normal file
29
QtUtils/Inc/CrashHandler.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef CRASHHANDLER_H
|
||||
#define CRASHHANDLER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <dbghelp.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class CrashHandler
|
||||
{
|
||||
public:
|
||||
static void registerCrashHandler();
|
||||
static void setDumpPath(const QString& path);
|
||||
static QString getDumpPath();
|
||||
|
||||
private:
|
||||
static QString dumpPath;
|
||||
static void crashHandlerFunction(int sig);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static LONG WINAPI windowsCrashHandler(struct _EXCEPTION_POINTERS* exceptionInfo);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CRASHHANDLER_H
|
||||
@ -8,13 +8,20 @@ win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# For Windows crash dump generation and MessageBox functions
|
||||
win32-msvc {
|
||||
LIBS += -lDbgHelp -luser32
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
|
||||
HEADERS += \
|
||||
Inc/StyledMessageBox.h
|
||||
Inc/StyledMessageBox.h \
|
||||
Inc/CrashHandler.h
|
||||
|
||||
SOURCES += \
|
||||
Src/StyledMessageBox.cpp
|
||||
Src/StyledMessageBox.cpp \
|
||||
Src/CrashHandler.cpp
|
||||
|
||||
# Default rules for deployment.
|
||||
unix {
|
||||
|
||||
199
QtUtils/Src/CrashHandler.cpp
Normal file
199
QtUtils/Src/CrashHandler.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
#include "CrashHandler.h"
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <dbghelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QString CrashHandler::dumpPath;
|
||||
|
||||
void CrashHandler::registerCrashHandler()
|
||||
{
|
||||
signal(SIGSEGV, crashHandlerFunction); // 段错误
|
||||
signal(SIGABRT, crashHandlerFunction); // abort调用
|
||||
signal(SIGFPE, crashHandlerFunction); // 浮点异常
|
||||
signal(SIGILL, crashHandlerFunction); // 非法指令
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
SetUnhandledExceptionFilter(windowsCrashHandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CrashHandler::setDumpPath(const QString& path)
|
||||
{
|
||||
dumpPath = path;
|
||||
QDir dir(dumpPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(dumpPath);
|
||||
}
|
||||
}
|
||||
|
||||
QString CrashHandler::getDumpPath()
|
||||
{
|
||||
if (dumpPath.isEmpty()) {
|
||||
dumpPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/crash_dumps";
|
||||
QDir dir(dumpPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(dumpPath);
|
||||
}
|
||||
}
|
||||
return dumpPath;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
LONG WINAPI CrashHandler::windowsCrashHandler(struct _EXCEPTION_POINTERS* exceptionInfo)
|
||||
{
|
||||
// 使用 C 函数生成时间戳,避免依赖 Qt(可能已损坏)
|
||||
time_t now = time(NULL);
|
||||
struct tm timeinfo;
|
||||
localtime_s(&timeinfo, &now);
|
||||
|
||||
char timestamp[64];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo);
|
||||
|
||||
// 获取dump路径
|
||||
QString dumpDir = CrashHandler::getDumpPath();
|
||||
|
||||
// 创建目录(如果不存在)
|
||||
CreateDirectoryW((WCHAR*)dumpDir.utf16(), NULL);
|
||||
|
||||
// 构建文件路径
|
||||
QString dumpFilePath = dumpDir + "/crash_dump_" + QString(timestamp) + ".dmp";
|
||||
QString logFilePath = dumpDir + "/crash_log_" + QString(timestamp) + ".txt";
|
||||
|
||||
// 先写日志文件
|
||||
FILE* logFile = _wfopen((WCHAR*)logFilePath.utf16(), L"w");
|
||||
if (logFile) {
|
||||
fprintf(logFile, "Application crashed at: %s\n", timestamp);
|
||||
fprintf(logFile, "Exception Code: 0x%08X\n", exceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
fprintf(logFile, "Exception Address: 0x%p\n", exceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
fprintf(logFile, "Dump file: %s\n", dumpFilePath.toLocal8Bit().constData());
|
||||
fclose(logFile);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// 创建 dump 文件
|
||||
HANDLE hFile = CreateFileW((WCHAR*)dumpFilePath.utf16(),
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION exceptionInfoEx;
|
||||
exceptionInfoEx.ThreadId = GetCurrentThreadId();
|
||||
exceptionInfoEx.ExceptionPointers = exceptionInfo;
|
||||
exceptionInfoEx.ClientPointers = FALSE;
|
||||
|
||||
// 使用更详细的 dump 类型
|
||||
MINIDUMP_TYPE dumpType = (MINIDUMP_TYPE)(
|
||||
MiniDumpWithDataSegs |
|
||||
MiniDumpWithHandleData |
|
||||
MiniDumpWithThreadInfo);
|
||||
|
||||
BOOL success = MiniDumpWriteDump(
|
||||
GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
hFile,
|
||||
dumpType,
|
||||
&exceptionInfoEx,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
CloseHandle(hFile);
|
||||
|
||||
if (success) {
|
||||
// 成功写入,显示消息框
|
||||
wchar_t msg[512];
|
||||
swprintf_s(msg, 512, L"程序崩溃!\n\nDump文件已保存至:\n%s\n\n请联系技术支持。",
|
||||
(WCHAR*)dumpFilePath.utf16());
|
||||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
wchar_t msg[256];
|
||||
swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(错误代码: %d)\n路径: %s",
|
||||
error, (WCHAR*)dumpFilePath.utf16());
|
||||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
}
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
wchar_t msg[256];
|
||||
swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(文件创建失败,错误代码: %d)\n路径: %s",
|
||||
error, (WCHAR*)dumpFilePath.utf16());
|
||||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
}
|
||||
#else
|
||||
// 非 MSVC 编译器,只显示崩溃信息
|
||||
wchar_t msg[256];
|
||||
swprintf_s(msg, 256, L"程序崩溃!\n\n日志文件: %s", (WCHAR*)logFilePath.utf16());
|
||||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
#endif
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CrashHandler::crashHandlerFunction(int sig)
|
||||
{
|
||||
// 使用 C 函数生成时间戳,避免依赖可能已损坏的 Qt
|
||||
time_t now = time(NULL);
|
||||
struct tm timeinfo;
|
||||
#ifdef Q_OS_WIN
|
||||
localtime_s(&timeinfo, &now);
|
||||
#else
|
||||
localtime_r(&now, &timeinfo);
|
||||
#endif
|
||||
|
||||
char timestamp[64];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo);
|
||||
|
||||
QString dumpDir = getDumpPath();
|
||||
QString dumpFilePath = dumpDir + "/crash_signal_" + QString(timestamp) + ".txt";
|
||||
|
||||
FILE* file = fopen(dumpFilePath.toLocal8Bit().constData(), "w");
|
||||
if (file) {
|
||||
fprintf(file, "Crash occurred at: %s\n", timestamp);
|
||||
fprintf(file, "Signal: %d (", sig);
|
||||
switch(sig) {
|
||||
case SIGSEGV: fprintf(file, "SIGSEGV - Segmentation fault"); break;
|
||||
case SIGABRT: fprintf(file, "SIGABRT - Abort signal"); break;
|
||||
case SIGFPE: fprintf(file, "SIGFPE - Floating point exception"); break;
|
||||
case SIGILL: fprintf(file, "SIGILL - Illegal instruction"); break;
|
||||
default: fprintf(file, "Unknown signal"); break;
|
||||
}
|
||||
fprintf(file, ")\n");
|
||||
|
||||
if (qApp) {
|
||||
fprintf(file, "Application: %s\n", qApp->applicationName().toLocal8Bit().constData());
|
||||
fprintf(file, "Version: %s\n", qApp->applicationVersion().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t msg[512];
|
||||
swprintf_s(msg, 512, L"程序崩溃(信号 %d)!\n\n日志文件已保存至:\n%s",
|
||||
sig, (WCHAR*)dumpFilePath.utf16());
|
||||
MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST);
|
||||
#endif
|
||||
}
|
||||
|
||||
// 调用默认的信号处理函数来终止程序
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
}
|
||||
@ -34,6 +34,7 @@ typedef struct
|
||||
SSG_treeGrowParam growParam;
|
||||
//SSG_objSortParam sortParam;
|
||||
//SSG_polarScanParam polarScanParam;
|
||||
int supportRotate;
|
||||
}SG_bagPositionParam;
|
||||
|
||||
typedef struct
|
||||
|
||||
@ -219,6 +219,7 @@ typedef struct
|
||||
double tree_value;
|
||||
SSG_ROIRectD roi;
|
||||
std::vector< SSG_basicFeature1D> treeNodes;
|
||||
int angleChkScalePos; //仅用于加速angleCheck的速度
|
||||
}SSG_featureTree;
|
||||
|
||||
typedef struct
|
||||
|
||||
@ -6,3 +6,7 @@
|
||||
#define SG_ERR_NOT_GRID_FORMAT -1003
|
||||
#define SG_ERR_LABEL_INFO_ERROR -1004
|
||||
#define SG_ERR_INVLD_SORTING_MODE -1005
|
||||
|
||||
//BQ_workpiece
|
||||
#define SX_ERR_INVLD_VTREE_NUM -2001
|
||||
#define SX_ERR_INVLD_HTREE_NUM -2002
|
||||
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.
5
Test/Test.pro
Normal file
5
Test/Test.pro
Normal file
@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
# 撕裂项目
|
||||
SUBDIRS += tcpclient/tcpclient_test.pro
|
||||
SUBDIRS += tcpserver/tcpserver_test.pro
|
||||
35
Test/camera/CMakeLists.txt
Normal file
35
Test/camera/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(CameraDemoTest)
|
||||
|
||||
# Set C++ standard
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
|
||||
|
||||
# Include directories
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/../../VrCommon/Inc
|
||||
${CMAKE_SOURCE_DIR}/../../SDK/VzNLSDK/Inc
|
||||
${CMAKE_SOURCE_DIR}/../../SDK/VzNLSDK/_Inc
|
||||
)
|
||||
LINK_DIRECTORIES(
|
||||
${CMAKE_SOURCE_DIR}/../../SDK/VzNLSDK/Arm/aarch64
|
||||
)
|
||||
|
||||
|
||||
# Add source files
|
||||
set(SOURCES
|
||||
test.cpp
|
||||
)
|
||||
|
||||
# Create executable
|
||||
add_executable(CameraDemoTest ${SOURCES} )
|
||||
|
||||
# Link libraries (you may need to adjust these paths based on your actual library locations)
|
||||
target_link_libraries(CameraDemoTest
|
||||
VzKernel
|
||||
VzNLDetect
|
||||
VzNLGraphics
|
||||
)
|
||||
300
Test/camera/test.cpp
Normal file
300
Test/camera/test.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
// VzNLSDK includes
|
||||
#include "../../SDK/VzNLSDK/Inc/VZNL_Common.h"
|
||||
#include "../../SDK/VzNLSDK/Inc/VZNL_Types.h"
|
||||
#include "../../SDK/VzNLSDK/Inc/VZNL_DetectLaser.h"
|
||||
#include "../../SDK/VzNLSDK/Inc/VZNL_RGBConfig.h"
|
||||
#include "../../SDK/VzNLSDK/Inc/VZNL_SwingMotor.h"
|
||||
|
||||
class CameraDemoTest
|
||||
{
|
||||
private:
|
||||
VZNLHANDLE m_hDevice;
|
||||
std::atomic<bool> m_bScanning;
|
||||
std::atomic<int> m_nScanDataCount;
|
||||
bool m_isRGBD;
|
||||
|
||||
public:
|
||||
CameraDemoTest()
|
||||
: m_hDevice(nullptr)
|
||||
, m_bScanning(false)
|
||||
, m_nScanDataCount(0)
|
||||
, m_isRGBD(false)
|
||||
{
|
||||
}
|
||||
|
||||
~CameraDemoTest()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
// Initialize camera
|
||||
bool InitializeCamera(bool isRGBD)
|
||||
{
|
||||
m_isRGBD = isRGBD;
|
||||
int nErrCode = 0;
|
||||
|
||||
// 初始化SDK
|
||||
SVzNLConfigParam sVzNlConfigParam;
|
||||
sVzNlConfigParam.nDeviceTimeOut = 5000; // 5 seconds timeout
|
||||
nErrCode = VzNL_Init(&sVzNlConfigParam);
|
||||
|
||||
// 搜索设备
|
||||
VzNL_ResearchDevice(keSearchDeviceFlag_EthLaserRobotEye);
|
||||
|
||||
// 获取搜索到的设备
|
||||
SVzNLEyeCBInfo* pCBInfo = nullptr;
|
||||
int nCount = 0;
|
||||
nErrCode = VzNL_GetEyeCBDeviceInfo(nullptr, &nCount);
|
||||
if (0 != nErrCode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(0 == nCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pCBInfo = new SVzNLEyeCBInfo[nCount];
|
||||
if (!pCBInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nErrCode = VzNL_GetEyeCBDeviceInfo(pCBInfo, &nCount);
|
||||
if (0 != nErrCode) {
|
||||
delete[] pCBInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
SVzNLOpenDeviceParam sVzNlOpenDeviceParam;
|
||||
sVzNlOpenDeviceParam.eEyeResolution = keVzNLEyeResolution_Unknown;
|
||||
sVzNlOpenDeviceParam.eSDKType = keSDKType_DetectLaser;
|
||||
sVzNlOpenDeviceParam.bStopStream = VzTrue;
|
||||
|
||||
m_hDevice = VzNL_OpenDevice(&pCBInfo[0], &sVzNlOpenDeviceParam, &nErrCode);
|
||||
|
||||
|
||||
// 清理pCBInfo数组
|
||||
delete[] pCBInfo;
|
||||
pCBInfo = nullptr;
|
||||
|
||||
|
||||
if(nullptr == m_hDevice)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(0 != nErrCode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SVzNLEyeDeviceInfoEx m_sEeyCBDeviceInfo;
|
||||
m_sEeyCBDeviceInfo.sEyeCBInfo.nSize = sizeof(SVzNLEyeDeviceInfoEx);
|
||||
VzNL_GetDeviceInfo(m_hDevice, (SVzNLEyeCBInfo *)(&m_sEeyCBDeviceInfo));
|
||||
|
||||
VzNL_BeginDetectLaser(m_hDevice);
|
||||
VzNL_EnableSwingMotor(m_hDevice, VzTrue);
|
||||
|
||||
SVzNLVersionInfo sVersionInfo;
|
||||
VzNL_GetVersion(m_hDevice, &sVersionInfo);
|
||||
std::cout << "version : " << sVersionInfo.szSDKVersion << std::endl;
|
||||
|
||||
// int nnnRet = VzNL_SetEthSendDataLength(m_pHandle, 1024);
|
||||
// LOG_DEBUG("SenddataLen ret : %d\n", nnnRet);
|
||||
|
||||
//启用RGBD
|
||||
int nRGBRet = VzNL_EnableRGB(m_hDevice, isRGBD ? VzTrue : VzFalse);
|
||||
std::cout << "Enable RGB " << (isRGBD ? "true" : "false") << " ret : " << nRGBRet << std::endl;
|
||||
|
||||
// 填充
|
||||
VzNL_EnableFillLaserPoint(m_hDevice, VzTrue);
|
||||
|
||||
VzNL_SetDeviceStatusNotifyEx(m_hDevice, StatusCallback, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Start scanning
|
||||
bool StartScanning()
|
||||
{
|
||||
std::cout << "\n--- Starting Scan ---" << std::endl;
|
||||
|
||||
if (!m_hDevice)
|
||||
{
|
||||
std::cout << "Error: Device not opened" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear previous data
|
||||
m_nScanDataCount = 0;
|
||||
|
||||
// Select data type based on scan mode
|
||||
EVzResultDataType dataType;
|
||||
dataType = m_isRGBD ? keResultDataType_PointXYZRGBA : keResultDataType_Position;
|
||||
|
||||
m_bScanning = true;
|
||||
|
||||
// Start automatic detection
|
||||
int ret = VzNL_StartAutoDetectEx(m_hDevice, dataType, keFlipType_None, ScanDataCallback, this);
|
||||
if (ret != 0)
|
||||
{
|
||||
m_bScanning = false;
|
||||
std::cout << "Error: Failed to start scanning, error code: " << ret << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "✓ Scanning started, data type: " << (m_isRGBD ? "RGBD" : "Position") << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop scanning
|
||||
bool StopScanning()
|
||||
{
|
||||
std::cout << "\n--- Stopping Scan ---" << std::endl;
|
||||
|
||||
if (!m_hDevice)
|
||||
{
|
||||
std::cout << "Error: Device not opened" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bScanning = false;
|
||||
|
||||
int ret = VzNL_StopAutoDetect(m_hDevice);
|
||||
if (ret != 0)
|
||||
{
|
||||
std::cout << "Error: Failed to stop scanning, error code: " << ret << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
VzNL_EndDetectLaser(m_hDevice);
|
||||
|
||||
std::cout << "✓ Scanning stopped" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsScanning()
|
||||
{
|
||||
return m_bScanning.load();
|
||||
}
|
||||
|
||||
// Get scan data count
|
||||
int GetScanDataCount()
|
||||
{
|
||||
return m_nScanDataCount.load();
|
||||
}
|
||||
|
||||
// Cleanup resources
|
||||
void Cleanup()
|
||||
{
|
||||
if (m_hDevice)
|
||||
{
|
||||
if (m_bScanning)
|
||||
{
|
||||
VzNL_StopAutoDetect(m_hDevice);
|
||||
}
|
||||
VzNL_CloseDevice(m_hDevice);
|
||||
m_hDevice = nullptr;
|
||||
}
|
||||
|
||||
// Destroy SDK
|
||||
VzNL_Destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
// Status callback function
|
||||
static void StatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pParam)
|
||||
{
|
||||
CameraDemoTest* pThis = static_cast<CameraDemoTest*>(pParam);
|
||||
if (!pThis) return;
|
||||
|
||||
std::cout << "Device status change: ";
|
||||
switch (eStatus)
|
||||
{
|
||||
case keDeviceWorkStatus_Eye_Comming:
|
||||
std::cout << "Eye connected" << std::endl;
|
||||
break;
|
||||
case keDeviceWorkStatus_Eye_Reconnect:
|
||||
std::cout << "Eye reconnected" << std::endl;
|
||||
break;
|
||||
case keDeviceWorkStatus_Working:
|
||||
std::cout << "Working" << std::endl;
|
||||
break;
|
||||
case keDeviceWorkStatus_Offline:
|
||||
std::cout << "Offline" << std::endl;
|
||||
break;
|
||||
case keDeviceWorkStatus_Device_Swing_Finish:
|
||||
std::cout << "Swing module completed" << std::endl;
|
||||
break;
|
||||
case keDeviceWorkStatus_Device_Auto_Stop:
|
||||
std::cout << "Device auto stopped, total scan data: " << pThis->GetScanDataCount() << std::endl;
|
||||
pThis->m_bScanning = false;
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown status: " << eStatus << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scan data callback function
|
||||
static void ScanDataCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pParam)
|
||||
{
|
||||
CameraDemoTest* pThis = static_cast<CameraDemoTest*>(pParam);
|
||||
if (!pThis || !pLaserLinePoint) return;
|
||||
|
||||
// Increment data count
|
||||
pThis->m_nScanDataCount++;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::cout << "Camera Demo Test Program (Direct VzNLSDK)" << std::endl;
|
||||
std::cout << "=========================================" << std::endl;
|
||||
|
||||
CameraDemoTest cameraTest;
|
||||
|
||||
// Check command line argument for RGBD mode
|
||||
bool isRGBD = (argc > 1 && std::string(argv[1]) == "rgbd");
|
||||
std::cout << "Scan mode: " << (isRGBD ? "RGBD" : "Position") << std::endl;
|
||||
|
||||
// Initialize camera
|
||||
if (!cameraTest.InitializeCamera(isRGBD))
|
||||
{
|
||||
std::cout << "Failed to initialize camera" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start scanning
|
||||
if (!cameraTest.StartScanning())
|
||||
{
|
||||
std::cout << "Failed to start scanning" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Wait for scanning to complete
|
||||
std::cout << "Scanning in progress..." << std::endl;
|
||||
while (cameraTest.IsScanning())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
// Stop scanning
|
||||
cameraTest.StopScanning();
|
||||
|
||||
// Display final results
|
||||
std::cout << "\n=== Final Results ===" << std::endl;
|
||||
std::cout << "Total scan data received: " << cameraTest.GetScanDataCount() << std::endl;
|
||||
|
||||
std::cout << "\nAll tests completed!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
64
Test/crash/test_crash.cpp
Normal file
64
Test/crash/test_crash.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QDebug>
|
||||
#include "../QtUtils/Inc/CrashHandler.h"
|
||||
|
||||
void testNullPointerCrash() {
|
||||
int* p = nullptr;
|
||||
*p = 42; // 触发访问违规异常
|
||||
}
|
||||
|
||||
void testDivisionByZero() {
|
||||
volatile int a = 10;
|
||||
volatile int b = 0;
|
||||
volatile int c = a / b; // 浮点异常
|
||||
qDebug() << c;
|
||||
}
|
||||
|
||||
void testAbort() {
|
||||
abort(); // 触发 SIGABRT
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName("CrashTest");
|
||||
app.setApplicationVersion("1.0");
|
||||
|
||||
// 注册崩溃处理器
|
||||
CrashHandler::setDumpPath("D:/crash_dumps");
|
||||
CrashHandler::registerCrashHandler();
|
||||
|
||||
QWidget window;
|
||||
QVBoxLayout* layout = new QVBoxLayout(&window);
|
||||
|
||||
QPushButton* btnNullPtr = new QPushButton("测试空指针崩溃(访问违规)");
|
||||
QPushButton* btnAbort = new QPushButton("测试 Abort 崩溃");
|
||||
QPushButton* btnNormal = new QPushButton("正常退出");
|
||||
|
||||
layout->addWidget(btnNullPtr);
|
||||
layout->addWidget(btnAbort);
|
||||
layout->addWidget(btnNormal);
|
||||
|
||||
QObject::connect(btnNullPtr, &QPushButton::clicked, []() {
|
||||
qDebug() << "触发空指针崩溃...";
|
||||
testNullPointerCrash();
|
||||
});
|
||||
|
||||
QObject::connect(btnAbort, &QPushButton::clicked, []() {
|
||||
qDebug() << "触发 abort 崩溃...";
|
||||
testAbort();
|
||||
});
|
||||
|
||||
QObject::connect(btnNormal, &QPushButton::clicked, &app, &QApplication::quit);
|
||||
|
||||
window.setWindowTitle("Crash Handler 测试");
|
||||
window.resize(300, 150);
|
||||
window.show();
|
||||
|
||||
qDebug() << "崩溃处理器已注册,dump路径:" << CrashHandler::getDumpPath();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
26
Test/crash/test_crash.pro
Normal file
26
Test/crash/test_crash.pro
Normal file
@ -0,0 +1,26 @@
|
||||
QT += core gui widgets
|
||||
|
||||
TEMPLATE = app
|
||||
CONFIG += c++17
|
||||
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# For Windows crash dump generation (only for MSVC)
|
||||
win32-msvc {
|
||||
LIBS += -lDbgHelp
|
||||
}
|
||||
|
||||
INCLUDEPATH += ../QtUtils/Inc
|
||||
|
||||
# Link QtUtils library
|
||||
win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../QtUtils/debug -lQtUtils
|
||||
} else:win32:CONFIG(release, debug|release) {
|
||||
LIBS += -L../QtUtils/release -lQtUtils
|
||||
}
|
||||
|
||||
SOURCES += test_crash.cpp
|
||||
|
||||
TARGET = test_crash
|
||||
391
Test/tcpclient/tcp_client_test.cpp
Normal file
391
Test/tcpclient/tcp_client_test.cpp
Normal file
@ -0,0 +1,391 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
|
||||
// 包含TCPClient头文件
|
||||
#include "IVrTCPClient.h"
|
||||
|
||||
class TCPClientTest
|
||||
{
|
||||
private:
|
||||
IVrTCPClient* m_pClient;
|
||||
std::atomic<bool> m_bRunning;
|
||||
std::atomic<bool> m_bConnected;
|
||||
std::string m_sServerIP;
|
||||
int m_nPort;
|
||||
std::atomic<int> m_nRecvCount;
|
||||
std::atomic<int> m_nSendCount;
|
||||
std::atomic<size_t> m_nTotalRecvBytes;
|
||||
std::thread m_sendThread;
|
||||
std::thread m_statsThread;
|
||||
std::mutex m_consoleMutex;
|
||||
std::chrono::steady_clock::time_point m_startTime;
|
||||
|
||||
// 测试配置
|
||||
bool m_bAutoSend; // 是否自动发送测试数据
|
||||
int m_nSendInterval; // 发送间隔(毫秒)
|
||||
int m_nSendSize; // 每次发送的数据大小(字节)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param serverIP 服务器IP地址
|
||||
* @param port 服务器端口
|
||||
* @param autoSend 是否自动发送测试数据
|
||||
* @param sendInterval 发送间隔(毫秒)
|
||||
* @param sendSize 每次发送的数据大小(字节)
|
||||
*/
|
||||
TCPClientTest(const std::string& serverIP, int port, bool autoSend = false,
|
||||
int sendInterval = 1000, int sendSize = 64)
|
||||
: m_pClient(nullptr), m_bRunning(false), m_bConnected(false),
|
||||
m_sServerIP(serverIP), m_nPort(port), m_nRecvCount(0),
|
||||
m_nSendCount(0), m_nTotalRecvBytes(0),
|
||||
m_bAutoSend(autoSend), m_nSendInterval(sendInterval), m_nSendSize(sendSize)
|
||||
{
|
||||
// 创建TCPClient实例
|
||||
m_pClient = IVrTCPClient::CreateInstance();
|
||||
if (!m_pClient) {
|
||||
std::cerr << "❌ Failed to create TCPClient instance" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TCPClientTest()
|
||||
{
|
||||
Stop();
|
||||
|
||||
// 等待线程结束
|
||||
if (m_sendThread.joinable()) {
|
||||
m_sendThread.join();
|
||||
}
|
||||
if (m_statsThread.joinable()) {
|
||||
m_statsThread.join();
|
||||
}
|
||||
|
||||
if (m_pClient) {
|
||||
m_pClient->CloseDevice();
|
||||
IVrTCPClient::DestroyInstance(m_pClient);
|
||||
m_pClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化客户端
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool Initialize()
|
||||
{
|
||||
if (!m_pClient) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintLog("🔌 Connecting to " + m_sServerIP + ":" + std::to_string(m_nPort));
|
||||
|
||||
// 连接设备
|
||||
int result = m_pClient->LinkDevice(m_sServerIP, m_nPort, true,
|
||||
[this](IVrTCPClient* pClient, bool connected, void* pParam) {
|
||||
this->OnConnectionEvent(pClient, connected, pParam);
|
||||
}, nullptr);
|
||||
|
||||
if (result != 0) {
|
||||
PrintLog("❌ Failed to connect to server, error code: " + std::to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 启动工作线程
|
||||
result = m_pClient->StartWork(
|
||||
[this](IVrTCPClient* pClient, const char* pData, const int nLen, void* pParam) {
|
||||
this->OnDataReceived(pClient, pData, nLen, pParam);
|
||||
}, nullptr);
|
||||
|
||||
if (result != 0) {
|
||||
PrintLog("❌ Failed to start client work thread, error code: " + std::to_string(result));
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintLog("✅ Client initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动客户端
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool Start()
|
||||
{
|
||||
m_bRunning = true;
|
||||
m_startTime = std::chrono::steady_clock::now();
|
||||
|
||||
// 启动统计线程
|
||||
m_statsThread = std::thread(&TCPClientTest::StatsLoop, this);
|
||||
|
||||
// 如果启用自动发送,启动发送线程
|
||||
if (m_bAutoSend) {
|
||||
m_sendThread = std::thread(&TCPClientTest::SendDataLoop, this);
|
||||
PrintLog("📤 Auto-send enabled: " + std::to_string(m_nSendSize) +
|
||||
" bytes every " + std::to_string(m_nSendInterval) + " ms");
|
||||
}
|
||||
|
||||
PrintLog("🚀 Client started, waiting for data...");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止客户端
|
||||
*/
|
||||
void Stop()
|
||||
{
|
||||
m_bRunning = false;
|
||||
|
||||
// 等待线程结束
|
||||
if (m_sendThread.joinable()) {
|
||||
m_sendThread.join();
|
||||
}
|
||||
if (m_statsThread.joinable()) {
|
||||
m_statsThread.join();
|
||||
}
|
||||
|
||||
PrintLog("🛑 Client stopped");
|
||||
PrintFinalStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取接收计数
|
||||
* @return 接收的消息数量
|
||||
*/
|
||||
int GetRecvCount() const
|
||||
{
|
||||
return m_nRecvCount.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取发送计数
|
||||
* @return 发送的消息数量
|
||||
*/
|
||||
int GetSendCount() const
|
||||
{
|
||||
return m_nSendCount.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取连接状态
|
||||
* @return true表示已连接,false表示未连接
|
||||
*/
|
||||
bool IsConnected() const
|
||||
{
|
||||
return m_bConnected.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 手动发送测试数据
|
||||
* @param message 要发送的消息
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool SendTestData(const std::string& message)
|
||||
{
|
||||
if (!m_pClient || !m_bConnected) {
|
||||
PrintLog("⚠️ Cannot send: not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = m_pClient->SendData(message.c_str(), static_cast<int>(message.length()));
|
||||
if (result) {
|
||||
m_nSendCount++;
|
||||
PrintLog("📤 Sent: " + message + " (" + std::to_string(message.length()) + " bytes)");
|
||||
} else {
|
||||
PrintLog("❌ Failed to send data");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 连接状态回调函数
|
||||
* @param pClient 客户端指针
|
||||
* @param connected 是否连接
|
||||
* @param pParam 参数指针
|
||||
*/
|
||||
void OnConnectionEvent(IVrTCPClient* pClient, bool connected, void* pParam)
|
||||
{
|
||||
m_bConnected = connected;
|
||||
if (connected) {
|
||||
PrintLog("✅ Connected to server");
|
||||
} else {
|
||||
PrintLog("❌ Disconnected from server (Auto-reconnect enabled)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 数据接收回调函数
|
||||
* @param pClient 客户端指针
|
||||
* @param pData 数据指针
|
||||
* @param nLen 数据长度
|
||||
* @param pParam 参数指针
|
||||
*/
|
||||
void OnDataReceived(IVrTCPClient* pClient, const char* pData, const int nLen, void* pParam)
|
||||
{
|
||||
m_nRecvCount++;
|
||||
m_nTotalRecvBytes += nLen;
|
||||
|
||||
// 打印接收到的数据(限制长度)
|
||||
std::string data(pData, std::min(nLen, 100));
|
||||
if (nLen > 100) {
|
||||
data += "...";
|
||||
}
|
||||
PrintLog("📥 Received #" + std::to_string(m_nRecvCount.load()) + ": [" + std::to_string(nLen) + " bytes] " + data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动发送数据循环
|
||||
*/
|
||||
void SendDataLoop()
|
||||
{
|
||||
int counter = 0;
|
||||
while (m_bRunning) {
|
||||
if (m_bConnected && m_pClient) {
|
||||
// 构造测试数据
|
||||
std::string message = "Client-Test-" + std::to_string(++counter);
|
||||
// 填充到指定大小
|
||||
if (message.length() < static_cast<size_t>(m_nSendSize)) {
|
||||
message.append(m_nSendSize - message.length(), 'X');
|
||||
} else if (message.length() > static_cast<size_t>(m_nSendSize)) {
|
||||
message.resize(m_nSendSize);
|
||||
}
|
||||
|
||||
bool result = m_pClient->SendData(message.c_str(), static_cast<int>(message.length()));
|
||||
if (result) {
|
||||
m_nSendCount++;
|
||||
} else {
|
||||
PrintLog("❌ Failed to send data");
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(m_nSendInterval));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 统计信息循环打印
|
||||
*/
|
||||
void StatsLoop()
|
||||
{
|
||||
while (m_bRunning) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
if (m_bRunning) {
|
||||
PrintStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 打印当前统计信息
|
||||
*/
|
||||
void PrintStats()
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - m_startTime).count();
|
||||
if (duration == 0) duration = 1; // 避免除零
|
||||
|
||||
double recvRate = static_cast<double>(m_nRecvCount.load()) / duration;
|
||||
double sendRate = static_cast<double>(m_nSendCount.load()) / duration;
|
||||
double bandwidth = static_cast<double>(m_nTotalRecvBytes.load()) / duration / 1024.0; // KB/s
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "\n📊 Statistics (Running " << duration << "s)"
|
||||
<< "\n Connected: " << (m_bConnected ? "Yes" : "No")
|
||||
<< "\n Received: " << m_nRecvCount.load() << " msgs (" << std::fixed << std::setprecision(2) << recvRate << " msg/s)"
|
||||
<< "\n Sent: " << m_nSendCount.load() << " msgs (" << std::fixed << std::setprecision(2) << sendRate << " msg/s)"
|
||||
<< "\n Bandwidth: " << std::fixed << std::setprecision(2) << bandwidth << " KB/s"
|
||||
<< "\n Total Recv: " << m_nTotalRecvBytes.load() << " bytes";
|
||||
|
||||
PrintLog(oss.str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 打印最终统计信息
|
||||
*/
|
||||
void PrintFinalStats()
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - m_startTime).count();
|
||||
if (duration == 0) duration = 1;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "\n📈 Final Statistics:"
|
||||
<< "\n Total Runtime: " << duration << " seconds"
|
||||
<< "\n Total Received: " << m_nRecvCount.load() << " messages"
|
||||
<< "\n Total Sent: " << m_nSendCount.load() << " messages"
|
||||
<< "\n Total Bytes Received: " << m_nTotalRecvBytes.load() << " bytes"
|
||||
<< "\n Average Receive Rate: " << std::fixed << std::setprecision(2)
|
||||
<< (static_cast<double>(m_nRecvCount.load()) / duration) << " msg/s"
|
||||
<< "\n Average Send Rate: " << std::fixed << std::setprecision(2)
|
||||
<< (static_cast<double>(m_nSendCount.load()) / duration) << " msg/s";
|
||||
|
||||
PrintLog(oss.str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 线程安全的日志打印
|
||||
* @param message 日志消息
|
||||
*/
|
||||
void PrintLog(const std::string& message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_consoleMutex);
|
||||
auto now = std::time(nullptr);
|
||||
char timeStr[20];
|
||||
std::strftime(timeStr, sizeof(timeStr), "%H:%M:%S", std::localtime(&now));
|
||||
std::cout << "[" << timeStr << "] " << message << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::cout << "TCP Client Test Program" << std::endl;
|
||||
std::cout << "=======================" << std::endl;
|
||||
|
||||
// 默认连接参数
|
||||
std::string serverIP = "127.0.0.1";
|
||||
int port = 8080;
|
||||
|
||||
if (argc > 1) {
|
||||
serverIP = argv[1];
|
||||
}
|
||||
if (argc > 2) {
|
||||
port = std::stoi(argv[2]);
|
||||
}
|
||||
|
||||
TCPClientTest clientTest(serverIP, port);
|
||||
|
||||
// 初始化客户端
|
||||
if (!clientTest.Initialize()) {
|
||||
std::cerr << "Failed to initialize client" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 启动客户端
|
||||
if (!clientTest.Start()) {
|
||||
std::cerr << "Failed to start client" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 运行一段时间或等待用户输入
|
||||
std::cout << "Client is running. Press Enter to stop..." << std::endl;
|
||||
while(true){
|
||||
std::cin.get();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
|
||||
// 停止客户端
|
||||
clientTest.Stop();
|
||||
|
||||
std::cout << "TCP Client Test completed!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
39
Test/tcpclient/tcpclient_test.pro
Normal file
39
Test/tcpclient/tcpclient_test.pro
Normal file
@ -0,0 +1,39 @@
|
||||
# TCP Client Test Project
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = tcpclient_test
|
||||
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# C++14标准
|
||||
CONFIG += c++14
|
||||
|
||||
# 源文件
|
||||
SOURCES += \
|
||||
tcp_client_test.cpp
|
||||
|
||||
# 头文件路径
|
||||
INCLUDEPATH += ../../VrNets/TCPClient/Inc
|
||||
|
||||
# Windows平台特定配置
|
||||
win32 {
|
||||
LIBS += -lws2_32
|
||||
}
|
||||
|
||||
# 目标文件输出路径
|
||||
DESTDIR = ./
|
||||
|
||||
|
||||
# Link libraries
|
||||
win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../VrNets/debug -lVrTcpClient
|
||||
LIBS += -L../../VrUtils/debug -lVrUtils
|
||||
} else:win32:CONFIG(release, debug|release) {
|
||||
LIBS += -L../../VrNets/release -lVrTcpClient
|
||||
LIBS += -L../../VrUtils/release -lVrUtils
|
||||
}else:unix:!macx {
|
||||
LIBS += -L../../VrNets -lVrTcpClient
|
||||
LIBS += -L../../VrUtils -lVrUtils
|
||||
}
|
||||
207
Test/tcpserver/tcp_server_test.cpp
Normal file
207
Test/tcpserver/tcp_server_test.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// 包含TCPServer头文件
|
||||
#include "IYTCPServer.h"
|
||||
|
||||
class TCPServerTest
|
||||
{
|
||||
private:
|
||||
IYTCPServer* m_pServer;
|
||||
std::atomic<bool> m_bRunning;
|
||||
int m_nPort;
|
||||
std::thread m_sendThread;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param port 服务器监听端口
|
||||
*/
|
||||
TCPServerTest(int port) : m_pServer(nullptr), m_bRunning(false), m_nPort(port)
|
||||
{
|
||||
// 创建TCPServer实例
|
||||
if (!VrCreatYTCPServer(&m_pServer)) {
|
||||
std::cerr << "Failed to create TCPServer instance" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TCPServerTest()
|
||||
{
|
||||
Stop();
|
||||
if (m_pServer) {
|
||||
m_pServer->Close();
|
||||
delete m_pServer;
|
||||
m_pServer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化服务器
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool Initialize()
|
||||
{
|
||||
if (!m_pServer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化socket
|
||||
if (!m_pServer->Init(m_nPort, false)) {
|
||||
std::cerr << "Failed to initialize server socket" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置事件回调
|
||||
m_pServer->SetEventCallback([this](const TCPClient* pClient, TCPServerEventType eventType) {
|
||||
this->OnServerEvent(pClient, eventType);
|
||||
});
|
||||
|
||||
// 启动服务器线程
|
||||
if (!m_pServer->Start([this](const TCPClient* pClient, const char* pData, const unsigned int nLen) {
|
||||
this->OnDataReceived(pClient, pData, nLen);
|
||||
}, false)) {
|
||||
std::cerr << "Failed to start server" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Server initialized successfully on port " << m_nPort << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动数据发送线程
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool Start()
|
||||
{
|
||||
if (!m_pServer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bRunning = true;
|
||||
m_sendThread = std::thread(&TCPServerTest::SendDataLoop, this);
|
||||
std::cout << "Server started, sending data to clients..." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止服务器
|
||||
*/
|
||||
void Stop()
|
||||
{
|
||||
m_bRunning = false;
|
||||
|
||||
if (m_sendThread.joinable()) {
|
||||
m_sendThread.join();
|
||||
}
|
||||
|
||||
if (m_pServer) {
|
||||
m_pServer->Stop();
|
||||
}
|
||||
|
||||
std::cout << "Server stopped" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 服务器事件回调函数
|
||||
* @param pClient 客户端指针
|
||||
* @param eventType 事件类型
|
||||
*/
|
||||
void OnServerEvent(const TCPClient* pClient, TCPServerEventType eventType)
|
||||
{
|
||||
switch (eventType) {
|
||||
case TCP_EVENT_CLIENT_CONNECTED:
|
||||
std::cout << "Client connected" << std::endl;
|
||||
break;
|
||||
case TCP_EVENT_CLIENT_DISCONNECTED:
|
||||
std::cout << "Client disconnected" << std::endl;
|
||||
break;
|
||||
case TCP_EVENT_CLIENT_EXCEPTION:
|
||||
std::cout << "Client exception" << std::endl;
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown event" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 数据接收回调函数
|
||||
* @param pClient 客户端指针
|
||||
* @param pData 数据指针
|
||||
* @param nLen 数据长度
|
||||
*/
|
||||
void OnDataReceived(const TCPClient* pClient, const char* pData, const unsigned int nLen)
|
||||
{
|
||||
// 服务器测试程序只需要发送数据,不需要处理接收的数据
|
||||
std::cout << "Received " << nLen << " bytes from client" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 持续发送数据的循环
|
||||
*/
|
||||
void SendDataLoop()
|
||||
{
|
||||
int counter = 0;
|
||||
char pData[1024];
|
||||
while (m_bRunning) {
|
||||
// 构造要发送的数据
|
||||
*(int *)(pData) = ++counter;
|
||||
|
||||
// 发送给所有连接的客户端
|
||||
if (m_pServer) {
|
||||
m_pServer->SendAllData(pData, sizeof(pData));
|
||||
}
|
||||
|
||||
// 每秒发送一次
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::cout << "TCP Server Test Program" << std::endl;
|
||||
std::cout << "=======================" << std::endl;
|
||||
|
||||
// 默认端口为8080
|
||||
int port = 8080;
|
||||
if (argc > 1) {
|
||||
port = std::stoi(argv[1]);
|
||||
}
|
||||
|
||||
TCPServerTest serverTest(port);
|
||||
|
||||
// 初始化服务器
|
||||
if (!serverTest.Initialize()) {
|
||||
std::cerr << "Failed to initialize server" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 启动服务器
|
||||
if (!serverTest.Start()) {
|
||||
std::cerr << "Failed to start server" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 等待用户输入以停止服务器
|
||||
std::cout << "Server is running. Press Enter to stop..." << std::endl;
|
||||
while(true){
|
||||
std::cin.get();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
|
||||
// 停止服务器
|
||||
serverTest.Stop();
|
||||
|
||||
std::cout << "TCP Server Test completed!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
37
Test/tcpserver/tcpserver_test.pro
Normal file
37
Test/tcpserver/tcpserver_test.pro
Normal file
@ -0,0 +1,37 @@
|
||||
# TCP Server Test Project
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = tcpserver_test
|
||||
|
||||
# C++14标准
|
||||
CONFIG += c++14
|
||||
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# 源文件
|
||||
SOURCES += \
|
||||
tcp_server_test.cpp
|
||||
|
||||
# 头文件路径
|
||||
INCLUDEPATH += ../../VrNets/TCPServer/Inc
|
||||
|
||||
# Windows平台特定配置
|
||||
win32 {
|
||||
LIBS += -lws2_32
|
||||
}
|
||||
|
||||
win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../VrNets/debug -lVrTCPServer
|
||||
LIBS += -L../../VrUtils/debug -lVrUtils
|
||||
}else:win32:CONFIG(release, debug|release){
|
||||
LIBS += -L../../VrNets/release -lVrTCPServer
|
||||
LIBS += -L../../VrUtils/release -lVrUtils
|
||||
}else:unix:!macx {
|
||||
LIBS += -L../../VrNets -lVrTCPServer
|
||||
LIBS += -L../../VrUtils -lVrUtils
|
||||
|
||||
# 添加系统库依赖
|
||||
LIBS += -lpthread
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user