我将展示如何将 Qt 数据库集成到前文的调试工具中,基于 ReferenceClass、PointerClass、SerialPort、ModbusTCPDebugger 和 ModbusConver

我将展示如何将 Qt 数据库(QtSql 模块)集成到前文的调试工具中,基于 ReferenceClass、PointerClass、SerialPort、ModbusTCPDebugger 和 ModbusConverter,并扩展 Qt GUI 以支持数据库操作。数据库将用于存储串口和 Modbus 数据(包括功能码 0x01、0x03、0x05、0x06),取代部分 CSV 日志功能,同时保留 QTreeView 和图表。代码将使用 SQLite 数据库(轻量、跨平台),支持中文字符,确保与异步 I/O、线程池和 TCP-to-RTU 转换兼容。GUI 将显示数据库查询结果,并允许用户管理数据。代码基于 Windows,提供 Linux 适配说明,Demo 通过 main 函数启动 GUI。

1. 设计目标

  • Qt 数据库集成:

    • 使用 QtSql 和 SQLite 存储串口数据(值、时间戳)和 Modbus 数据(功能码、地址、值、时间戳)。

    • 支持插入、查询、删除操作,确保中文字符正确存储和显示。

    • 替换部分 CSV 日志功能,提供更强大的数据管理。

  • GUI 增强:

    • QTreeView:显示设备数据层次结构(串口和 Modbus 数据)。

    • QTableView:显示数据库查询结果(替代前文的 QTableWidget)。

    • 控件:添加查询输入框、删除按钮,支持按时间或功能码筛选。

    • 图表:实时更新寄存器值曲线。

  • 功能:

    • 存储:串口和 Modbus 数据自动存入数据库。

    • 查询:支持按时间范围、功能码或地址查询。

    • 删除:允许删除指定记录。

    • 中文支持:确保数据库和 GUI 正确处理中文(如标签、日志)。

  • Modbus 功能码:

    • 保持 0x01(读线圈)、0x03(读寄存器)、0x05(写线圈)、0x06(写寄存器)。

  • 场景:调试工具记录设备数据到数据库,实时查询和分析,支持中文界面。

  • 优化:异步 I/O(QTcpSocket)、线程池(QThreadPool)、数据库事务提升性能。

2. 前置类调整

以下调整 SerialPort(保持不变)、ModbusConverter(不变)、ReferenceClass、PointerClass 和 ModbusTCPDebugger,以支持数据库存储。

ReferenceClass(添加数据库存储)

cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "SerialPort.h"

class ReferenceClass : public QObject {
    Q_OBJECT
private:
    std::vector& data;
    std::string& buffer;
    SerialPort& serial;
    QSqlDatabase db;

public:
    ReferenceClass(std::vector& d, std::string& b, SerialPort& s, QObject* parent = nullptr)
        : QObject(parent), data(d), buffer(b), serial(s) {
        if (data.empty()) {
            throw std::runtime_error("数据向量不能为空");
        }
        db = QSqlDatabase::addDatabase("QSQLITE", "serial_connection");
        db.setDatabaseName("debug_data.db");
        if (!db.open()) {
            throw std::runtime_error("数据库打开失败: " + db.lastError().text().toStdString());
        }
        QSqlQuery query(db);
        query.exec("CREATE TABLE IF NOT EXISTS serial_data ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   "timestamp TEXT, "
                   "type TEXT, "
                   "data TEXT)");
    }

    ~ReferenceClass() {
        db.close();
    }

    void asyncReadFromSerial(std::function&)> callback) {
        serial.asyncRead(data, [this, callback](const std::vector& serialData) {
            std::string str(serialData.begin(), serialData.end());
            buffer += str;
            std::string num;
            for (char c : str) {
                if (c == ' ') {
                    if (!num.empty()) data.push_back(std::stoi(num));
                    num.clear();
                } else {
                    num += c;
                }
            }
            if (!num.empty()) data.push_back(std::stoi(num));
            saveToDatabase("接收", serialData);
            callback(serialData);
            emit dataUpdated();
        });
    }

    void writeToSerial(const std::string& command) {
        std::vector data(command.begin(), command.end());
        serial.write(data);
        buffer += "发送: " + command;
        saveToDatabase("发送", data);
        emit dataUpdated();
    }

    const std::vector& getData() const { return data; }
    const std::string& getBuffer() const { return buffer; }

