4. ADS 通信库选型与开发环境
ADS 通信库的选型,本质上是在回答三个问题:谁发起通信(上位机应用、PLC 任务还是第三方系统)、用什么语言实现(C#、C++、Python、Java 等)、以及运行在什么平台(Windows 桌面、Linux 网关、嵌入式设备)。TwinCAT 官方与社区围绕这些问题提供了多条技术路径,理解它们的边界,比记住某个 API 的调用方式更重要。
一、ADS 通信库的四个层次
从底层到高层,ADS 通信能力大致分为四层。最底层是 ADS DLL(TcAdsDll.dll / TcAdsDll.so),它直接暴露 C 风格函数,如 AdsPortOpen()、AdsSyncReadReq()、AdsSyncWriteReq(),负责建立 AMS 路由、封装 AMS 报文并调用 TCP/UDP 传输。这一层最贴近协议,也最繁琐:每次读写都要手动填充 AmsAddr、AdsSymbolInfo 等结构体,并自行处理字节序与类型转换。
第二层是 TwinCAT.Ads API(.NET 库,命名空间 TwinCAT.Ads)。它把 DLL 封装为面向对象的类,核心是 AdsClient 与 AdsSession。开发者通过 Connect() 建立连接,用 ReadValue()/WriteValue() 直接操作强类型变量,用 CreateVariableHandle() 获取句柄以提升重复读写效率。它还支持通知(Notification)机制,可在变量变化时触发回调,而不必轮询。
第三层是各语言客户端库。Python 常用 pyads,Java 有 ADS-Java,C++ 可直接调用 DLL 或使用 TcAdsDef.h 头文件。这些库大多是对 DLL 的再封装,接口风格各异,但底层 AMS 报文完全一致。
第四层是 TwinCAT 内部的 PLC 库(Tc2_System 中的 ADSREAD/ADSWRITE 功能块),用于 PLC 之间或 PLC 与外部设备互访,属于“设备侧发起”的通信。
二、TwinCAT.Ads API 的关键对象
在 .NET 环境下,AdsClient 是最常用的入口。一次典型的同步读写流程如下:先设置 AmsNetId 与 AmsPort(PLC 运行时通常为 851),调用 Connect();随后用 CreateVariableHandle("MAIN.nSpeed") 获取句柄,再用 ReadValue<int>(handle) 读取。句柄一旦建立,后续读写只需传递整数句柄,避免每次解析符号名,这对高频采集尤为重要。
若需要实时感知变量变化,应使用通知机制:AdsClient.AddDeviceNotification() 注册变量与传输模式(如 AdsTransMode.OnChange),并指定循环时间与最大延迟。回调触发时,数据已由 ADS 驱动推送到本地,无需轮询。需要注意的是,通知回调运行在非 UI 线程,更新界面时必须做线程调度。
对于需要管理多个连接或希望复用会话的场景,AdsSession 更合适。它把连接、句柄、通知的生命周期统一管理,配合 IAdsConnection 接口便于单元测试与依赖注入。
三、各语言客户端库对比
| 库 | 语言/平台 | 寻址方式 | 通知支持 | 典型场景 |
|---|---|---|---|---|
| TwinCAT.Ads | C# / .NET,Windows | 符号名 + 句柄 | 完整 | 上位机、HMI、数据采集 |
| pyads | Python,Windows/Linux | 符号名 + 句柄 | 支持回调 | 脚本、数据分析、快速原型 |
| ADS DLL | C/C++,Windows/Linux | 地址 + 句柄 | 需自行封装 | 嵌入式、实时性要求高 |
| Tc2_System | PLC 侧 | 符号名/地址 | 功能块轮询 | PLC 间互访 |
选择时有一个容易忽略的约束:AMS 路由。无论用哪个库,目标设备的 AmsNetId 必须可达。本机通信通常使用 127.0.0.1.1.1 或本机 NetId;跨机通信则需要在 TwinCAT 系统托盘中添加路由,或通过 StaticRoutes.xml 配置。若路由未建立,任何库都会在 Connect() 阶段抛出 AdsErrorException,错误码常见为 0x20(目标端口未找到)或 0x745(路由未添加)。
四、工程创建与最小可运行示例
以 C# 控制台工程为例,创建步骤为:新建 .NET 项目 → 通过 NuGet 安装 Beckhoff.TwinCAT.Ads → 引用命名空间 → 编写连接与读写代码。下面是一个读取 PLC 中 MAIN.nSpeed(INT 型)的最小示例:
using TwinCAT.Ads;
var client = new AdsClient();
client.Connect("127.0.0.1.1.1", 851); // AmsNetId, PLC 端口
int handle = client.CreateVariableHandle("MAIN.nSpeed");
int speed = (int)client.ReadValue(handle); // 同步读取
Console.WriteLine($"nSpeed = {speed}");
client.WriteValue(handle, 1500); // 写入示例值
client.DeleteVariableHandle(handle);
client.Disconnect();
运行前需确认:TwinCAT 处于 Run 模式、PLC 工程已激活、变量 MAIN.nSpeed 已下载。若读取返回 0 而实际值非 0,优先检查变量是否被优化({attribute 'TcAccess'} 或“支持 ADS 访问”选项),以及句柄是否创建成功。
pyads 完成等价操作:plc = pyads.Connection('127.0.0.1.1.1', 851),随后 plc.read_by_name('MAIN.nSpeed', pyads.PLCTYPE_INT)。注意 pyads 在 Linux 上需要先安装 ADS 路由服务,Windows 上则依赖已安装的 TwinCAT 运行时。
CreateVariableHandle 都应有对应的 DeleteVariableHandle,或改用 AdsSession 自动管理生命周期。
选型的最终判断标准不是“哪个库更高级”,而是“哪个库能让你的通信链路最短、维护成本最低”。.NET 上位机优先 TwinCAT.Ads API;脚本与数据分析优先 pyads;对时序和报文有极致要求时再下沉到 ADS DLL。把 AMS 路由和句柄管理这两件事做扎实,无论选哪条路径,通信都会稳定可控。