ADS通信分为两种:同步方式和异步方式。
ADS 客户端向ADS 服务器发送ADS 请求,在通信过程中客户端程序停止执行,直到获得ADS 服务器返回的响应
又可分为变量名方式和地址方式
ADS 客户端向ADS 服务器发送ADS 请求,同时客户端继续自己的工作。ADS 服务器处理请求后,把响应以Call-back 函数方式发给客户端。
在TwinCAT PLC程序中每个变量都有一个句柄(Handle)。在对变量进行操作之前,首先我们要通过相关路径得到变量的句柄,然后进行读写操作,操作完毕后对句柄进行释放。
在TwinCAT PLC中一个变量的地址由两部分组成,即GroupIndex和OffsetIndex, GroupIndex为该变量所在的寄存器类型,为一常量; OffsetIndex为该变量在寄存器中得地址偏移量,为一变量。
PLC 变量地址与ADS 地址之间的对应关系:
可以在项目中查看偏移地址:
在.pro文件中添加头文件路径和lib文件
INCLUDEPATH += $$PWD/include
LIBS += $$PWD/lib/TcAdsDll.lib
在.h中添加头文件
#include
#include "TcAdsDef.h"
#include "TcAdsAPI.h"
声明全局变量
AmsAddr Addr;//定义AMS地址变量
PAmsAddr pAddr;//定义端口地址变量
long nErr;
USHORT nAdsState; //PLC状态信息
USHORT nDeviceState;
long nPort;
pAddr = &Addr;
nPort = AdsPortOpen();//打开ADS通信端口
nErr = AdsGetLocalAddress(pAddr);//自动获取本地地址
if (nErr)
{
QMessageBox::about(nullptr, "Warning", QString("Error: AdsGetLocalAddress: "));
}
else
{
qDebug()<<"AdsPortOpen Successfully" << '\n';
}
pAddr->port = 851;//TC3通信使用的为851端口
//关闭端口通信
nErr = AdsPortClose();
if (nErr)
{
qDebug()<< "Error: AdsPortClose: " << nErr << '\n';
}
//向PLC读取PLC的状态信息
nErr = AdsSyncReadStateReq(pAddr, &nAdsState, &nDeviceState);
if (nErr)
{
qDebug()<<"Error: AdsSyncReadStateReq: " << nErr << '\n';
}
else
{
qDebug()<<"PLCState: " << nAdsState << '\n'; // 输出PLC状态信息
}
nAdsState = ADSSTATE_RUN;
void *pData = nullptr;
nErr = AdsSyncWriteControlReq(pAddr, nAdsState, nDeviceState, 0, pData);
if (nErr)
{
qDebug() << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
}
nAdsState = ADSSTATE_STOP;
void *pData = nullptr;
nErr = AdsSyncWriteControlReq(pAddr, nAdsState, nDeviceState, 0, pData);
if (nErr)
{
qDebug() << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
}
bool BOOL1; //定义布尔量
nErr = AdsSyncReadReq(pAddr, 0x4020, 0x0, 0x1, &BOOL1); //从ADS服务器同步读取数据,pAddr:ADS设备的地址,0x4020:段地址,0x0偏移地址,0x1:数据长度,&BOOL1:接收数据的缓存
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
else
{
if(BOOL1==true)
{
ui->lblReadRes->setText("1");
}
else
{
ui->lblReadRes->setText("0");
}
}
}
bool BOOL1; //定义布尔量
BOOL1=ui->line_Write->text().toInt();
nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x0, 0x1, &BOOL1 ); //同步写数据到ADS设备,pAddr:ADS设备的地址,0x4020:段地址,0x0偏移地址,0x1:数据长度,@BOOL1:接收数据的缓存
if (nErr)
{
qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
以Int数据类型为例:
int INT1; //定义整型量
nErr = AdsSyncReadReq(pAddr, 0x4020, 0x8, 0x4, &INT1);
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
}
else
{
ui->lblReadRes->setText(QString::number(INT1,10));
}
//向PLC写入整型量
int INT1;
INT1=ui->line_Write->text().toInt();
nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x8, 0x4, &INT1);
if (nErr)
{
qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n';
}
unsigned long lHdlVar; //创建句柄
char String[]={"MAIN.string_test"}; //定义字符串
char szVar []={"MAIN.string_test"};
// 同步写数据到ADS服务器并从ADS设备接收数据,pAddr:ADS设备的地址 0x0:偏移地址 sizeof(lHdlVar):由ADS设备返回的句柄大小 &lHdlVar:由ADS设备返回的数据缓存 sizeof(szVar):写入ADS设备的数据大小 szVar:写入ADS设备的数据缓存
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(String), &String); //从ADS服务器同步读取数据
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
else
{
ui->lblReadRes->setText(String);
}
//向PLC写入字符串
unsigned long lHdlVar; //创建句柄
QByteArray ba;
char* temp;
char String[10];
QString str = ui->line_Write->text();
ba = str.toLatin1();
temp = ba.data();
strncpy_s(String,temp,10);
char szVar []={"MAIN.string_test"};
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(String), &String); //同步写数据到ADS设备
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n'; //检查获取地址的操作是否执行成功
}
unsigned long lHdlVar2; //创建句柄
short Array[5]; //定义数组
char szVar2[]={"MAIN.Array1"};
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar2), &lHdlVar2, sizeof(szVar2), szVar2);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
}
nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar2, sizeof(Array), & Array[0]);
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
}
else
{
ui->line_ArrayRead_1->setText(QString("%1").arg(Array[0]));
ui->line_ArrayRead_2->setText(QString("%1").arg(Array[1]));
ui->line_ArrayRead_3->setText(QString("%1").arg(Array[2]));
ui->line_ArrayRead_4->setText(QString("%1").arg(Array[3]));
ui->line_ArrayRead_5->setText(QString("%1").arg(Array[4]));
}
unsigned long lHdlVar2; //创建句柄
short Array[5]; //定义数组
char szVar2[]={"MAIN.Array1"};
Array[0] = ui->line_ArrayWrite_1->text().toShort();
Array[1] = ui->line_ArrayWrite_2->text().toShort();
Array[2] = ui->line_ArrayWrite_3->text().toShort();
Array[3] = ui->line_ArrayWrite_4->text().toShort();
Array[4] = ui->line_ArrayWrite_5->text().toShort();
//得到Array1的句柄
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar2), &lHdlVar2, sizeof(szVar2), szVar2);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
}
//通过句柄向PLC写入数组
nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar2, sizeof(Array), & Array[0]);
if (nErr)
{
qDebug() << "Error: AdsSyncReadReq: " << nErr << '\n';
}
struct PlcVarstruct //定义结构体
{
int intVal; //整型
float floatVal; //浮点型
bool boolVal; //布尔型
}PlcVar;
unsigned long lHdlVar3;
char szVar3[] = { "MAIN.PLCVar" };
//从PLC中读取结构体
//获取结构体的句柄
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar3), &lHdlVar3, sizeof(szVar3), szVar3);
if (nErr)
{
qDebug() << "Test:Error: AdsSyncReadWriteReq: " << nErr << '\n';
}
//通过句柄获取所需结构体的数值
nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar3, sizeof(PlcVar), &PlcVar);
if (nErr)
{
qDebug() << "Test:Error: AdsSyncReadReq: " << nErr << '\n';
}
//输出结构体中各个变量的数值
ui->line_StructRead_1->setText(QString::number(PlcVar.intVal,10));
ui->line_StructRead_2->setText(QString("%1").arg(PlcVar.floatVal));
if(PlcVar.boolVal == true)
{
ui->line_StructRead_3->setText("1");
}
else
{
ui->line_StructRead_3->setText("0");
}
//向PLC写入结构体
//输入结构体的数值
struct PlcVarstruct //定义结构体
{
int intVal; //整型
float floatVal; //浮点型
bool boolVal; //布尔型
}PlcVar;
unsigned long lHdlVar3;
char szVar3[] = { "MAIN.PLCVar" };
PlcVar.intVal = ui->line_StructWrite_1->text().toInt();
PlcVar.floatVal = ui->line_StructWrite_2->text().toFloat();
if(ui->line_StructWrite_3->text().trimmed() == "1")
{
PlcVar.boolVal = true;
}
else
{
PlcVar.boolVal = false;
}
//得到PlcVar的句柄
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar3), &lHdlVar3, sizeof(szVar3), &szVar3);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
}
//通过之前获取的句柄向PLC写入结构体
AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar3, sizeof(PlcVar), &PlcVar);
if (nErr)
{
qDebug() << "Error: AdsSyncWriteReq: " << nErr << '\n';
}
void MainWindow::on_pushButton_NotifyOpen_clicked()
{
ULONG hNotification, hUser;
AdsNotificationAttrib adsNotificationAttrib;
char szVar []={"MAIN.Notify"};
// set the attributes of the notification
adsNotificationAttrib.cbLength = 4;
adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
adsNotificationAttrib.nMaxDelay = 0;
adsNotificationAttrib.nCycleTime = 10000000; // 1sec
// get handle
nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(hUser), &hUser, sizeof(szVar), szVar);
if (nErr)
{
qDebug() << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
}
// initiate the transmission of the PLC-variable
nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VALBYHND, hUser, &adsNotificationAttrib, Callback, hUser, &hNotification);
if (nErr)
{
qDebug() << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n'<<endl;
}
}
// Callback-function
void __stdcall CALLBACK MainWindow::Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
Q_UNUSED(pAddr);
Q_UNUSED(hUser);
unsigned char *ch=pNotification->data;//疑问:数据类型为unsigned char,只能到 0~255
qDebug()<< static_cast<unsigned long>(*ch);
qDebug()<< pNotification->hNotification<<'\n';
//char *str1 = (char *)(ch);
main->ui->line_Notify->setText(QString::number(*ch,10));
}
static void __stdcall CALLBACK Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser);