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