皮带撕裂接受网络图片进行显示

This commit is contained in:
yiyi 2025-08-27 23:10:36 +08:00
parent 66332da0eb
commit c86572de95
34 changed files with 4035 additions and 1047 deletions

View File

@ -5,6 +5,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TEMPLATE = app
CONFIG += c++17
QMAKE_CXXFLAGS += /utf-8
SUBDIRS += ../BeltTearingConfig/BeltTearingConfig.pro \
../VrNet/VrTcpClient.pro

View File

@ -44,9 +44,10 @@ inline std::string BeltTearingWorkStatusToString(BeltTearingWorkStatus status) {
// 皮带撕裂检测结果结构体
struct BeltTearingResult {
QImage image; // 检测图像
QImage image; // 检测图像
bool bImageValid = false;
QString result; // 检测结果描述
double confidence = 0.0; // 置信度
double confidence = 0.0; // 置信度
QString serverName; // 服务器名称
QDateTime timestamp; // 时间戳
};
@ -59,6 +60,9 @@ public:
// 状态文字回调
virtual void OnStatusUpdate(const QString& statusMessage) = 0;
// 回调需要显示几个图像
virtual void OnNeedShowImageCount(int count) = 0;
// 撕裂检测结果回调
virtual void OnTearingResult(const BeltTearingResult& result) = 0;

View File

@ -7,6 +7,11 @@
#include <QString>
#include "widgets/DeviceStatusWidget.h"
enum class ByteDataType {
Text = 0x01,
Image = 0x02
};
class BeltTearingPresenter : public QWidget
{
Q_OBJECT
@ -24,6 +29,9 @@ public:
// 设置设备状态控件
void setDeviceStatusWidget(DeviceStatusWidget* widget) { m_deviceStatusWidget = widget; }
// 设置状态更新接口
void setStatusUpdate(IStatusUpdate* statusUpdate) { m_statusUpdate = statusUpdate; }
private:
// IBeltTearingPresenter interface implementation
bool initializeConfig(const QString &configPath);
@ -40,7 +48,7 @@ private slots:
void onTcpError(const QString &serverName, const QString &error);
private:
IStatusUpdate* m_statusUpdate; // 状态更新接口
IVrBeltTearingConfig * m_config;
DeviceStatusWidget* m_deviceStatusWidget; // 设备状态控件
QMap<QString, VrTcpClient*> m_tcpClients; // 服务器名称 -> TCP客户端映射

View File

@ -61,6 +61,10 @@ bool BeltTearingPresenter::initializeConfig(const QString &configPath)
LOG_WARNING("No servers configured");
return false;
}
if(m_statusUpdate){
m_statusUpdate->OnNeedShowImageCount(servers.size());
}
// 清空现有配置
m_serverInfos.clear();
@ -238,10 +242,40 @@ void BeltTearingPresenter::onDisconnected(const QString &serverName)
void BeltTearingPresenter::onDataReceived(const QString &serverName, const QByteArray &data)
{
// 尝试解析图像数据
QImage image;
if (image.loadFromData(data)) {
// 解析数据
quint8 dataType;
quint32 dataSize;
QDataStream stream(data);
stream.setByteOrder(QDataStream::BigEndian);
stream >> dataType >> dataSize;
QByteArray byteArray = data.mid(5, dataSize);
BeltTearingResult tearResult;
tearResult.bImageValid = false;
if (dataType == static_cast<int>(ByteDataType::Text))
{
// 处理文本数据
QString textData = QString::fromUtf8(byteArray);
// 这里可以添加处理文本数据的逻辑
LOG_DEBUG("Received text data from server %s: %s \n", serverName.toStdString().c_str(), textData.toStdString().c_str());
}
else if (dataType == static_cast<int>(ByteDataType::Image))
{
// 处理图像数据
QImage image;
if (tearResult.image.loadFromData(byteArray)) {
// 这里可以添加处理图像数据的逻辑
tearResult.bImageValid = true;
LOG_DEBUG("Received image data from server %s, size: %dx%d \n", serverName.toStdString().c_str(), image.width(), image.height());
}
}
if(m_statusUpdate){
tearResult.serverName = serverName;
tearResult.timestamp = QDateTime::currentDateTime();
m_statusUpdate->OnTearingResult(tearResult);
}
}

View File

@ -45,6 +45,7 @@ MainWindow::MainWindow(QWidget *parent)
// 将设备状态控件与Presenter关联
m_presenter->setDeviceStatusWidget(m_deviceStatusWidget);
m_presenter->setStatusUpdate(this);
m_presenter->Init();
@ -106,10 +107,7 @@ void MainWindow::resizeEvent(QResizeEvent* event)
}
void MainWindow::on_btn_test_clicked()
{
// 初始化4个图片格子
m_gridView->initImages(4);
{
// 定义图片资源路径
QStringList imageResources = {
"C:/project/QT/GrabBag/GrabBagApp/resource/config_camera.png",
@ -137,22 +135,17 @@ void MainWindow::OnStatusUpdate(const QString &statusMessage)
LOG_DEBUG("Status update: %s", statusMessage.toStdString().c_str());
}
void MainWindow::OnTearingResult(const BeltTearingResult &result)
// 回调需要显示几个图像
void MainWindow::OnNeedShowImageCount(int count)
{
// 显示撕裂检测结果
QString resultText = QString("服务器: %1, 结果: %2, 置信度: %3")
.arg(result.serverName)
.arg(result.result)
.arg(result.confidence);
statusBar()->showMessage(resultText);
LOG_DEBUG("Tearing result: %s", resultText.toStdString().c_str());
m_gridView->initImages(count);
}
void MainWindow::OnTearingResult(const BeltTearingResult &result)
{
// 如果图像有效,显示在网格控件中
if (!result.image.isNull()) {
static int imageIndex = 0;
m_gridView->setImages(imageIndex % 4, result.image);
imageIndex++;
if (result.bImageValid && !result.image.isNull()) {
m_gridView->setImages(0, result.image);
}
}

View File

@ -29,6 +29,7 @@ public:
// IStatusUpdate接口方法声明
void OnStatusUpdate(const QString& statusMessage) override;
void OnNeedShowImageCount(int count) override;
void OnTearingResult(const BeltTearingResult& result) override;
void OnServerConnected(const QString& serverName) override;
void OnServerDisconnected(const QString& serverName) override;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1920</width>
<height>1080</height>
<height>1032</height>
</rect>
</property>
<property name="windowTitle">
@ -162,7 +162,7 @@ background-color: rgba(255, 255, 255, 0);</string>
<string>工作状态</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="btn_close">
@ -250,36 +250,36 @@ background-color: rgba(255, 255, 255, 0);</string>
<x>20</x>
<y>140</y>
<width>1311</width>
<height>891</height>
<height>850</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgb(37, 38, 42)</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
<enum>QFrame::Raised</enum>
</property>
</widget>
<widget class="QFrame" name="device_status">
<property name="geometry">
<rect>
<x>1344</x>
<y>852</y>
<y>850</y>
<width>556</width>
<height>178</height>
<height>140</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(37, 38, 42);</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
<enum>QFrame::Raised</enum>
</property>
</widget>
<widget class="QFrame" name="detect_data">
@ -295,10 +295,10 @@ background-color: rgba(255, 255, 255, 0);</string>
<string notr="true">background-color: rgb(37, 38, 42);</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
<enum>QFrame::Raised</enum>
</property>
</widget>
<zorder>groupBox</zorder>

View File

@ -8,9 +8,9 @@
ImageGridWidget::ImageGridWidget(QWidget* parent)
: QWidget(parent) {
m_layout = new QGridLayout(this);
m_layout->setContentsMargins(8, 8, 8, 8);
m_layout->setHorizontalSpacing(8);
m_layout->setVerticalSpacing(8);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setHorizontalSpacing(0);
m_layout->setVerticalSpacing(0);
setLayout(m_layout);
// 设置控件大小策略为可扩展
@ -127,14 +127,14 @@ void ImageGridWidget::rebuildGrid() {
}
void ImageGridWidget::resizeEvent(QResizeEvent* event) {
// QWidget::resizeEvent(event);
QWidget::resizeEvent(event);
// // 防止递归调用,只在尺寸真正变化时更新
// static QSize lastSize;
// if (lastSize != event->size()) {
// lastSize = event->size();
// updateTileSizes();
// }
// 防止递归调用,只在尺寸真正变化时更新
static QSize lastSize;
if (lastSize != event->size()) {
lastSize = event->size();
updateTileSizes();
}
}
void ImageGridWidget::updateTileSizes() {
@ -146,13 +146,15 @@ void ImageGridWidget::updateTileSizes() {
int availableHeight = height() - m_layout->contentsMargins().top() - m_layout->contentsMargins().bottom();
// 计算每个格子的基础尺寸(根据窗口大小动态调整)
int baseTileWidth = qMax(100, availableWidth / m_columns - m_layout->horizontalSpacing() * (m_columns - 1));
int baseTileHeight = qMax(100, availableHeight / m_rows - m_layout->verticalSpacing() * (m_rows - 1));
// 确保减去的间距不会导致负数
int horizontalSpacingTotal = m_layout->horizontalSpacing() * (m_columns - 1);
int verticalSpacingTotal = m_layout->verticalSpacing() * (m_rows - 1);
int baseTileWidth = qMax(100, (availableWidth - horizontalSpacingTotal) / m_columns);
int baseTileHeight = qMax(100, (availableHeight - verticalSpacingTotal) / m_rows);
// 根据窗口大小动态调整尺寸
m_sizeNormal = QSize(baseTileWidth, baseTileHeight);
m_sizeSelected = QSize(baseTileWidth * 1.5, baseTileHeight * 1.5);
m_sizeOther = QSize(baseTileWidth * 0.75, baseTileHeight * 0.75);
m_sizeExpanded = QSize(availableWidth, availableHeight);
for (int i = 0; i < m_tiles.size(); ++i) {

View File

@ -43,7 +43,5 @@ protected:
int m_columns {0};
int m_rows {0};
QSize m_sizeNormal {160,160};
QSize m_sizeSelected {240,240};
QSize m_sizeOther {120,120};
QSize m_sizeExpanded {400,300};
};

View File

@ -79,7 +79,7 @@ void ImageTileWidget::paintEvent(QPaintEvent*) {
p.setRenderHint(QPainter::Antialiasing, true);
// 绘制背景
p.fillRect(rect(), QColor(245, 245, 245));
p.fillRect(rect(), QColor(37, 38, 42));
if (m_expanded) {
// 展开模式:全屏显示图片,在左下角显示图片名称
@ -97,13 +97,13 @@ void ImageTileWidget::paintEvent(QPaintEvent*) {
// 绘制图片(完整显示,不超出区域)
p.drawPixmap(x, y, scaled);
} else {
p.setPen(Qt::gray);
p.setPen(Qt::white);
p.drawText(imageRect, Qt::AlignCenter, "No Image");
}
} else {
// 正常模式:图片完全显示,不拉伸
QRect inner = rect().adjusted(8, 8, -8, -8);
QRect inner = rect().adjusted(0, 0, 0, 0);
if (!m_pix.isNull()) {
// 使用KeepAspectRatio保持比例确保整个图片可见
QPixmap scaled = m_pix.scaled(inner.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
@ -114,7 +114,7 @@ void ImageTileWidget::paintEvent(QPaintEvent*) {
p.drawPixmap(x, y, scaled);
} else {
p.setPen(Qt::gray);
p.setPen(Qt::white);
p.drawText(inner, Qt::AlignCenter, "No Image");
}
@ -138,10 +138,10 @@ void ImageTileWidget::paintEvent(QPaintEvent*) {
}
// 绘制边框
QPen border(m_selected ? QColor(66, 133, 244) : QColor(200, 200, 200));
border.setWidth(m_selected ? 3 : 1);
QPen border(QColor(0, 0, 0));
border.setWidth(1);
p.setPen(border);
p.drawRoundedRect(rect().adjusted(1,1,-1,-1), 6, 6);
p.drawRoundedRect(rect().adjusted(1,1,-1,-1), 1, 1);
}
void ImageTileWidget::mousePressEvent(QMouseEvent* ev) {

View File

@ -0,0 +1,121 @@
#include "BeltTearingAlgo.h"
#include <climits>
#include <iostream>
BeltTearingAlgo::BeltTearingAlgo() {
algoParam.differnceBinTh = 1.0;
algoParam.extractPara.gapChkWin = 7;
algoParam.extractPara.sameGapTh = 20.0;
algoParam.scanXScale = 4.0;
algoParam.scanYScale = 0.6;
algoParam.tearingMinGap = 50.0; //两个同位置的纵撕的最小间隔。大于此门限视为两个撕裂
algoParam.tearingMinLen = 30.0;
lineIndex = 0;
// 初始化
ResetParameter();
}
void BeltTearingAlgo::ResetParameter() {
total_tearings.clear();
beltTearings_new.clear();
beltTearings_growing.clear();
beltTearings_ended.clear();
beltTearings_unknown.clear();
lineIndex = 0;
int errCode = 0;
sg_detectBeltTearing(
NULL, //空扫描线,用于复位内部静态变量
0,
0,
&errCode,
hLineWorkers,
beltTearings_new,
beltTearings_growing,
beltTearings_ended,
beltTearings_unknown, //未判明,应用无需处理。
algoParam);
}
void BeltTearingAlgo::Training(std::vector<SVzNL3DLaserLine> lines) {
// 本算法无需训练
}
void BeltTearingAlgo::Training(SVzNL3DLaserLine* lines) {
// 本算法无需训练
}
std::vector<SSG_beltTearingInfo> BeltTearingAlgo::Predit(SVzNL3DLaserLine* a_line, int lineId) {
total_tearings.clear();
int errCode = 0;
sg_detectBeltTearing(
a_line, //一条扫描线
lineIndex,
a_line->nPositionCnt,
&errCode,
hLineWorkers,
beltTearings_new,
beltTearings_growing,
beltTearings_ended,
beltTearings_unknown, //未判明,应用无需处理。
algoParam);
if (beltTearings_ended.size() > 0) //收集撕裂点
{
total_tearings.insert(total_tearings.end(), beltTearings_ended.begin(), beltTearings_ended.end());
}
return total_tearings;
}
std::vector<SSG_beltTearingInfo> BeltTearingAlgo::Predit(std::vector<SVzNL3DLaserLine> lines, int lineIdx) {
total_tearings.clear();
if (lines.empty()) {
return total_tearings;
}
if (lineIndex >= INT_MAX - lines.size() ) {
lineIndex = 0;
}
hLineWorkers.resize(lines[0].nPositionCnt);
int errCode = 0;
for(int i = 0; i < lines.size(); i ++) {
SVzNL3DLaserLine* a_line = &lines[i];
sg_detectBeltTearing(
a_line, //一条扫描线
lineIndex,
a_line->nPositionCnt,
&errCode,
hLineWorkers,
beltTearings_new,
beltTearings_growing,
beltTearings_ended,
beltTearings_unknown, //未判明,应用无需处理。
algoParam);
lineIndex += 1;
// if (beltTearings_unknown.size() > 0) {
// std::cout << "未知的撕裂数量: " << beltTearings_unknown.size() << std::endl;
// }
// if (beltTearings_growing.size() > 0) {
// std::cout << "进行中的撕裂数量: " << beltTearings_growing.size() << std::endl;
// }
if (beltTearings_new.size() > 0) //收集撕裂点
{
total_tearings.insert(total_tearings.end(), beltTearings_new.begin(), beltTearings_new.end());
}
if (beltTearings_ended.size() > 0) //收集撕裂点
{
total_tearings.insert(total_tearings.end(), beltTearings_ended.begin(), beltTearings_ended.end());
}
else if (i == lines.size() - 1) //测试数据最后一条扫描线。(实际中没有)
{
total_tearings.insert(total_tearings.end(), beltTearings_growing.begin(), beltTearings_growing.end());
}
}
// TODO: 返回值有可能为空,也有可能为当前检测的撕裂
return total_tearings;
}

View File

@ -0,0 +1,28 @@
#ifndef BELTTEARINGALGO_H
#define BELTTEARINGALGO_H
#include "beltTearingDetection_Export.h"
using namespace std;
class BeltTearingAlgo
{
public:
BeltTearingAlgo();
void ResetParameter();
void Training(SVzNL3DLaserLine* lines);
void Training(std::vector<SVzNL3DLaserLine> lines);
std::vector<SSG_beltTearingInfo> Predit(SVzNL3DLaserLine* lines, int lineId);
std::vector<SSG_beltTearingInfo> Predit(std::vector<SVzNL3DLaserLine> lines, int lineIdx);
private:
SSG_beltTearingParam algoParam;
std::vector<SSG_hLineProInfo> hLineWorkers;
std::vector<SSG_beltTearingInfo> beltTearings_new;
std::vector<SSG_beltTearingInfo> beltTearings_growing;
std::vector<SSG_beltTearingInfo> beltTearings_ended;
std::vector<SSG_beltTearingInfo> beltTearings_unknown; //未判明,应用无需处理。
std::vector<SSG_beltTearingInfo> total_tearings;
int lineIndex = 0;
};
#endif // BELTTEARINGALGO_H

View File

@ -0,0 +1,209 @@
#include "BeltTearingPresenter.h"
#include <QUuid>
#include <QDataStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QBuffer>
#include <QByteArray>
#include <QDateTime>
#include <iostream>
BeltTearingPresenter::BeltTearingPresenter(QObject *parent)
: QObject(parent)
, m_tcpServer(new QTcpServer(this))
, m_dataTimer(new QTimer(this))
, m_port(0)
{
// 连接信号槽
connect(m_tcpServer, &QTcpServer::newConnection, this, &BeltTearingPresenter::onNewConnection);
connect(m_dataTimer, &QTimer::timeout, this, &BeltTearingPresenter::onSendSimulatedData);
}
BeltTearingPresenter::~BeltTearingPresenter()
{
stopServer();
}
bool BeltTearingPresenter::startServer(quint16 port)
{
if (m_tcpServer->isListening()) {
stopServer();
}
m_port = port;
bool result = m_tcpServer->listen(QHostAddress::Any, port);
if (result) {
std::cout << "TCP server started on port " << port << std::endl;
} else {
std::cout << "Failed to start TCP server: " << m_tcpServer->errorString().toStdString() << std::endl;
}
return result;
}
void BeltTearingPresenter::stopServer()
{
// 断开所有客户端连接
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
it.value()->disconnectFromHost();
}
m_clients.clear();
// 停止服务器监听
if (m_tcpServer->isListening()) {
m_tcpServer->close();
}
// 停止数据发送定时器
stopSendingSimulatedData();
m_port = 0;
}
void BeltTearingPresenter::startSendingSimulatedData()
{
// 启动定时器每2秒发送一次模拟数据
m_dataTimer->start(1000);
}
void BeltTearingPresenter::stopSendingSimulatedData()
{
if (m_dataTimer->isActive()) {
m_dataTimer->stop();
}
}
void BeltTearingPresenter::onNewConnection()
{
while (m_tcpServer->hasPendingConnections()) {
QTcpSocket *socket = m_tcpServer->nextPendingConnection();
// 生成客户端ID
QString clientId = generateClientId(socket);
// 存储客户端连接
m_clients[clientId] = socket;
// 连接客户端信号
connect(socket, &QTcpSocket::disconnected, this, &BeltTearingPresenter::onClientDisconnected);
std::cout << "Client connected: " << clientId.toStdString() << std::endl;
// 发送欢迎消息
QByteArray welcomeMsg = "Welcome to BeltTearing Server!";
socket->write(welcomeMsg);
}
}
void BeltTearingPresenter::onClientDisconnected()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) {
return;
}
// 查找断开连接的客户端ID
QString clientId;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
if (it.value() == socket) {
clientId = it.key();
break;
}
}
if (!clientId.isEmpty()) {
m_clients.remove(clientId);
std::cout << "Client disconnected: " << clientId.toStdString() << std::endl;
}
socket->deleteLater();
}
void BeltTearingPresenter::onSendSimulatedData()
{
// 向所有连接的客户端发送模拟数据
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
QTcpSocket *socket = it.value();
if (socket->state() == QAbstractSocket::ConnectedState) {
// 随机选择发送图像或日志记录
sendSimulatedImage(socket);
sendSimulatedLogRecords(socket);
}
}
}
void BeltTearingPresenter::sendSimulatedImage(QTcpSocket *socket)
{
// 创建一个模拟的图像
cv::Mat image(480, 640, CV_8UC3, cv::Scalar(128, 128, 128));
// 在图像上绘制一些随机内容
cv::circle(image, cv::Point(320, 240), 50, cv::Scalar(0, 0, 255), -1);
cv::rectangle(image, cv::Rect(100, 100, 200, 150), cv::Scalar(0, 255, 0), 2);
static unsigned int s_imageCount = 0;
// 在图像上叠加数字
QString numberText = QString::number(++s_imageCount); // 生成一个随机数
cv::putText(image, numberText.toStdString(), cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);
// 编码图像
std::vector<uchar> imgBuffer;
std::vector<int> params = {cv::IMWRITE_PNG_COMPRESSION, 3};
cv::imencode(".png", image, imgBuffer, params);
QByteArray byteArray(reinterpret_cast<const char*>(imgBuffer.data()), imgBuffer.size());
// 创建数据包
QByteArray package;
QDataStream stream(&package, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::BigEndian);
stream << quint8(static_cast<quint8>(ByteDataType::Image)) << quint32(byteArray.size());
package.append(byteArray);
package.append("___END___\r\n");
// 发送数据
socket->write(package);
std::cout << "Sent simulated image data to client" << std::endl;
}
void BeltTearingPresenter::sendSimulatedLogRecords(QTcpSocket *socket)
{
// 创建模拟日志记录
QJsonArray logRecords;
for (int i = 0; i < 3; i++) {
QJsonObject record;
record["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
record["level"] = QString("INFO");
record["message"] = QString("Simulated log message %1").arg(i + 1);
record["source"] = QString("BeltTearingDetector");
logRecords.append(record);
}
// 将日志记录转换为JSON字符串
QJsonDocument doc(logRecords);
QByteArray message = doc.toJson(QJsonDocument::Compact);
// 创建数据包
QByteArray package;
QDataStream stream(&package, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::BigEndian);
stream << quint8(static_cast<quint8>(ByteDataType::Text)) << quint32(message.size());
package.append(message);
package.append("___END___\r\n");
// 发送数据
socket->write(package);
std::cout << "Sent simulated log records to client" << std::endl;
}
QString BeltTearingPresenter::generateClientId(QTcpSocket *socket)
{
// 使用UUID生成唯一的客户端ID
return QUuid::createUuid().toString();
}

View File

@ -0,0 +1,50 @@
#ifndef BELTTEARINGPRESENTER_H
#define BELTTEARINGPRESENTER_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QMap>
#include <opencv2/opencv.hpp>
// 假设ByteDataType枚举定义
enum class ByteDataType : quint8 {
Text = 1,
Image = 2
};
class BeltTearingPresenter : public QObject
{
Q_OBJECT
public:
explicit BeltTearingPresenter(QObject *parent = nullptr);
~BeltTearingPresenter();
// 初始化TCP服务器
bool startServer(quint16 port);
void stopServer();
// 模拟数据发送
void startSendingSimulatedData();
void stopSendingSimulatedData();
private slots:
void onNewConnection();
void onClientDisconnected();
void onSendSimulatedData();
private:
QTcpServer *m_tcpServer;
QMap<QString, QTcpSocket*> m_clients;
QTimer *m_dataTimer;
quint16 m_port;
// 模拟数据发送方法
void sendSimulatedImage(QTcpSocket *socket);
void sendSimulatedLogRecords(QTcpSocket *socket);
QString generateClientId(QTcpSocket *socket);
};
#endif // BELTTEARINGPRESENTER_H

View File

@ -0,0 +1,104 @@
QT += core gui
QT += serialport
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TEMPLATE = app
CONFIG += c++11 console
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
INCLUDEPATH += $$PWD/../VrCommon/Inc
INCLUDEPATH += $$PWD/../VrUtils/Inc
INCLUDEPATH += $$PWD/Presenter/Inc
INCLUDEPATH += $$PWD/Utils/Inc
INCLUDEPATH += $$PWD/../VrConfig/Inc
INCLUDEPATH += $$PWD/../VrEyeDevice/Inc
SOURCES += \
BeltTearingAlgo.cpp \
beltTearingDetection.cpp \
main.cpp \
BeltTearingPresenter.cpp
HEADERS += \
SG_baseAlgo_Export.h \
SG_baseDataType.h \
SG_errCode.h \
beltTearingDetection_Export.h \
BeltTearingAlgo.h \
BeltTearingPresenter.h
FORMS +=
win32:CONFIG(debug, debug|release) {
LIBS += -L../VrUtils/debug -lVrUtils
LIBS += -L../VrEyeDevice/debug -lVrEyeDevice
}else:win32:CONFIG(release, debug|release){
LIBS += -L../VrUtils/release -lVrUtils
LIBS += -L../VrEyeDevice/release -lVrEyeDevice
}else:unix:!macx {
# Unix/Linux平台库链接(包括交叉编译)
# 注意链接顺序:依赖关系从高到低
LIBS += -L../VrUtils -lVrUtils
LIBS += -L../VrEyeDevice -lVrEyeDevice
# 添加系统库依赖
LIBS += -lpthread
}
#linux下的为unix windows下用的win32
INCLUDEPATH += ../SDK/VzNLSDK/_Inc
INCLUDEPATH += ../SDK/VzNLSDK/Inc
win32:CONFIG(release, debug|release): {
LIBS += -L$$PWD/../SDK/VzNLSDK/Windows/x64/Release
LIBS += -lVzKernel -lVzNLDetect -lVzNLGraphics
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../SDK/VzNLSDK/Windows/x64/Debug
LIBS += -lVzKerneld -lVzNLDetectd -lVzNLGraphicsd
}
else:unix:!macx: {
LIBS += -L$$PWD/../SDK/VzNLSDK/Arm/aarch64
LIBS += -lVzEyeSecurityLoader-shared -lVzKernel -lVzNLDetect -lVzNLGraphics
}
# 算法
INCLUDEPATH += ../SDK/OpenCV320/include
win32:CONFIG(release, debug|release): {
LIBS += -L$$PWD/../SDK/OpenCV320/Windows/vc14/Release -lopencv_world320
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../SDK/OpenCV320/Windows/vc14/Release -lopencv_world320
}
else:unix:!macx: {
LIBS += -L$$PWD/../SDK/OpenCV320/Arm/aarch64 -lopencv_core -lopencv_imgproc -lopencv_highgui
}
# 添加libmodbus依赖
win32 {
LIBS += -lws2_32
LIBS += Advapi32.lib
}
# Default rules for deployment.
unix {
target.path = /usr/lib
# Link real-time library for POSIX shared memory functions
LIBS += -lrt
}
!isEmpty(target.path): INSTALLS += target
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

View File

@ -0,0 +1,232 @@
#pragma once
#if defined(SG_API_LIBRARY)
# define SG_APISHARED_EXPORT __declspec(dllexport)
#else
# define SG_APISHARED_EXPORT __declspec(dllimport)
#endif
#include "SG_baseDataType.h"
#include <vector>
#include <opencv2/opencv.hpp>
//滤除离群点:z跳变门限方法大于门限视为不连续根据连续段点数量判断噪声
void sg_lineDataRemoveOutlier(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam,
std::vector<SVzNL3DPosition>& filerData,
std::vector<int>& noisePts);
//滤除离群点z跳变门限方法,改变原始数据
void sg_lineDataRemoveOutlier_changeOriginData(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam);
//滤除离群点:点距离门限方法(大于门限视为不连续,根据连续段点数量判断噪声)
void sg_lineDataRemoveOutlier_ptDistMethod(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam,
std::vector<SVzNL3DPosition>& filerData,
std::vector<int>& noisePts);
//平滑
void sg_lineDataSmoothing(
std::vector<SVzNL3DPosition>& input,
int smoothWin,
std::vector<SVzNL3DPosition>& output);
//VZ_APISHARED_EXPORT void sg_getLineMeanVar();
void sg_getLineLVFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_slopeParam slopeParam,
const SSG_VFeatureParam valleyPara,
SSG_lineFeature* line_features);
//获取扫描线拐点特征
void sg_getLineCornerFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
SSG_lineFeature* line_features);
/// <summary>
/// 提取激光线上的极值点(极大值点和极小值点)
///
/// </summary>
void sg_getLineLocalPeaks(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double scaleWin,
std::vector< SSG_basicFeature1D>& localMax,
std::vector< SSG_basicFeature1D>& localMin);
void sg_getLineDownJumps(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double jumpTh,
std::vector< SSG_basicFeature1D>& downJumps);
//对feature进行生长
void sg_getFeatureGrowingTrees(
std::vector<SSG_lineFeature>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//对ending进行生长
void sg_getEndingGrowingTrees(
std::vector<SSG_2DValueI>& lineEndings,
SVzNL3DLaserLine* laser3DPoints,
bool isVScan,
int featureType,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//对扫描线上的
void sg_LVFeatureGrowing(
std::vector<SSG_lineFeature>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_bagParam bagParam,
SSG_treeGrowParam growParam,
std::vector<SSG_2DValueI>& edgePts_0,
std::vector<SSG_2DValueI>& edgePts_1);
void sg_peakFeatureGrowing(
std::vector<std::vector< SSG_basicFeature1D>>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
SSG_meanVar _computeMeanVar(double* data, int size);
///搜索局部最高点z最小点
///搜索方法:每次步进搜索窗口长度的一半。对局部最高点进行标记,防止被重复记录。
void sg_getLocalPeaks(
SVzNL3DLaserLine* scanLines,
int lineNum,
std::vector<SSG_2DValueI>& peaks,
SSG_localPkParam searchWin);
/// <summary>
/// 区域生长法:以局部最高点作为生长种子进行生长
/// 生长方法与一般的区域生长不同:以种子点为圆心作圆周扫描,记录扫描到的边界
/// </summary>
//void sg_peakPolarScan(cv::Mat& edgeMask, SVzNL2DPoint a_peak, SSG_polarScanParam polarScanParam, std::vector< SSG_2DValueI>& rgnContour);
//从Peak点进行水平垂直扫描得到区域边界
void sg_peakXYScan(
SVzNL3DLaserLine* laser3DPoints,
int lineNum,
cv::Mat& featureEdgeMask,
SSG_2DValueI a_peak,
SSG_treeGrowParam growParam,
SSG_bagParam bagParam,
bool rgnPtAsEdge,
std::vector< SSG_lineConotours>& topContour,
std::vector< SSG_lineConotours>& bottomContour,
std::vector< SSG_lineConotours>& leftContour,
std::vector< SSG_lineConotours>& rightContour,
int* maxEdgeId_top,
int* maxEdgeId_btm,
int* maxEdgeId_left,
int* maxEdgeId_right);
//取出与给定edgeId相同的边界点
void sg_getContourPts(
std::vector< SSG_lineConotours>& contour_all,
int vldEdgeId,
std::vector< SSG_2DValueI>& contourFilter,
int* lowLevelFlag);
//从边界点对中取出与给定edgeId相同的边界点
void sg_getPairingContourPts(
std::vector<SSG_conotourPair>& contourPairs,
std::vector<SSG_intPair>& idPairs,
std::vector< SSG_conotourPair>& contourFilter,
SVzNLRangeD range,
bool isTBDir,
int* lowLevelFlag_0,
int* lowLevelFlag_1);
//取出最短距离的边界点
void sg_contourPostProc(
std::vector< SSG_contourPtInfo>& contour,
int maxEdgeIdx,
double sameConturDistTh,
std::vector< SSG_2DValueI>& contourFilter,
int sideID,
int* blockFlag);
//距离变换
//input, output均为float型
void sg_distanceTrans(const cv::Mat input, cv::Mat& output, int distType);
/// <summary>
/// 以5x5方式寻找localPeaks
/// </summary>
/// <param name="input"></param>
/// <param name="peaks"></param>
void sg_getLocalPeaks_distTransform(
cv::Mat& input,
std::vector<SSG_2DValueI>& peaks,
SSG_localPkParam searchWin);
/// <summary>
/// 使用模板法提取直角特征
/// 水平向下直角特征拐点左侧deltaZ在一个很小的范围内拐点右侧deltaY在一个很小的范围内
/// </summary>
void sg_getLineRigthAngleFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_lineRightAngleParam templatePara_HF,
const SSG_lineRightAngleParam templatePara_FH,
const SSG_lineRightAngleParam templatePara_HR,
const SSG_lineRightAngleParam templatePara_RH,
SSG_lineFeature* line_features);
//计算扫描ROI
SVzNL3DRangeD sg_getScanDataROI(
SVzNL3DLaserLine* laser3DPoints,
int lineNum);
//XY平面直线拟合
void lineFitting(
std::vector< SVzNL3DPoint>& inliers,
double* _k,
double* _b);
//Bresenham算法
void drawLine(
int x0,
int y0,
int x1,
int y1,
std::vector<SVzNL2DPoint>& pts);
/// <summary>
/// 两步法标注
/// </summary>
/// <param name="bwImg"> 目标点为“1” 空白点为“0”</param>
/// <param name="labImg"> 标注结果。每个点为rgnID, ID从2开始 </param>
/// <param name="labelRgns"></param>
void SG_TwoPassLabel(
const cv::Mat& bwImg,
cv::Mat& labImg,
std::vector<SSG_Region>& labelRgns,
int connectivity = 4);
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sg_getPlaneCalibPara(
SVzNL3DLaserLine* laser3DPoints,
int lineNum);
// 从旋转矩阵计算欧拉角Z-Y-X顺序
SSG_EulerAngles rotationMatrixToEulerZYX(const double R[3][3]);
// 从欧拉角计算旋转矩阵Z-Y-X顺序
void eulerToRotationMatrixZYX(const SSG_EulerAngles& angles, double R[3][3]);

View File

@ -0,0 +1,337 @@
#pragma once
#include <VZNL_Types.h>
#include <vector>
#define PI 3.141592654
// 定义欧拉角结构体(单位:度)
typedef struct{
double roll; // 绕X轴旋转横滚
double pitch; // 绕Y轴旋转俯仰
double yaw; // 绕Z轴旋转偏航
}SSG_EulerAngles;
//以工作轴为X轴(扫描方向平行地面与Z轴垂直的轴为Y轴垂直地面的轴为Z轴
typedef struct
{
bool validFlag; //指示结果是否有效
double x;
double y;
double z;
double pitchAngle; //俯仰角绕Y轴的偏转, 弧度
double rollAngle; //滚转角绕X轴的偏转, 弧度
double yawAngle; //偏转角绕Z轴的偏转, 弧度
}SSG_6AxisAttitude;
typedef struct
{
bool validFlag; //指示结果是否有效
double x;
double y;
double z;
}SSG_3AxisAttitude;
typedef enum
{
keSG_PoseSorting_Uknown = 0,
keSG_PoseSorting_ZMin2Max, ///按Z排序
keSG_PoseSorting_L2R_T2B, ///从左到右,从上到下排序
keSG_PoseSorting_T2B_L2R, ///从上到下,从左到右排序
} ESG_poseSortingMode;
typedef struct
{
int data_0;
int data_1;
int idx;
}SSG_intPair;
typedef struct
{
double left;
double right;
double top;
double bottom;
}SSG_ROIRectD;
#define LINE_FEATURE_NUM 16
#define LINE_FEATURE_UNDEF 0
#define LINE_FEATURE_L_JUMP_H2L 1
#define LINE_FEATURE_L_JUMP_L2H 2
#define LINE_FEATURE_L_SLOPE_H2L 3
#define LINE_FEATURE_L_SLOPE_L2H 4
#define LINE_FEATURE_V_SLOPE 5
#define LINE_FEATURE_LINE_ENDING_0 6 //ending起点
#define LINE_FEATURE_LINE_ENDING_1 7 //ending终点
#define LINE_FEATURE_RGN_EDGE 8 //迭代处理时已经得到的目标的边缘点标记
#define LINE_FEATURE_RIGHT_ANGLE_HR 9 //直角特征:水平-上升
#define LINE_FEATURE_RIGHT_ANGLE_HF 10 //直角特征:水平-下降
#define LINE_FEATURE_RIGHT_ANGLE_RH 11 //直角特征:上升-水平
#define LINE_FEATURE_RIGHT_ANGLE_FH 12 //直角特征:下降-水平
#define LINE_FEATURE_PEAK_TOP 13
#define LINE_FEATURE_PEAK_BOTTOM 14
#define LINE_FEATURE_CORNER_V 15
typedef struct
{
int featureType;
SVzNL2DPoint jumpPos2D;
SVzNL3DPoint jumpPos;
}SSG_basicFeature1D;
typedef struct
{
int lineIdx;
double gap_start;
double gap_width;
double gap_depth;
SVzNL3DPosition gapPt_0; //gap的端点
SVzNL3DPosition gapPt_1; //gap的端点
SSG_ROIRectD roi;
}SSG_basicFeatureGap;
typedef struct
{
int lineIdx;
std::vector<SSG_basicFeature1D> features;
std::vector<SSG_basicFeature1D> endings;
}SSG_lineFeature;
typedef struct
{
/* 点是否连续门限,小于此门限,视为点连续 */
double continuityTh; //点连续门限。当使用z跳变判断连续性时为z跳变门限当使用点距离判断时为点距离门限
/* 噪声判断门限,当连续段的点数据小于此门限,视为噪声 */
double outlierTh;
}SSG_outlierFilterParam;
typedef struct
{
double LSlopeZWin; //对L型Slope进行坡度计算的窗口长度
double validSlopeH;
double minLJumpH;
double minEndingGap;//连续段门限。大于此门限,为不连续
}SSG_slopeParam;
typedef struct
{
double minEndingGap; //连续段门限。大于此门限,为不连续
double scale; //计算方向角的窗口比例尺
double cornerTh; //拐角门限,大于此门限,为有效拐点
double jumpCornerTh_1; //判断拐角是否为跳变的两个门限。当一个门限大于jumpCornerTh_1且另一个小于jumpCornerTh_2时跳变
double jumpCornerTh_2;
}SSG_cornerParam;
typedef struct
{
double H_len; //直角特征水平段的长度
double V_len; //直角特征垂直段的长度
double maxDelta;
}SSG_lineRightAngleParam;
typedef struct
{
double valleyMinH;
double valleyMaxW;
}SSG_VFeatureParam;
typedef struct
{
double sameGapTh;
int gapChkWin;
}SSG_tearFeatureExtactPara;
typedef struct
{
double yDeviation_max;//生长时相邻特征最大的Y偏差
double zDeviation_max; //生长时相邻特征最大的Z偏差
int maxLineSkipNum; //生长时相邻特征的最大线间隔, -1时使用maxDkipDistance
double maxSkipDistance; //若maxLineSkipNum为-1 使用此参数.设为-1时此参数无效
double minLTypeTreeLen; //生长树最少的节点数目。小于此数目的生长树被移除
double minVTypeTreeLen; //生长树最少的节点数目。小于此数目的生长树被移除
}SSG_treeGrowParam;
typedef struct
{
double bagL; //长
double bagW; //宽
double bagH; //高
}SSG_bagParam;
typedef struct
{
ESG_poseSortingMode sortMode;
}SSG_objSortParam;
typedef struct
{
double angleStep;
double radiusStep;
}SSG_polarScanParam;
typedef struct
{
int treeState;
int treeType;
int sLineIdx;
int eLineIdx;
SSG_ROIRectD roi;
std::vector< SSG_basicFeature1D> treeNodes;
}SSG_featureTree;
typedef struct
{
int vTreeFlag;
int treeIdx;
int treeType;
int sLineIdx;
int eLineIdx;
SSG_ROIRectD roi;
}SSG_treeInfo;
typedef struct
{
int seachW_lines;
int searchW_pts;
}SSG_localPkParam;
typedef struct
{
int x;
int y;
int value;
double valueD;
int sideID; //1-T, 2-B, 3-L, 4-R
}SSG_2DValueI;
typedef struct
{
int start;
int len;
int value;
}SSG_RUN;
typedef struct
{
double mean;
double var;
}SSG_meanVar;
typedef struct
{
int x;
int y;
int type;
int edgeId;
int blockFlag;//遮挡标志
double scanDist;
SVzNL3DPoint edgePt;
}SSG_contourPtInfo;
typedef struct
{
int lineIdx;
std::vector<SSG_contourPtInfo> contourPts;
}SSG_lineConotours;
typedef struct
{
int lineIdx;
int edgeId_0;
int edgeId_1;
double ptPairDist;
SSG_contourPtInfo contourPt_0;
SSG_contourPtInfo contourPt_1;
}SSG_conotourPair;
typedef struct
{
double x;
double y;
double z;
double x_roll;
double y_pitch;
double z_yaw;
}SSG_6DOF;
typedef struct
{
double L;
double W;
double H;
double angle;
SVzNL3DPoint endings[4];
} SSG_boxCarDimension;
typedef struct
{
SVzNL3DPoint opCenter; //定子中心位置
int neighbourNum; //相邻目标数量
double opAngle; //定子外部相隔120度操作点的操作角度。以Y形为基准角度0度)
double obstacleDist; //距离周边障碍的距离
}SSG_motorStatorPosition;
typedef struct
{
int pkRgnIdx;
SVzNLSizeD objSize;
SVzNL2DPoint pos2D;
SSG_6DOF centerPos;
}SSG_peakRgnInfo;
typedef struct
{
SVzNL3DPoint sPt;
SVzNL3DPoint ePt;
double scanDist;
int ptNum;
}SSG_contourEdgeInfo;
typedef struct
{
double meanDist;
double varDist;
double minDist;
double maxDist;
int matchNum;
int level2_num; //level2为高可信度(两端为Jump或Ending;
int level1_num; //level1-为中可信度一端为Jump或Ending
SSG_ROIRectD roi;
}SSG_edgeMatchInfo;
typedef struct
{
int id1;
int id2;
int matchPts;
int matchType; //可信度, 2为高可信度(两端为Jump或Ending; 1-为中可信度一端为Jump或Ending0-为低可信度
double matchValue;
double varValue;
double minMatchValue;
double maxMatchValue;
SSG_ROIRectD roi;
}SSG_matchPair;
typedef struct
{
int objIdx;
bool isValid; //是否为合格目标。顶层目标,但编织袋横放(扫描到长边)为不合格目标
SSG_ROIRectD objROI;
SVzNL3DPoint objPos;
SSG_6DOF graspPos;
}SSG_sideBagInfo;
typedef struct
{
SVzNLRect roi;
int ptCounter;
int labelID;
}SSG_Region;
typedef struct
{
double planeCalib[9]; //旋转矩阵,将点云地面调平
double planeHeight;//地面调平后的高度,用于去除地面数据
double invRMatrix[9]; //旋转矩阵,回到原坐标系
}SSG_planeCalibPara;

View File

@ -0,0 +1,7 @@
#pragma once
#define SG_ERR_3D_DATA_INVLD -1000
#define SG_ERR_FOUND_NO_TOP_PLANE -1001
#define SG_ERR_NOT_GRID_FORMAT -1002
#define SG_ERR_LABEL_INFO_ERROR -1003
#define SG_ERR_INVLD_SORTING_MODE -1004

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
#pragma once
#if defined(SG_API_LIBRARY)
# define SG_APISHARED_EXPORT __declspec(dllexport)
#else
# define SG_APISHARED_EXPORT __declspec(dllimport)
#endif
#include "SG_baseDataType.h"
#include <vector>
#define ENABLE_CROSS_WISE_TEAR 0
#define OUTPUT_TEARING_POINTS 1
#define KEEP_GAP_AS_FEATURE 1
#define SCAN_BUFF_SIZE 21 //缓存区大小缓存21条扫描线处理中间扫描线
typedef struct
{
double scanXScale; //3D扫描的X精度取决于两条扫描线的间距。
double scanYScale; //3D扫描的Y精度取决于点间距。scanXScale和scanYScale可以在现场安装后扫描数据得到
double differnceBinTh; //一阶差分二值化门限。其物理意义为撕裂斜面的斜率值。默认为1.0
SSG_tearFeatureExtactPara extractPara;
double tearingMinLen; //最小撕裂长度,达到此长度时输出撕裂
double tearingMinGap; //两个同位置的撕裂的间隔。小于此间隔时,视为同一个撕裂。
}SSG_beltTearingParam;
typedef enum
{
keSG_tearType_Uknown = 0,
keSG_tearType_MachineDir, //纵撕
keSG_tearType_CrossWise,//横撕
}ESG_tearType;
typedef enum
{
keSG_tearStatus_Uknown = 0,
keSG_tearStatus_New, //新发现的撕裂
keSG_tearStatus_Growing, //持续的撕裂
keSG_tearStatus_Ended, //撕裂结束
keSG_tearStatus_Invalid, //无效撕裂(可能被合并)
}ESG_tearStatus;
typedef struct
{
int tearID; //每个撕裂有个唯一的序号
ESG_tearStatus tearStatus;
ESG_tearType tearType;
int statLineIdx; //撕裂开始时的扫描线序号
int endLineIdx; //撕裂结束时的扫描线序号,当撕裂持续时,为最新的扫描线序号
double tearDepth;//撕裂深度。-1表示是贯穿型撕裂
double tearWidth;//撕裂宽度
SSG_ROIRectD roi; //撕裂的ROI范围为{左,右,上,下}
#if OUTPUT_TEARING_POINTS
std::vector<SVzNL3DPosition> pts; //撕裂的边界点debug或显示目的
#endif
}SSG_beltTearingInfo;
//处理状态机
typedef enum
{
keSG_HLineProc_Init = 0, //初态
keSG_HLineProc_First_Half,
keSG_HLineProc_Wait_Second,
keSG_HLineProc_Second_Half,
}ESG_HLineProcSM;
typedef struct
{
int hLineIdx;
//SVzNL3DPosition scanBuff[SCAN_BUFF_SIZE]; //循环buff
//int buffHead;//循环buff的头位置
//int buffSize;//循环buff
SVzNL3DPosition preVldData;
SVzNL3DPosition gapPos;
ESG_HLineProcSM hLineSM; //水平扫描线处理状态机
SSG_RUN firstHalf;
SVzNL3DPosition firstHalf_startPt;
SVzNL3DPosition firstHalf_endPt;
SSG_RUN secondHalf;
SVzNL3DPosition secondHalf_startPt;
SVzNL3DPosition secondHalf_endPt;
double gapMaxZ;
}SSG_hLineProInfo;
void sg_detectBeltTearing(
SVzNL3DLaserLine* laser3DPoints, //一条扫描线
int lineIdx,
int nPointCount, //每条扫描线的点数量。当采用Grid数据格式时每条扫描线的点的数量是相同的。必须使用Grid格式
int* errCode,
std::vector<SSG_hLineProInfo>& hLineWorkers,
std::vector<SSG_beltTearingInfo>& beltTearings_new,
std::vector<SSG_beltTearingInfo>& beltTearings_growing,
std::vector<SSG_beltTearingInfo>& beltTearings_ended,
std::vector<SSG_beltTearingInfo>& beltTearings_unknown, //未判明,应用无需处理。
const SSG_beltTearingParam measureParam);

View File

@ -0,0 +1,25 @@
#include <iostream>
#include <QCoreApplication>
#include "BeltTearingPresenter.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// 创建并初始化Presenter
BeltTearingPresenter presenter;
// 启动TCP服务器监听端口12345
if (!presenter.startServer(12345)) {
std::cout << "Failed to start server" << std::endl;
return -1;
}
// 开始发送模拟数据
presenter.startSendingSimulatedData();
std::cout << "BeltTearing Server started. Press Ctrl+C to exit." << std::endl;
return app.exec();
}

View File

@ -10,13 +10,15 @@ SUBDIRS += ../Module/ModbusTCPServer/ModbusTCPServer.pro
SUBDIRS += ../VrEyeDevice/VrEyeDevice.pro
# 撕裂项目
SUBDIRS += ../GrabBagConfig/GrabBagConfig.pro
SUBDIRS += ../GrabBagApp/GrabBagApp.pro
# 拆包项目
# SUBDIRS += ../GrabBagConfig/GrabBagConfig.pro
# SUBDIRS += ../GrabBagApp/GrabBagApp.pro
# SUBDIRS += ../GrabBagConfigCmd
# 撕裂项目
SUBDIRS += ../VrNets/VrTcpClient.pro
SUBDIRS += ../BeltTearingConfig/BeltTearingConfig.pro
SUBDIRS += ../BeltTearingApp/BeltTearingApp.pro
# SUBDIRS += ../BeltTearingServer/BeltTearingServer.pro
SUBDIRS += ../VrNets/VrTcpServer.pro
SUBDIRS += ../BeltTearingServer/BeltTearingServer.pro

View File

@ -1,65 +1,65 @@
#ifndef IY_MODBUS_TCP_SERVER_H
#define IY_MODBUS_TCP_SERVER_H
#include <functional>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <memory>
#include <unordered_map>
class IYModbusTCPServer {
public:
// 错误代码枚举
enum class ErrorCode {
SUCCESS = 0,
ILLEGAL_FUNCTION = 1,
ILLEGAL_DATA_ADDRESS = 2,
ILLEGAL_DATA_VALUE = 3,
SERVER_FAILURE = 4,
ACKNOWLEDGE = 5,
SERVER_BUSY = 6,
NEGATIVE_ACKNOWLEDGE = 7,
MEMORY_PARITY = 8,
GATEWAY_PATH_UNAVAILABLE = 10,
GATEWAY_TARGET_FAILED = 11
};
// 回调函数类型定义
// 写线圈回调函数unitId, startAddress, quantity, values -> ErrorCode
using WriteCoilsCallback = std::function<ErrorCode(uint8_t, uint16_t, uint16_t, const uint8_t*)>;
// 写保持寄存器回调函数unitId, startAddress, quantity, values -> ErrorCode
using WriteRegistersCallback = std::function<ErrorCode(uint8_t, uint16_t, uint16_t, const uint16_t*)>;
// 连接状态回调函数isConnected -> void
using ConnectionStatusCallback = std::function<void(bool)>;
public:
virtual ~IYModbusTCPServer() = default;
static bool CreateInstance(IYModbusTCPServer** ppModbusTCPServer);
// 启动/停止服务器
virtual int start(int port = 502, int maxConnections = 10) = 0;
virtual void stop() = 0;
// 设置回调函数
virtual void setWriteCoilsCallback(WriteCoilsCallback callback) = 0;
virtual void setWriteRegistersCallback(WriteRegistersCallback callback) = 0;
virtual void setConnectionStatusCallback(ConnectionStatusCallback callback) = 0;
// 更新数据接口 - 线程安全
virtual void updateCoil(uint16_t address, bool value) = 0;
virtual void updateCoils(uint16_t startAddress, const std::vector<bool>& values) = 0;
virtual void updateDiscreteInput(uint16_t address, bool value) = 0;
virtual void updateDiscreteInputs(uint16_t startAddress, const std::vector<bool>& values) = 0;
virtual void updateHoldingRegister(uint16_t address, uint16_t value) = 0;
virtual void updateHoldingRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) = 0;
virtual void updateInputRegister(uint16_t address, uint16_t value) = 0;
virtual void updateInputRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) = 0;
};
#endif // MODBUS_TCP_SERVER_H
#ifndef IY_MODBUS_TCP_SERVER_H
#define IY_MODBUS_TCP_SERVER_H
#include <functional>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <memory>
#include <unordered_map>
class IYModbusTCPServer {
public:
// 错误代码枚举
enum class ErrorCode {
SUCCESS = 0,
ILLEGAL_FUNCTION = 1,
ILLEGAL_DATA_ADDRESS = 2,
ILLEGAL_DATA_VALUE = 3,
SERVER_FAILURE = 4,
ACKNOWLEDGE = 5,
SERVER_BUSY = 6,
NEGATIVE_ACKNOWLEDGE = 7,
MEMORY_PARITY = 8,
GATEWAY_PATH_UNAVAILABLE = 10,
GATEWAY_TARGET_FAILED = 11
};
// 回调函数类型定义
// 写线圈回调函数unitId, startAddress, quantity, values -> ErrorCode
using WriteCoilsCallback = std::function<ErrorCode(uint8_t, uint16_t, uint16_t, const uint8_t*)>;
// 写保持寄存器回调函数unitId, startAddress, quantity, values -> ErrorCode
using WriteRegistersCallback = std::function<ErrorCode(uint8_t, uint16_t, uint16_t, const uint16_t*)>;
// 连接状态回调函数isConnected -> void
using ConnectionStatusCallback = std::function<void(bool)>;
public:
virtual ~IYModbusTCPServer() = default;
static bool CreateInstance(IYModbusTCPServer** ppModbusTCPServer);
// 启动/停止服务器
virtual int start(int port = 502, int maxConnections = 10) = 0;
virtual void stop() = 0;
// 设置回调函数
virtual void setWriteCoilsCallback(WriteCoilsCallback callback) = 0;
virtual void setWriteRegistersCallback(WriteRegistersCallback callback) = 0;
virtual void setConnectionStatusCallback(ConnectionStatusCallback callback) = 0;
// 更新数据接口 - 线程安全
virtual void updateCoil(uint16_t address, bool value) = 0;
virtual void updateCoils(uint16_t startAddress, const std::vector<bool>& values) = 0;
virtual void updateDiscreteInput(uint16_t address, bool value) = 0;
virtual void updateDiscreteInputs(uint16_t startAddress, const std::vector<bool>& values) = 0;
virtual void updateHoldingRegister(uint16_t address, uint16_t value) = 0;
virtual void updateHoldingRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) = 0;
virtual void updateInputRegister(uint16_t address, uint16_t value) = 0;
virtual void updateInputRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) = 0;
};
#endif // MODBUS_TCP_SERVER_H

View File

@ -1,6 +1,7 @@
QT -= gui
CONFIG += c++17 console
QMAKE_CXXFLAGS += /utf-8
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings

File diff suppressed because it is too large Load Diff

View File

@ -1,139 +1,139 @@
#ifndef MODBUS_TCP_SERVER_H
#define MODBUS_TCP_SERVER_H
#include "IYModbusTCPServer.h"
#include <functional>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <memory>
#include <unordered_map>
// Platform-specific includes for socket operations
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <errno.h>
#endif
// libmodbus headers
extern "C" {
#include "modbus.h"
#include "modbus-tcp.h"
}
class ModbusTCPServer : public IYModbusTCPServer {
private:
// 客户端连接结构体
struct ClientConnection {
int socket;
modbus_t* modbusCtx;
ClientConnection(int sock, modbus_t* ctx) : socket(sock), modbusCtx(ctx) {}
~ClientConnection() {
if (modbusCtx) {
modbus_free(modbusCtx);
}
#ifdef _WIN32
if (socket != -1) {
closesocket(socket);
}
#else
if (socket != -1) {
close(socket);
}
#endif
}
};
public:
ModbusTCPServer();
virtual ~ModbusTCPServer() override;
// 实现IYModbusTCPServer接口
// 启动/停止服务器
virtual int start(int port = 502, int maxConnections = 10) override;
virtual void stop() override;
bool isRunning() const { return m_isRunning.load(); }
// 设置回调函数
virtual void setWriteCoilsCallback(WriteCoilsCallback callback) override { m_writeCoilsCallback = callback; }
virtual void setWriteRegistersCallback(WriteRegistersCallback callback) override { m_writeRegistersCallback = callback; }
virtual void setConnectionStatusCallback(ConnectionStatusCallback callback) override { m_connectionStatusCallback = callback; }
// 数据更新接口 - 线程安全
virtual void updateCoil(uint16_t address, bool value) override;
virtual void updateCoils(uint16_t startAddress, const std::vector<bool>& values) override;
virtual void updateDiscreteInput(uint16_t address, bool value) override;
virtual void updateDiscreteInputs(uint16_t startAddress, const std::vector<bool>& values) override;
virtual void updateHoldingRegister(uint16_t address, uint16_t value) override;
virtual void updateHoldingRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) override;
virtual void updateInputRegister(uint16_t address, uint16_t value) override;
virtual void updateInputRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) override;
// 获取错误信息
std::string getLastError() const;
private:
// 服务器主循环
void serverLoop();
// 客户端连接管理
void handleNewConnection();
void handleClientData(std::shared_ptr<ClientConnection> client);
void removeClient(int socket);
void processModbusRequest(std::shared_ptr<ClientConnection> client, const uint8_t* query, int queryLength);
// 数据读取处理器libmodbus内部调用
static int readCoilsHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readDiscreteInputsHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readHoldingRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readInputRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
// 数据写入函数libmodbus内部调用
static int writeCoilsHandler(modbus_t *ctx, int function_code, int addr, int nb, const uint8_t *req, int req_length, void *user_data);
static int writeRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, const uint8_t *req, int req_length, void *user_data);
private:
// libmodbus context
modbus_t* m_modbusCtx;
modbus_mapping_t* m_mapping;
// 服务器状态
std::atomic<bool> m_isRunning;
std::atomic<bool> m_shouldStop;
std::unique_ptr<std::thread> m_serverThread;
int m_serverSocket;
int m_port;
int m_maxConnections;
// 客户端连接管理
std::unordered_map<int, std::shared_ptr<ClientConnection>> m_clients;
std::mutex m_clientsMutex;
// 数据存储 - 使用libmodbus的mapping结构
std::mutex m_dataMutex;
// 回调函数
WriteCoilsCallback m_writeCoilsCallback;
WriteRegistersCallback m_writeRegistersCallback;
ConnectionStatusCallback m_connectionStatusCallback;
// 错误信息
mutable std::mutex m_errorMutex;
std::string m_lastError;
// 设置错误信息
void setLastError(const std::string& error);
};
#endif // MODBUS_TCP_SERVER_H
#ifndef MODBUS_TCP_SERVER_H
#define MODBUS_TCP_SERVER_H
#include "IYModbusTCPServer.h"
#include <functional>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <memory>
#include <unordered_map>
// Platform-specific includes for socket operations
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <errno.h>
#endif
// libmodbus headers
extern "C" {
#include "modbus.h"
#include "modbus-tcp.h"
}
class ModbusTCPServer : public IYModbusTCPServer {
private:
// 客户端连接结构体
struct ClientConnection {
int socket;
modbus_t* modbusCtx;
ClientConnection(int sock, modbus_t* ctx) : socket(sock), modbusCtx(ctx) {}
~ClientConnection() {
if (modbusCtx) {
modbus_free(modbusCtx);
}
#ifdef _WIN32
if (socket != -1) {
closesocket(socket);
}
#else
if (socket != -1) {
close(socket);
}
#endif
}
};
public:
ModbusTCPServer();
virtual ~ModbusTCPServer() override;
// 实现IYModbusTCPServer接口
// 启动/停止服务器
virtual int start(int port = 502, int maxConnections = 10) override;
virtual void stop() override;
bool isRunning() const { return m_isRunning.load(); }
// 设置回调函数
virtual void setWriteCoilsCallback(WriteCoilsCallback callback) override { m_writeCoilsCallback = callback; }
virtual void setWriteRegistersCallback(WriteRegistersCallback callback) override { m_writeRegistersCallback = callback; }
virtual void setConnectionStatusCallback(ConnectionStatusCallback callback) override { m_connectionStatusCallback = callback; }
// 数据更新接口 - 线程安全
virtual void updateCoil(uint16_t address, bool value) override;
virtual void updateCoils(uint16_t startAddress, const std::vector<bool>& values) override;
virtual void updateDiscreteInput(uint16_t address, bool value) override;
virtual void updateDiscreteInputs(uint16_t startAddress, const std::vector<bool>& values) override;
virtual void updateHoldingRegister(uint16_t address, uint16_t value) override;
virtual void updateHoldingRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) override;
virtual void updateInputRegister(uint16_t address, uint16_t value) override;
virtual void updateInputRegisters(uint16_t startAddress, const std::vector<uint16_t>& values) override;
// 获取错误信息
std::string getLastError() const;
private:
// 服务器主循环
void serverLoop();
// 客户端连接管理
void handleNewConnection();
void handleClientData(std::shared_ptr<ClientConnection> client);
void removeClient(int socket);
void processModbusRequest(std::shared_ptr<ClientConnection> client, const uint8_t* query, int queryLength);
// 数据读取处理器libmodbus内部调用
static int readCoilsHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readDiscreteInputsHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readHoldingRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
static int readInputRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, uint8_t *rsp, int *rsp_length, void *user_data);
// 数据写入函数libmodbus内部调用
static int writeCoilsHandler(modbus_t *ctx, int function_code, int addr, int nb, const uint8_t *req, int req_length, void *user_data);
static int writeRegistersHandler(modbus_t *ctx, int function_code, int addr, int nb, const uint8_t *req, int req_length, void *user_data);
private:
// libmodbus context
modbus_t* m_modbusCtx;
modbus_mapping_t* m_mapping;
// 服务器状态
std::atomic<bool> m_isRunning;
std::atomic<bool> m_shouldStop;
std::unique_ptr<std::thread> m_serverThread;
int m_serverSocket;
int m_port;
int m_maxConnections;
// 客户端连接管理
std::unordered_map<int, std::shared_ptr<ClientConnection>> m_clients;
std::mutex m_clientsMutex;
// 数据存储 - 使用libmodbus的mapping结构
std::mutex m_dataMutex;
// 回调函数
WriteCoilsCallback m_writeCoilsCallback;
WriteRegistersCallback m_writeRegistersCallback;
ConnectionStatusCallback m_connectionStatusCallback;
// 错误信息
mutable std::mutex m_errorMutex;
std::string m_lastError;
// 设置错误信息
void setLastError(const std::string& error);
};
#endif // MODBUS_TCP_SERVER_H

