diff --git a/App/BeltTearing/BeltTearingApp/IStatusUpdate.h b/App/BeltTearing/BeltTearingApp/IStatusUpdate.h index 470a9d9..7839c10 100644 --- a/App/BeltTearing/BeltTearingApp/IStatusUpdate.h +++ b/App/BeltTearing/BeltTearingApp/IStatusUpdate.h @@ -62,16 +62,16 @@ struct TearingData static TearingData fromJsonObject(const QJsonObject &json) { TearingData data; - data.level = json["level"].toString(); - data.id = json["id"].toString(); + data.level = json["level"].toString(); + data.id = json["id"].toString(); data.tearStatus = json["tearStatus"].toString(); - data.tearType = json["tearType"].toString(); - data.statLineIdx = json["statLineIdx"].toString(); - data.endLineIdx = json["endLineIdx"].toString(); - data.tearDepth = json["tearDepth"].toString(); - data.tearWidth = json["tearWidth"].toString(); - data.tearLength = json["tearLength"].toString(); - data.older = json["older"].toString(); + data.tearType = json["tearType"].toString(); + data.statLineIdx = json["statLineIdx"].toString(); + data.endLineIdx = json["endLineIdx"].toString(); + data.tearDepth = json["tearDepth"].toString(); + data.tearWidth = json["tearWidth"].toString(); + data.tearLength = json["tearLength"].toString(); + data.older = json["older"].toString(); return data; } diff --git a/App/BeltTearing/BeltTearingApp/Presenter/Src/BeltTearingPresenter.cpp b/App/BeltTearing/BeltTearingApp/Presenter/Src/BeltTearingPresenter.cpp index 4af7514..cb5bdb6 100644 --- a/App/BeltTearing/BeltTearingApp/Presenter/Src/BeltTearingPresenter.cpp +++ b/App/BeltTearing/BeltTearingApp/Presenter/Src/BeltTearingPresenter.cpp @@ -290,9 +290,6 @@ void BeltTearingPresenter::handleTcpDataReceived(const QString &serverName, cons // 将新数据添加到缓存 m_dataBuffers[serverName].append(pData, nLen); - LOG_DEBUG("Buffered data for %s: buffer_size=%d, new_data=%d\n", - serverName.toStdString().c_str(), m_dataBuffers[serverName].size(), nLen); - // 检查是否有完整的数据包 QByteArray &buffer = m_dataBuffers[serverName]; const QByteArray endMarker = "___END___\r\n"; @@ -301,8 +298,7 @@ void BeltTearingPresenter::handleTcpDataReceived(const QString &serverName, cons while (endPos != -1) { // 找到完整数据包 QByteArray completePacket = buffer.left(endPos); - LOG_DEBUG("Found complete packet for %s: size=%d\n", - serverName.toStdString().c_str(), completePacket.size()); + // LOG_DEBUG("Found complete packet for %s: size=%d\n", serverName.toStdString().c_str(), completePacket.size()); // 处理完整数据包 processCompletePacket(serverName, completePacket); @@ -323,8 +319,7 @@ void BeltTearingPresenter::handleTcpDataReceived(const QString &serverName, cons void BeltTearingPresenter::processCompletePacket(const QString &serverName, const QByteArray &completeData) { - LOG_DEBUG("Processing complete packet from %s: size=%d\n", - serverName.toStdString().c_str(), completeData.size()); + // LOG_DEBUG("Processing complete packet from %s: size=%d\n", serverName.toStdString().c_str(), completeData.size()); // 解析数据包协议头 if (completeData.size() < 5) { @@ -347,8 +342,7 @@ void BeltTearingPresenter::processCompletePacket(const QString &serverName, cons QByteArray payloadData = completeData.mid(5, dataSize); - LOG_DEBUG("Parsed packet: dataType=%d, dataSize=%d, payload_size=%d\n", - dataType, dataSize, payloadData.size()); + // LOG_DEBUG("Parsed packet: dataType=%d, dataSize=%d, payload_size=%d\n", dataType, dataSize, payloadData.size()); BeltTearingResult tearResult; tearResult.bImageValid = false; @@ -357,7 +351,6 @@ void BeltTearingPresenter::processCompletePacket(const QString &serverName, cons { // 处理文本数据 QString textData = QString::fromUtf8(payloadData); - LOG_DEBUG("Received text data: %s\n", textData.left(100).toStdString().c_str()); // 解析JSON数据 QJsonDocument doc = QJsonDocument::fromJson(textData.toUtf8()); @@ -380,16 +373,12 @@ void BeltTearingPresenter::processCompletePacket(const QString &serverName, cons } } } - LOG_DEBUG("Processed text data from server %s\n", serverName.toStdString().c_str()); } else if (dataType == static_cast(ByteDataType::Image)) { // 处理图像数据 - LOG_DEBUG("Attempting to load image from %d bytes\n", payloadData.size()); if (tearResult.image.loadFromData(payloadData)) { tearResult.bImageValid = true; - LOG_DEBUG("Successfully loaded image: %dx%d\n", - tearResult.image.width(), tearResult.image.height()); } else { LOG_ERROR("Failed to load image from data\n"); } diff --git a/App/BeltTearing/BeltTearingApp/main.cpp b/App/BeltTearing/BeltTearingApp/main.cpp index aff48df..55ac408 100644 --- a/App/BeltTearing/BeltTearingApp/main.cpp +++ b/App/BeltTearing/BeltTearingApp/main.cpp @@ -1,10 +1,21 @@ #include "mainwindow.h" #include +#include +#include +#include +#include +#include int main(int argc, char *argv[]) { QApplication a(argc, argv); + + // Register meta types for signal-slot connections + qRegisterMetaType>("QVector"); + qRegisterMetaType>("QList"); + qRegisterMetaType("QAbstractItemModel::LayoutChangeHint"); + MainWindow w; w.show(); return a.exec(); diff --git a/App/BeltTearing/BeltTearingApp/mainwindow.cpp b/App/BeltTearing/BeltTearingApp/mainwindow.cpp index c8e29a2..dd08734 100644 --- a/App/BeltTearing/BeltTearingApp/mainwindow.cpp +++ b/App/BeltTearing/BeltTearingApp/mainwindow.cpp @@ -162,7 +162,6 @@ void MainWindow::on_btn_test_clicked() } - void MainWindow::on_btn_camera_clicked() { if(nullptr == ui_dialognetconfig){ diff --git a/App/BeltTearing/BeltTearingApp/mainwindow.ui b/App/BeltTearing/BeltTearingApp/mainwindow.ui index e68c0b6..670a964 100644 --- a/App/BeltTearing/BeltTearingApp/mainwindow.ui +++ b/App/BeltTearing/BeltTearingApp/mainwindow.ui @@ -45,7 +45,7 @@ background-color: rgba(255, 255, 255, 0); - 840 + 960 21 220 80 @@ -68,7 +68,7 @@ border: none; - 555 + 440 21 220 80 @@ -165,7 +165,7 @@ background-color: rgba(255, 255, 255, 0); - 1220 + 1260 21 80 80 @@ -187,7 +187,7 @@ background-color: rgba(255, 255, 255, 0); - 1360 + 1400 21 80 80 @@ -206,6 +206,29 @@ background-color: rgba(255, 255, 255, 0); + + + + 700 + 21 + 220 + 80 + + + + + 18 + + + + image: url(:/resource/config_algo.png); +background-color: rgb(38, 40, 47); +border: none; + + + + + diff --git a/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.cpp b/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.cpp index efa7bf9..8d518ff 100644 --- a/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.cpp +++ b/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include TearingDataTableWidget::TearingDataTableWidget(QWidget *parent) : QWidget(parent) @@ -32,7 +35,8 @@ void TearingDataTableWidget::setupTable() { // 设置表头标签 QStringList headers; - headers << "ID" << "等级" << "状态" << "类型" /*<< "起始行" << "结束行" */<< "深度" << "宽度" << "长度" << "老化"; + // headers << "ID" << "等级" << "状态" << "类型" /*<< "起始行" << "结束行" */<< "深度" << "宽度" << "长度" << "老化"; + headers << "ID" << "状态" << "类型" << "深度(mm)" << "宽度(mm)" << "长度(mm)"; // 设置表格属性 m_tableWidget->setColumnCount(headers.size()); @@ -50,14 +54,12 @@ void TearingDataTableWidget::setupTable() m_tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // 设置列宽 - m_tableWidget->setColumnWidth(0, 52); // ID列 - m_tableWidget->setColumnWidth(1, 45); // 等级列 - m_tableWidget->setColumnWidth(2, 80); // 状态列 - m_tableWidget->setColumnWidth(3, 45); // 类型列 - m_tableWidget->setColumnWidth(4, 80); // 深度列 - m_tableWidget->setColumnWidth(5, 80); // 宽度列 - m_tableWidget->setColumnWidth(6, 80); // 长度列 - m_tableWidget->setColumnWidth(7, 80); // 老化列 + m_tableWidget->setColumnWidth(0, 62); // ID列 + m_tableWidget->setColumnWidth(1, 100); // 状态列 + m_tableWidget->setColumnWidth(2, 80); // 类型列 + m_tableWidget->setColumnWidth(3, 100); // 深度列 + m_tableWidget->setColumnWidth(4, 100); // 宽度列 + m_tableWidget->setColumnWidth(5, 100); // 长度列 #endif // 设置样式表 m_tableWidget->setStyleSheet( @@ -75,26 +77,47 @@ void TearingDataTableWidget::setupTable() " padding: 4px;" "}" ); + + // 启用表格排序功能 + m_tableWidget->setSortingEnabled(true); + + // 设置默认按ID列升序排序 + m_tableWidget->sortItems(0, Qt::AscendingOrder); } void TearingDataTableWidget::addData(const TearingData &data) { - // 将数据显示到QTableWidget上 - int row = m_tableWidget->rowCount(); - m_tableWidget->insertRow(row); + // 查找是否已存在相同ID的行 + int existingRow = findExistingRowById(data.id); + + int row; + if (existingRow >= 0) { + // 如果存在相同ID,更新该行 + row = existingRow; + } else { + // 如果不存在,插入新行 + row = m_tableWidget->rowCount(); + m_tableWidget->insertRow(row); + } + + // 使用结构体数据填充表格,状态和类型显示中文,并设置居中对齐 + NumericTableWidgetItem *idItem = new NumericTableWidgetItem(data.id); + idItem->setTextAlignment(Qt::AlignCenter); // ID列文字居中显示 + m_tableWidget->setItem(row, 0, idItem); - // 使用结构体数据填充表格 - m_tableWidget->setItem(row, 0, new QTableWidgetItem(data.id)); - m_tableWidget->setItem(row, 1, new QTableWidgetItem(data.level)); - m_tableWidget->setItem(row, 2, new QTableWidgetItem(data.tearStatus)); - m_tableWidget->setItem(row, 3, new QTableWidgetItem(data.tearType)); - // m_tableWidget->setItem(row, 4, new QTableWidgetItem(data.statLineIdx)); - // m_tableWidget->setItem(row, 5, new QTableWidgetItem(data.endLineIdx)); - m_tableWidget->setItem(row, 4, new QTableWidgetItem(data.tearDepth)); - m_tableWidget->setItem(row, 5, new QTableWidgetItem(data.tearWidth)); - m_tableWidget->setItem(row, 6, new QTableWidgetItem(data.tearLength)); - m_tableWidget->setItem(row, 7, new QTableWidgetItem(data.older)); + QTableWidgetItem *statusItem = new QTableWidgetItem(getTearStatusText(data.tearStatus)); + statusItem->setTextAlignment(Qt::AlignCenter); // 状态列文字居中显示 + m_tableWidget->setItem(row, 1, statusItem); + QTableWidgetItem *typeItem = new QTableWidgetItem(getTearTypeText(data.tearType)); + typeItem->setTextAlignment(Qt::AlignCenter); // 类型列文字居中显示 + m_tableWidget->setItem(row, 2, typeItem); + + m_tableWidget->setItem(row, 3, new QTableWidgetItem(data.tearDepth)); + m_tableWidget->setItem(row, 4, new QTableWidgetItem(data.tearWidth)); + // m_tableWidget->setItem(row, 5, new QTableWidgetItem(data.tearLength)); + m_tableWidget->setItem(row, 5, new QTableWidgetItem(data.statLineIdx)); + // 设置item的文本颜色 for (int i = 0; i < m_tableWidget->horizontalHeader()->count(); i++) { QTableWidgetItem *item = m_tableWidget->item(row, i); @@ -102,6 +125,9 @@ void TearingDataTableWidget::addData(const TearingData &data) item->setForeground(QBrush(Qt::white)); } } + + // 保持表格按ID列排序 + m_tableWidget->sortItems(0, Qt::AscendingOrder); } void TearingDataTableWidget::addData(const std::vector &dataList) @@ -122,3 +148,85 @@ void TearingDataTableWidget::clearData() { m_tableWidget->setRowCount(0); } + +QString TearingDataTableWidget::getTearStatusText(const QString &status) +{ + // 将ESG_tearStatus枚举值转换为中文显示 + if (status == "0" || status.contains("Uknown")) { + return "未知"; + } else if (status == "1" || status.contains("New")) { + return "新发现"; + } else if (status == "2" || status.contains("Growing")) { + return "正在增长"; + } else if (status == "3" || status.contains("Ended")) { + return "撕裂结束"; + } else if (status == "4" || status.contains("Invalid")) { + return "无效撕裂"; + } + return status; // 如果无法识别,返回原值 +} + +QString TearingDataTableWidget::getTearTypeText(const QString &type) +{ + // 将ESG_tearType枚举值转换为中文显示 + if (type == "0" || type.contains("Uknown")) { + return "未知"; + } else if (type == "1" || type.contains("MachineDir")) { + return "纵撕"; + } else if (type == "2" || type.contains("CrossWise")) { + return "横撕"; + } + return type; // 如果无法识别,返回原值 +} + +int TearingDataTableWidget::findExistingRowById(const QString &id) +{ + // 在表格中查找具有相同ID的行 + for (int row = 0; row < m_tableWidget->rowCount(); ++row) { + QTableWidgetItem *item = m_tableWidget->item(row, 0); // ID在第0列 + if (item && item->text() == id) { + return row; + } + } + return -1; // 未找到 +} + +// NumericTableWidgetItem类的实现 +NumericTableWidgetItem::NumericTableWidgetItem(const QString &text) + : QTableWidgetItem(text) +{ +} + +bool NumericTableWidgetItem::operator<(const QTableWidgetItem &other) const +{ + // 尝试将文本转换为数字进行比较 + double thisValue = toDouble(); + double otherValue = static_cast(&other)->toDouble(); + + // 如果两个值都有效,则按数字比较 + if (thisValue != -1 && otherValue != -1) { + return thisValue < otherValue; + } + + // 否则按默认的字符串比较 + return QTableWidgetItem::operator<(other); +} + +double NumericTableWidgetItem::toDouble() const +{ + bool ok; + // 尝试直接转换为double + double value = text().toDouble(&ok); + if (ok) { + return value; + } + + // 如果直接转换失败,尝试提取数字部分 + QRegExp rx("(\\d+)"); + if (rx.indexIn(text()) != -1) { + return rx.cap(1).toDouble(&ok); + } + + // 如果所有方法都失败,返回-1表示无效值 + return -1; +} diff --git a/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.h b/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.h index b511abb..ff73deb 100644 --- a/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.h +++ b/App/BeltTearing/BeltTearingApp/widgets/TearingDataTableWidget.h @@ -3,12 +3,25 @@ #include #include +#include #include #include #include #include #include "IStatusUpdate.h" +// 自定义TableWidgetItem类,支持按数字值排序 +class NumericTableWidgetItem : public QTableWidgetItem +{ +public: + NumericTableWidgetItem(const QString &text); + + bool operator<(const QTableWidgetItem &other) const override; + +private: + double toDouble() const; +}; + class TearingDataTableWidget : public QWidget { Q_OBJECT @@ -25,6 +38,12 @@ public: private: void setupUI(); void setupTable(); + QString getTearStatusText(const QString &status); + QString getTearTypeText(const QString &type); + int findExistingRowById(const QString &id); + + // 排序相关的私有方法 + void connectHeaderSignals(); QTableWidget *m_tableWidget; }; diff --git a/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.cpp b/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.cpp index c8ed5e2..c688298 100644 --- a/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.cpp +++ b/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.cpp @@ -199,22 +199,22 @@ void BeltTearingPresenter::sendTestData(std::string fileName){ std::vector sVzNLPostion; sVzNLPostion.clear(); - bool bFindLineNum = true; - int nLaserPointIdx = 0; - + int nIndex = 0; SVzLaserLineData pLaserLine; - + while (std::getline(inputFile, line)) { if (line.find("Line_") == 0) { if(!sVzNLPostion.empty()){ - pLaserLine.llFrameIdx = nLaserPointIdx; pLaserLine.p3DPoint = sVzNLPostion.data(); pLaserLine.nPointCount = sVzNLPostion.size(); processPointCloudData(&pLaserLine); } + sscanf(line.c_str(), "Line_%lld_%lld", &pLaserLine.llFrameIdx, &pLaserLine.llTimeStamp); sVzNLPostion.clear(); + nIndex++; + } else if (line.find("{") == 0) { float lx, ly, rx, ry; SVzNL3DPosition pos; @@ -226,6 +226,10 @@ void BeltTearingPresenter::sendTestData(std::string fileName){ sVzNLPostion.push_back(pos); } + // if(nIndex > 500){ + // break; + // } + // std::this_thread::sleep_for(std::chrono::milliseconds(2)); } @@ -398,31 +402,34 @@ void BeltTearingPresenter::processPointCloudData(const SVzLaserLineData* pLaserL // 将激光线数据添加到队列(用于图像生成) addLaserLineToQueue(pLaserLine); - - // 调用SDK算法 int errorCode = 0; - std::vector beltTearings_new; - std::vector beltTearings_growing; - std::vector beltTearings_ended; - std::vector beltTearings_unknown; if(!m_bInitAlgo){ m_hLineWorkers.resize(pLaserLine->nPointCount); + m_beltTearings_new.clear(); + m_beltTearings_new.shrink_to_fit(); + m_beltTearings_growing.clear(); + m_beltTearings_growing.shrink_to_fit(); + m_beltTearings_ended.clear(); + m_beltTearings_ended.shrink_to_fit(); + m_beltTearings_unknown.clear(); + m_beltTearings_unknown.shrink_to_fit(); + sg_detectBeltTearing( NULL, //空扫描线,用于复位内部静态变量 0, 0, &errorCode, m_hLineWorkers, - beltTearings_new, - beltTearings_growing, - beltTearings_ended, - beltTearings_unknown, //未判明,应用无需处理。 + m_beltTearings_new, + m_beltTearings_growing, + m_beltTearings_ended, + m_beltTearings_unknown, //未判明,应用无需处理。 m_algorithmParam); - m_bInitAlgo = true; + m_bInitAlgo = true; } // 使用SDK算法进行撕裂检测 @@ -437,27 +444,36 @@ void BeltTearingPresenter::processPointCloudData(const SVzLaserLineData* pLaserL algorithmData.nPositionCnt, &errorCode, m_hLineWorkers, - beltTearings_new, - beltTearings_growing, - beltTearings_ended, - beltTearings_unknown, + m_beltTearings_new, + m_beltTearings_growing, + m_beltTearings_ended, + m_beltTearings_unknown, m_algorithmParam ); // 合并所有检测结果 std::vector allResults; - allResults.reserve(beltTearings_new.size() + beltTearings_growing.size() + - beltTearings_ended.size() + beltTearings_unknown.size()); + allResults.reserve(m_beltTearings_new.size() + m_beltTearings_growing.size() + m_beltTearings_ended.size()); - allResults.insert(allResults.end(), beltTearings_new.begin(), beltTearings_new.end()); - // allResults.insert(allResults.end(), beltTearings_growing.begin(), beltTearings_growing.end()); - allResults.insert(allResults.end(), beltTearings_ended.begin(), beltTearings_ended.end()); - // allResults.insert(allResults.end(), beltTearings_unknown.begin(), beltTearings_unknown.end()); + allResults.insert(allResults.end(), m_beltTearings_new.begin(), m_beltTearings_new.end()); + allResults.insert(allResults.end(), m_beltTearings_growing.begin(), m_beltTearings_growing.end()); + allResults.insert(allResults.end(), m_beltTearings_ended.begin(), m_beltTearings_ended.end()); + // allResults.insert(allResults.end(), m_beltTearings_unknown.begin(), m_beltTearings_unknown.end()); LOG_DEBUG("line count : %d algo detect count: %d (new:%d, growing:%d, ended:%d, unknown:%d)[%d]\n", - algorithmData.nPositionCnt, - allResults.size(), beltTearings_new.size(), beltTearings_growing.size(), - beltTearings_ended.size(), beltTearings_unknown.size(), errorCode); + algorithmData.nPositionCnt, + allResults.size(), m_beltTearings_new.size(), m_beltTearings_growing.size(), + m_beltTearings_ended.size(), m_beltTearings_unknown.size(), errorCode); + + for(auto& item : m_beltTearings_new){ + LOG_DEBUG("-----new %lld ID: %d st: %d depth: %f \n", pLaserLine->llFrameIdx, item.tearID, item.tearStatus, item.tearDepth); + } + +#if 1 + for(auto& item : m_beltTearings_ended){ + LOG_DEBUG("-----end %lld ID: %d st: %d depth: %f \n", pLaserLine->llFrameIdx, item.tearID, item.tearStatus, item.tearDepth); + } +#endif // 发送检测结果 if (!allResults.empty()) { diff --git a/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.h b/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.h index ee43207..424903d 100644 --- a/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.h +++ b/App/BeltTearing/BeltTearingServer/BeltTearingPresenter.h @@ -86,14 +86,19 @@ private: // SDK算法相关 std::atomic m_bInitAlgo{false}; std::vector m_hLineWorkers; + SSG_beltTearingParam m_algorithmParam; + std::vector m_beltTearings_new; + std::vector m_beltTearings_growing; + std::vector m_beltTearings_ended; + std::vector m_beltTearings_unknown; // 激光线队列管理 std::deque m_laserLineQueue; // 激光线数据队列 std::mutex m_queueMutex; // 队列访问互斥锁 unsigned long long m_lineCounter; // 新增线条计数器 static const int MAX_QUEUE_SIZE = 200; // 最大队列大小 - static const int IMAGE_GENERATION_INTERVAL = 10; // 每10条线生成一次图像 + static const int IMAGE_GENERATION_INTERVAL = 20; // 每20条线生成一次图像 // 相机状态回调函数 static void OnStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);