signals:
    void dataUpdated();

private:
    void saveToDatabase(const std::string& type, const std::vector& data) {
        QSqlQuery query(db);
        query.prepare("INSERT INTO serial_data (timestamp, type, data) VALUES (:timestamp, :type, :data)");
        query.bindValue(":timestamp", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
        query.bindValue(":type", QString::fromStdString(type));
        QString hexData;
        for (auto byte : data) {
            hexData += QString("%1 ").arg(byte, 2, 16, QChar('0')).toUpper();
        }
        query.bindValue(":data", hexData);
        if (!query.exec()) {
            qWarning() << "数据库插入失败:" << query.lastError().text();
        }
    }
};

调整:

  • 初始化 SQLite 数据库(debug_data.db),创建 serial_data 表。

  • saveToDatabase:存储时间戳、类型(发送/接收)、HEX 数据。

  • 支持中文(接收、发送)。

PointerClass(支持数据库存储)

cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class PointerClass : public QObject {
    Q_OBJECT
private:
    std::vector* data;
    std::string* buffer;
    std::unique_ptr dynamicArray;
    size_t arraySize;
    QTcpSocket* tcpSocket;
    uint16_t transactionId;
    QSqlDatabase db;

public:
    PointerClass(std::vector* d, std::string* b, size_t size, const std::string& host, int port, QObject* parent = nullptr)
        : QObject(parent), data(d), buffer(b), arraySize(size), tcpSocket(new QTcpSocket(this)), transactionId(0) {
        if (!data || !buffer) {
            throw std::runtime_error("指针不能为空");
        }
        dynamicArray = std::unique_ptr(new int[size]);
        for (size_t i = 0; i < size; ++i) {
            dynamicArray[i] = static_cast(i);
        }
        db = QSqlDatabase::addDatabase("QSQLITE", "modbus_connection");
        db.setDatabaseName("debug_data.db");
        if (!db.open()) {
            throw std::runtime_error("数据库打开失败: " + db.lastError().text().toStdString());
        }
        QSqlQuery query(db);
        query.exec("CREATE TABLE IF NOT EXISTS modbus_data ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   "timestamp TEXT, "
                   "type TEXT, "
                   "function_code TEXT, "
                   "data TEXT)");
        tcpSocket->connectToHost(QString::fromStdString(host), port);
        connect(tcpSocket, &QTcpSocket::connected, this, &PointerClass::onConnected);
        connect(tcpSocket, &QTcpSocket::readyRead, this, &PointerClass::onReadyRead);
        connect(tcpSocket, &QTcpSocket::disconnected, this, &PointerClass::onDisconnected);
    }

    ~PointerClass() {
        db.close();
    }

    void asyncModbusRequest(uint16_t functionCode, uint16_t startAddr, uint16_t countOrValue,
                           std::function&)> callback) {
        if (tcpSocket->state() != QAbstractSocket::ConnectedState) {
            emit errorOccurred("未连接");
            return;
        }
        std::vector frame = createModbusFrame(functionCode, startAddr, countOrValue);
        tcpSocket->write(QByteArray(reinterpret_cast(frame.data()), frame.size()));
        *buffer += "发送 Modbus: ";
        for (auto it = frame.begin(); it != frame.end(); ++it) {
            *buffer += std::to_string(static_cast(*it) & 0xFF) + " ";
        }
        saveToDatabase("发送", functionCode, frame);
        connect(this, &PointerClass::responseReceived, this, [=](const std::vector& data) {
            saveToDatabase("接收", functionCode, data);
            callback(data);
            emit dataUpdated();
        }, Qt::SingleShotConnection);
    }

    void updateDynamicArray(size_t index, int value) {
        if (index >= arraySize) {
            throw std::out_of_range("动态数组索引越界");
        }
        dynamicArray[index] = value;
    }

    std::vector parseModbusResponse(const std::vector& response) {
        std::vector values;
        if (response.size() >= 9) {
            uint8_t functionCode = response[7];
            if (functionCode == 0x03) {
                for (size_t i = 0; i < response[8] / 2; ++i) {
                    values.push_back((response[9 + 2 * i] << 8) | response[9 + 2 * i + 1]);
                    data->push_back(values.back());
                }
            } else if (functionCode == 0x01) {
                for (size_t i = 0; i < response[8]; ++i) {
                    uint8_t byte = response[9 + i];
                    for (size_t j = 0; j < 8; ++j) {
                        values.push_back((byte >> j) & 1);
                        data->push_back(values.back());
                    }
                }
            } else if (functionCode == 0x05 || functionCode == 0x06) {
                values.push_back((response[10] << 8) | response[11]);
                data->push_back(values.back());
            }
        }
        return values;
    }

    const std::vector& getData() const { return *data; }
    const std::string& getBuffer() const { return *buffer; }