27
VrNets/VrTcpServer.pro Normal file
View File

@ -0,0 +1,27 @@
QT += core network
TARGET = VrTcpServer
TEMPLATE = lib
CONFIG += c++11 staticlib
# 源文件
SOURCES += \
tcpServer/Src/VrTcpServer.cpp
# 头文件
HEADERS += \
tcpServer/Inc/IVrTcpServer.h \
tcpServer/Inc/VrTcpServer.h
# 包含路径
INCLUDEPATH += \
tcpServer/Inc
# 安装路径
target.path = $$[QT_INSTALL_LIBS]
INSTALLS += target
# 头文件安装路径
headers.files = $$HEADERS
headers.path = $$[QT_INSTALL_HEADERS]/VrTcpServer
INSTALLS += headers

View File

@ -1,55 +1,55 @@
#ifndef IVRTCPCLIENT_H
#define IVRTCPCLIENT_H
#include <QObject>
#include <QByteArray>
#include <QHostAddress>
class IVrTcpClient : public QObject
{
Q_OBJECT
public:
explicit IVrTcpClient(QObject *parent = nullptr) : QObject(parent) {}
virtual ~IVrTcpClient() {}
// 连接服务器
virtual bool connectToServer(const QString &host, quint16 port) = 0;
virtual bool connectToServer(const QHostAddress &address, quint16 port) = 0;
// 断开连接
virtual void disconnectFromServer() = 0;
// 发送数据
virtual qint64 sendData(const QByteArray &data) = 0;
virtual qint64 sendData(const char *data, qint64 size) = 0;
// 获取连接状态
virtual bool isConnected() const = 0;
// 获取服务器地址和端口
virtual QHostAddress serverAddress() const = 0;
virtual quint16 serverPort() const = 0;
signals:
// 连接状态变化信号
void connected();
void disconnected();
void connectionError(const QString &errorString);
// 数据接收信号
void dataReceived(const QByteArray &data);
// 数据发送信号
void dataSent(qint64 bytesSent);
protected:
// 设置连接状态(供子类使用)
void setConnected(bool connected) { m_connected = connected; }
bool m_connected = false;
QHostAddress m_serverAddress;
quint16 m_serverPort = 0;
};
#endif // IVRTCPCLIENT_H
#ifndef IVRTCPCLIENT_H
#define IVRTCPCLIENT_H
#include <QObject>
#include <QByteArray>
#include <QHostAddress>
class IVrTcpClient : public QObject
{
Q_OBJECT
public:
explicit IVrTcpClient(QObject *parent = nullptr) : QObject(parent) {}
virtual ~IVrTcpClient() {}
// 连接服务器
virtual bool connectToServer(const QString &host, quint16 port) = 0;
virtual bool connectToServer(const QHostAddress &address, quint16 port) = 0;
// 断开连接
virtual void disconnectFromServer() = 0;
// 发送数据
virtual qint64 sendData(const QByteArray &data) = 0;
virtual qint64 sendData(const char *data, qint64 size) = 0;
// 获取连接状态
virtual bool isConnected() const = 0;
// 获取服务器地址和端口
virtual QHostAddress serverAddress() const = 0;
virtual quint16 serverPort() const = 0;
signals:
// 连接状态变化信号
void connected();
void disconnected();
void connectionError(const QString &errorString);
// 数据接收信号
void dataReceived(const QByteArray &data);
// 数据发送信号
void dataSent(qint64 bytesSent);
protected:
// 设置连接状态(供子类使用)
void setConnected(bool connected) { m_connected = connected; }
bool m_connected = false;
QHostAddress m_serverAddress;
quint16 m_serverPort = 0;
};
#endif // IVRTCPCLIENT_H

