跳到主要内容

AoE (ADS over EtherCAT)

AoE 协议实现了 ADS (Automation Device Specification) over EtherCAT 通信,支持 Beckhoff TwinCAT 和类似设备的 ADS 通信。

通过 slave.AoE() 访问。从站不支持 AoE 时为 null

属性

属性类型读写说明
DefaultTimeoutUsint只读默认超时时间(微秒),500000

数据读写

Read(int indexGroup, int indexOffset, int length, int timeoutUs)

public byte[] Read(int indexGroup, int indexOffset, int length, int timeoutUs)
public byte[] Read(int indexGroup, int indexOffset, int length) // 默认超时

读取 ADS 数据。

参数:

  • indexGroup (int) — 索引组
  • indexOffset (int) — 索引偏移
  • length (int) — 读取长度
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • byte[] — 读取的数据,失败返回 null

示例:

byte[] status = slave.AoE().Read(0x4020, 0, 4);
if (status != null) {
int value = (status[0] & 0xFF) | ((status[1] & 0xFF) << 8)
| ((status[2] & 0xFF) << 16) | ((status[3] & 0xFF) << 24);
System.out.printf("状态值: 0x%08X%n", value);
}

异步重载:

public CompletableFuture<byte[]> readAsync(int indexGroup, int indexOffset, int length, int timeoutUs)
public CompletableFuture<byte[]> readAsync(int indexGroup, int indexOffset, int length)

异步读取 ADS 数据。底层失败时 future 以 AoE.AoEProtocolException 异常完成。

slave.AoE().readAsync(0x4020, 0, 4)
.thenAccept(data -> System.out.printf("状态长度: %d%n", data.length))
.exceptionally(ex -> { System.err.println("失败: " + ex.getMessage()); return null; });

Write(int indexGroup, int indexOffset, byte[] data, int timeoutUs)

public boolean Write(int indexGroup, int indexOffset, byte[] data, int timeoutUs)
public boolean Write(int indexGroup, int indexOffset, byte[] data) // 默认超时

写入 ADS 数据。

参数:

  • indexGroup (int) — 索引组
  • indexOffset (int) — 索引偏移
  • data (byte[]) — 写入数据
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • boolean — 成功返回 true

示例:

byte[] data = { 0x06, 0x00, 0x00, 0x00 };
slave.AoE().Write(0x4020, 0, data);

异步重载:

public CompletableFuture<Boolean> writeAsync(int indexGroup, int indexOffset, byte[] data, int timeoutUs)
public CompletableFuture<Boolean> writeAsync(int indexGroup, int indexOffset, byte[] data)

ReadWrite(int indexGroup, int indexOffset, int readLength, byte[] writeData, int timeoutUs)

public byte[] ReadWrite(int indexGroup, int indexOffset, int readLength, byte[] writeData, int timeoutUs)

同时读写数据(ADS ReadWrite 命令)。

