GrabBag/App/BeltTearing/BeltTearingApp/Presenter/Src/BeltTearingPresenter.cpp

600 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "BeltTearingPresenter.h"
#include "IVrTCPClient.h"
#include "PathManager.h"
#include "VrLog.h"
#include <QDebug>
#include <QTimer>
#include <QMetaObject>
#include <QImage>
#include <QBuffer>
#include <QVariant>
#include <QSettings>
#include <QDir>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include "widgets/DeviceStatusWidget.h"
#include "VrDateUtils.h"
#include <thread>
BeltTearingPresenter::BeltTearingPresenter(QWidget* parent)
: QWidget(parent)
, m_config(nullptr)
{
// 创建配置实例
IVrBeltTearingConfig::CreateInstance(&m_config);
// 连接异步数据处理信号和槽
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<DeviceInfo> 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<int>(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<int>(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<int>(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<quint32>(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<int>(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()) {
tearResult.result.push_back(TearingData::fromJsonObject(value.toObject()));
}
}
tearResult.bResultVaild = !tearResult.result.empty();
}
}
break;
}
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") {
// 处理配置响应数据
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<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) {
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();
}
}