Qt通过ADS实现倍福TwinCAT通信

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 <Windows.h>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"

声明全局变量

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

打开/关闭ADS通信

    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读取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量

    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、float等变量

以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';
    }

读/写String量

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

猜你喜欢

转载自blog.csdn.net/woshigaowei5146/article/details/104903143