signals:
    void dataUpdated();
    void responseReceived(const std::vector& data);
    void errorOccurred(const QString& error);
    void connectionStateChanged(bool connected);

private slots:
    void onConnected() { emit connectionStateChanged(true); }
    void onDisconnected() { emit connectionStateChanged(false); }
    void onReadyRead() {
        QByteArray response = tcpSocket->readAll();
        std::vector data(response.begin(), response.end());
        emit responseReceived(data);
    }

private:
    std::vector createModbusFrame(uint16_t functionCode, uint16_t startAddr, uint16_t countOrValue) {
        std::vector frame(12);
        transactionId++;
        frame[0] = (transactionId >> 8) & 0xFF;
        frame[1] = transactionId & 0xFF;
        frame[2] = 0x00;
        frame[3] = 0x00;
        frame[4] = 0x00;
        frame[5] = 0x06;
        frame[6] = 0x01;
        frame[7] = functionCode;
        frame[8] = (startAddr >> 8) & 0xFF;
        frame[9] = startAddr & 0xFF;
        frame[10] = (countOrValue >> 8) & 0xFF;
        frame[11] = countOrValue & 0xFF;
        return frame;
    }

    void saveToDatabase(const std::string& type, uint16_t functionCode, const std::vector& data) {
        QSqlQuery query(db);
        query.prepare("INSERT INTO modbus_data (timestamp, type, function_code, data) VALUES (:timestamp, :type, :function_code, :data)");
        query.bindValue(":timestamp", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
        query.bindValue(":type", QString::fromStdString(type));
        query.bindValue(":function_code", QString("0x%1").arg(functionCode, 2, 16, QChar('0')).toUpper());
        QString hexData;
        for (auto byte : data) {
            hexData += QString("%1 ").arg(byte, 2, 16, QChar('0')).toUpper();
        }
        query.bindValue(":data", hexData);
        if (!query.exec()) {
            qWarning() << "数据库插入失败:" << query.lastError().text();
        }
    }
};

调整:

  • 初始化 SQLite 数据库,创建 modbus_data 表。

  • saveToDatabase:存储时间戳、类型、功能码、HEX 数据。

  • 支持中文(发送、接收、未连接)。

ModbusTCPDebugger(保持不变)

使用前文的实现,支持 0x01、0x03、0x05、0x06,无需调整。

3. 扩展的 Qt GUI(添加数据库查询)

cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ReferenceClass.h"
#include "PointerClass.h"
#include "ModbusTCPDebugger.h"
#include "ModbusConverter.h"

