撕裂基本功能調試完成
This commit is contained in:
parent
a355fa904c
commit
04a4adf34f
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
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