View File

@ -1,39 +1,39 @@
#ifndef VRTCPCLIENT_H
#define VRTCPCLIENT_H
#include "IVrTcpClient.h"
#include <QTcpSocket>
#include <QTimer>
class VrTcpClient : public IVrTcpClient
{
Q_OBJECT
public:
explicit VrTcpClient(QObject *parent = nullptr);
~VrTcpClient() override;
// 接口实现
bool connectToServer(const QString &host, quint16 port) override;
bool connectToServer(const QHostAddress &address, quint16 port) override;
void disconnectFromServer() override;
qint64 sendData(const QByteArray &data) override;
qint64 sendData(const char *data, qint64 size) override;
bool isConnected() const override;
QHostAddress serverAddress() const override;
quint16 serverPort() const override;
// 额外功能:自动重连
void setAutoReconnect(bool autoReconnect);
bool autoReconnect() const;
void setReconnectInterval(int msec);
int reconnectInterval() const;
private:
QTcpSocket *m_socket;
QTimer *m_reconnectTimer;
bool m_autoReconnect = false;
};
#endif // VRTCPCLIENT_H
#ifndef VRTCPCLIENT_H
#define VRTCPCLIENT_H
#include "IVrTcpClient.h"
#include <QTcpSocket>
#include <QTimer>
class VrTcpClient : public IVrTcpClient
{
Q_OBJECT
public:
explicit VrTcpClient(QObject *parent = nullptr);
~VrTcpClient() override;
// 接口实现
bool connectToServer(const QString &host, quint16 port) override;
bool connectToServer(const QHostAddress &address, quint16 port) override;
void disconnectFromServer() override;
qint64 sendData(const QByteArray &data) override;
qint64 sendData(const char *data, qint64 size) override;
bool isConnected() const override;
QHostAddress serverAddress() const override;
quint16 serverPort() const override;
// 额外功能:自动重连
void setAutoReconnect(bool autoReconnect);
bool autoReconnect() const;
void setReconnectInterval(int msec);
int reconnectInterval() const;
private:
QTcpSocket *m_socket;
QTimer *m_reconnectTimer;
bool m_autoReconnect = false;
};
#endif // VRTCPCLIENT_H

