首页 > 其他分享 >Qt 通过ADS实现倍福TwinCAT通信

Qt 通过ADS实现倍福TwinCAT通信

时间:2023-08-04 09:13:49浏览次数:48  
标签:Qt 倍福 nErr pAddr TwinCAT qDebug ui line ADS

ADS通信分为两种:同步方式和异步方式。

同步方式
  ADS 客户端向ADS 服务器发送ADS 请求,在通信过程中客户端程序停止执行,直到获得ADS 服务器返回的响应
  又可分为变量名方式和地址方式

异步方式
  ADS 客户端向ADS 服务器发送ADS 请求,同时客户端继续自己的工作。ADS 服务器处理请求后,把响应以Call-back 函数方式发给客户端。

变量名方式
  在TwinCAT PLC程序中每个变量都有一个句柄(Handle)。在对变量进行操作之前,首先我们要通过相关路径得到变量的句柄,然后进行读写操作,操作完毕后对句柄进行释放。

地址方式
  在TwinCAT PLC中一个变量的地址由两部分组成,即GroupIndex和OffsetIndex, GroupIndex为该变量所在的寄存器类型,为一常量; OffsetIndex为该变量在寄存器中得地址偏移量,为一变量。
PLC 变量地址与ADS 地址之间的对应关系:

 可以在项目中查看偏移地址:

基本配置

在.pro文件中添加头文件路径和lib文件

1 INCLUDEPATH += $$PWD/include
2 LIBS    += $$PWD/lib/TcAdsDll.lib

在.h中添加头文件

1 #include <Windows.h>
2 #include "TcAdsDef.h"
3 #include "TcAdsAPI.h"

声明全局变量

1     AmsAddr  Addr;//定义AMS地址变量
2     PAmsAddr pAddr;//定义端口地址变量
3     long nErr;
4     USHORT  nAdsState;    //PLC状态信息
5     USHORT  nDeviceState;

打开/关闭ADS通信

 1     long nPort;
 2     pAddr = &Addr;
 3 
 4     nPort = AdsPortOpen();//打开ADS通信端口
 5     nErr = AdsGetLocalAddress(pAddr);//自动获取本地地址
 6     if (nErr)
 7     {
 8         QMessageBox::about(nullptr, "Warning", QString("Error: AdsGetLocalAddress: "));
 9     }
10     else
11     {
12         qDebug()<<"AdsPortOpen Successfully" << '\n';
13     }
14     pAddr->port = 851;//TC3通信使用的为851端口
1     //关闭端口通信
2     nErr = AdsPortClose();
3     if (nErr)
4     {
5         qDebug()<< "Error: AdsPortClose: " << nErr << '\n';
6     }

读取/控制PLC状态

 1     //向PLC读取PLC的状态信息
 2     nErr = AdsSyncReadStateReq(pAddr, &nAdsState, &nDeviceState);
 3     if (nErr)
 4     {
 5         qDebug()<<"Error: AdsSyncReadStateReq: " << nErr << '\n';
 6     }
 7     else
 8     {
 9         qDebug()<<"PLCState: " << nAdsState << '\n'; // 输出PLC状态信息
10     }
1     nAdsState = ADSSTATE_RUN;
2     void *pData = nullptr;
3     nErr = AdsSyncWriteControlReq(pAddr, nAdsState, nDeviceState, 0, pData);
4     if (nErr)
5     {
6         qDebug() << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
7     }
1     nAdsState = ADSSTATE_STOP;
2     void *pData = nullptr;
3     nErr = AdsSyncWriteControlReq(pAddr, nAdsState, nDeviceState, 0, pData);
4     if (nErr)
5     {
6         qDebug() << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
7     }

读/写Bool量

 1     bool BOOL1;        //定义布尔量
 2     nErr = AdsSyncReadReq(pAddr, 0x4020, 0x0, 0x1, &BOOL1); //从ADS服务器同步读取数据,pAddr:ADS设备的地址,0x4020:段地址,0x0偏移地址,0x1:数据长度,&BOOL1:接收数据的缓存
 3     if (nErr)
 4     {
 5         qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
 6     }
 7     else
 8     {
 9         if(BOOL1==true)
10         {
11             ui->lblReadRes->setText("1");
12         }
13         else
14         {
15             ui->lblReadRes->setText("0");
16         }
17     }
1     bool BOOL1;        //定义布尔量
2     BOOL1=ui->line_Write->text().toInt();
3     nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x0, 0x1, &BOOL1 ); //同步写数据到ADS设备,pAddr:ADS设备的地址,0x4020:段地址,0x0偏移地址,0x1:数据长度,@BOOL1:接收数据的缓存
4     if (nErr)
5     {
6         qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
7     }

