#include "BeltTearingPresenter.h" #include "IVrTCPClient.h" #include "PathManager.h" #include "VrLog.h" #include #include #include #include #include #include #include #include #include #include #include #include "widgets/DeviceStatusWidget.h" #include "VrDateUtils.h" #include BeltTearingPresenter::BeltTearingPresenter(QWidget* parent) : QWidget(parent) , m_config(nullptr) { // 创建配置实例 IVrBeltTearingConfig::CreateInstance(&m_config); // 注册配置改变通知回调 if (m_config) { m_config->SetConfigChangeNotify(this); } // 连接异步数据处理信号和槽 connect(this, &BeltTearingPresenter::tcpDataReceivedAsync, this, &BeltTearingPresenter::onTcpDataReceivedAsync, Qt::QueuedConnection); } BeltTearingPresenter::~BeltTearingPresenter() { disconnectFromServer(); // 删除所有TCP客户端 for (auto it = m_tcpClients.begin(); it != m_tcpClients.end(); ++it) { IVrTCPClient::DestroyInstance(it.value()); } m_tcpClients.clear(); if (m_config) { delete m_config; m_config = nullptr; } } void BeltTearingPresenter::Init() { QString configPath = PathManager::GetConfigFilePath(); 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"); } } 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()) { LOG_WARNING("Failed to load config from: %s \n", configPath.toStdString().c_str()); return false; } if (configResult.servers.empty()) { LOG_WARNING("No servers configured"); return false; } // 清空现有配置 m_serverInfos.clear(); // 获取所有服务器配置 const auto &servers = configResult.servers; // 存储所有启用的服务器信息 QList devices; QStringList deviceAliases; for (const auto &server : servers) { QString serverAliaseName = QString("%1_%2").arg(QString::fromStdString(server.name)).arg(CVrDateUtils::GetTimestamp()); m_serverInfos[serverAliaseName] = server; // 添加到设备列表 devices.append(DeviceInfo(QString::fromStdString(server.name), serverAliaseName, QString::fromStdString(server.ip), DeviceStatus::Offline, true)); deviceAliases.append(serverAliaseName); LOG_DEBUG("Server configured: %s %s:%d \n", serverAliaseName.toStdString().c_str(), server.ip.c_str(), server.port); } LOG_DEBUG("Init config finish. Found %d enabled servers \n", m_serverInfos.size()); if(m_statusUpdate){ m_statusUpdate->OnNeedShowImageCount(deviceAliases); } // 连接到所有服务器 for(size_t i = 0 ; i < servers.size() ; i++){ connectToServer(servers[i], deviceAliases[i]); } LOG_DEBUG("Config loaded successfully. Found %d enabled servers\n", m_serverInfos.size()); return true; } bool BeltTearingPresenter::connectToServer(const ServerInfo &serverInfo, const QString &aliasName) { QString targetServerName = aliasName; // 创建TCP客户端(如果不存在) if (!m_tcpClients.contains(targetServerName)) { IVrTCPClient *client = IVrTCPClient::CreateInstance(); if (!client) { LOG_ERROR("Failed to create TCP client for %s", targetServerName.toStdString().c_str()); return false; } m_tcpClients[targetServerName] = client; m_connectionStatus[targetServerName] = false; } IVrTCPClient *client = m_tcpClients[targetServerName]; // 连接服务器 - 启用TCP客户端自动重连 int linkResult = client->LinkDevice(serverInfo.ip, serverInfo.port, true, // 启用自动重连 [this, targetServerName](IVrTCPClient* pClient, bool connected, void* pParam) { LOG_DEBUG("connectToServer %s link status : %d \n", targetServerName.toStdString().c_str(), connected); 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) { LOG_ERROR("Failed to initiate connection to %s", targetServerName.toStdString().c_str()); return false; } return true; } void BeltTearingPresenter::disconnectFromServer(const QString &serverName) { if (serverName.isEmpty()) { // 断开所有连接 for (auto it = m_tcpClients.begin(); it != m_tcpClients.end(); ++it) { it.value()->CloseDevice(); m_connectionStatus[it.key()] = false; } // 清空所有数据缓存 m_dataBuffers.clear(); } else { // 断开指定服务器连接 if (m_tcpClients.contains(serverName)) { m_tcpClients[serverName]->CloseDevice(); m_connectionStatus[serverName] = false; // 清空该服务器的数据缓存 m_dataBuffers.remove(serverName); } } } bool BeltTearingPresenter::isConnected(const QString &serverName) const { if(serverName.isEmpty()) return false; QString targetServerName = serverName; if (m_connectionStatus.contains(targetServerName)) { return m_connectionStatus[targetServerName]; } return false; } bool BeltTearingPresenter::sendData(const QByteArray &data, const QString &serverName) { if(serverName.isEmpty()) return false; QString targetServerName = serverName; if (!isConnected(targetServerName)) { LOG_ERROR("Not connected to server: %s", targetServerName.toStdString().c_str()); return false; } if (data.isEmpty()) { LOG_ERROR("Empty data to send"); return false; } if (!m_tcpClients.contains(targetServerName)) { LOG_ERROR("Server client not found: %s", targetServerName.toStdString().c_str()); return false; } bool success = m_tcpClients[targetServerName]->SendData(data.constData(), data.size()); if (!success) { LOG_ERROR("Failed to send data to %s", targetServerName.toStdString().c_str()); } 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) { // 通知主界面设备状态变更 if (m_statusUpdate) { m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast(DeviceStatus::Online)); m_statusUpdate->OnServerConnected(serverName); m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Ready); m_statusUpdate->OnStatusUpdate(QString("TCP客户端 %1 连接成功").arg(serverName)); } } void BeltTearingPresenter::onDisconnected(const QString &serverName) { // 通知主界面设备状态变更 if (m_statusUpdate) { m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast(DeviceStatus::Offline)); m_statusUpdate->OnServerDisconnected(serverName); m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Disconnected); m_statusUpdate->OnStatusUpdate(QString("TCP客户端 %1 连接断开").arg(serverName)); } } void BeltTearingPresenter::onTcpError(const QString &serverName, const QString &error) { // 通知主界面设备状态变更和错误信息 if (m_statusUpdate) { m_statusUpdate->OnDeviceStatusChanged(serverName, static_cast(DeviceStatus::Error)); m_statusUpdate->OnWorkStatusChanged(BeltTearingWorkStatus::Error); m_statusUpdate->OnErrorOccurred(QString("TCP客户端 %1 错误: %2").arg(serverName, error)); } } void BeltTearingPresenter::handleTcpDataReceived(const QString &aliasName, const char* pData, const int nLen) { if (!pData || nLen <= 0) return; // LOG_DEBUG("Received data from %s: size=%d\n", aliasName.toStdString().c_str(), nLen); // 将接收到的数据转换为QByteArray并通过信号发射到主线程异步处理 QByteArray dataBuffer(pData, nLen); emit tcpDataReceivedAsync(aliasName, dataBuffer); } void BeltTearingPresenter::processCompletePacket(const QString &aliasName, const QByteArray &completeData) { // LOG_DEBUG("Processing complete packet from %s: size=%d\n", aliasName.toStdString().c_str(), completeData.size()); // 解析数据包协议头 if (completeData.size() < 5) { LOG_ERROR("Packet too small: %d bytes\n", completeData.size()); return; } quint8 dataType; quint32 dataSize; QDataStream stream(completeData); stream.setByteOrder(QDataStream::BigEndian); stream >> dataType >> dataSize; // 验证数据包完整性 if (dataSize + 5 > static_cast(completeData.size())) { LOG_ERROR("Incomplete packet: expected %d+5, got %d bytes\n", dataSize, completeData.size()); return; } QByteArray payloadData = completeData.mid(5, dataSize); // LOG_DEBUG("Parsed packet: dataType=%d, dataSize=%d, payload_size=%d\n", dataType, dataSize, payloadData.size()); BeltTearingResult tearResult; tearResult.bImageValid = false; switch (dataType) { case static_cast(ByteDataType::Text): { // 处理文本数据 QString textData = QString::fromUtf8(payloadData); // 解析JSON数据 QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(textData.toUtf8(), &parseError); if (parseError.error == QJsonParseError::NoError && !doc.isNull() && doc.isArray()) { // 处理撕裂数据数组 QJsonArray jsonArray = doc.array(); if (!jsonArray.isEmpty()) { // 预分配空间以提高性能 tearResult.result.reserve(tearResult.result.size() + jsonArray.size()); for (const QJsonValue &value : jsonArray) { if (value.isObject()) { QJsonObject jsonObj = value.toObject(); QString dataType = jsonObj["type"].toString(); // 检查是否为温度数据 if (dataType == "temperature") { // 处理温度数据 float temperature = jsonObj["value"].toString().toFloat(); if (m_statusUpdate) { m_statusUpdate->OnTemperatureUpdate(aliasName, temperature); } } else { // 处理撕裂数据 tearResult.result.push_back(TearingData::fromJsonObject(jsonObj)); } } } tearResult.bResultVaild = !tearResult.result.empty(); } } break; } case static_cast(ByteDataType::Image): { // 处理图像数据 if (tearResult.image.loadFromData(payloadData)) { tearResult.bImageValid = true; } else { LOG_ERROR("Failed to load image from data\n"); } break; } case static_cast(ByteDataType::ReadConfig): case static_cast(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") { // 处理配置响应数据 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(); } return; } } break; } default: { LOG_ERROR("Unknown data type %d\n", dataType); return; } } // 通知上层处理结果 if (m_statusUpdate) { tearResult.serverName = QString::fromStdString(m_serverInfos[aliasName].name); tearResult.aliasName = aliasName; tearResult.timestamp = QDateTime::currentDateTime(); 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"); } } } void BeltTearingPresenter::handleTcpLinkStatus(const QString &aliasName, bool connected) { m_connectionStatus[aliasName] = connected; if (connected) { onConnected(aliasName); } else { onDisconnected(aliasName); } } bool BeltTearingPresenter::sendParametersToServer(const ByteDataType dataType, const QString& aliasName, const QByteArray& paramData) { if (!m_serverInfos.contains(aliasName)) { LOG_ERROR("Server not found: %s\n", aliasName.toStdString().c_str()); return false; } if (!m_connectionStatus.value(aliasName, false)) { LOG_ERROR("Server not connected: %s\n", aliasName.toStdString().c_str()); return false; } LOG_DEBUG("req : %s \n", aliasName.toStdString().c_str()); IVrTCPClient* tcpClient = m_tcpClients.value(aliasName, nullptr); if (!tcpClient) { LOG_ERROR("TCP client not found for server: %s\n", aliasName.toStdString().c_str()); return false; } // 构建数据包协议头 QByteArray packet; QDataStream stream(&packet, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::BigEndian); // 数据类型: Text (1) quint8 nType = static_cast(dataType); quint32 dataSize = static_cast(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) { LOG_INFO("Algorithm parameters sent to server %s successfully\n", aliasName.toStdString().c_str()); } else { LOG_ERROR("Failed to send algorithm parameters to server %s\n", aliasName.toStdString().c_str()); } 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); } void BeltTearingPresenter::ResetDetect(const QString& targetServerAlias) { LOG_INFO("Resetting detection for server: %s\n", targetServerAlias.toStdString().c_str()); // 检查目标服务端是否存在且已连接 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; } 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; } // 通知UI清空数据 if (m_statusUpdate) { m_statusUpdate->OnStatusUpdate(QString("正在重新检测服务器: %1...").arg(targetServerAlias)); // 清空数据的操作应该由UI层处理 } // 向指定的服务端发送重新检测指令 QJsonObject resetCommand; resetCommand["command"] = "resetDetect"; resetCommand["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); QJsonDocument doc(resetCommand); QByteArray commandData = doc.toJson(QJsonDocument::Compact); // 只向指定的服务器发送重新检测指令 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)); } } } 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(); } } void BeltTearingPresenter::OnConfigChanged(const BeltTearingConfigResult& configResult) { LOG_INFO("Configuration changed notification received in BeltTearingApp\n"); // 断开所有现有连接 disconnectFromServer(); // 清空现有配置 m_serverInfos.clear(); // 重新初始化配置 QString configPath = PathManager::GetConfigFilePath(); initializeConfig(configPath); // 通知UI更新状态 if (m_statusUpdate) { m_statusUpdate->OnStatusUpdate("配置已更新,正在重新连接服务器..."); } // 发出配置更新信号 emit configUpdated(); LOG_INFO("Configuration updated and servers reconnected\n"); }