Qt 通过ADS实现倍福TwinCAT通信

发布时间 2023-08-04 08:54:23作者: 一杯清酒邀明月

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);