View File

@ -1,150 +1,150 @@
#include "VrTcpClient.h"
#include <QTcpSocket>
#include <QTimer>
#include <QDebug>
VrTcpClient::VrTcpClient(QObject *parent)
: IVrTcpClient(parent)
, m_socket(new QTcpSocket(this))
, m_reconnectTimer(new QTimer(this))
{
// 设置重连定时器
m_reconnectTimer->setInterval(5000); // 5秒重连间隔
m_reconnectTimer->setSingleShot(true);
// 连接信号槽
connect(m_socket, &QTcpSocket::connected, this, [this]() {
setConnected(true);
m_reconnectTimer->stop();
emit connected();
});
connect(m_socket, &QTcpSocket::disconnected, this, [this]() {
setConnected(false);
emit disconnected();
// 如果设置了自动重连,则启动重连定时器
if (m_autoReconnect) {
m_reconnectTimer->start();
}
});
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred),
this, [this](QAbstractSocket::SocketError error) {
setConnected(false);
emit connectionError(m_socket->errorString());
});
connect(m_socket, &QTcpSocket::readyRead, this, [this]() {
QByteArray data = m_socket->readAll();
if (!data.isEmpty()) {
emit dataReceived(data);
}
});
connect(m_socket, &QTcpSocket::bytesWritten, this, &IVrTcpClient::dataSent);
connect(m_reconnectTimer, &QTimer::timeout, this, [this]() {
if (m_autoReconnect && !m_serverAddress.isNull() && m_serverPort > 0) {
connectToServer(m_serverAddress, m_serverPort);
}
});
}
VrTcpClient::~VrTcpClient()
{
disconnectFromServer();
}
bool VrTcpClient::connectToServer(const QString &host, quint16 port)
{
m_serverAddress = QHostAddress(host);
m_serverPort = port;
if (m_serverAddress.isNull()) {
// 如果是主机名,直接连接
m_socket->connectToHost(host, port);
return true;
} else {
return connectToServer(m_serverAddress, port);
}
}
bool VrTcpClient::connectToServer(const QHostAddress &address, quint16 port)
{
m_serverAddress = address;
m_serverPort = port;
if (isConnected()) {
disconnectFromServer();
}
m_socket->connectToHost(address, port);
return true;
}
void VrTcpClient::disconnectFromServer()
{
m_reconnectTimer->stop();
if (m_socket->state() != QAbstractSocket::UnconnectedState) {
m_socket->disconnectFromHost();
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->waitForDisconnected(3000);
}
}
setConnected(false);
}
qint64 VrTcpClient::sendData(const QByteArray &data)
{
if (!isConnected()) {
return -1;
}
return m_socket->write(data);
}
qint64 VrTcpClient::sendData(const char *data, qint64 size)
{
if (!isConnected()) {
return -1;
}
return m_socket->write(data, size);
}
bool VrTcpClient::isConnected() const
{
return m_connected && m_socket->state() == QAbstractSocket::ConnectedState;
}
QHostAddress VrTcpClient::serverAddress() const
{
return m_serverAddress;
}
quint16 VrTcpClient::serverPort() const
{
return m_serverPort;
}
void VrTcpClient::setAutoReconnect(bool autoReconnect)
{
m_autoReconnect = autoReconnect;
if (!autoReconnect) {
m_reconnectTimer->stop();
}
}
bool VrTcpClient::autoReconnect() const
{
return m_autoReconnect;
}
void VrTcpClient::setReconnectInterval(int msec)
{
m_reconnectTimer->setInterval(msec);
}
int VrTcpClient::reconnectInterval() const
{
return m_reconnectTimer->interval();
}
#include "VrTcpClient.h"
#include <QTcpSocket>
#include <QTimer>
#include <QDebug>
VrTcpClient::VrTcpClient(QObject *parent)
: IVrTcpClient(parent)
, m_socket(new QTcpSocket(this))
, m_reconnectTimer(new QTimer(this))
{
// 设置重连定时器
m_reconnectTimer->setInterval(5000); // 5秒重连间隔
m_reconnectTimer->setSingleShot(true);
// 连接信号槽
connect(m_socket, &QTcpSocket::connected, this, [this]() {
setConnected(true);
m_reconnectTimer->stop();
emit connected();
});
connect(m_socket, &QTcpSocket::disconnected, this, [this]() {
setConnected(false);
emit disconnected();
// 如果设置了自动重连,则启动重连定时器
if (m_autoReconnect) {
m_reconnectTimer->start();
}
});
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred),
this, [this](QAbstractSocket::SocketError error) {
setConnected(false);
emit connectionError(m_socket->errorString());
});
connect(m_socket, &QTcpSocket::readyRead, this, [this]() {
QByteArray data = m_socket->readAll();
if (!data.isEmpty()) {
emit dataReceived(data);
}
});
connect(m_socket, &QTcpSocket::bytesWritten, this, &IVrTcpClient::dataSent);
connect(m_reconnectTimer, &QTimer::timeout, this, [this]() {
if (m_autoReconnect && !m_serverAddress.isNull() && m_serverPort > 0) {
connectToServer(m_serverAddress, m_serverPort);
}
});
}
VrTcpClient::~VrTcpClient()
{
disconnectFromServer();
}
bool VrTcpClient::connectToServer(const QString &host, quint16 port)
{
m_serverAddress = QHostAddress(host);
m_serverPort = port;
if (m_serverAddress.isNull()) {
// 如果是主机名,直接连接
m_socket->connectToHost(host, port);
return true;
} else {
return connectToServer(m_serverAddress, port);
}
}
bool VrTcpClient::connectToServer(const QHostAddress &address, quint16 port)
{
m_serverAddress = address;
m_serverPort = port;
if (isConnected()) {
disconnectFromServer();
}
m_socket->connectToHost(address, port);
return true;
}
void VrTcpClient::disconnectFromServer()
{
m_reconnectTimer->stop();
if (m_socket->state() != QAbstractSocket::UnconnectedState) {
m_socket->disconnectFromHost();
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->waitForDisconnected(3000);
}
}
setConnected(false);
}
qint64 VrTcpClient::sendData(const QByteArray &data)
{
if (!isConnected()) {
return -1;
}
return m_socket->write(data);
}
qint64 VrTcpClient::sendData(const char *data, qint64 size)
{
if (!isConnected()) {
return -1;
}
return m_socket->write(data, size);
}
bool VrTcpClient::isConnected() const
{
return m_connected && m_socket->state() == QAbstractSocket::ConnectedState;
}
QHostAddress VrTcpClient::serverAddress() const
{
return m_serverAddress;
}
quint16 VrTcpClient::serverPort() const
{
return m_serverPort;
}
void VrTcpClient::setAutoReconnect(bool autoReconnect)
{
m_autoReconnect = autoReconnect;
if (!autoReconnect) {
m_reconnectTimer->stop();
}
}
bool VrTcpClient::autoReconnect() const
{
return m_autoReconnect;
}
void VrTcpClient::setReconnectInterval(int msec)
{
m_reconnectTimer->setInterval(msec);
}
int VrTcpClient::reconnectInterval() const
{
return m_reconnectTimer->interval();
}

