撕裂基本功能調試完成

This commit is contained in:
yiyi 2025-10-12 16:46:46 +08:00
parent a355fa904c
commit 04a4adf34f
22 changed files with 1500 additions and 86 deletions

View File

@ -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

View File

@ -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替代

View File

@ -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();
}
}

View File

@ -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();

View File

@ -82,7 +82,7 @@ MainWindow::MainWindow(QWidget *parent)
// 设置表格最大行数限制,防止内存占用过大
if (m_tearingDataTableWidget) {
m_tearingDataTableWidget->setMaximumRows(500);
m_tearingDataTableWidget->setMaximumRows(100);
}
m_presenter->Init();

View File

@ -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);
}
// 重新启用更新

View File

@ -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());

View File

@ -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();

View File

@ -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();

View File

@ -25,3 +25,6 @@ SUBDIRS += ../App/BeltTearing/BeltTearing.pro
#焊接
SUBDIRS += ../App/LapWeld/LapWeld.pro
# Test 测试
# SUBDIRS += ../Test/Test.pro

View 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

View File

@ -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 {

View 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
View File

@ -0,0 +1,5 @@
TEMPLATE = subdirs
# 撕裂项目
SUBDIRS += tcpclient/tcpclient_test.pro
SUBDIRS += tcpserver/tcpserver_test.pro

View 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
View 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
View 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
View 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

View 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;
}

View 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
}

View 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;
}

View 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
}