目录
一、前言
二、操作说明
三、源码简析
四、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
#include
#include
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 m_vecData;
};
#endif // CMODEL_H
CModel.cpp
#include "CModel.h"
#include
#include
#include
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
②界面交互类MainWindow
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
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
#include
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> 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; jledit_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;
}
链接:https://pan.baidu.com/s/1mwUi4TPLWWoVer7dAjZ8Ow
提取码:exzj