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