前言
我是按照博主lucky-billy 的 Qt中使用TCP和MC协议与三菱Q系列PLC通信
实现了Qt中使用TCP和MC协议与三菱Q系列PLC通信 。该博主把Qt使用TCP和MC协议与三菱Q系列PLC通信的理论部分讲得很好,但贴出来的代码太少了,对初学者可能不太友好。我这篇博客的侧重的是把代码写出来,因此在我看这篇博客之前,强烈建议大家先把lucky-billy博主的Qt中使用TCP和MC协议与三菱Q系列PLC通信这篇博客看完。
实操
看了 Qt中使用TCP和MC协议与三菱Q系列PLC通信
之后,我们知道报文格式是这样的
通信示例:
从地址 D100 开始依次写入 3 个软元件的值:
50 00 00 FF FF 03 00 12 00 10 00 01 14 00 00 64 00 00 A8 03 00 95 19 02 12 30 11
D0 00 00 FF FF 03 00 02 00 00 00
读取地址 D100 开始连续 5 个软元件的值:
50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 64 00 00 A8 05 00
D0 00 00 FF FF 03 00 0C 00 00 00 95 19 02 12 30 11 00 00 00 00
我们只需按照协议要求,向plc发送指令就能实现plc通信
配置
这是 .pro 文件的内容
切记要加上 network,如果没有下载该组件需要使用 MaintenanceTool.exe 进行下载
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
Utils.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
res.qrc
代码
这个是程序的ui代码:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>938</width>
<height>654</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_6" rowstretch="1,1,3,3,4">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="styleSheet">
<string notr="true">ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}"); </string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>IP Address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="ip">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="port">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="connect">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>connect</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="disconnect">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>disconnect</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="styleSheet">
<string notr="true">ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}"); </string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>soft component:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="soft">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<item>
<property name="text">
<string>D</string>
</property>
</item>
<item>
<property name="text">
<string>M</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>First Address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="address">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Length:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="length">
<property name="sizeIncrement">
<size>
<width>996</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_4">
<property name="styleSheet">
<string notr="true">ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}"); </string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Write Data:</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>11</width>
<height>41</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="writebtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>write</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="writeEdit">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="styleSheet">
<string notr="true">ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}"); </string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Read Data:</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="readbtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Read</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="readEdit">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="styleSheet">
<string notr="true">ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}"); </string>
</property>
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>log:</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>158</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="clearPtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>clear</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="log">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
运行起来的效果是这样的:
我创建了一个工具类,待会运行时会用得到
#include <QApplication>
#include <QRegularExpression>
class Utils{
public:
static QString addSpacesToHex(const QByteArray info) {
QString hexStr = info.toHex().toUpper();
QString result;
for (int i = 0; i < hexStr.length(); i += 2) {
// 检查是否到达了字符串的末尾,并且不是偶数长度的字符串(即可能只剩下一个字符)
if (i + 1 < hexStr.length()) {
// 添加两个字符和一个空格
result.append(hexStr.mid(i, 2)).append(" ");
} else {
// 如果是奇数长度的字符串,直接添加最后一个字符(不添加空格)
result.append(hexStr.mid(i));
}
}
return result.trimmed(); // 如果不想在字符串末尾有额外的空格,可以使用trimmed()去除
}
// 例子: (15,4) --》 10 00 (1234,4) --》 D2 04
static QString convertToIntHexWithSpacesFlip(int num, int bit)
{
// 首先将整数转换为十六进制,并转换为大写
QString hexStr = QString::number(num, 16).toUpper();
// 如果转换后的字符串长度小于指定的位数,则前面补零
QString formattedStr = hexStr.rightJustified(bit, '0');
QString result;
if(bit == 4){
result.append(formattedStr.mid(2,2)).append(" ").append(formattedStr.mid(0,2));
}else if(bit == 6){
result.append(formattedStr.mid(4,2)).append(" ").append(formattedStr.mid(2,2)).append(" ").append(formattedStr.mid(0,2));
}else{
qDebug() << "bit 数值错误" << Qt::endl;
}
return result;
}
static int convertToIntDecimalFlip(QString hexStr){
int result;
QString str;
bool k;
if(hexStr.length() == 4){
str.append(hexStr.mid(2,2)).append(hexStr.mid(0,2));
result = str.toInt(&k,16);
if(!k){
qDebug() <<str<<"无法转化"<< Qt::endl;
}
}else if(hexStr.length() == 6){
str.append(hexStr.mid(4,2)).append(hexStr.mid(2,2)).append(hexStr.mid(0,2));
result = str.toInt(&k,16);
if(!k){
qDebug() <<str<<"无法转化"<< Qt::endl;
}
}else{
qDebug() << "convertToIntDecimalFlip 出错!" << Qt::endl;
result = -1;
}
return result;
}
static bool formattedCorrectly(QStringList writeData,QString address){
QRegularExpression re("^[\\d\\s]+$");
if(writeData.length() > 0){
for(QString item:writeData){
if(!re.match(item).hasMatch()){
qDebug() << "writeData内的元素:{"+item+"}包含除数字和空格之外的字符,无法发送" << Qt::endl;
return false;
}
if(item.toInt() > 65535){
qDebug() << "writeData内的元素:{"+item+"}大于发送数据的上限,无法发送" << Qt::endl;
return false;
}
}
}
if (address.length() > 0 && !re.match(address).hasMatch()) {
qDebug() << "address:{"+address+"}包含除数字和空格之外的字符,无法发送" << Qt::endl;
return false;
}
int startAddress = address.toInt();
if(startAddress > 16777215){
qDebug() << "起始位置{"+address+"}超出了范围上限,无法发送";
return false;
}
return true;
}
};
然后就是 mainwindows.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
class QTcpSocket;
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
QTcpSocket *network;
int connectionTimeout = 5000; // 连接超时时间,单位毫秒
int sendTimeout = 5000; // 发送超时时间,单位毫秒
QString currentTime;
QString head = "50 00 00 FF FF 03 00";
QString timeoutStr = "10 00";
QString writeCommand = "01 14";
QString readCommand = "01 04";
QString subCommand = "00 00";
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_clearPtn_clicked();
void on_writebtn_clicked();
void on_disconnect_clicked();
void on_readbtn_clicked();
void on_connect_clicked();
void on_ip_textChanged(const QString &arg1);
void on_port_textChanged(const QString &arg1);
void readResponseData();
private:
Ui::MainWindow *ui;
void Recode_logs(QByteArray info, QString type);
void setState(bool state);
// test
QByteArray CreateData_TEST();
void ReadData_TEST();
void Response_Test(QByteArray);
};
#endif // MAINWINDOW_H
mainwindows.cpp 文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "Utils.cpp"
#include <QDebug>
#include <QTcpSocket>
#include <QDateTime>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
network = new QTcpSocket(this);
connect(network, &QTcpSocket::connected, [](){ qDebug() << "Connected to PLC successfully !" << Qt::endl; });
connect(network, &QTcpSocket::disconnected, [](){ qDebug() << "Disconnected from plc !" << Qt::endl; });
connect(network, &QTcpSocket::stateChanged, [](QAbstractSocket::SocketState socketState){
qDebug() << "SocketState changed: " << socketState;
});
connect(network, &QTcpSocket::readyRead, this, &MainWindow::readResponseData);
//界面初始化
ui->groupBox-> setStyleSheet ("QGroupBox {border: 0;}");
ui->groupBox_2-> setStyleSheet ("QGroupBox {border: 0;}");
ui->groupBox_3-> setStyleSheet ("QGroupBox {border: 0;}");
ui->groupBox_4-> setStyleSheet ("QGroupBox {border: 0;}");
ui->groupBox_5-> setStyleSheet ("QGroupBox {border: 0;}");
ui->address->setPlaceholderText("0");
ui->writeEdit->setPlaceholderText("00 00");
ui->log->setReadOnly(true);
ui->readEdit->setReadOnly(true);
ui->length->setReadOnly(false);
setState(false);
}
void MainWindow::readResponseData()
{
QByteArray response = network->readAll();
Recode_logs(response,"Response");
ui->log->appendPlainText("");
// 从 response里读取数据,并上传到readEdit
QString hexStr = response.toHex().toUpper().trimmed();
if(hexStr.startsWith("D000")){
/*
* 例子:
* "D0 00 00 FF FF 03 00 0B 00 51 C0 00 FF FF 03 00 01 04 00 00"
* 51 C0 错误信息 访问地址:00 FF FF 03 00 01 04 指令 00 00 子指令
*/
if(hexStr.mid(18,4) == "51C0"){
qDebug() << "错误信息" << Qt::endl;
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("错误!\n发生错误的地址:" + hexStr.mid(22,10) + " ,发生错误的指令:" + hexStr.mid(32,4) + "\n发生错误的子指令:" + hexStr.mid(36,4));
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
}
int point = 14;
QString tmp = hexStr.mid(point,4);
int count = (Utils::convertToIntDecimalFlip(tmp) - 2)/2;
if(count > 0){
point += 8;
QString str;
int data;
for(int i = 0;i < count;i++){
data = Utils::convertToIntDecimalFlip(hexStr.mid(point,4));
point+=4;
str.append(QString::number(data)).append(" ");
}
if(str.length() > 0){
str = str.left(str.length() - 1);
}
ui->readEdit->appendPlainText(str);
ui->readEdit->appendPlainText("");
}
}else{
qDebug() << "响应错误" << Qt::endl;
}
}
void MainWindow::setState(bool state){
ui->readEdit->setEnabled(state);
ui->writeEdit->setEnabled(state);
ui->length->setEnabled(state);
ui->address->setEnabled(state);
ui->writebtn->setEnabled(state);
ui->readbtn->setEnabled(state);
ui->soft->setEnabled(state);
ui->disconnect->setEnabled(state);
}
void MainWindow::Recode_logs(QByteArray info, QString type)
{
currentTime = QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]");
QString str = Utils::addSpacesToHex(info);
QString log = currentTime + "[" + type +"]" + str;
ui->log->appendPlainText(log);
}
void MainWindow::on_ip_textChanged(const QString &arg1)
{
Q_UNUSED(arg1);
setState(false);
}
void MainWindow::on_port_textChanged(const QString &arg1)
{
Q_UNUSED(arg1);
setState(false);
}
void MainWindow::on_connect_clicked()
{
// 连接到主机
network->connectToHost(ui->ip->text(), ui->port->text().toInt());
qDebug() << "....";
if (network->waitForConnected(connectionTimeout)) {
qDebug() << "连接成功";
setState(true);
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("成功连接");
info.setIcon(QMessageBox::Information);
// 添加额外的按钮
info.addButton("确定", QMessageBox::AcceptRole);
// 显示对话框
info.exec();
}else{
qDebug() << "连接失败";
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("连接失败");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
disconnect();
setState(false);
}
}
void MainWindow::on_disconnect_clicked()
{
if (network->state() == QAbstractSocket::ConnectedState) {
network->disconnectFromHost(); // 正确地断开网络连接
qDebug() << "成功断开连接" << Qt::endl;
setState(false);
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("成功断开连接");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
}
}
void MainWindow::on_writebtn_clicked()
{
QString address = ui->address->text();
QString soft = ui->soft->currentIndex() == 0 ? "A8" : "90";
QStringList writeData = ui->writeEdit->toPlainText().split(" ", Qt::SkipEmptyParts);
if(writeData.length() > ui->length->value()){
qDebug() << "要写的内容大于length,无法发送";
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("要传输的内容大于 Length,无法发送,请调整write内容或length");
info.setIcon(QMessageBox::Information);
// 添加额外的按钮
info.addButton("确定", QMessageBox::AcceptRole);
// 显示对话框
info.exec();
return;
}
bool checkResult = Utils::formattedCorrectly(writeData,address);
if(!checkResult){
qDebug() << "format 错误" << Qt::endl;
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("格式错误,请检查你的数据内容和起始软元件");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
return;
}
// 将10进制转为16进制 翻转
QString count = Utils::convertToIntHexWithSpacesFlip(ui->length->text().toInt(), 4);
address = Utils::convertToIntHexWithSpacesFlip(address.toInt(),6);
QString content;
for(QString item : writeData) {
content.append(Utils::convertToIntHexWithSpacesFlip(item.toInt(), 4)).append(" ");
}
int diffrence = ui->length->value() - writeData.length();
while(diffrence > 0){
content.append("00 00 ");
diffrence--;
}
if (content.length() > 0) {
content.remove(content.length() - 1, 1);
}
QString data = timeoutStr + " " + writeCommand + " " + subCommand + " " + address + " " + soft + " " + count + " " + content;
QString temp = data;
temp.replace(" ", "");
int len = temp.length()/2;
QString lengthStr = Utils::convertToIntHexWithSpacesFlip(len, 4);
// 组装最终要发送的数据
QString result = head + " " + lengthStr + " " + data;
// 转换为 QByteArray 并发送
QByteArray array = QByteArray::fromHex(result.toLatin1());
network->write(array);
// 等待数据被发送
if (!network->waitForBytesWritten(sendTimeout)) {
on_disconnect_clicked();
qDebug() << "发送数据超时,断开连接";
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("发送数据超时,已断开连接");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
}
Recode_logs(array,"Write");
}
void MainWindow::on_readbtn_clicked()
{
QString address = ui->address->text();
if(ui->length->value() == 0){
qDebug() << "length长度为0,无法发送";
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("length长度为0,无法发送\n请调整length的大小");
info.setIcon(QMessageBox::Information);
// 添加额外的按钮
info.addButton("确定", QMessageBox::AcceptRole);
// 显示对话框
info.exec();
return;
}
bool checkResult = Utils::formattedCorrectly(QStringList(),address);
if(!checkResult){
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("格式错误,请检查你的起始软元件");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
return;
}
QString soft = ui->soft->currentIndex() == 0 ? "A8" : "90";
// 将10进制转为16进制 翻转
QString count = Utils::convertToIntHexWithSpacesFlip(ui->length->value(), 4);
address = Utils::convertToIntHexWithSpacesFlip(address.toInt(),6);
QString data = timeoutStr + " " + readCommand + " " + subCommand + " " + address + " " + soft + " " + count;
QString temp = data;
temp.replace(" ", "");
int len = temp.length()/2;
QString lengthStr = Utils::convertToIntHexWithSpacesFlip(len, 4);
// 组装最终要发送的数据
QString result = head + " " + lengthStr +" " + data;
// 转换为 QByteArray 并发送
QByteArray array = QByteArray::fromHex(result.toLatin1());
network->write(array);
// 等待数据被发送
if (!network->waitForBytesWritten(sendTimeout)) {
qDebug() << "发送数据超时,断开连接";
QMessageBox info(this);
info.setWindowTitle("info");
info.setText("发送数据超时,断开连接");
info.setIcon(QMessageBox::Information);
info.addButton("确定", QMessageBox::AcceptRole);
info.exec();
disconnect();
}
Recode_logs(array,"Read");
}
void MainWindow::on_clearPtn_clicked()
{
ui->log->clear();
}
MainWindow::~MainWindow()
{
delete ui;
}
运行
启动程序,只需要将plc对应的ip和port输入,然后点击connect 按钮。如果没有连接出错就可以实现plc的通信了。
1. 写操作
我们想要向plc写入内容时,需要将想写入的内容写在WriteData中并且每个数之间用空格隔开,Length也需要自己手动输入,如果length大于WriteData里面的内容则会在输入内容的后面补零,如果length小于WriteData里面的内容则不会发送出指令,会弹出报错窗口,First Address代表输入的起始位置
2.读操作
读操作我们只需要确认我们想要读取数据的First Address(起始地址)和想要读取的Length(长度),点击read按钮,就能进行读取
读取的内容会打印在Read Data上,以十进制的方式展示
log 会打印 读写操作 的请求报文和响应报文,我们可以通过返回的响应报文判断命令是否执行成功,如果执行失败,我们可以查看文档,找到错误的原因
出错代码:
点击clear按钮可以清除log里的内容
总结
要想实现Qt中使用TCP和MC协议与三菱Q系列PLC通信,需要先知道它们之间的通信报文格式
按照格式,将报文拼接起来,实现对plc的通信。
如果不理解它的通信格式,可以看这一篇博客 Qt中使用TCP和MC协议与三菱Q系列PLC通信