读/写Int、float等变量

以Int数据类型为例:

 1     int INT1;            //定义整型量
 2     nErr = AdsSyncReadReq(pAddr, 0x4020, 0x8, 0x4, &INT1);
 3     if (nErr)
 4     {
 5         qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
 6     }
 7     else
 8     {
 9         ui->lblReadRes->setText(QString::number(INT1,10));
10     }
1     //向PLC写入整型量
2     int INT1;
3     INT1=ui->line_Write->text().toInt();
4     nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x8, 0x4, &INT1);
5     if (nErr)
6     {
7         qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n';
8     }

读/写String量

 1     unsigned long lHdlVar;    //创建句柄
 2     char String[]={"MAIN.string_test"};    //定义字符串
 3     char szVar []={"MAIN.string_test"};
 4 
 5     // 同步写数据到ADS服务器并从ADS设备接收数据,pAddr:ADS设备的地址 0x0:偏移地址 sizeof(lHdlVar):由ADS设备返回的句柄大小 &lHdlVar:由ADS设备返回的数据缓存 sizeof(szVar):写入ADS设备的数据大小 szVar:写入ADS设备的数据缓存
 6     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
 7     if (nErr)
 8     {
 9         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
10     }
11     nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(String), &String); //从ADS服务器同步读取数据
12     if (nErr)
13     {
14       qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
15     }
16     else
17     {
18         ui->lblReadRes->setText(String);
19     }
 1     //向PLC写入字符串
 2     unsigned long lHdlVar;    //创建句柄
 3 
 4     QByteArray ba;
 5     char* temp;
 6     char String[10];
 7     QString str = ui->line_Write->text();
 8     ba = str.toLatin1();
 9     temp = ba.data();
10     strncpy_s(String,temp,10);
11 
12     char szVar []={"MAIN.string_test"};
13 
14     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
15     if (nErr)
16     {
17         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
18     }
19     nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(String), &String); //同步写数据到ADS设备
20     if (nErr)
21     {
22         qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
23     }

读/写数组

 1     unsigned long lHdlVar2;       //创建句柄
 2     short Array[5];    //定义数组
 3     char szVar2[]={"MAIN.Array1"};
 4 
 5     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar2), &lHdlVar2, sizeof(szVar2), szVar2);
 6     if (nErr)
 7     {
 8         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
 9     }
10     nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar2, sizeof(Array), & Array[0]);
11     if (nErr)
12     {
13         qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
14     }
15     else
16     {
17         ui->line_ArrayRead_1->setText(QString("%1").arg(Array[0]));
18         ui->line_ArrayRead_2->setText(QString("%1").arg(Array[1]));
19         ui->line_ArrayRead_3->setText(QString("%1").arg(Array[2]));
20         ui->line_ArrayRead_4->setText(QString("%1").arg(Array[3]));
21         ui->line_ArrayRead_5->setText(QString("%1").arg(Array[4]));
22     }
 1     unsigned long lHdlVar2;       //创建句柄
 2     short Array[5];    //定义数组
 3     char szVar2[]={"MAIN.Array1"};
 4 
 5     Array[0] = ui->line_ArrayWrite_1->text().toShort();
 6     Array[1] = ui->line_ArrayWrite_2->text().toShort();
 7     Array[2] = ui->line_ArrayWrite_3->text().toShort();
 8     Array[3] = ui->line_ArrayWrite_4->text().toShort();
 9     Array[4] = ui->line_ArrayWrite_5->text().toShort();
10 
11     //得到Array1的句柄
12     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar2), &lHdlVar2, sizeof(szVar2), szVar2);
13     if (nErr)
14     {
15         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
16     }
17     //通过句柄向PLC写入数组
18     nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar2, sizeof(Array), & Array[0]);
19     if (nErr)
20     {
21         qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
22     }

