Compare commits

..

No commits in common. "cd74e80b7022585e3fb51bf4acea75c5c08dfff5" and "e0f929fab3d020ae0659cb76beb9d1059f5a56a7" have entirely different histories.

8 changed files with 87 additions and 272 deletions

View File

@ -5,25 +5,15 @@
#include <QWidget> #include <QWidget>
#include <QDebug> #include <QDebug>
#include <QMutex> #include <QMutex>
#include <QDateTime>
extern "C"{ extern "C"{
#include "libmodbus/modbus.h" #include "libmodbus/modbus.h"
} }
#if defined(_MSC_VER) && (_MSC_VER >= 1600)
# pragma execution_character_set("utf-8")
#endif
typedef struct { typedef struct {
int addr; int addr;
uint16_t *val1; float val1;
uint16_t len;
QString time; QString time;
bool succ;
}CapData; }CapData;
@ -35,7 +25,6 @@ typedef struct{
typedef struct T_Config{ typedef struct T_Config{
QString com; QString com;
int baurate;
int addr; int addr;
int rate; int rate;
QList<Req> req; QList<Req> req;
@ -72,96 +61,53 @@ public:
mMux.unlock(); mMux.unlock();
} }
} }
void Pause(){
mPause = true;
}
void Continue(){
mPause = false;
}
void Run(void *v) override{ void Run(void *v) override{
// 读取1 // 读取1
float x = 0; float x = 0;
while(mRuning){ while(mRuning){
int count = 0; int count = 0;
if(mPause){
QThread::msleep(100);
continue;
}
for(int it = 0;it < mConf.size();it++) { for(int it = 0;it < mConf.size();it++) {
auto itr = mConf.at(it); auto itr = mConf.at(it);
uint16_t dat[10] = {0}; uint16_t dat[10] = {0};
mMod = modbus_new_rtu(itr->com.toStdString().c_str(),
57600, 'N', 8, 1); //相同的端口只能同时打开一个
if(mMod == nullptr){ modbus_set_debug(mMod,true);
mMod = modbus_new_rtu(itr->com.toStdString().c_str(), modbus_set_slave(mMod,itr->addr); //设置modbus从机地址
itr->baurate, 'N', 8, 1); //相同的端口只能同时打开一个 modbus_connect(mMod);
modbus_set_response_timeout(mMod,1000,1000);
modbus_set_debug(mMod,true); QThread::msleep(300);
modbus_set_slave(mMod,itr->addr); //设置modbus从机地址
modbus_connect(mMod);
modbus_set_response_timeout(mMod,3,0);
}
for(auto s = 0;s < itr->req.length();s++){ for(auto s = 0;s < itr->req.length();s++){
QThread::msleep(itr->rate);
QThread::msleep(1000);
int ret = modbus_read_registers(mMod,itr->req[s].addr,itr->req[s].len,dat); int ret = modbus_read_registers(mMod,itr->req[s].addr,itr->req[s].len,dat);
mMux.lock(); mMux.lock();
QString log = QDateTime::currentDateTime().toString("yyyy-mm-dd hh:MM:ss ") + "发送数据:";
for(int i =0;i < req_length;i++){
log += QString::asprintf("0x%02x ",req[i]);
}
emit(Info(log));
log = QDateTime::currentDateTime().toString("yyyy-mm-dd hh:MM:ss ") + "接收数据:";
for(int i =0;i < resp_length;i++){
log += QString::asprintf("0x%02x ",rsp[i]);
}
if(ret < 0){
log += "超时";
}
emit(Info(log));
bool succ = true;
if(mListData.contains(itr->addr)){ if(mListData.contains(itr->addr)){
if(ret < 0){
succ = false;
}
uint16_t *val = new uint16_t[itr->req[s].len];
memcpy(val,dat,itr->req[s].len*2);
mListData[itr->addr]->push_front(new CapData{ mListData[itr->addr]->push_front(new CapData{
itr->req[s].addr, itr->addr,
val, float(float(dat[0])),
itr->req[s].len,
QDateTime::currentDateTime().toString("yyyy-mm-dd hh:MM:ss"),
true
}); });
}else{ }else{
uint16_t *val = new uint16_t[itr->req[s].len];
mListData[itr->addr] = new QList<CapData *>; mListData[itr->addr] = new QList<CapData *>;
mListData[itr->addr]->push_front(new CapData{ mListData[itr->addr]->push_front(new CapData{
itr->req[s].addr, itr->addr,
val, float(float(dat[0])),
itr->req[s].len,
QDateTime::currentDateTime().toString("yyyy-mm-dd hh:MM:ss"),
succ
}); });
} }
x += 3.1415*2 /256;
mMux.unlock(); mMux.unlock();
} }
modbus_close(mMod);
} }
qDebug()<<"keep alive"; qDebug()<<"keep alive";
QThread::msleep(100); QThread::msleep(100);
} }
modbus_close(mMod);
// 读取2 // 读取2
} }
@ -182,10 +128,9 @@ public:
} }
QMap<int,QList<CapData *>*> mListData; QMap<int,QList<CapData *>*> mListData;
QMutex mMux; QMutex mMux;
modbus_t* mMod = nullptr; modbus_t* mMod;
bool mRuning; bool mRuning;
QList<Config*> mConf; QList<Config*> mConf;
bool mPause = false;
}; };
extern ASyncReadData *gAsyncData; extern ASyncReadData *gAsyncData;