class DebugWindow : public QMainWindow {
    Q_OBJECT
private:
    ReferenceClass* refClass;
    PointerClass* ptrClass;
    QTextEdit* logText;
    QLineEdit* commandInput;
    QComboBox* commandType;
    QTableView* dataTable;
    QSqlTableModel* tableModel;
    QTreeView* deviceTree;
    QStandardItemModel* treeModel;
    QChart* chart;
    QChartView* chartView;
    QLineSeries* regSeries;
    QTimer* updateTimer;
    QLabel* statusLabel;
    QDateTimeEdit* startTimeEdit;
    QLineEdit* filterInput;
    std::vector lastRegValues;
    QSqlDatabase db;

public:
    DebugWindow(ReferenceClass* ref, PointerClass* ptr, QWidget* parent = nullptr)
        : QMainWindow(parent), refClass(ref), ptrClass(ptr) {
        setWindowTitle("调试工具");
        resize(1200, 800);
        QWidget* centralWidget = new QWidget(this);
        setCentralWidget(centralWidget);
        QVBoxLayout* mainLayout = new QVBoxLayout(centralWidget);

        // 数据库
        db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("debug_data.db");
        if (!db.open()) {
            logText->append("数据库打开失败: " + db.lastError().text());
        }

        // 状态标签
        statusLabel = new QLabel("未连接", this);
        mainLayout->addWidget(statusLabel);

        // 日志区域
        logText = new QTextEdit(this);
        logText->setReadOnly(true);
        mainLayout->addWidget(logText);

        // 设备树和表格布局
        QHBoxLayout* dataLayout = new QHBoxLayout;
        deviceTree = new QTreeView(this);
        treeModel = new QStandardItemModel(this);
        treeModel->setHorizontalHeaderLabels({"设备数据"});
        deviceTree->setModel(treeModel);
        dataLayout->addWidget(deviceTree);
        dataTable = new QTableView(this);
        tableModel = new QSqlTableModel(this, db);
        tableModel->setTable("modbus_data");
        tableModel->setHeaderData(1, Qt::Horizontal, "时间");
        tableModel->setHeaderData(2, Qt::Horizontal, "类型");
        tableModel->setHeaderData(3, Qt::Horizontal, "功能码");
        tableModel->setHeaderData(4, Qt::Horizontal, "数据");
        tableModel->select();
        dataTable->setModel(tableModel);
        dataTable->setColumnHidden(0, true); // 隐藏 ID
        dataLayout->addWidget(dataTable);
        mainLayout->addLayout(dataLayout);

        // 图表
        chart = new QtCharts::QChart;
        regSeries = new QtCharts::QLineSeries;
        chart->addSeries(regSeries);
        chart->createDefaultAxes();
        chart->setTitle("寄存器值");
        chartView = new QtCharts::QChartView(chart);
        chartView->setRenderHint(QPainter::Antialiasing);
        mainLayout->addWidget(chartView);

        // 查询和删除
        QHBoxLayout* queryLayout = new QHBoxLayout;
        startTimeEdit = new QDateTimeEdit(this);
        startTimeEdit->setDateTime(QDateTime::currentDateTime().addDays(-1));
        filterInput = new QLineEdit(this);
        filterInput->setPlaceholderText("输入功能码 (如 0x03)");
        QPushButton* queryButton = new QPushButton("查询", this);
        QPushButton* deleteButton = new QPushButton("删除选中", this);
        queryLayout->addWidget(new QLabel("开始时间:"));
        queryLayout->addWidget(startTimeEdit);
        queryLayout->addWidget(new QLabel("功能码:"));
        queryLayout->addWidget(filterInput);
        queryLayout->addWidget(queryButton);
        queryLayout->addWidget(deleteButton);
        mainLayout->addLayout(queryLayout);

        // 命令输入
        QHBoxLayout* inputLayout = new QHBoxLayout;
        commandType = new QComboBox(this);
        commandType->addItems({"串口", "Modbus 读线圈 (0x01)", "Modbus 读寄存器 (0x03)", "Modbus 写线圈 (0x05)", "Modbus 写寄存器 (0x06)"});
        commandInput = new QLineEdit(this);
        QPushButton* sendButton = new QPushButton("发送", this);
        inputLayout->addWidget(commandType);
        inputLayout->addWidget(commandInput);
        inputLayout->addWidget(sendButton);
        mainLayout->addLayout(inputLayout);

        // 定时器
        updateTimer = new QTimer(this);
        updateTimer->start(500);

        // 信号与槽
        connect(sendButton, &QPushButton::clicked, this, &DebugWindow::sendCommand);
        connect(queryButton, &QPushButton::clicked, this, &DebugWindow::queryData);
        connect(deleteButton, &QPushButton::clicked, this, &DebugWindow::deleteSelected);
        connect(updateTimer, &QTimer::timeout, this, &DebugWindow::updateUI);
        connect(refClass, &ReferenceClass::dataUpdated, this, &DebugWindow::updateUI);
        connect(ptrClass, &PointerClass::dataUpdated, this, &DebugWindow::updateUI);
        connect(ptrClass, &PointerClass::connectionStateChanged, this, &DebugWindow::updateStatus);
        connect(ptrClass, &PointerClass::errorOccurred, this, &DebugWindow::showError);

        // 初始请求
        refClass->asyncReadFromSerial([this](const std::vector& data) {
            logText->append("串口: " + QString::fromStdString(formatData(data)));
        });
        ptrClass->asyncModbusRequest(0x03, 0, 4, [this](const std::vector& data) {
            logText->append("Modbus: " + QString::fromStdString(formatData(data)));
        });
        updateTree();
    }

private slots:
    void sendCommand() {
        std::string cmd = commandInput->text().toStdString();
        std::stringstream ss(cmd);
        std::string type = commandType->currentText().toStdString();
        if (type == "串口") {
            refClass->writeToSerial(cmd + "\n");
            logText->append("发送串口: " + QString::fromStdString(cmd));
        } else if (type == "Modbus 读线圈 (0x01)") {
            int start, count;
            ss >> start >> count;
            ptrClass->asyncModbusRequest(0x01, start, count, [this](const std::vector& data) {
                logText->append("Modbus 线圈: " + QString::fromStdString(formatData(data)));
            });
        } else if (type == "Modbus 读寄存器 (0x03)") {
            int start, count;
            ss >> start >> count;
            ptrClass->asyncModbusRequest(0x03, start, count, [this](const std::vector& data) {
                logText->append("Modbus 寄存器: " + QString::fromStdString(formatData(data)));
            });
        } else if (type == "Modbus 写线圈 (0x05)") {
            int addr, value;
            ss >> addr >> value;
            ptrClass->asyncModbusRequest(0x05, addr, value ? 0xFF00 : 0x0000, [this](const std::vector& data) {
                logText->append("Modbus 写线圈: " + QString::fromStdString(formatData(data)));
            });
            std::vector tcpFrame = ptrClass->createModbusFrame(0x05, addr, value ? 0xFF00 : 0x0000);
            std::vector rtuFrame = ModbusConverter::tcpToRtu(tcpFrame);
            refClass->writeToSerial(std::string(rtuFrame.begin(), rtuFrame.end()));
        } else if (type == "Modbus 写寄存器 (0x06)") {
            int addr, value;
            ss >> addr >> value;
            ptrClass->asyncModbusRequest(0x06, addr, value, [this](const std::vector& data) {
                logText->append("Modbus 写寄存器: " + QString::fromStdString(formatData(data)));
            });
            std::vector tcpFrame = ptrClass->createModbusFrame(0x06, addr, value);
            std::vector rtuFrame = ModbusConverter::tcpToRtu(tcpFrame);
            refClass->writeToSerial(std::string(rtuFrame.begin(), rtuFrame.end()));
        }
        commandInput->clear();
        updateTree();
    }

