介绍
TwinCAT3
TwinCAT3是Beckhoff推出的一款基于PC的控制器软件,简单理解是一套集成开发环境,里边有各种分析工具以及通信中间件;开发者可以很方便的用它来进行IPC和PLC之间的通信连接
ADS
倍福ADS(Automation Device Specification)是一个跨设备的网络通信协议(基于TCP/IP),依托TwinCAT提供的通信组件,它允许PLC、IPC之间进行数据分发、读写,支持同步、异步通信方式
环境搭建
VS2019
安装C++桌面开发组件
安装完Visual Studio版本信息页面如下
TwinCAT3
在使用ADS之前需要先安装TWinCAT3环境,我这里装的是V3.1.4024.55,属于TwinCAT3 Full版本,下载页面:https://www.beckhoff.com.cn/en-en/download/650023470
TwinCAT3 Full版本分XAR和XAE两部分,需要搭配Visual Studio版本使用
- XAE:eXtended Automation Engineerin,XAE是基于Visual Studio作为开发环境,进行多种语言的编程和硬件组态
- XAR:eXtended Automation Runtime,XAR是实时运行环境,对 TwinCAT 模块加载、执行、管理、实时运行与调用
TwinCAT3安装比较简单,勾选相应的组件,默认安装就可以了
Qt6.7.2
使用在线工具安装Qt6.7.2,第一次打开需要使用--mirror
命令指定软件源
qt-online-installer-windows-x64-4.8.0.exe --mirror https://mirror.nju.edu.cn/qt
Qt组件勾选
通信实验
操作流程
- VS2019 XAE新建PLC工程,配置工程,编译工程登入后可以查看PLC变量内存地址
- QtCreator新建C++ ADS Reader工程,根据PLC工程的IP和端口配置ADS连接
- QtCreator新建C++ ADS Writer工程,根据PLC工程的IP和端口配置ADS连接
- 在Reader进行数据读取操作,在Writer进行数据写入操作
PLC
编写PLC程序:定义BOOL、INT、REAL等3个变量类型数据
PROGRAM MAIN
VARboolVar1 AT%M*:BOOL;intVar2 AT%M*:INT;realVar3 AT%M*:REAL;
END_VAR
编译生成PLC应用并登入
打开Target Brownser
查看变量内存地址分布
变量 | 类型 | group | offset |
---|---|---|---|
boolVar1 | BOOL | 0x4020 | 0x5DFF0 |
intVar2 | INT | 0x4020 | 0x5DFF2 |
realVar3 | REAL | 0x4020 | 0x5DFF4 |
PLC和C++之间变量大小对应关系
变量类型 | C++ | 长度 |
---|---|---|
BOOL | bool | 1 |
INT | int | 4 |
UINT | unsigned int | 4 |
REAL | float | 4 |
LREAL | double | 8 |
Reader
建立ADS连接后读取指定地址(group + offset)的变量数据
#include <QCoreApplication>
#include <Windows.h>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"int main(int argc, char *argv[])
{long ret, port;AmsAddr addr;bool byte;port = AdsPortOpen();ret = AdsGetLocalAddress(&addr);if (ret){qDebug() << "AdsGetLocalAddress : " << ret;}if (port == addr.port){qDebug() << "LocalAdsPort: " << port << " opened!";}else{qDebug() << "LocalAdsPort open failed!";}addr.port = 851;int time = 0;int indexGroup = 0x4020;float var;while (1){ret = AdsSyncReadReq(&addr, indexGroup, 0x5dff0, sizeof(bool), &byte);if(!ret) {qDebug() << "boolVar1 : " << byte;}ret = AdsSyncReadReq(&addr, indexGroup, 0x5dff2, sizeof(int), &time);if(!ret) {qDebug() << "intVar2 : " << time;}ret = AdsSyncReadReq(&addr, indexGroup, 0x5dff4, sizeof(float), &var);if(!ret) {qDebug() << "realVar3 : " << var;}Sleep(1000);}return 0;
}
Writer
建立ADS连接后往指定变量地址(group + offset)写入数据
#include <QCoreApplication>
#include <Windows.h>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"int main(int argc, char *argv[])
{long ret, port;AmsAddr addr;bool byte = false;port = AdsPortOpen();ret = AdsGetLocalAddress(&addr);if (ret){qDebug() << "AdsGetLocalAddress : " << ret;}if (port == addr.port){qDebug() << "LocalAdsPort: " << port << " opened!";}else{qDebug() << "LocalAdsPort open failed!";}addr.port = 851;int time = 0;int indexGroup = 0x4020;while (1){byte = !byte;ret = AdsSyncWriteReq(&addr, indexGroup, 0x5dff0, sizeof(bool), &byte);time++;ret = AdsSyncWriteReq(&addr, indexGroup, 0x5dff2, sizeof(int), &time);float var = time * 0.9 + 3.14;ret = AdsSyncWriteReq(&addr, indexGroup, 0x5dff4, sizeof(float), &var);Sleep(1000);}return 0;
}
CMakeLists.txt
两个工程的CMake写法参考:需要include几个头文件(afxstr.h、TcAdsAPI.h、TcAdsDef.h、wingdi.h),然后链接TcAdsDll这个库
cmake_minimum_required(VERSION 3.14)project(QtADS LANGUAGES CXX)set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)include_directories(${CMAKE_SOURCE_DIR}/Include)link_directories(${CMAKE_SOURCE_DIR}/lib)add_executable(QtADS
main.cpp
)
target_link_libraries(QtADS Qt${QT_VERSION_MAJOR}::Core TcAdsDll)include(GNUInstallDirs)
install(TARGETS QtADSLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
接口说明
- 获取库版本号:AdsGetDllVersion
- 打开通信端口:ADSPortOpen
- 关闭通信端口:AdsPortClose
- 获取本机地址:AdsGetLocalAddress
- 获取错误信息:AdsGetLastError
- 同步写:AdsSyncWriteReq
- 同步读:AdsSyncReadReq
- 设备通知回调:AdsSyncAddDeviceNotificationReq
开源库
介绍
Qt封装的ADS组件也能很方便地进行ADS通信,支持QADSBOOL、QADSDINT、QADSDWORD、QADSENUM、QADSLREAL、QADSSTRING及对应的数组类型,所有数据类型都要持有通信的网络端口信息、节点信息,都要定义相应的value、setValue方法,但目前还没有支持Qt6,但改一改的话也可以用,需要进行两处改动:
① 将QString::SkipEmptyParts改为Qt::SkipEmptyParts
QString::SkipEmptyParts
改为
Qt::SkipEmptyParts
② QByteArray insert成员报错时需要把成员变成QByteArray兼容类型,通过toLatin1函数来转换
QByteArray sendValue = QByteArray(adsSymbolSize(),'\0');
sendValue.insert(0,val);
改为
QByteArray sendValue = QByteArray(adsSymbolSize(),'\0');
sendValue.insert(0,val.toLatin1());
示例
以BOOL类型数据访问为例:QADSBOOL通过配置端口、网络信息、节点名称等信息来访问节点
QADSBOOL *value = new QADSBOOL(this, 851, "local", "localhost", "MAIN.bTestVar1", QADSPLCVariable::ON_DEMAND, 0);
QtADS库封装了value、setValue等接口来进行读写操作
value->value();
value->setValue(false);
参考
【1】https://tr.beckhoff.com.cn/pluginfile.php/44857/mod_resource/content/0/ADS%E9%AB%98%E7%BA%A7%E5%9F%B9%E8%AE%AD.pdf
【2】https://tr.beckhoff.com.cn/mod/folder/view.php?id=2058
【3】https://github.com/Framatome/QtADS