View File

@ -0,0 +1,38 @@
#ifndef IVRTCPSERVER_H
#define IVRTCPSERVER_H
#include <QObject>
#include <QHostAddress>
class IVrTcpServer : public QObject
{
Q_OBJECT
public:
explicit IVrTcpServer(QObject *parent = nullptr);
~IVrTcpServer() override;
// 服务器接口
virtual bool startServer(quint16 port) = 0;
virtual void stopServer() = 0;
virtual bool isListening() const = 0;
virtual quint16 serverPort() const = 0;
// 数据发送接口
virtual qint64 broadcastData(const QByteArray &data) = 0;
virtual qint64 broadcastData(const char *data, qint64 size) = 0;
signals:
// 服务器状态信号
void serverStarted();
void serverStopped();
void serverError(const QString &error);
// 客户端连接信号
void clientConnected(const QString &clientId);
void clientDisconnected(const QString &clientId);
void dataReceived(const QString &clientId, const QByteArray &data);
void dataSent(const QString &clientId, qint64 bytes);
};
#endif // IVRTCPSERVER_H

View File

@ -0,0 +1,48 @@
#ifndef VRTCPSERVER_H
#define VRTCPSERVER_H
#include "IVrTcpServer.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QMap>
#include <QTimer>
class VrTcpServer : public IVrTcpServer
{
Q_OBJECT
public:
explicit VrTcpServer(QObject *parent = nullptr);
~VrTcpServer() override;
// 接口实现
bool startServer(quint16 port) override;
void stopServer() override;
bool isListening() const override;
quint16 serverPort() const override;
// 客户端管理
QStringList getClientIds() const;
bool disconnectClient(const QString &clientId);
qint64 sendDataToClient(const QString &clientId, const QByteArray &data);
qint64 sendDataToClient(const QString &clientId, const char *data, qint64 size);
qint64 broadcastData(const QByteArray &data);
qint64 broadcastData(const char *data, qint64 size);
QHostAddress getClientAddress(const QString &clientId) const;
quint16 getClientPort(const QString &clientId) const;
private slots:
void onNewConnection();
void onClientDisconnected();
void onClientReadyRead();
void onClientBytesWritten(qint64 bytes);
void onServerError(QAbstractSocket::SocketError error);
private:
QTcpServer *m_server;
QMap<QString, QTcpSocket*> m_clients;
quint16 m_port;
QString generateClientId(QTcpSocket *socket);
};
#endif // VRTCPSERVER_H