读/写结构体

 1     struct PlcVarstruct            //定义结构体
 2     {
 3         int intVal;            //整型
 4         float floatVal;            //浮点型
 5         bool  boolVal;            //布尔型
 6     }PlcVar;
 7     unsigned long lHdlVar3;
 8     char szVar3[] = { "MAIN.PLCVar" };
 9 
10     //从PLC中读取结构体
11     //获取结构体的句柄
12     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar3), &lHdlVar3, sizeof(szVar3), szVar3);
13     if (nErr)
14     {
15         qDebug() << "Test:Error: AdsSyncReadWriteReq: " << nErr << '\n';
16     }
17     //通过句柄获取所需结构体的数值
18     nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar3, sizeof(PlcVar), &PlcVar);
19     if (nErr)
20     {
21         qDebug() << "Test:Error: AdsSyncReadReq: " << nErr << '\n';
22     }
23     //输出结构体中各个变量的数值
24     ui->line_StructRead_1->setText(QString::number(PlcVar.intVal,10));
25     ui->line_StructRead_2->setText(QString("%1").arg(PlcVar.floatVal));
26     if(PlcVar.boolVal == true)
27     {
28         ui->line_StructRead_3->setText("1");
29     }
30     else
31     {
32         ui->line_StructRead_3->setText("0");
33     }
 1     //向PLC写入结构体
 2     //输入结构体的数值
 3     struct PlcVarstruct            //定义结构体
 4     {
 5         int intVal;            //整型
 6         float floatVal;            //浮点型
 7         bool  boolVal;            //布尔型
 8     }PlcVar;
 9     unsigned long lHdlVar3;
10     char szVar3[] = { "MAIN.PLCVar" };
11 
12     PlcVar.intVal = ui->line_StructWrite_1->text().toInt();
13     PlcVar.floatVal = ui->line_StructWrite_2->text().toFloat();
14     if(ui->line_StructWrite_3->text().trimmed() == "1")
15     {
16         PlcVar.boolVal = true;
17     }
18     else
19     {
20         PlcVar.boolVal = false;
21     }
22 
23     //得到PlcVar的句柄
24     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar3), &lHdlVar3, sizeof(szVar3), &szVar3);
25     if (nErr)
26     {
27         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
28     }
29     //通过之前获取的句柄向PLC写入结构体
30     AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar3, sizeof(PlcVar), &PlcVar);
31     if (nErr)
32     {
33         qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n';
34     }

事件通知

 1 void MainWindow::on_pushButton_NotifyOpen_clicked()
 2 {
 3     ULONG                  hNotification, hUser;
 4     AdsNotificationAttrib  adsNotificationAttrib;
 5     char                   szVar []={"MAIN.Notify"};
 6 
 7     // set the attributes of the notification
 8     adsNotificationAttrib.cbLength = 4;
 9     adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
10     adsNotificationAttrib.nMaxDelay = 0;
11     adsNotificationAttrib.nCycleTime = 10000000; // 1sec
12 
13     // get handle
14     nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(hUser), &hUser, sizeof(szVar), szVar);
15     if (nErr)
16     {
17         qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
18     }
19 
20     // initiate the transmission of the PLC-variable
21     nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VALBYHND, hUser, &adsNotificationAttrib, Callback, hUser, &hNotification);
22     if (nErr)
23     {
24         qDebug() << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n'<<endl;
25     }
26 }
27 
28 
29 // Callback-function
30 void __stdcall CALLBACK MainWindow::Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
31 {
32     Q_UNUSED(pAddr);
33     Q_UNUSED(hUser);
34 
35     unsigned char *ch=pNotification->data;//疑问:数据类型为unsigned char,只能到 0~255
36     qDebug()<< static_cast<unsigned long>(*ch);
37     qDebug()<< pNotification->hNotification<<'\n';
38 
39     //char *str1 = (char *)(ch);
40     main->ui->line_Notify->setText(QString::number(*ch,10));
41 }
static void __stdcall CALLBACK Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser);

 

标签:Qt,倍福,nErr,pAddr,TwinCAT,qDebug,ui,line,ADS
From: https://www.cnblogs.com/ybqjymy/p/17604969.html

