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 与外部设备互访,属于“设备侧发起”的通信。

ADS 通信库分层与选型决策 第4层 · PLC 库 Tc2_System:ADSREAD / ADSWRITE 功能块 适用:PLC 任务间、PLC 与外部设备互访 第3层 · 语言客户端库 pyads(Python)· ADS-Java · C++ 封装 适用:跨语言、跨平台快速集成 第2层 · TwinCAT.Ads API AdsClient / AdsSession · 强类型读写 · 通知 适用:.NET 上位机、HMI、数据采集 第1层 · ADS DLL TcAdsDll.dll / .so · C 风格函数 适用:极致控制、无托管环境 选型决策路径 开发语言是 .NET? 是 → TwinCAT.Ads API 需要跨平台 / 脚本化? 是 → pyads / Java 库 需精细控制 AMS 报文? 是 → 直接调用 ADS DLL 通信由 PLC 侧发起? → 使用 Tc2_System 功能块 公共底座:AMS 路由(AmsNetId + Port)· TCP/UDP 传输 · 符号/句柄寻址 所有上层库最终都封装为相同的 AMS 报文,差异只在接口风格与平台支持

二、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.AdsC# / .NET,Windows符号名 + 句柄完整上位机、HMI、数据采集
pyadsPython,Windows/Linux符号名 + 句柄支持回调脚本、数据分析、快速原型
ADS DLLC/C++,Windows/Linux地址 + 句柄需自行封装嵌入式、实时性要求高
Tc2_SystemPLC 侧符号名/地址功能块轮询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 访问”选项),以及句柄是否创建成功。

Python 侧可用 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 运行时。
不要在同一进程内混用多个 ADS 库去连接同一端口而不释放句柄,句柄泄漏会导致 PLC 侧资源耗尽。每次 CreateVariableHandle 都应有对应的 DeleteVariableHandle,或改用 AdsSession 自动管理生命周期。

选型的最终判断标准不是“哪个库更高级”,而是“哪个库能让你的通信链路最短、维护成本最低”。.NET 上位机优先 TwinCAT.Ads API;脚本与数据分析优先 pyads;对时序和报文有极致要求时再下沉到 ADS DLL。把 AMS 路由和句柄管理这两件事做扎实,无论选哪条路径,通信都会稳定可控。