View File

@ -0,0 +1,281 @@
#include "VrTcpServer.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QUuid>
#include <QDebug>
VrTcpServer::VrTcpServer(QObject *parent)
: IVrTcpServer(parent)
, m_server(new QTcpServer(this))
, m_port(0)
{
// 连接服务器信号
connect(m_server, &QTcpServer::newConnection, this, &VrTcpServer::onNewConnection);
connect(m_server, &QTcpServer::acceptError, this, [this](QAbstractSocket::SocketError error) {
emit serverError(m_server->errorString());
});
}
VrTcpServer::~VrTcpServer()
{
stopServer();
}
bool VrTcpServer::startServer(quint16 port)
{
if (m_server->isListening()) {
stopServer();
}
m_port = port;
bool result = m_server->listen(QHostAddress::Any, port);
if (result) {
emit serverStarted();
} else {
emit serverError(m_server->errorString());
}
return result;
}
void VrTcpServer::stopServer()
{
// 断开所有客户端连接
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
it.value()->disconnectFromHost();
}
m_clients.clear();
// 停止服务器监听
if (m_server->isListening()) {
m_server->close();
emit serverStopped();
}
m_port = 0;
}
bool VrTcpServer::isListening() const
{
return m_server->isListening();
}
quint16 VrTcpServer::serverPort() const
{
return m_port;
}
QStringList VrTcpServer::getClientIds() const
{
return m_clients.keys();
}
bool VrTcpServer::disconnectClient(const QString &clientId)
{
if (!m_clients.contains(clientId)) {
return false;
}
QTcpSocket *socket = m_clients[clientId];
socket->disconnectFromHost();
return true;
}
qint64 VrTcpServer::sendDataToClient(const QString &clientId, const QByteArray &data)
{
if (!m_clients.contains(clientId)) {
return -1;
}
QTcpSocket *socket = m_clients[clientId];
if (socket->state() != QAbstractSocket::ConnectedState) {
return -1;
}
return socket->write(data);
}
qint64 VrTcpServer::sendDataToClient(const QString &clientId, const char *data, qint64 size)
{
if (!m_clients.contains(clientId)) {
return -1;
}
QTcpSocket *socket = m_clients[clientId];
if (socket->state() != QAbstractSocket::ConnectedState) {
return -1;
}
return socket->write(data, size);
}
QHostAddress VrTcpServer::getClientAddress(const QString &clientId) const
{
if (!m_clients.contains(clientId)) {
return QHostAddress();
}
return m_clients[clientId]->peerAddress();
}
quint16 VrTcpServer::getClientPort(const QString &clientId) const
{
if (!m_clients.contains(clientId)) {
return 0;
}
return m_clients[clientId]->peerPort();
}
void VrTcpServer::onNewConnection()
{
while (m_server->hasPendingConnections()) {
QTcpSocket *socket = m_server->nextPendingConnection();
// 生成客户端ID
QString clientId = generateClientId(socket);
// 存储客户端连接
m_clients[clientId] = socket;
// 连接客户端信号
connect(socket, &QTcpSocket::disconnected, this, &VrTcpServer::onClientDisconnected);
connect(socket, &QTcpSocket::readyRead, this, &VrTcpServer::onClientReadyRead);
connect(socket, &QTcpSocket::bytesWritten, this, &VrTcpServer::onClientBytesWritten);
// 发送客户端连接信号
emit clientConnected(clientId);
}
}
void VrTcpServer::onClientDisconnected()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) {
return;
}
// 查找断开连接的客户端ID
QString clientId;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
if (it.value() == socket) {
clientId = it.key();
break;
}
}
if (!clientId.isEmpty()) {
m_clients.remove(clientId);
emit clientDisconnected(clientId);
}
socket->deleteLater();
}
void VrTcpServer::onClientReadyRead()
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) {
return;
}
// 查找客户端ID
QString clientId;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
if (it.value() == socket) {
clientId = it.key();
break;
}
}
if (!clientId.isEmpty()) {
QByteArray data = socket->readAll();
if (!data.isEmpty()) {
emit dataReceived(clientId, data);
}
}
}
void VrTcpServer::onClientBytesWritten(qint64 bytes)
{
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) {
return;
}
// 查找客户端ID
QString clientId;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
if (it.value() == socket) {
clientId = it.key();
break;
}
}
if (!clientId.isEmpty()) {
emit dataSent(clientId, bytes);
}
}
void VrTcpServer::onServerError(QAbstractSocket::SocketError error)
{
Q_UNUSED(error)
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) {
return;
}
// 查找客户端ID
QString clientId;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
if (it.value() == socket) {
clientId = it.key();
break;
}
}
if (!clientId.isEmpty()) {
emit serverError(QString("Client %1 error: %2").arg(clientId, socket->errorString()));
}
}
QString VrTcpServer::generateClientId(QTcpSocket *socket)
{
// 使用UUID生成唯一的客户端ID
return QUuid::createUuid().toString();
}
qint64 VrTcpServer::broadcastData(const QByteArray &data)
{
qint64 totalBytesWritten = 0;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
QTcpSocket *socket = it.value();
if (socket->state() == QAbstractSocket::ConnectedState) {
qint64 bytesWritten = socket->write(data);
if (bytesWritten > 0) {
totalBytesWritten += bytesWritten;
}
}
}
return totalBytesWritten;
}
qint64 VrTcpServer::broadcastData(const char *data, qint64 size)
{
qint64 totalBytesWritten = 0;
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
QTcpSocket *socket = it.value();
if (socket->state() == QAbstractSocket::ConnectedState) {
qint64 bytesWritten = socket->write(data, size);
if (bytesWritten > 0) {
totalBytesWritten += bytesWritten;
}
}
}
return totalBytesWritten;
}

