QT製作Modbus組包小工具

目錄

一、前言

二、操作說明

三、源碼簡析

四、Demo/小工具


一、前言

我們常用Modbus協議來讀取儀器的某一或多個寄存器的值,但在現實中,可能讀取的寄存器過多(例如幾百個);當出現問題的時候,不容易分析每個寄存器的值,此Modbus組包小工具,可以很好顯示對應的寄存器。

 

二、操作說明

1. 寄存器->組幀

輸入開始寄存器、結束寄存器(數字),點擊【顯示寄存器】,則會顯示寄存器的值(默認全0);一個寄存器16位,H和L都是十六進制數。

如果你想修改某一寄存器的值,則點擊相應表格,例如我這把50號寄存器的H改爲5B;點擊Pack,就把50-100寄存器的值,以16進制組幀顯示

2. 組幀->寄存器

例如我收到了一串16進制數據:5A 00 00 33 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

我現在知道,寄存器從0開始(可以修改開始寄存器),那我想知道這串數據對應寄存器的所有值,則操作如下:

在輸入框輸入獲取到的數據,點擊UnPack,就可以看到每個寄存器對應的高低位

3. 組包

Modbus最後的組包,例如我加入包頭01 03 22,再添加寄存器的數據,點擊組包,則會在末尾自動添加CRC16

 

三、源碼簡析

①表格類CModel

CModel.h

#ifndef CMODEL_H
#define CMODEL_H

#include <QAbstractTableModel>
#include <QStringList>
#include <QVector>


class CModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit CModel();
    ~CModel();

    //行數
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    //列數
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
    //顯示的數據
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    //從表格界面修改數據
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    //顯示行首
    virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    //設置表格可選性等
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;

    //設置開始寄存器、結束寄存器後,顯示錶格,默認寄存器的高低16位都爲0
    void setParam(int sIndex, int eIndex);
    QString pack();
    bool unpack(const QString& str, int sIndex, int &eIndex);

private:
    //行首值
    QStringList                 m_horHeard;
    //表格數據
    QVector<QStringList>        m_vecData;
};

#endif // CMODEL_H

CModel.cpp

#include "CModel.h"
#include <QCoreApplication>
#include <QDebug>
#include <QTextCodec>


CModel::CModel()
{
    //設置行首值
    m_horHeard << tr("Register")
               << tr("H")
               << tr("L");
}


CModel::~CModel()
{
}


int CModel::rowCount(const QModelIndex &parent) const
{
    return m_vecData.count();
}

int CModel::columnCount(const QModelIndex &parent) const
{
    return m_horHeard.count();
}

QVariant CModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    if (role == Qt::DisplayRole || role == Qt::EditRole)
    {
        //行
        int iRow = index.row();
        //列
        int iColumn = index.column();
        switch (iColumn)
        {
        case 0:
            return m_vecData.at(iRow).at(iColumn);
            break;
        case 1:
            return m_vecData.at(iRow).at(iColumn);
            break;
        case 2:
            return m_vecData.at(iRow).at(iColumn);
            break;
        default:
            break;
        }
    }
    else if (role == Qt::TextAlignmentRole)
    {
        return Qt::AlignCenter;
    }

    return QVariant();
}

//這裏界面修改, 數據變動的地方
bool CModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    if (role == Qt::DisplayRole ||role == Qt::EditRole)
    {
        int iRow = index.row();
        int iColumn = index.column();
        QStringList strList = m_vecData.at(iRow);
        switch (iColumn)
        {
        case 0:
            strList.replace(iColumn, value.toString());
            m_vecData.replace(iRow, strList);
            break;
        case 1:
            strList.replace(iColumn, value.toString());
            m_vecData.replace(iRow, strList);
            break;
        case 2:
            strList.replace(iColumn, value.toString());
            m_vecData.replace(iRow, strList);
            break;
        default:
            break;
        }

        //reset函數可以立即刷新表格數據
        reset();
        return true;
    }

    return false;
}


QVariant CModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
    {
        return m_horHeard.at(section);
    }

    return QVariant();
}


Qt::ItemFlags CModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flag = QAbstractTableModel::flags(index);
    if (index.column() > 0)
    {
        flag |= Qt::ItemIsEditable;
    }

    return flag;
}

void CModel::setParam(int sIndex, int eIndex)
{
    m_vecData.clear();
    for(int i = sIndex; i <= eIndex; i++)
    {
        QString sIndex = QString("%1").arg(i);
        QStringList strlist;
        strlist  << sIndex
                 << "00"
                 << "00";
        m_vecData.push_back(strlist);
    }

    reset();
}

QString CModel::pack()
{
    QString str;
    for(int i = 0; i < m_vecData.count(); i++)
    {
        QStringList strlist = m_vecData[i];
        //第一列不要
        for(int j = 1; j < strlist.count(); j++)
        {
            str.append(strlist[j]);
            str.append(" ");
        }
    }

    return str;
}

