2025-08-27 23:10:36 +08:00
|
|
|
|
#include "BeltTearingPresenter.h"
|
|
|
|
|
|
#include <QUuid>
|
|
|
|
|
|
#include <QDataStream>
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
|
#include <QBuffer>
|
|
|
|
|
|
#include <QByteArray>
|
|
|
|
|
|
#include <QDateTime>
|
|
|
|
|
|
#include <iostream>
|
2025-08-31 21:08:28 +08:00
|
|
|
|
#include <QRandomGenerator>
|
2025-08-27 23:10:36 +08:00
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
{
|
2025-08-31 21:08:28 +08:00
|
|
|
|
static unsigned int nCount = 0;
|
|
|
|
|
|
nCount++;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
// 向所有连接的客户端发送模拟数据
|
|
|
|
|
|
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
|
|
|
|
|
|
QTcpSocket *socket = it.value();
|
|
|
|
|
|
if (socket->state() == QAbstractSocket::ConnectedState) {
|
2025-08-31 21:08:28 +08:00
|
|
|
|
if(nCount % 2 == 0){
|
|
|
|
|
|
sendSimulatedImage(socket);
|
|
|
|
|
|
}else{
|
|
|
|
|
|
sendSimulatedTearingData(socket);
|
|
|
|
|
|
}
|
2025-08-27 23:10:36 +08:00
|
|
|
|
// 随机选择发送图像或日志记录
|
2025-08-31 21:08:28 +08:00
|
|
|
|
// sendSimulatedImage(socket);
|
|
|
|
|
|
// sendSimulatedLogRecords(socket);
|
|
|
|
|
|
// sendSimulatedTearingData(socket);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-31 21:08:28 +08:00
|
|
|
|
void BeltTearingPresenter::sendSimulatedTearingData(QTcpSocket *socket)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 创建模拟撕裂数据
|
|
|
|
|
|
QJsonArray tearingDataArray;
|
|
|
|
|
|
|
|
|
|
|
|
// 生成随机数量的撕裂数据(1-3个)
|
|
|
|
|
|
int tearingCount = QRandomGenerator::global()->bounded(1, 4);
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < tearingCount; i++) {
|
|
|
|
|
|
QJsonObject tearingInfo;
|
|
|
|
|
|
|
|
|
|
|
|
// 随机生成撕裂数据
|
|
|
|
|
|
int level = QRandomGenerator::global()->bounded(1, 4); // 1-3级
|
|
|
|
|
|
int tearStatus = QRandomGenerator::global()->bounded(1, 4); // 1-3状态
|
|
|
|
|
|
|
|
|
|
|
|
QString statusStr;
|
|
|
|
|
|
switch (tearStatus) {
|
|
|
|
|
|
case 1: statusStr = "新的撕裂"; break;
|
|
|
|
|
|
case 2: statusStr = "进行中"; break;
|
|
|
|
|
|
case 3: statusStr = "已完成"; break;
|
|
|
|
|
|
default: statusStr = "无效撕裂"; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 填充撕裂信息
|
|
|
|
|
|
tearingInfo.insert("level", QString::number(level));
|
|
|
|
|
|
tearingInfo.insert("id", QString::number(QRandomGenerator::global()->bounded(1000, 9999)));
|
|
|
|
|
|
tearingInfo.insert("tearStatus", statusStr);
|
|
|
|
|
|
tearingInfo.insert("tearType", QString::number(QRandomGenerator::global()->bounded(1, 5)));
|
|
|
|
|
|
tearingInfo.insert("statLineIdx", QString::number(QRandomGenerator::global()->bounded(1, 100)));
|
|
|
|
|
|
tearingInfo.insert("endLineIdx", QString::number(QRandomGenerator::global()->bounded(1, 100)));
|
|
|
|
|
|
tearingInfo.insert("tearDepth", QString::number(QRandomGenerator::global()->bounded(1, 1000)));
|
|
|
|
|
|
tearingInfo.insert("tearWidth", QString::number(QRandomGenerator::global()->bounded(1, 500)));
|
|
|
|
|
|
tearingInfo.insert("tearLength", QString::number(QRandomGenerator::global()->bounded(1, 1000)));
|
|
|
|
|
|
tearingInfo.insert("roiLeft", QString::number(QRandomGenerator::global()->bounded(1, 200)));
|
|
|
|
|
|
tearingInfo.insert("roiRight", QString::number(QRandomGenerator::global()->bounded(200, 400)));
|
|
|
|
|
|
tearingInfo.insert("roiTop", QString::number(QRandomGenerator::global()->bounded(1, 100)));
|
|
|
|
|
|
tearingInfo.insert("roiBottom", QString::number(QRandomGenerator::global()->bounded(100, 300)));
|
|
|
|
|
|
tearingInfo.insert("imageFile", QString("/Image/Tearing/image_%1__.jpg").arg(QRandomGenerator::global()->bounded(1, 100)));
|
|
|
|
|
|
tearingInfo.insert("older", (QRandomGenerator::global()->bounded(0, 2) == 0) ? "-" : "旧伤");
|
|
|
|
|
|
|
|
|
|
|
|
tearingDataArray.append(tearingInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将撕裂数据转换为JSON字符串
|
|
|
|
|
|
QJsonDocument doc(tearingDataArray);
|
|
|
|
|
|
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 tearing data to client" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 23:10:36 +08:00
|
|
|
|
QString BeltTearingPresenter::generateClientId(QTcpSocket *socket)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用UUID生成唯一的客户端ID
|
|
|
|
|
|
return QUuid::createUuid().toString();
|
|
|
|
|
|
}
|