    void queryData() {
        QString filter = QString("timestamp >= '%1'").arg(startTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss"));
        if (!filterInput->text().isEmpty()) {
            filter += QString(" AND function_code = '%1'").arg(filterInput->text());
        }
        tableModel->setFilter(filter);
        tableModel->select();
    }

    void deleteSelected() {
        QItemSelectionModel* selection = dataTable->selectionModel();
        QModelIndexList indices = selection->selectedRows();
        for (const auto& index : indices) {
            tableModel->removeRow(index.row());
        }
        tableModel->submitAll();
        tableModel->select();
    }

    void updateUI() {
        tableModel->select();
        updateChart(ptrClass->getData());
        updateTree();
    }

    void updateStatus(bool connected) {
        statusLabel->setText(connected ? "已连接" : "未连接");
        statusLabel->setStyleSheet(connected ? "color: green" : "color: red");
    }

    void showError(const QString& error) {
        logText->append("错误: " + error);
    }

private:
    void updateTree() {
        treeModel->clear();
        treeModel->setHorizontalHeaderLabels({"设备数据"});
        QStandardItem* root = new QStandardItem("设备");
        QStandardItem* serialNode = new QStandardItem("串口数据");
        QStandardItem* modbusNode = new QStandardItem("Modbus 数据");
        root->appendRow(serialNode);
        root->appendRow(modbusNode);

        // 串口数据
        const auto& serialData = refClass->getData();
        for (size_t i = 0; i < serialData.size(); ++i) {
            serialNode->appendRow(new QStandardItem(QString("值 %1: %2").arg(i).arg(serialData[i])));
        }

        // Modbus 数据
        QStandardItem* regNode = new QStandardItem("寄存器");
        QStandardItem* coilNode = new QStandardItem("线圈");
        modbusNode->appendRow(regNode);
        modbusNode->appendRow(coilNode);
        const auto& modbusData = ptrClass->getData();
        for (size_t i = 0; i < std::min(modbusData.size(), 4); ++i) {
            regNode->appendRow(new QStandardItem(QString("寄存器 %1: %2").arg(i).arg(modbusData[i])));
        }
        ptrClass->asyncModbusRequest(0x01, 0, 8, [this](const std::vector& data) {
            auto values = ptrClass->parseModbusResponse(data);
            QStandardItem* coilNode = treeModel->item(0)->child(1)->child(1);
            for (size_t i = 0; i < values.size(); ++i) {
                coilNode->appendRow(new QStandardItem(QString("线圈 %1: %2").arg(i).arg(values[i])));
            }
        });

        treeModel->appendRow(root);
        deviceTree->expandAll();
    }

    void updateChart(const std::vector& values) {
        if (values == lastRegValues) return;
        lastRegValues = values;
        regSeries->clear();
        for (size_t i = 0; i < values.size(); ++i) {
            regSeries->append(i, values[i]);
        }
        chart->axes(Qt::Horizontal)->first()->setRange(0, values.size());
        chart->axes(Qt::Vertical)->first()->setRange(0, *std::max_element(values.begin(), values.end()) + 100);
    }

    std::string formatData(const std::vector& data) {
        std::stringstream ss;
        for (auto byte : data) {
            ss << std::hex << std::setw(2) << std::setfill('0') << (static_cast(byte) & 0xFF) << " ";
        }
        return ss.str();
    }
};

4. Main 函数

cpp

#include 
#include "ReferenceClass.h"
#include "PointerClass.h"
#include "ModbusTCPDebugger.h"
#include "ModbusConverter.h"

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);
    try {
        std::vector data = {10, 20, 30};
        std::string buffer = "初始";
        SerialPort serial("COM3", CBR_9600);

        ModbusTCPDebugger server("0.0.0.0", 502);
        ReferenceClass refClass(data, buffer, serial);
        PointerClass ptrClass(&data, &buffer, 3, "127.0.0.1", 502);

        DebugWindow window(&refClass, &ptrClass);
        window.show();
        return app.exec();
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }
}