View File

@ -0,0 +1,60 @@
#include "../Inc/VrTcpServer.h"
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
VrTcpServer server;
// 连接信号槽
QObject::connect(&server, &IVrTcpServer::serverStarted, []() {
qDebug() << "Server started!";
});
QObject::connect(&server, &IVrTcpServer::serverStopped, []() {
qDebug() << "Server stopped!";
});
QObject::connect(&server, &IVrTcpServer::serverError, [](const QString &error) {
qDebug() << "Server error:" << error;
});
QObject::connect(&server, &IVrTcpServer::clientConnected, [&server](const QString &clientId) {
qDebug() << "Client connected:" << clientId;
// 发送欢迎消息给新连接的客户端
QByteArray welcomeMessage = "Welcome to the server!";
server.sendDataToClient(clientId, welcomeMessage);
});
QObject::connect(&server, &IVrTcpServer::clientDisconnected, [](const QString &clientId) {
qDebug() << "Client disconnected:" << clientId;
});
QObject::connect(&server, &IVrTcpServer::dataReceived, [&server](const QString &clientId, const QByteArray &data) {
qDebug() << "Received data from" << clientId << ":" << data.toHex();
// 将收到的数据广播给所有客户端
QByteArray message = "Broadcast from " + clientId.toUtf8() + ": " + data;
server.broadcastData(message);
});
// 启动服务器
if (!server.startServer(12345)) {
qDebug() << "Failed to start server";
return -1;
}
qDebug() << "Server listening on port 12345";
// 10秒后停止服务器并退出
QTimer::singleShot(10000, [&server, &app]() {
server.stopServer();
app.quit();
});
return app.exec();
}