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 64,再添加寄存器的数据,点击组包,则会在末尾自动添加CRC16

4. IEEE754浮点数和16进制间转换

①浮点转16进制

②十六进制转浮点

三、源码简析

①表格类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();
    //浮点转16进制
    void on_btn_decTohex_clicked();
    //16进制转浮点
    void on_btn_hecTodec_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();
}

void MainWindow::on_btn_decTohex_clicked()
{
    ui->ledit_hex->clear();

    union
    {
        float fValue;
        unsigned char cValue[4];
    }value;

    value.fValue = ui->ledit_dec->text().toFloat();

    QString str;
    for(int i = 3; i >= 0; i--)
    {
        //把10进制转换为16进制
        QString sHex = QString("%1").arg(value.cValue[i], 2, 16, QChar('0'));
        str.append(sHex + " ");
    }

    //全部转为大写字母
    str = str.toUpper();
    ui->ledit_hex->setText(str);
}

void MainWindow::on_btn_hecTodec_clicked()
{
    ui->ledit_dec->clear();

    union
    {
        float fValue;
        unsigned char cValue[4];
    }value;

    QString str = ui->ledit_hex->text();
    str.remove(" ");

    if(str.length() != 8)
    {
        QMessageBox::warning(this, "warning", "hex is wrong!");
        return;
    }

    //10进制转16进制
    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 hex = strHex.toInt(0, 16);
        value.cValue[3-j] = hex;
    }

    QString text = QString("%1").arg(value.fValue);
    ui->ledit_dec->setText(text);
}


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/1mwUi4TPLWWoVer7dAjZ8Ow 
提取码:exzj

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值