5. 功能说明

  • 数据库集成:

    • 表结构:

      • serial_data:id(主键)、timestamp(时间戳)、type(发送/接收)、data(HEX)。

      • modbus_data:id、timestamp、type、function_code(如 0x03)、data。

    • 操作:

      • 插入:saveToDatabase 自动记录数据。

      • 查询:通过 QSqlTableModel 按时间或功能码筛选。

      • 删除:选中 QTableView 行后删除记录。

    • 中文支持:使用 QString::fromStdString 确保 UTF-8 编码。

  • GUI:

    • QTreeView:显示设备 > 串口数据/Modbus 数据 > 值/寄存器/线圈。

    • QTableView:显示 modbus_data 表,支持筛选和删除。

    • 查询控件:QDateTimeEdit 设置开始时间,QLineEdit 输入功能码。

    • 图表:实时更新寄存器曲线。

    • 命令输入:支持串口命令和 Modbus 请求(0x01、0x03、0x05、0x06)。

  • TCP-to-RTU:

    • 0x05 和 0x06 请求生成 RTU 帧,通过串口发送。

  • 数据结构:

    • std::vector 存储 Modbus 帧。

    • std::string 存储缓冲区。

    • std::unique_ptr 管理动态数组。

6. 输出示例

假设串口返回 "1 2 3",Modbus 返回寄存器 {0x1234, 0x5678},线圈 {0, 1}:

  • 日志区域:

    串口: 01 02 03
    Modbus: 00 01 00 00 00 07 01 03 04 12 34 56 78
    Modbus 线圈: 00 02 00 00 00 04 01 01 01 02
  • 树形视图:

    设备
    ├── 串口数据
    │   ├── 值 0: 10
    │   ├── 值 1: 20
    │   ├── 值 2: 30
    │   ├── 值 3: 1
    │   ├── 值 4: 2
    │   └── 值 5: 3
    └── Modbus 数据
        ├── 寄存器
        │   ├── 寄存器 0: 4660
        │   └── 寄存器 1: 22136
        └── 线圈
            ├── 线圈 0: 0
            └── 线圈 1: 1
  • 表格(QTableView):

    时间                  类型  功能码  数据
    2025-06-03 19:31:00  发送  0x03    00 01 00 00 00 06 01 03 00 00 00 04
    2025-06-03 19:31:00  接收  0x03    00 01 00 00 00 07 01 03 04 12 34 56 78
  • 图表:显示曲线(4660, 22136)。

  • 数据库(debug_data.db):

    • serial_data:存储 "01 02 03" 等。

    • modbus_data:存储功能码和 HEX 数据。

