2025-08-24 23:24:33 +08:00
|
|
|
|
#include "BeltTearingPresenter.h"
|
2025-09-18 23:49:32 +08:00
|
|
|
|
#include "IVrTCPClient.h"
|
2025-08-24 23:24:33 +08:00
|
|
|
|
#include "PathManager.h"
|
|
|
|
|
|
#include "VrLog.h"
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
#include <QTimer>
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include <QMetaObject>
|
2025-08-24 23:24:33 +08:00
|
|
|
|
#include <QImage>
|
|
|
|
|
|
#include <QBuffer>
|
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
|
#include <QSettings>
|
|
|
|
|
|
#include <QDir>
|
2025-08-31 21:08:28 +08:00
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QJsonArray>
|
2025-08-24 23:24:33 +08:00
|
|
|
|
#include "widgets/DeviceStatusWidget.h"
|
|
|
|
|
|
#include "VrDateUtils.h"
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
BeltTearingPresenter::BeltTearingPresenter(QWidget* parent)
|
|
|
|
|
|
: QWidget(parent)
|
|
|
|
|
|
, m_config(nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 创建配置实例
|
|
|
|
|
|
IVrBeltTearingConfig::CreateInstance(&m_config);
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 连接异步数据处理信号和槽
|
|
|
|
|
|
connect(this, &BeltTearingPresenter::tcpDataReceivedAsync,
|
|
|
|
|
|
this, &BeltTearingPresenter::onTcpDataReceivedAsync,
|
|
|
|
|
|
Qt::QueuedConnection);
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BeltTearingPresenter::~BeltTearingPresenter()
|
|
|
|
|
|
{
|
|
|
|
|
|
disconnectFromServer();
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
// 删除所有TCP客户端
|
|
|
|
|
|
for (auto it = m_tcpClients.begin(); it != m_tcpClients.end(); ++it) {
|
2025-09-18 23:49:32 +08:00
|
|
|
|
IVrTCPClient::DestroyInstance(it.value());
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
m_tcpClients.clear();
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
if (m_config) {
|
|
|
|
|
|
delete m_config;
|
|
|
|
|
|
m_config = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
QString configPath = PathManager::GetConfigFilePath();
|
2025-08-31 21:08:28 +08:00
|
|
|
|
bool result = initializeConfig(configPath);
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
|
// Even if config loading fails, we should notify the UI about the number of images to show (0)
|
|
|
|
|
|
if(m_statusUpdate){
|
|
|
|
|
|
m_statusUpdate->OnNeedShowImageCount(QStringList());
|
|
|
|
|
|
}
|
|
|
|
|
|
LOG_DEBUG("Init config finished with no configuration\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_DEBUG("Init config finish \n");
|
|
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::initializeConfig(const QString &configPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!m_config) {
|
|
|
|
|
|
LOG_WARNING("Config instance is null");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BeltTearingConfigResult configResult = m_config->LoadConfig(configPath.toStdString());
|
|
|
|
|
|
if (configResult.servers.empty()) {
|
2025-08-31 21:08:28 +08:00
|
|
|
|
LOG_WARNING("Failed to load config from: %s \n", configPath.toStdString().c_str());
|
2025-08-24 23:24:33 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
if (configResult.servers.empty()) {
|
2025-08-24 23:24:33 +08:00
|
|
|
|
LOG_WARNING("No servers configured");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空现有配置
|
|
|
|
|
|
m_serverInfos.clear();
|
2025-10-08 21:45:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取所有服务器配置
|
|
|
|
|
|
const auto &servers = configResult.servers;
|
|
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
// 存储所有启用的服务器信息
|
|
|
|
|
|
QList<DeviceInfo> devices;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
QStringList deviceAliases;
|
|
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
for (const auto &server : servers) {
|
2025-08-31 21:08:28 +08:00
|
|
|
|
QString serverAliaseName = QString("%1_%2").arg(QString::fromStdString(server.name)).arg(CVrDateUtils::GetTimestamp());
|
|
|
|
|
|
m_serverInfos[serverAliaseName] = server;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加到设备列表
|
2025-08-31 21:08:28 +08:00
|
|
|
|
devices.append(DeviceInfo(QString::fromStdString(server.name), serverAliaseName, QString::fromStdString(server.ip), DeviceStatus::Offline, true));
|
2025-09-18 23:49:32 +08:00
|
|
|
|
deviceAliases.append(serverAliaseName);
|
2025-08-31 21:08:28 +08:00
|
|
|
|
LOG_DEBUG("Server configured: %s %s:%d \n", serverAliaseName.toStdString().c_str(), server.ip.c_str(), server.port);
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-08-31 21:08:28 +08:00
|
|
|
|
LOG_DEBUG("Init config finish. Found %d enabled servers \n", m_serverInfos.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(m_statusUpdate){
|
|
|
|
|
|
m_statusUpdate->OnNeedShowImageCount(deviceAliases);
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 连接到所有服务器
|
2025-10-08 21:45:37 +08:00
|
|
|
|
for(size_t i = 0 ; i < servers.size() ; i++){
|
2025-09-18 23:49:32 +08:00
|
|
|
|
connectToServer(servers[i], deviceAliases[i]);
|
|
|
|
|
|
}
|
2025-08-31 21:08:28 +08:00
|
|
|
|
LOG_DEBUG("Config loaded successfully. Found %d enabled servers\n", m_serverInfos.size());
|
|
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
bool BeltTearingPresenter::connectToServer(const ServerInfo &serverInfo, const QString &aliasName)
|
2025-08-24 23:24:33 +08:00
|
|
|
|
{
|
2025-10-08 21:45:37 +08:00
|
|
|
|
QString targetServerName = aliasName;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建TCP客户端(如果不存在)
|
|
|
|
|
|
if (!m_tcpClients.contains(targetServerName)) {
|
2025-09-18 23:49:32 +08:00
|
|
|
|
IVrTCPClient *client = IVrTCPClient::CreateInstance();
|
|
|
|
|
|
if (!client) {
|
|
|
|
|
|
LOG_ERROR("Failed to create TCP client for %s", targetServerName.toStdString().c_str());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
m_tcpClients[targetServerName] = client;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_connectionStatus[targetServerName] = false;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
IVrTCPClient *client = m_tcpClients[targetServerName];
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 连接服务器 - 启用TCP客户端自动重连
|
2025-09-18 23:49:32 +08:00
|
|
|
|
int linkResult = client->LinkDevice(serverInfo.ip, serverInfo.port, true, // 启用自动重连
|
|
|
|
|
|
[this, targetServerName](IVrTCPClient* pClient, bool connected, void* pParam) {
|
2025-09-21 22:20:24 +08:00
|
|
|
|
LOG_DEBUG("connectToServer %s link status : %d \n", targetServerName.toStdString().c_str(), connected);
|
2025-09-18 23:49:32 +08:00
|
|
|
|
this->handleTcpLinkStatus(targetServerName, connected);
|
|
|
|
|
|
}, this
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("connectToServer %s ret : %d \n", targetServerName.toStdString().c_str(), linkResult);
|
|
|
|
|
|
|
|
|
|
|
|
// 启动工作线程
|
|
|
|
|
|
int workResult = client->StartWork(
|
|
|
|
|
|
[this, targetServerName](IVrTCPClient* pClient, const char* pData, const int nLen, void* pParam) {
|
|
|
|
|
|
this->handleTcpDataReceived(targetServerName, pData, nLen);
|
|
|
|
|
|
}, this
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (workResult != 0) {
|
|
|
|
|
|
LOG_ERROR("Failed to start TCP client work thread for %s", targetServerName.toStdString().c_str());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (linkResult != 0) {
|
2025-08-24 23:24:33 +08:00
|
|
|
|
LOG_ERROR("Failed to initiate connection to %s", targetServerName.toStdString().c_str());
|
2025-09-18 23:49:32 +08:00
|
|
|
|
return false;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::disconnectFromServer(const QString &serverName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (serverName.isEmpty()) {
|
|
|
|
|
|
// 断开所有连接
|
|
|
|
|
|
for (auto it = m_tcpClients.begin(); it != m_tcpClients.end(); ++it) {
|
2025-09-18 23:49:32 +08:00
|
|
|
|
it.value()->CloseDevice();
|
|
|
|
|
|
m_connectionStatus[it.key()] = false;
|
|
|
|
|
|
}
|
2025-10-12 16:46:46 +08:00
|
|
|
|
// 清空所有数据缓存
|
|
|
|
|
|
m_dataBuffers.clear();
|
2025-08-24 23:24:33 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 断开指定服务器连接
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_tcpClients.contains(serverName)) {
|
|
|
|
|
|
m_tcpClients[serverName]->CloseDevice();
|
|
|
|
|
|
m_connectionStatus[serverName] = false;
|
2025-10-12 16:46:46 +08:00
|
|
|
|
// 清空该服务器的数据缓存
|
|
|
|
|
|
m_dataBuffers.remove(serverName);
|
2025-09-18 23:49:32 +08:00
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::isConnected(const QString &serverName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if(serverName.isEmpty()) return false;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
QString targetServerName = serverName;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (m_connectionStatus.contains(targetServerName)) {
|
|
|
|
|
|
return m_connectionStatus[targetServerName];
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BeltTearingPresenter::sendData(const QByteArray &data, const QString &serverName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(serverName.isEmpty()) return false;
|
|
|
|
|
|
QString targetServerName = serverName;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
if (!isConnected(targetServerName)) {
|
|
|
|
|
|
LOG_ERROR("Not connected to server: %s", targetServerName.toStdString().c_str());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
if (data.isEmpty()) {
|
|
|
|
|
|
LOG_ERROR("Empty data to send");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
if (!m_tcpClients.contains(targetServerName)) {
|
|
|
|
|
|
LOG_ERROR("Server client not found: %s", targetServerName.toStdString().c_str());
|
2025-09-18 23:49:32 +08:00
|
|
|
|
return false;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
bool success = m_tcpClients[targetServerName]->SendData(data.constData(), data.size());
|
2025-08-24 23:24:33 +08:00
|
|
|
|
if (!success) {
|
|
|
|
|
|
LOG_ERROR("Failed to send data to %s", targetServerName.toStdString().c_str());
|
|
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-08-24 23:24:33 +08:00
|
|
|
|
return success;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString BeltTearingPresenter::getServerIp(const QString &serverName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_serverInfos.contains(serverName)) {
|
|
|
|
|
|
return QString::fromStdString(m_serverInfos[serverName].ip);
|
|
|
|
|
|
}
|
|
|
|
|
|
return QString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
quint16 BeltTearingPresenter::getServerPort(const QString &serverName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_serverInfos.contains(serverName)) {
|
|
|
|
|
|
return m_serverInfos[serverName].port;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString BeltTearingPresenter::getServerDisplayName(const QString &serverName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_serverInfos.contains(serverName)) {
|
|
|
|
|
|
return QString::fromStdString(m_serverInfos[serverName].name);
|
|
|
|
|
|
}
|
|
|
|
|
|
return QString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::onConnected(const QString &serverName)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 通知主界面设备状态变更
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_statusUpdate) {
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast<int>(DeviceStatus::Online));
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_statusUpdate->OnServerConnected(serverName);
|
|
|
|
|
|
m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Ready);
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("TCP客户端 %1 连接成功").arg(serverName));
|
|
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::onDisconnected(const QString &serverName)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 通知主界面设备状态变更
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_statusUpdate) {
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast<int>(DeviceStatus::Offline));
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_statusUpdate->OnServerDisconnected(serverName);
|
|
|
|
|
|
m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Disconnected);
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("TCP客户端 %1 连接断开").arg(serverName));
|
|
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
void BeltTearingPresenter::onTcpError(const QString &serverName, const QString &error)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 通知主界面设备状态变更和错误信息
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast<int>(DeviceStatus::Error));
|
|
|
|
|
|
m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Error);
|
|
|
|
|
|
m_statusUpdate->OnErrorOccurred(QString("TCP客户端 %1 错误: %2").arg(serverName, error));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
void BeltTearingPresenter::handleTcpDataReceived(const QString &aliasName, const char* pData, const int nLen)
|
2025-09-21 22:20:24 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!pData || nLen <= 0) return;
|
|
|
|
|
|
|
2025-10-12 16:46:46 +08:00
|
|
|
|
// LOG_DEBUG("Received data from %s: size=%d\n", aliasName.toStdString().c_str(), nLen);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-10-12 16:46:46 +08:00
|
|
|
|
// 将接收到的数据转换为QByteArray并通过信号发射到主线程异步处理
|
|
|
|
|
|
QByteArray dataBuffer(pData, nLen);
|
|
|
|
|
|
emit tcpDataReceivedAsync(aliasName, dataBuffer);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
void BeltTearingPresenter::processCompletePacket(const QString &aliasName, const QByteArray &completeData)
|
2025-08-24 23:24:33 +08:00
|
|
|
|
{
|
2025-10-08 21:45:37 +08:00
|
|
|
|
// LOG_DEBUG("Processing complete packet from %s: size=%d\n", aliasName.toStdString().c_str(), completeData.size());
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 解析数据包协议头
|
|
|
|
|
|
if (completeData.size() < 5) {
|
|
|
|
|
|
LOG_ERROR("Packet too small: %d bytes\n", completeData.size());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 23:10:36 +08:00
|
|
|
|
quint8 dataType;
|
|
|
|
|
|
quint32 dataSize;
|
2025-08-24 23:24:33 +08:00
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
QDataStream stream(completeData);
|
2025-08-27 23:10:36 +08:00
|
|
|
|
stream.setByteOrder(QDataStream::BigEndian);
|
|
|
|
|
|
stream >> dataType >> dataSize;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证数据包完整性
|
|
|
|
|
|
if (dataSize + 5 > static_cast<quint32>(completeData.size())) {
|
|
|
|
|
|
LOG_ERROR("Incomplete packet: expected %d+5, got %d bytes\n", dataSize, completeData.size());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QByteArray payloadData = completeData.mid(5, dataSize);
|
|
|
|
|
|
|
2025-09-24 22:36:13 +08:00
|
|
|
|
// LOG_DEBUG("Parsed packet: dataType=%d, dataSize=%d, payload_size=%d\n", dataType, dataSize, payloadData.size());
|
2025-08-27 23:10:36 +08:00
|
|
|
|
|
|
|
|
|
|
BeltTearingResult tearResult;
|
|
|
|
|
|
tearResult.bImageValid = false;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-29 00:56:53 +08:00
|
|
|
|
switch (dataType) {
|
|
|
|
|
|
case static_cast<int>(ByteDataType::Text):
|
|
|
|
|
|
{
|
|
|
|
|
|
// 处理文本数据
|
|
|
|
|
|
QString textData = QString::fromUtf8(payloadData);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
2025-09-29 00:56:53 +08:00
|
|
|
|
// 解析JSON数据
|
|
|
|
|
|
QJsonParseError parseError;
|
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(textData.toUtf8(), &parseError);
|
|
|
|
|
|
if (parseError.error == QJsonParseError::NoError && !doc.isNull() && doc.isArray()) {
|
2025-08-31 21:08:28 +08:00
|
|
|
|
// 处理撕裂数据数组
|
|
|
|
|
|
QJsonArray jsonArray = doc.array();
|
|
|
|
|
|
if (!jsonArray.isEmpty()) {
|
2025-09-29 00:56:53 +08:00
|
|
|
|
// 预分配空间以提高性能
|
|
|
|
|
|
tearResult.result.reserve(tearResult.result.size() + jsonArray.size());
|
|
|
|
|
|
|
2025-08-31 21:08:28 +08:00
|
|
|
|
for (const QJsonValue &value : jsonArray) {
|
|
|
|
|
|
if (value.isObject()) {
|
|
|
|
|
|
tearResult.result.push_back(TearingData::fromJsonObject(value.toObject()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
tearResult.bResultVaild = !tearResult.result.empty();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-29 00:56:53 +08:00
|
|
|
|
break;
|
2025-08-31 21:08:28 +08:00
|
|
|
|
}
|
2025-09-29 00:56:53 +08:00
|
|
|
|
case static_cast<int>(ByteDataType::Image):
|
|
|
|
|
|
{
|
|
|
|
|
|
// 处理图像数据
|
|
|
|
|
|
if (tearResult.image.loadFromData(payloadData)) {
|
|
|
|
|
|
tearResult.bImageValid = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_ERROR("Failed to load image from data\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case static_cast<int>(ByteDataType::ReadConfig):
|
|
|
|
|
|
case static_cast<int>(ByteDataType::WriteConfig):
|
|
|
|
|
|
{
|
|
|
|
|
|
// 处理配置数据
|
|
|
|
|
|
QString textData = QString::fromUtf8(payloadData);
|
|
|
|
|
|
|
|
|
|
|
|
// 解析JSON数据
|
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(textData.toUtf8());
|
|
|
|
|
|
if (!doc.isNull() && doc.isObject()) {
|
|
|
|
|
|
QJsonObject jsonObj = doc.object();
|
|
|
|
|
|
QString command = jsonObj["command"].toString();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是配置响应
|
|
|
|
|
|
if (command == "configResponse") {
|
|
|
|
|
|
// 处理配置响应数据
|
2025-10-08 21:45:37 +08:00
|
|
|
|
emit serverDataReceived(aliasName, jsonObj);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 检查是否是重新检测响应
|
|
|
|
|
|
else if (command == "resetDetectResponse") {
|
|
|
|
|
|
// 处理重新检测响应
|
|
|
|
|
|
QString status = jsonObj["status"].toString();
|
|
|
|
|
|
QString message = jsonObj["message"].toString();
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Received reset detect response from server %s: status=%s, message=%s\n",
|
|
|
|
|
|
aliasName.toStdString().c_str(),
|
|
|
|
|
|
status.toStdString().c_str(),
|
|
|
|
|
|
message.toStdString().c_str());
|
|
|
|
|
|
|
|
|
|
|
|
// 通过状态更新接口通知上层
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("来自服务器 %1 的重新检测响应: %2").arg(aliasName).arg(message));
|
|
|
|
|
|
// 清空撕裂数据表,避免残留数据
|
|
|
|
|
|
m_statusUpdate->OnClearTearingData();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 00:56:53 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR("Unknown data type %d\n", dataType);
|
|
|
|
|
|
return;
|
2025-08-27 23:10:36 +08:00
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 通知上层处理结果
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_statusUpdate) {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
tearResult.serverName = QString::fromStdString(m_serverInfos[aliasName].name);
|
|
|
|
|
|
tearResult.aliasName = aliasName;
|
|
|
|
|
|
tearResult.timestamp = QDateTime::currentDateTime();
|
2025-09-29 00:56:53 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
m_statusUpdate->OnTearingResult(tearResult);
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Exception occurred while notifying tearing result: %s\n", e.what());
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
|
LOG_ERROR("Unknown exception occurred while notifying tearing result\n");
|
|
|
|
|
|
}
|
2025-09-18 23:49:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
void BeltTearingPresenter::handleTcpLinkStatus(const QString &aliasName, bool connected)
|
2025-09-18 23:49:32 +08:00
|
|
|
|
{
|
2025-10-08 21:45:37 +08:00
|
|
|
|
m_connectionStatus[aliasName] = connected;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (connected) {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
onConnected(aliasName);
|
2025-09-18 23:49:32 +08:00
|
|
|
|
} else {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
onDisconnected(aliasName);
|
2025-09-18 23:49:32 +08:00
|
|
|
|
}
|
2025-08-24 23:24:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
bool BeltTearingPresenter::sendParametersToServer(const ByteDataType dataType, const QString& aliasName, const QByteArray& paramData)
|
2025-09-29 00:56:53 +08:00
|
|
|
|
{
|
2025-10-08 21:45:37 +08:00
|
|
|
|
if (!m_serverInfos.contains(aliasName)) {
|
|
|
|
|
|
LOG_ERROR("Server not found: %s\n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
if (!m_connectionStatus.value(aliasName, false)) {
|
|
|
|
|
|
LOG_ERROR("Server not connected: %s\n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
LOG_DEBUG("req : %s \n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
IVrTCPClient* tcpClient = m_tcpClients.value(aliasName, nullptr);
|
2025-09-29 00:56:53 +08:00
|
|
|
|
if (!tcpClient) {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
LOG_ERROR("TCP client not found for server: %s\n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建数据包协议头
|
|
|
|
|
|
QByteArray packet;
|
|
|
|
|
|
QDataStream stream(&packet, QIODevice::WriteOnly);
|
|
|
|
|
|
stream.setByteOrder(QDataStream::BigEndian);
|
|
|
|
|
|
|
|
|
|
|
|
// 数据类型: Text (1)
|
|
|
|
|
|
quint8 nType = static_cast<quint8>(dataType);
|
|
|
|
|
|
quint32 dataSize = static_cast<quint32>(paramData.size());
|
|
|
|
|
|
|
|
|
|
|
|
stream << nType << dataSize;
|
|
|
|
|
|
if(paramData.size() > 0){
|
|
|
|
|
|
packet.append(paramData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加数据包结束标记
|
|
|
|
|
|
packet.append("___END___\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 发送数据
|
|
|
|
|
|
bool success = tcpClient->SendData(packet.constData(), packet.size());
|
|
|
|
|
|
if (success) {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
LOG_INFO("Algorithm parameters sent to server %s successfully\n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
} else {
|
2025-10-08 21:45:37 +08:00
|
|
|
|
LOG_ERROR("Failed to send algorithm parameters to server %s\n", aliasName.toStdString().c_str());
|
2025-09-29 00:56:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BeltTearingPresenter::handleServerInfoResponse(const QString& serverName, const QJsonObject& responseObj)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("Received server info response from %s\n", serverName.toStdString().c_str());
|
|
|
|
|
|
|
|
|
|
|
|
if (responseObj.contains("serverInfo")) {
|
|
|
|
|
|
QJsonObject serverInfo = responseObj["serverInfo"].toObject();
|
|
|
|
|
|
QString serverVersion = serverInfo["version"].toString();
|
|
|
|
|
|
QString serverStatus = serverInfo["status"].toString();
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Server %s info: version=%s, status=%s\n",
|
|
|
|
|
|
serverName.toStdString().c_str(),
|
|
|
|
|
|
serverVersion.toStdString().c_str(),
|
|
|
|
|
|
serverStatus.toStdString().c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (responseObj.contains("algorithmParams")) {
|
|
|
|
|
|
QJsonObject algorithmParams = responseObj["algorithmParams"].toObject();
|
|
|
|
|
|
LOG_INFO("Server %s algorithm params received\n", serverName.toStdString().c_str());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 通过状态更新接口通知上层
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate("从服务器 " + serverName + " 获取信息成功");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发出信号通知服务器数据已接收
|
|
|
|
|
|
emit serverDataReceived(serverName, responseObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
void BeltTearingPresenter::ResetDetect(const QString& targetServerAlias)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("Resetting detection for server: %s\n", targetServerAlias.toStdString().c_str());
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
// 检查目标服务端是否存在且已连接
|
|
|
|
|
|
if (!m_serverInfos.contains(targetServerAlias)) {
|
|
|
|
|
|
LOG_ERROR("Target server not found: %s\n", targetServerAlias.toStdString().c_str());
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("未找到目标服务器: %1").arg(targetServerAlias));
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
if (!isConnected(targetServerAlias)) {
|
|
|
|
|
|
LOG_ERROR("Target server not connected: %s\n", targetServerAlias.toStdString().c_str());
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("目标服务器未连接: %1").arg(targetServerAlias));
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
// 通知UI清空数据
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("正在重新检测服务器: %1...").arg(targetServerAlias));
|
|
|
|
|
|
// 清空数据的操作应该由UI层处理
|
|
|
|
|
|
}
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
// 向指定的服务端发送重新检测指令
|
|
|
|
|
|
QJsonObject resetCommand;
|
|
|
|
|
|
resetCommand["command"] = "resetDetect";
|
|
|
|
|
|
resetCommand["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
QJsonDocument doc(resetCommand);
|
|
|
|
|
|
QByteArray commandData = doc.toJson(QJsonDocument::Compact);
|
2025-10-12 16:46:46 +08:00
|
|
|
|
|
2025-10-08 21:45:37 +08:00
|
|
|
|
// 只向指定的服务器发送重新检测指令
|
|
|
|
|
|
bool success = sendParametersToServer(ByteDataType::Text, targetServerAlias, commandData);
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
LOG_INFO("Reset detect command sent to server: %s\n", targetServerAlias.toStdString().c_str());
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("重新检测指令已发送至服务器: %1").arg(targetServerAlias));
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_ERROR("Failed to send reset detect command to server: %s\n", targetServerAlias.toStdString().c_str());
|
|
|
|
|
|
if (m_statusUpdate) {
|
|
|
|
|
|
m_statusUpdate->OnStatusUpdate(QString("发送重新检测指令失败: %1").arg(targetServerAlias));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 16:46:46 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|