bool CModel::unpack(const QString &str, int sIndex, int &eIndex)
{
    QString sStr = str;
    sStr.remove(" ");

    if(sStr.length()%4 != 0)
    {
        return false;
    }

    m_vecData.clear();

    for(int i=0,j=0; j<sStr.length()/4; i+=4,j++)
    {
        QStringList sList;
        QString strH;
        QString strL;
        strH.append(sStr.at(i));
        strH.append(sStr.at(i+1));
        strL.append(sStr.at(i+2));
        strL.append(sStr.at(i+3));

        QString strIndex = QString("%1").arg(sIndex+j);
        sList << strIndex
              << strH
              << strL;
        m_vecData.append(sList);

        eIndex = sIndex+j;
    }

    reset();
    return true;
}




②界面交互類MainWindow

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class CModel;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    //CRC16
    int crc16_modbus(unsigned char *buf, unsigned int len);

private slots:
    //顯示寄存器table
    void on_btn_show_clicked();
    //寄存器的數據組幀
    void on_btn_pack_clicked();
    //幀的數據顯示在寄存器table上
    void on_btn_unpack_clicked();
    //添加包頭,組包
    void on_btn_pack2_clicked();
    //清空數據
    void on_btn_clear1_clicked();
    void on_btn_clear2_clicked();
    void on_btn_clear3_clicked();

private:
    Ui::MainWindow *ui;

    CModel *m_pModel;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "CTable/CModel.h"
#include <QDebug>
#include <QMessageBox>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("PackApp");

    m_pModel = new CModel;

    ui->tableView->setModel(m_pModel);

    //設置表格屬性
    ui->tableView->setEditTriggers(QTableView::AllEditTriggers);
    ui->tableView->setSelectionBehavior(QTableView::SelectRows);
    ui->tableView->setSelectionMode(QTableView::SingleSelection);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
}

MainWindow::~MainWindow()
{
    delete ui;
    if(m_pModel != NULL)
    {
        delete m_pModel;
        m_pModel = NULL;
    }
}

void MainWindow::on_btn_show_clicked()
{
    int startRrgister = ui->ledit_startReg->text().toInt();
    int endRrgister = ui->ledit_endReg->text().toInt();
    m_pModel->setParam(startRrgister, endRrgister);
}

void MainWindow::on_btn_pack_clicked()
{
    if(ui->ledit_startReg->text().toInt() > ui->ledit_endReg->text().toInt())
    {
        QMessageBox::warning(this, "warning", "Register input wrong!");
        return;
    }

    ui->textEdit->clear();

    QString sValueStr = m_pModel->pack();
    ui->textEdit->append(sValueStr);
}

void MainWindow::on_btn_unpack_clicked()
{
    int eIndex = 0;
    bool bRet = m_pModel->unpack(ui->textEdit->toPlainText(),
                                 ui->ledit_startReg->text().toInt(),
                                 eIndex);
    if(bRet)
    {
        QString sEIndex = QString("%1").arg(eIndex);
        ui->ledit_endReg->setText(sEIndex);
    }
    else
    {
        QMessageBox::warning(this, "warning", "hex is wrong!");
    }
}


void MainWindow::on_btn_pack2_clicked()
{
    ui->textEdit3->clear();
    QString str = ui->textEdit2->toPlainText();
    str.remove(" ");
    if(str.length()%2 != 0)
    {
        QMessageBox::warning(this, "warning", "hex is wrong!");
        return;
    }

    unsigned char buffer[str.length()/2];
    for(int i=0,j=0; j<str.length()/2; i+=2,j++)
    {
        QString strHex;
        strHex.append("0x");
        strHex.append(str.at(i));
        strHex.append(str.at(i+1));

        int value = strHex.toInt(0, 16);
        buffer[j] = value;
    }

    int crc = crc16_modbus(buffer, str.length()/2);
    int crc16_H = ((crc&0xFF00) >> 8);
    int crc16_L = (crc&0xFF);

    QString sCRC16 = QString("%1 %2").arg(crc16_H, 2, 16, QChar('0')).arg(crc16_L, 2, 16, QChar('0'));
    sCRC16 = sCRC16.toUpper();
    ui->ledit_crc16->setText(sCRC16);
    ui->textEdit3->append(ui->textEdit2->toPlainText() + sCRC16);
}

void MainWindow::on_btn_clear1_clicked()
{
    ui->textEdit->clear();
}

void MainWindow::on_btn_clear2_clicked()
{
    ui->textEdit2->clear();
}

void MainWindow::on_btn_clear3_clicked()
{
    ui->textEdit3->clear();
}

int MainWindow::crc16_modbus(unsigned char* buf, unsigned int len)
{
    unsigned int crc=0xFFFF;
    unsigned int i,j;

    for (j = 0; j < len; j++)
    {
        crc=crc^*buf++;
        for(i = 0; i<8; i++)
        {
            if ((crc&0x0001) > 0)
            {
                crc=crc>>1;
                crc=crc^0xa001;
            }
            else
            {
                crc = crc>>1;
            }
        }
    }

    crc = ((crc>>8) | ((crc&0xFF) << 8));

    return crc;
}

 

四、Demo/小工具

鏈接:https://pan.baidu.com/s/1UMLIf8AhuzqDzak5AS_N9Q 
提取碼:m7kl 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章