相关文章

  • Qt 调用倍福TwinCAT通讯模块(TcAdsDll)
    Qt实现TwinCAT通讯目前这种方式是通过调用TwinCAT提供的AdsApi与倍福PLC通讯的。要求本机安装TwinCAT(无需作为主机,但是可能这个api依赖TwinCAT的一些服务)。关于AdsApi的官方资料请看这里,有函数的详细解释,还有例子。你值得拥有。https://infosys.beckhoff.com/english.php?conte......
  • Qt TwinCAT3中的变量回调函数的时间戳读取方式
    官网提供了例程,官网真是个宝库。基本ADS的操作都里面有例程了,但是可能会稍微分散一点,不过多看几遍,也就慢慢整理你所需要的东西出来了。https://infosys.beckhoff.com/index_en.htm1#include<Windows.h>2#include<conio.h>3#include<winbase.h>45#include<TcA......
  • Qt ADS中通过变量名访问变量的值
    在倍福提供的TwinCATADS的库的C(C++)接口中,有两种方式可以访问到PLC中的变量:根据地址访问、根据变量名访问。根据地址来访问的代码如下:1...2QStringhostNetId="192.168.12.51.1.1";3inthostPort=851;4AmsAddrtargetAddr=createAddr(hostNetId,hostPort);//这个......
  • TwinCAT隐藏开机画面
    当我们使用Ads和PLC通讯时,需要在我们的工控机上安装TwinCat3,用于搭建ads环境。当我们通过TwinCat的相应工具把PLC添加进来设备列表并且成功通讯之后,其实在下次启动工控机时,可以不启动TwinCat的UI界面,一样可以正常通过Ads和PLC通讯。(可能是只需要启动TwinCAT的一些服务就行,服务......
  • pycharm配置QtDesigner
    一、安装需要的库pipinstallpyqt5pipinstallpyqt5-tools 二、配置QtDesigner 配置 QtDesignerName:QtDesignerGroup:QtProgram:C:\python\venv\Lib\site-packages\qt5_applications\Qt\bin\designer.exeWorkingdirectory:$ProjectFileDir$ 配置 PyUICNa......
  • iTOP-STM32MP157开发板一键烧写 QT 程序到开发板
    1根据上一小节设置好编译套件后,打开自己的qt工程,然后点击qtcreator里面的项目,把编译器切换成上一章节设置好的的编译器,如下图所示:2然后打开要编译的QT代码的pro文件,在里面添加以下代码,这俩行代码的意思是说把编译的可执行程序下载到开发板的/opt目录下并执行。target.pa......
  • MQTT:轻量级消息传输协议在物联网中的应用
    随着物联网技术的发展,越来越多的设备需要进行实时通信和数据交换。在这样的背景下,MQTT(MessageQueuingTelemetryTransport)作为一种轻量级的消息传输协议,逐渐成为物联网领域的热门选择。本文将介绍MQTT协议的基本概念、特点以及在物联网中的应用,同时通过代码实例演示如何使用MQTT......
  • python使用mqtt
    一、安装mqtt服务器安装对应的软件:https://www.emqx.io/zh/downloads推荐使用docker安装默认账号和密码:admin、public 二、编写代码消息发布程序importtimeimportjsonimportpsutilimportrandomfrompaho.mqttimportclientasmqtt_clientbroker='127.0.0.1......
  • Qt+GDAL开发笔记(二):在windows系统msvc207x64编译GDAL库、搭建开发环境和基础Demo
    前言  上一篇使用mingw32版本的gdal,过程曲折,为更好的更方便搭建环境,在windows上msvc方式对于库比较友好。<br>大地坐标简介概述  大地坐标(Geodeticcoordinate)是大地测量中以参考椭球面为基准面的坐标,地面点P的位置用大地经度L、大地纬度B和大地高H表示。原理  当点在......
  • iTOP-RK3588开发板Ubuntu 系统交叉编译 Qt 工程-命令行交叉编译
    使用源码rk3588_linux/buildroot/output/rockchip_rk3588/host/bin/qmake交叉编译QT工程。最后烧写编译好的buildroot镜像,将编译好的QT工程可执行程序在buildroot系统上运行。交叉编译QT工程如下所示,首先进入QLed的工程目录下。然后使用以下命令交叉编译QT工程,如下......