View File

@ -75,8 +75,7 @@ void MainWindow::on_pushButton_3_clicked()
} }
SubForm *p1 = new SubForm(ui->comboBox_2->currentText(), SubForm *p1 = new SubForm(ui->comboBox_2->currentText(),
ui->comboBox->currentText().toInt(), ui->comboBox->currentText().toInt(),
ui->lineEdit_2->text().toInt(), ui->lineEdit_2->text().toInt());
ui->lineEdit_3->text().toInt());
qDebug()<<ui->lineEdit_2->text(); qDebug()<<ui->lineEdit_2->text();
ui->tabWidget->addTab((QWidget*)p1,"address:" + ui->lineEdit_2->text()); ui->tabWidget->addTab((QWidget*)p1,"address:" + ui->lineEdit_2->text());
this->mForms[ui->lineEdit_2->text()] = p1; this->mForms[ui->lineEdit_2->text()] = p1;

View File

@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>又一个modbus调试工具</string> <string>modbus rtu monitor</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,9"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,9">
@ -33,7 +33,6 @@
<widget class="QLineEdit" name="lineEdit_2"> <widget class="QLineEdit" name="lineEdit_2">
<property name="font"> <property name="font">
<font> <font>
<family>Arial</family>
<pointsize>12</pointsize> <pointsize>12</pointsize>
</font> </font>
</property> </property>
@ -92,7 +91,7 @@
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>采样间隔(秒):</string> <string>采样间隔(秒):</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -1,27 +0,0 @@
#### 又一个modbus调试工具
最近混迹物联网企业,发现目前缺少一个简易可用的modbus调试工具,本软件旨在为开发者提供一个简单modbus测试工具。</br>
主打一个代码简单易修改。</br>
特点:
- 基于QT5
- 基于libmodbus
- 完全开源,代码量少
- 三方库完全跨平台,linux/windows。
#### 已实现功能:
当前仅实现寄存器监控功能。</br>
后期根据心情和捐款情况可能会逐步增加todo项功能。</br>
##### 寄存器监控功能
1. 返回值导出。
2. 单个寄存器绘图。
3. 消息报文记录。
[![image.png](https://www.testingcloud.club/sapi/api/image_download/203b900b-61fa-11ee-8ba6-525400797f57.png)](界面)
#### todo
1. 配置保存为json文件,不用每次都手动指定监控项。
2. 协议上目前仅支持RTU,增加对modbus-tcp和modbus-ascii的支持。

45
sub.ui
View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>602</width> <width>1394</width>
<height>381</height> <height>892</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -69,20 +69,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>暂停</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>继续</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -111,32 +97,7 @@
<widget class="QTableView" name="tableView"/> <widget class="QTableView" name="tableView"/>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <widget class="QtCharts::QChartView" name="graphicsView"/>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>绘图:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>记录</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QtCharts::QChartView" name="graphicsView"/>
</item>
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </item>

View File

@ -15,7 +15,7 @@
#endif #endif
SubForm::SubForm(QString addr,int baurate,int device_addr,int rate) SubForm::SubForm(QString addr,int baurate,int device_addr)
:ui(new Ui::Form) { :ui(new Ui::Form) {
ui->setupUi(this); ui->setupUi(this);
@ -24,17 +24,14 @@ SubForm::SubForm(QString addr,int baurate,int device_addr,int rate)
QString *com = new QString(addr); QString *com = new QString(addr);
qDebug()<<*com; qDebug()<<*com;
mModel = new QStandardItemModel(this); mModel = new QStandardItemModel(this);
mModel->setHorizontalHeaderLabels(QStringList() << "寄存器地址" << "返回值"<<"读取时间"<<"操作"); mModel->setHorizontalHeaderLabels(QStringList() << "寄存器地址" << "返回值"<<"读取时间");
ui->tableView->setModel(mModel); ui->tableView->setModel(mModel);
ui->tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
mConfig = new Config; mConfig = new Config;
mConfig->addr = device_addr; mConfig->addr = device_addr;
mConfig->com = *com; mConfig->com = *com;
mConfig->baurate = baurate;
mConfig->rate = rate;
connect(pTimer,&QTimer::timeout,[=]() { connect(pTimer,&QTimer::timeout,[=](){
static float sd = 0; static float sd = 0;
if(gAsyncData == nullptr) if(gAsyncData == nullptr)
return; return;
@ -42,76 +39,19 @@ SubForm::SubForm(QString addr,int baurate,int device_addr,int rate)
int ret = gAsyncData->TakeLast(device_addr,&z); int ret = gAsyncData->TakeLast(device_addr,&z);
if(ret == 0){ if(ret == 0){
bool succ = z->succ; mSeries1->append(sd,z->val1);
if(this->mLines.find(z->addr) != mLines.end()){
static int i = 0;
qDebug()<< mLines[z->addr]->count()<<"fsdf";
mLines[z->addr]->append( (mCapMax + 1),float(z->val1[0]));
mCapMax++;
}else{
mLines[z->addr] = new QLineSeries;
mChart->addSeries(mLines[z->addr]); //
mLines[z->addr]->attachAxis(mAxisX); //
mLines[z->addr]->attachAxis(mAxisY);
mLines[z->addr]->append( (mCapMax + 1),float(z->val1[0]));
mLines[z->addr]->setName(QString::asprintf("%d",z->addr));
mCapMax++;
}
// mSeries1->append(sd,z->val1);
pTime.append(QDateTime::currentDateTime().toString("yyyyMMdd-hh:mm:ss")); pTime.append(QDateTime::currentDateTime().toString("yyyyMMdd-hh:mm:ss"));
QStandardItem *found = nullptr; mModel->clear();
qDebug()<<"mModel->columnCount()"<<mModel->columnCount() mModel->setHorizontalHeaderLabels(QStringList() << "寄存器地址" << "返回值"<<"读取时间");
<<mModel->rowCount();
int i = 0;
int j = 0;
for(;i < mModel->rowCount();i++){
for(;j < mModel->columnCount();j++){
if(mModel->item((i,j)) != nullptr){
qDebug()<<mModel->item((i,j))->data(Qt::DisplayRole);
if(mModel->item((i,j))->data(Qt::DisplayRole).toInt() == z->addr){
found = mModel->item((i,j));
break;
}
}
}
}
if(found == 0){ QStandardItem *item = new QStandardItem(QString("%1").arg(z->val1));
QStandardItem *item; mModel->setItem(0, 1, item);
if(succ)
item = new QStandardItem(QString("%1").arg(z->val1[0]));
else
item = new QStandardItem(QString("超时"));
mModel->setItem(i, 1, item);
QStandardItem *item1 = new QStandardItem(QString("%1").arg(z->addr));
mModel->setItem(i, 0, item1);
mModel->setItem(i, 2, new QStandardItem(QString("%1").arg(z->time)));
QPushButton *button = new QPushButton(this);
ui->tableView->setIndexWidget(mModel->index(i,3),button);
button->setText("绘图");
connect(button,&QPushButton::clicked,[=](){
qDebug()<<i<<mModel->item(i,0)->data(Qt::DisplayRole).toInt();
if(this->mLines[mModel->item(i,0)->data(Qt::DisplayRole).toInt()]->isVisible()){
this->mLines[mModel->item(i,0)->data(Qt::DisplayRole).toInt()]->hide();
}else{
this->mLines[mModel->item(i,0)->data(Qt::DisplayRole).toInt()]->show();
}
});
}else{ QStandardItem *item1 = new QStandardItem(QString("%1").arg(z->addr));
mModel->item(found->row(),0)->setData(QString("%1").arg(z->addr),Qt::DisplayRole); mModel->setItem(0, 0, item1);
if(succ)
mModel->item(found->row(),1)->setData(QString("%1").arg(z->val1[0]),Qt::DisplayRole);
else
mModel->item(found->row(),1)->setData(QString("超时"),Qt::DisplayRole);
mModel->item(found->row(),2)->setData(QString("%1").arg(z->time),Qt::DisplayRole);
}
sd ++; sd ++;
@ -119,15 +59,13 @@ SubForm::SubForm(QString addr,int baurate,int device_addr,int rate)
mMaxX += 1024; mMaxX += 1024;
mAxisX->setMax(mMaxX); mAxisX->setMax(mMaxX);
} }
delete z;
} }
}); });
pTimer->start(10); pTimer->start(100);
qDebug()<<QDateTime::currentDateTime().toString(); qDebug()<<QDateTime::currentDateTime().toString();
ui->lineEdit_2->setText(addr); ui->lineEdit_2->setText(addr);
gAsyncData->AddConfig(this->mConfig); gAsyncData->AddConfig(this->mConfig);
ui->tableView->setColumnWidth(2,230);
connect(gAsyncData,SIGNAL(Info(QString)),this,SLOT( on_log(QString)));
} }
void SubForm::on_pushButton_clicked() { void SubForm::on_pushButton_clicked() {
@ -144,24 +82,19 @@ void SubForm::on_pushButton_clicked() {
return; return;
} }
QTextStream out(&file); QTextStream out(&file);
for(auto itr = mLines.begin();itr != mLines.end();itr++){
out << tr((QString::asprintf("%d",itr.key()) + ",").toStdString().c_str());
}
out <<tr("x value ")<<"\n";
// //
out << tr("x,") << tr("error,") << tr("speed,")<< tr("temperature,")<<
tr("input volate,")<<tr("input current,")<<"motor temperature"<<"\n";
for(int i = 0;i < p1.size();i++){
for(int i = 0;i < mCapMax;i++){ out <<pTime.at(i).toStdString().c_str()<< ","
for(auto itr = mLines.begin();itr != mLines.end();itr++){ << p1.at(i)<< "," ;
itr.value()->count(); out << p2.at(i)<< ",";
if((i + itr.value()->count()) > mCapMax){ out << p3.at(i)<< ",";
out <<tr(QString::asprintf("%.1f,", out << p4.at(i)<< ",";
itr.value()->at(i + itr.value()->count() - mCapMax).y()).toStdString().c_str()); out << p5.at(i)<< ",";
}else{ out << p6.at(i)<<"\n";
out <<tr("x value,");
}
}
out <<tr(QString::asprintf("%d,",i).toStdString().c_str())<<"\n";
} }
// //
file.close(); file.close();
@ -214,6 +147,24 @@ void SubForm::init_charts()
Qt::SolidPattern)); Qt::SolidPattern));
mAxisY->setTitleText("temperature (°C)"); mAxisY->setTitleText("temperature (°C)");
mAxisY2->setMin(0); //Y围
mAxisY2->setMax(100);
mAxisY2->setTickCount(11);
mAxisY2->setLabelFormat("%d");
mAxisY2->setLinePenColor(QColor(Qt::yellow)); //
mAxisY2->setGridLineColor(QColor(Qt::yellow));
mAxisY2->setGridLinePen(QPen(Qt::white,0.1,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
mAxisY2->setGridLineVisible(true);
mAxisY2->setLinePen(QPen(Qt::yellow,1,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
mAxisY2->setLabelsFont(QFont("",8,18,false));
mAxisY2->setLabelsBrush(QBrush(QColor(Qt::white),
Qt::SolidPattern));
mAxisY2->setLabelsColor(QColor(Qt::white));
mAxisY2->setTitleFont(QFont("",13,20,false));
mAxisY2->setTitleBrush(QBrush(QColor(Qt::white),
Qt::SolidPattern));
mAxisY2->setTitleText("wet %");
mChart->addAxis(mAxisX,Qt::AlignBottom); //λmChartеλ mChart->addAxis(mAxisX,Qt::AlignBottom); //λmChartеλ
mChart->addAxis(mAxisY,Qt::AlignLeft); mChart->addAxis(mAxisY,Qt::AlignLeft);
mChart->addAxis(mAxisY2,Qt::AlignRight); mChart->addAxis(mAxisY2,Qt::AlignRight);
@ -239,6 +190,7 @@ void SubForm::init_charts()
mChart->legend()->setFont(font);//б mChart->legend()->setFont(font);//б
font.setPointSizeF(12); font.setPointSizeF(12);
mChart->legend()->setFont(font);//С mChart->legend()->setFont(font);//С
mChart->legend()->setFont(QFont(""));//
mChart->legend()->setLabelColor(Qt::white); // mChart->legend()->setLabelColor(Qt::white); //
} }
@ -257,24 +209,6 @@ void SubForm::on_pushButton_2_clicked() {
mConfig->req.append(Req{ mConfig->req.append(Req{
ui->lineEdit->text().toUShort(), ui->lineEdit->text().toUShort(),
ui->lineEdit_3->text().toUShort(), ui->lineEdit_3->text().toUShort(),
}); });
}
void SubForm::on_log(QString info)
{
qDebug()<<info;
ui->textEdit->append(info + "\r\n");
}
void SubForm::on_pushButton_3_clicked()
{
gAsyncData->Pause();
}
void SubForm::on_pushButton_4_clicked()
{
gAsyncData->Continue();
} }

View File

@ -22,11 +22,12 @@ QT_END_NAMESPACE
using namespace QtCharts; using namespace QtCharts;
class SubForm :QWidget class SubForm :QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
SubForm(QString,int baurate,int device_addr,int rate); SubForm(QString,int baurate,int device_addr);
void init_charts(); void init_charts();
private slots: private slots:
@ -36,12 +37,6 @@ private slots:
void on_pushButton_2_clicked(); void on_pushButton_2_clicked();
void on_log(QString);
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private: private:
Ui::Form *ui; Ui::Form *ui;
QChart *mChart; QChart *mChart;
@ -51,15 +46,27 @@ private:
int baurate; int baurate;
QLineSeries *mSeries1; QLineSeries *mSeries1;
QMap<uint16_t,QLineSeries*> mLines; QLineSeries *mSeries2;
QLineSeries *mSeries3;
QLineSeries *mSeries4;
QLineSeries *mSeries5;
QLineSeries *mSeries6;
QVector<QString> pTime; QVector<QString> pTime;
QVector<float> p1;
QVector<float> p2;
QVector<float> p3;
QVector<float> p4;
QVector<float> p5;
QVector<float> p6;
Config *mConfig; Config *mConfig;
float mMaxX; float mMaxX;
QDateTime mStartTime; QDateTime mStartTime;
bool mStart; bool mStart;
QStandardItemModel *mModel; QStandardItemModel *mModel;
int mCapMax = 0;
}; };

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 5.0.2, 2023-10-05T00:37:49. --> <!-- Written by QtCreator 5.0.2, 2023-09-27T20:46:07. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>
@ -80,9 +80,6 @@
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/> <valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value> <value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap> </valuemap>
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
<value type="bool" key="UseGlobalSettings">true</value>
</valuemap>
</valuemap> </valuemap>
</data> </data>
<data> <data>