7. Linux 适配

  • QtSql:确保安装 libqt5sql5-sqlite。

  • SerialPort:替换 Windows API 为 POSIX(open, read, write)。

  • QtNetwork:跨平台,无需修改。

  • 编译:

    g++ -o app main.cpp ReferenceClass.cpp PointerClass.cpp SerialPort.cpp ModbusTCPDebugger.cpp ModbusConverter.cpp -std=c++11 -pthread $(pkg-config --cflags --libs Qt5Widgets Qt5Charts Qt5Network Qt5Sql)
  • 桥接:使用 socat:

    socat TCP-LISTEN:12345,reuseaddr /dev/ttyUSB0,raw,echo=0,b9600

8. 扩展建议

  • 其他数据库:支持 MySQL 或 PostgreSQL(替换 QSQLITE 为 QMYSQL/QPSQL)。

  • QTreeView 增强:支持编辑寄存器/线圈,触发数据库更新。

  • 查询增强:添加结束时间筛选、数据导出为 CSV。

  • 更多功能码:

    • 0x0F:写多个线圈。

    • 0x10:写多个寄存器。

9. 注意事项

  • 中文编码:确保源文件为 UTF-8,数据库使用 UTF-8 编码。

  • 线程安全:QSqlDatabase 需为每个连接分配独立实例(通过 addDatabase 的连接名)。

  • 性能:大数据量时,使用数据库索引或限制查询范围。

  • 依赖:安装 libqt5sql5-sqlite。

如果你需要 MySQL 支持、QTreeView 编辑功能、CSV 导出,或更多功能码(如 0x0F),请提供细节,我可以进一步定制!

你可能感兴趣的:(C++,c++,开发语言)