参数:

  • indexGroup (int) — 索引组
  • indexOffset (int) — 索引偏移
  • readLength (int) — 读取长度
  • writeData (byte[]) — 写入数据(可为 null
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • byte[] — 读取的数据,失败返回 null

异步重载:

public CompletableFuture<byte[]> readWriteAsync(int indexGroup, int indexOffset, int readLength,
byte[] writeData, int timeoutUs)

设备信息

ReadDeviceInfo(int timeoutUs)

public DeviceInfo ReadDeviceInfo(int timeoutUs)
public DeviceInfo ReadDeviceInfo()

读取 ADS 设备信息。

返回值:

相关结构:

public static class DeviceInfo {
public boolean Success; // 是否成功
public byte MajorVer; // 主版本号
public byte MinorVer; // 次版本号
public short Build; // 编译号
public String DeviceName; // 设备名称
}

示例:

AoE.DeviceInfo info = slave.AoE().ReadDeviceInfo();
if (info.Success)
System.out.printf("设备: %s v%d.%d.%d%n",
info.DeviceName, info.MajorVer, info.MinorVer, info.Build);

ReadState(int timeoutUs)

public AdsState ReadState(int timeoutUs)
public AdsState ReadState()

读取 ADS 状态。

返回值:

相关结构:

public static class AdsState {
public boolean Success; // 是否成功
public short AdsState; // ADS 状态
public short DeviceState; // 设备状态
}

ADS 状态码含义:

  • 0Invalid 无效状态
  • 1Idle 空闲
  • 2Reset 复位
  • 3Init 初始化
  • 4Start 启动
  • 5Run 运行
  • 6Stop 停止
  • 7SaveConfig 保存配置
  • 8LoadConfig 加载配置
  • 9PowerFailure 电源故障
  • 10PowerGood 电源正常
  • 11Error 错误
  • 15Config 配置

示例:

AoE.AdsState state = slave.AoE().ReadState();
if (state.Success)
System.out.println("ADS 状态: " + AoE.GetAdsStateDescription(state.AdsState));

WriteControl(short adsState, short deviceState, byte[] data, int timeoutUs)

public boolean WriteControl(short adsState, short deviceState, byte[] data, int timeoutUs)
public boolean WriteControl(short adsState, short deviceState)

写入控制命令(切换设备状态)。

参数:

  • adsState (short) — 目标 ADS 状态
  • deviceState (short) — 目标设备状态
  • data (byte[]) — 附加数据(可选)
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • boolean — 成功返回 true

示例:

slave.AoE().WriteControl((short) 5, (short) 0);  // 切换到 Run 状态

GetAdsStateDescription(int adsState)

public static String GetAdsStateDescription(int adsState)

获取 ADS 状态码的中文描述。

数据订阅

Subscribe(int indexGroup, int indexOffset, int dataLength, NotificationCallback callback, int cycleTimeMs)

public int Subscribe(int indexGroup, int indexOffset, int dataLength,
NotificationCallback callback, int cycleTimeMs)

订阅数据变化通知。

参数:

  • indexGroup (int) — 索引组
  • indexOffset (int) — 索引偏移
  • dataLength (int) — 数据长度
  • callback (NotificationCallback) — 数据变化回调
  • cycleTimeMs (int) — 检查周期(毫秒)

返回值:

  • int — 通知句柄,失败返回 -1

相关结构:

@FunctionalInterface
public interface NotificationCallback {
void OnNotification(short slave, int handle, byte[] data, long timestamp);
}

示例:

int handle = slave.AoE().Subscribe(0x4020, 0, 4, (slaveNo, h, data, ts) -> {
int value = (data[0] & 0xFF) | ((data[1] & 0xFF) << 8)
| ((data[2] & 0xFF) << 16) | ((data[3] & 0xFF) << 24);
System.out.printf("数据变化: 0x%08X%n", value);
}, 100);

Unsubscribe(int handle)

public boolean Unsubscribe(int handle)

取消指定订阅。

UnsubscribeAll()

public void UnsubscribeAll()

取消所有订阅。

GetActiveSubscriptions()

public int[] GetActiveSubscriptions()

获取所有活跃订阅的句柄列表。

SendCommand(short targetPort, short commandId, byte[] commandData, int timeoutUs)

public byte[] SendCommand(short targetPort, short commandId, byte[] commandData, int timeoutUs)

发送原始 ADS 命令(高级 API,常规场景请使用 Read/Write/Subscribe)。

参数:

  • targetPort (short) — 目标端口
  • commandId (short) — 命令 ID
  • commandData (byte[]) — 命令数据
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • byte[] — 响应数据,失败返回 null

AoE 配置

SetConfig(byte[] targetNetId, short targetPort, byte[] sourceNetId, short sourcePort)

public boolean SetConfig(byte[] targetNetId, short targetPort, byte[] sourceNetId, short sourcePort)

设置 AoE 路由配置(AMS NetID 和端口)。

参数:

  • targetNetId (byte[]) — 目标 AMS NetID(6 字节)
  • targetPort (short) — 目标 AMS 端口
  • sourceNetId (byte[]) — 源 AMS NetID(6 字节)
  • sourcePort (short) — 源 AMS 端口

返回值:

  • boolean — 成功返回 true

示例:

byte[] targetNetId = { 5, 80, (byte)187, (byte)177, 1, 1 };
byte[] sourceNetId = { (byte)192, (byte)168, 1, 100, 1, 1 };
slave.AoE().SetConfig(targetNetId, (short) 851, sourceNetId, (short) 32768);

InitializeSlaveNetId(byte[] netId, int timeoutUs)

public boolean InitializeSlaveNetId(byte[] netId, int timeoutUs)
public boolean InitializeSlaveNetId(byte[] netId)

初始化从站 AoE Net ID(ETG.1020 §9.4)。在 INIT→PreOp 状态切换期间调用,将 Net ID 写入从站 ADS 路由表。

参数:

  • netId (byte[]) — 6 字节的 AMS Net ID
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • boolean — 成功返回 true

示例:

byte[] netId = { 5, 80, (byte)187, (byte)177, 1, 1 };
slave.AoE().InitializeSlaveNetId(netId);

GetConfig()

public AoEConfig GetConfig()

获取当前 AoE 路由配置。

返回值:

相关结构:

public static class AoEConfig {
public boolean Success; // 是否成功
public byte[] TargetNetId; // 目标 AMS NetID (6 字节)
public short TargetPort; // 目标 AMS 端口
public byte[] SourceNetId; // 源 AMS NetID (6 字节)
public short SourcePort; // 源 AMS 端口
}

跨协议网关

通过 AoE 路由访问其他邮箱协议(ETG.1020),支持 CoE 和 SoE 协议的透明转发。

ReadCoEViaAoE(int index, int subindex, int readLength, int timeoutUs)

public byte[] ReadCoEViaAoE(int index, int subindex, int readLength, int timeoutUs)
public byte[] ReadCoEViaAoE(int index, int subindex, int readLength)

通过 AoE 路由读取 CoE 对象(IndexGroup=0xF302)。

参数:

  • index (int) — CoE 对象索引
  • subindex (int) — CoE 子索引
  • readLength (int) — 期望读取长度
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • byte[] — 读取的数据,失败返回 null

示例:

// 通过 AoE 网关读取 CoE 对象 0x6041:0(状态字)
byte[] data = slave.AoE().ReadCoEViaAoE(0x6041, 0, 2);
if (data != null) {
int statusWord = (data[0] & 0xFF) | ((data[1] & 0xFF) << 8);
System.out.printf("状态字: 0x%04X%n", statusWord);
}

WriteCoEViaAoE(int index, int subindex, byte[] data, int timeoutUs)

public boolean WriteCoEViaAoE(int index, int subindex, byte[] data, int timeoutUs)
public boolean WriteCoEViaAoE(int index, int subindex, byte[] data)

通过 AoE 路由写入 CoE 对象(IndexGroup=0xF302)。

参数:

  • index (int) — CoE 对象索引
  • subindex (int) — CoE 子索引
  • data (byte[]) — 写入数据
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • boolean — 成功返回 true

示例:

// 通过 AoE 网关写入 CoE 对象 0x6040:0(控制字)
byte[] ctrlWord = { 0x0F, 0x00 };
slave.AoE().WriteCoEViaAoE(0x6040, 0, ctrlWord);

writeCoEViaAoEAsync(int index, int subindex, byte[] data, int timeoutUs)

public CompletableFuture<Boolean> writeCoEViaAoEAsync(int index, int subindex, byte[] data, int timeoutUs)

异步版本的 WriteCoEViaAoE,可与 await / CompletableFuture 协作。

ReadSoEViaAoE(int idn, int readLength, int timeoutUs)

public byte[] ReadSoEViaAoE(int idn, int readLength, int timeoutUs)
public byte[] ReadSoEViaAoE(int idn, int readLength)

通过 AoE 路由读取 SoE IDN(IndexGroup=0xF420)。

参数:

  • idn (int) — SoE IDN 编号
  • readLength (int) — 期望读取长度
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • byte[] — 读取的数据,失败返回 null

WriteSoEViaAoE(int idn, byte[] data, int timeoutUs)

public boolean WriteSoEViaAoE(int idn, byte[] data, int timeoutUs)
public boolean WriteSoEViaAoE(int idn, byte[] data)

通过 AoE 路由写入 SoE IDN(IndexGroup=0xF420)。

参数:

  • idn (int) — SoE IDN 编号
  • data (byte[]) — 写入数据
  • timeoutUs (int) — 超时时间(微秒)

返回值:

  • boolean — 成功返回 true

writeSoEViaAoEAsync(int idn, byte[] data, int timeoutUs)

public CompletableFuture<Boolean> writeSoEViaAoEAsync(int idn, byte[] data, int timeoutUs)

异步版本的 WriteSoEViaAoE

完整示例

数据读写

AoE aoe = slave.AoE();

// 读取数据
byte[] status = aoe.Read(0x4020, 0, 4);
if (status != null) {
int value = (status[0] & 0xFF) | ((status[1] & 0xFF) << 8)
| ((status[2] & 0xFF) << 16) | ((status[3] & 0xFF) << 24);
System.out.printf("状态: 0x%08X%n", value);
}

// 写入数据
byte[] writeData = { 0x06, 0x00, 0x00, 0x00 };
aoe.Write(0x4020, 0, writeData);

// 设备信息
AoE.DeviceInfo info = aoe.ReadDeviceInfo();
if (info.Success)
System.out.printf("设备: %s v%d.%d.%d%n",
info.DeviceName, info.MajorVer, info.MinorVer, info.Build);

数据订阅

AoE aoe = slave.AoE();

int handle = aoe.Subscribe(0x4020, 0, 4, (slaveNo, h, data, ts) -> {
System.out.println("数据变化");
}, 100);

Thread.sleep(10000);
aoe.UnsubscribeAll();

跨协议网关

AoE aoe = slave.AoE();

// 通过 AoE 读取 CoE 对象
byte[] statusWord = aoe.ReadCoEViaAoE(0x6041, 0, 2);
if (statusWord != null) {
int value = (statusWord[0] & 0xFF) | ((statusWord[1] & 0xFF) << 8);
System.out.printf("状态字: 0x%04X%n", value);
}

// 通过 AoE 写入 CoE 对象
byte[] ctrlWord = { 0x0F, 0x00 };
aoe.WriteCoEViaAoE(0x6040, 0, ctrlWord);

// 通过 AoE 读取 SoE IDN
byte[] idnData = aoe.ReadSoEViaAoE(32, 4);
if (idnData != null) {
int value = (idnData[0] & 0xFF) | ((idnData[1] & 0xFF) << 8)
| ((idnData[2] & 0xFF) << 16) | ((idnData[3] & 0xFF) << 24);
System.out.printf("IDN 32 值: %d%n", value);
}