主动异步隔离 (Async Isolation)
本页是"主动异步隔离层"的参考。 它把
build/setState/ SDO 读写 / 静态扫描这些会阻塞总线/邮箱的同步操作,包装成"提交即返回CompletableFuture、后台串行执行、可取消、带进度"的异步版本,避免在 Swing / JavaFX / 服务线程上卡死,并保证同一主站任意时刻只有一个操作打总线。语义对齐 C# —— 实现与 C# 主动异步隔离 逐点一致:每主站串行闸(单线程
ExecutorService,等价SemaphoreSlim(1,1))、关闭守门、取消后自动恢复干净状态、独立静态扫描闸。命名遵循 Java 惯例(...Async后缀 +CompletableFuture)。旧同步 API 全部保留。
异步隔离层位于包 com.darra.ethercat.master(AsyncGate)与 com.darra.ethercat.slave(CoE),经 JNA 调用底层运行时库。AsyncGate.runExclusiveAsync(name, supplier) 为核心串行闸。
一、设计要点
| 要点 | 实现 |
|---|---|
| 串行 (Serial) | 每主站一把 AsyncGate = 单线程 daemon ExecutorService(FIFO,等价 SemaphoreSlim(1,1))。同一主站的 build / setState / SDO 严格串行,绝不并发打总线/邮箱。SDO 经 AsyncGate.forMaster(index) 反查父闸排队。 |
| 后台隔离 (Isolation) | ...Async 提交即返回 CompletableFuture,阻塞 native 在后台线程执行,不堵调用线程。 |
| 守门 (Shutdown Guard) | volatile shuttingDown;提交前 + 进 native 前各查一次。close() 先调 asyncGate.shutdown() 再 dll.Dispose(顺序正确),防 use-after-free。 |
| 取消 (Cancel) | 对返回的 CompletableFuture 调 cancel(true) → whenComplete 中断正在执行的 native 阻塞调用并自动恢复到可继续接受新操作的干净状态,不影响后续操作。静态扫描的取消走独立通道,与主站取消互不干扰。 |
| 进度 (Progress) | Consumer<String> 回调 + InitProgressListener。回调在 executor 后台线程触发,调用方需自己 marshal 回 UI。 |
所有进度回调都在 executor 后台线程 触发,不是 EDT / JavaFX Application Thread。Swing 用 SwingUtilities.invokeLater、JavaFX 用 Platform.runLater,否则跨线程访问 UI 控件会出问题。
master.buildAsync(msg ->
// ✅ marshal 回 EDT
SwingUtilities.invokeLater(() -> statusLabel.setText(msg))
);
二、API 总览
| 类别 | 方法 | 返回类型 | 读写 | 说明 |
|---|---|---|---|---|
| 主站一条龙 | buildAsync(Consumer<String> progress) | CompletableFuture<Boolean> | 异步 | 异步执行 连接→扫描→配置→进 OP,带进度 |
| 主站一条龙 | buildAsync() | CompletableFuture<Boolean> | 异步 | 无进度回调的构建 |
| 状态切换 | setStateAsync(EcState) | CompletableFuture<Boolean> | 异步 | 异步状态切换,经串行闸排队 |
| 状态切换 | setStateAsync(EcState, Consumer<String>) | CompletableFuture<Boolean> | 异步 | 带进度的异步状态切换 |
| 网络 | setNetworkAsync(String, String) | CompletableFuture<Integer> | 异步 | 异步设主/冗余网卡(链式串行用) |
| 静态扫描 | scanAsync(String adapter, String secondary) | CompletableFuture<List<...>> | 异步 | 异步静态扫描,走独立扫描闸 + 独立取消通道 |
| 静态扫描 | scanAsync(String adapter) | CompletableFuture<List<...>> | 异步 | 单网卡静态扫描 |
| 从站 SDO | CoE.sdoReadAsync(...) | CompletableFuture<byte[]> | 异步 | 异步读 SDO(抛 CoEAbortException 风格) |
| 从站 SDO | CoE.sdoWriteAsync(...) | CompletableFuture<Void> | 异步 | 异步写 SDO |
| 从站 SDO | CoE.SDOReadAsync(...) | CompletableFuture<byte[]> | 异步 | C# 风格:失败返回 null |
| 从站 SDO | CoE.SDOWriteAsync(...) | CompletableFuture<Boolean> | 异步 | C# 风格:失败返回 false |
| 串行闸 | AsyncGate.runExclusiveAsync(String, Supplier<T>) | CompletableFuture<T> | 异步 | 核心串行闸,自定义异步操作入闸 |
| 静态闸 | AsyncGate.runStaticExclusiveAsync(...) | CompletableFuture<T> | 异步 | 进程级独立静态扫描闸 |
| 进度 | setInitProgressListener(InitProgressListener) | void | 写 | 注册初始化进度多播监听器(后台线程触发) |
| 状态查询 | isAsyncOperationInProgress() | boolean | 只读 | 当前是否有异步操作在串行闸中执行 |
三、方法详解
buildAsync(Consumer<String> progress)
public CompletableFuture<Boolean> buildAsync(Consumer<String> progress)
public CompletableFuture<Boolean> buildAsync()
异步执行"连接 → 扫描 → 配置 → 进 OP"一条龙。提交即返回 CompletableFuture,构建在 executor 后台线程串行执行。
参数:
progress(Consumer<String>) — 进度回调,在后台线程触发,调用方自行 marshal 回 UI
返回值:
CompletableFuture<Boolean>— 完成时为true构建成功并进入 OP
示例:
CompletableFuture<Boolean> f = master.buildAsync(msg ->
SwingUtilities.invokeLater(() -> statusLabel.setText(msg)));
f.thenAccept(ok -> {
if (!ok) System.out.println("构建失败");
});
setStateAsync(EcState state)
public CompletableFuture<Boolean> setStateAsync(EcState state)
public CompletableFuture<Boolean> setStateAsync(EcState state, Consumer<String> progress)
异步状态切换,经主站串行闸排队,与同主站的 build / SDO 互斥串行。
示例:
master.setStateAsync(EcState.OP).thenAccept(ok -> { /* ... */ });
CoE.sdoReadAsync / sdoWriteAsync
public CompletableFuture<byte[]> sdoReadAsync(int index, int subIndex, boolean completeAccess)
public CompletableFuture<Void> sdoWriteAsync(int index, int subIndex, byte[] data, boolean completeAccess)
// C# 风格重载(失败返回 null / false 而非抛异常)
public CompletableFuture<byte[]> SDOReadAsync(int index, int subIndex, boolean completeAccess)
public CompletableFuture<Boolean> SDOWriteAsync(int index, int subIndex, byte[] data, boolean completeAccess)
异步 SDO 读写。经 AsyncGate.forMaster(masterIndex) 反查父主站的同一把串行闸排队,绝不与同主站 PDO / 状态切换并发打邮箱。
示例:
master.slave(1).getCoE().sdoReadAsync(0x6041, 0x00, false)
.thenAccept(data -> System.out.println("读到 " + data.length + " 字节"));
scanAsync(String adapter, String secondaryAdapter)
public CompletableFuture<List<ScannedSlaveInfo>> scanAsync(String adapter, String secondaryAdapter)
public CompletableFuture<List<ScannedSlaveInfo>> scanAsync(String adapter)
异步静态扫描。走进程级独立扫描闸,取消时通过独立的扫描取消通道中断(与主站操作的取消严格区分,互不干扰)。
setInitProgressListener(InitProgressListener)
public void setInitProgressListener(InitProgressListener listener)
注册初始化进度多播监听器。回调在后台线程触发,Swing / JavaFX 需自行 marshal。
四、取消语义
对返回的 CompletableFuture 调 cancel(true),隔离层会中断正在执行的 native 阻塞调用,并在中断后自动把隔离层恢复到可继续接受新操作的干净状态:
| 操作类型 | 取消行为 |
|---|---|
| 主站 build / setState / SDO | 中断当前主站操作,自动清理中断状态 |
| 静态扫描 scanAsync | 走独立取消通道中断扫描,与主站取消互不干扰 |
取消由隔离层完整托管:中断后会自动恢复,后续 build / setState 不会被前一次取消影响,应用层无需任何额外清理动作。
CompletableFuture<Boolean> f = master.buildAsync(null);
cancelButton.addActionListener(e -> f.cancel(true)); // 取消并自动清理
兼容性
异步隔离层以 AsyncGate(master 包)、CoE(slave 包)和 MasterAsync(sugar 包)三组公开类型对外暴露,经 JNA 调用底层运行时库。原同步 build / setState / setNetwork / SDORead / SDOWrite 全部保留,向后兼容。