跳到主要内容

主动异步隔离 (Async Isolation)

本页是"主动异步隔离层"的参考。 它把 EcInit / SetStateSequence / SDO 读写 / QuickSlaveCount 这些会阻塞总线/邮箱的同步操作,包装成"入队即返回、后台 worker 串行执行、可取消、带完成回调"的异步版本。C 无 async/await,采用后台 worker 线程 + 完成回调模型,避免在主线程上卡死,并保证同一主站任意时刻只有一个操作打总线

语义对齐 C# —— 实现与 C# 主动异步隔离 逐点一致:每主站串行闸(单 worker 线程 + FIFO 队列,等价 SemaphoreSlim(1,1))、关闭守门、取消复位 abort latch、独立静态扫描闸。命名遵循 C 惯例(darra_* 前缀 + _async 后缀 + 回调)。旧同步 API 全部保留。

头文件与 FFI

异步隔离层随 SDK 头文件提供,通过 dll_t 函数指针表调底层运行时库(阻塞操作 EcInit / SetStateSequence / QuickSlaveCount / dx_sdo_read[_ex] / dx_sdo_write[_ex])。每主站一个 darra_async_gate_t

一、设计要点

要点实现
串行 (Serial)每主站一个 darra_async_gate_t = 单 worker 线程 + FIFO 任务队列(mutex + cond,等价 SemaphoreSlim(1,1))。同主站的 Build / SetState / SDO 严格串行,绝不并发打总线/邮箱。SDO 走父主站的同一把闸。
后台隔离 (Isolation)*_async 入队即返回(不堵调用方),阻塞 native 在 worker 线程跑;native 调用在队列锁外执行(阻塞期间不锁队列)。
守门 (Shutdown Guard)darra_async_gate_destroyshutting_down;入队与 worker 取任务开跑前各查一次,关闭中绝不再打 native,残余任务以 canceled=1 回调掉(不丢用户回调),防 use-after-free。
取消 (Cancel)主站取消会中断当前在跑的阻塞操作并复位内部取消状态(隔离层自动完成,不复位会毒化后续 Build / SetState)。扫描取消走独立的取消通道(独立内部状态,与主站取消严格区分)。
进度 (Progress)progress_cb 在 worker 线程逐步上报,与 done_cb 同线程。输入串 / SDO 数据入队时深拷贝(调用方可立即释放原 buf);SDO 读返回的 native 缓冲在回调后由 FreeMemory 释放。
进度/完成回调在后台 worker 线程 — 必须自己 marshal

所有 progress_cb / *_done 回调都在后台 worker 线程触发,不是主线程。回主线程用你自己的线程间机制(条件变量 / 消息队列 / PostMessage 等),不要在回调里直接动主线程的 UI / 全局状态而不加锁。头文件已注明回调非主线程。

二、API 总览

回调签名共 5 类:progress + build_done / setstate_done / scan_done / sdo_read_done / sdo_write_done,均在 worker 线程触发。

类别函数返回类型读写说明
闸生命周期darra_async_gate_create(mi)darra_async_gate_t*为某主站创建串行闸(单 worker + FIFO 队列)
闸生命周期darra_async_gate_cancel(gate)void取消当前在跑/排队任务(自动复位内部取消状态)
闸生命周期darra_async_gate_destroy(gate)void置 shutting_down + 关闭闸 + 回收残余任务
闸生命周期darra_async_gate_busy(gate)int只读当前是否有任务在闸中执行
主站一条龙darra_build_async_json(gate, json, progress_cb, done_cb, user)int异步异步从 JSON 配置 连接→扫描→配置→进 OP
主站一条龙darra_build_async_file(gate, path, progress_cb, done_cb, user)int异步异步从配置文件构建
状态切换darra_set_state_async(gate, state, progress_cb, done_cb, user)int异步异步状态切换,经串行闸排队
从站 SDOdarra_sdo_read_async(gate, slave, idx, sub, ca, done_cb, user)int异步异步读 SDO,转调父主站同一把闸
从站 SDOdarra_sdo_write_async(gate, slave, idx, sub, data, len, ca, done_cb, user)int异步异步写 SDO(入队深拷贝 data)
静态扫描darra_scan_async(adapter, secondary, done_cb, user)int异步异步静态扫描,走独立扫描闸 + 独立 AbortScan
静态扫描darra_scan_async_cancel()void取消静态扫描(AbortScan → sleep → ResetScanAbort)
静态扫描darra_scan_async_shutdown()void关闭静态扫描 worker

三、函数详解

darra_async_gate_create / destroy

darra_async_gate_t* darra_async_gate_create(uint16_t master_index);
void darra_async_gate_destroy(darra_async_gate_t* gate);
int darra_async_gate_busy(darra_async_gate_t* gate);
void darra_async_gate_cancel(darra_async_gate_t* gate);

为某主站创建/销毁串行闸。darra_async_gate_destroy 先置 shutting_down,再关闭 —— 残余任务以 canceled=1 回调掉,不丢用户回调,防 use-after-free。

darra_build_async_json / darra_build_async_file

int darra_build_async_json(darra_async_gate_t* gate, const char* json,
darra_progress_cb progress_cb,
darra_build_done_cb done_cb, void* user);
int darra_build_async_file(darra_async_gate_t* gate, const char* path,
darra_progress_cb progress_cb,
darra_build_done_cb done_cb, void* user);

异步执行"连接 → 扫描 → 配置 → 进 OP"一条龙。入队即返回;progress_cb 逐阶段、done_cb 完成时在 worker 线程触发。输入串入队时深拷贝(调用方可立即释放)。

参数:

  • gate — 主站串行闸
  • json / path — 配置(JSON 串 / 文件路径)
  • progress_cb — 进度回调,worker 线程触发,自行 marshal(可空)
  • done_cb — 完成回调(携带 canceled 标志)
  • user — 透传用户指针

返回值:

  • int0 入队成功;非 0 入队失败(如闸正在关闭)

示例:

static void on_progress(const char* msg, void* user) {
/* worker 线程: 推消息队列回主线程, 不直接动 UI */
}
static void on_build_done(int ok, int canceled, void* user) {
/* worker 线程 */
}

darra_async_gate_t* gate = darra_async_gate_create(1);
darra_build_async_json(gate, cfg_json, on_progress, on_build_done, NULL);

darra_set_state_async

int darra_set_state_async(darra_async_gate_t* gate, uint8_t state,
darra_progress_cb progress_cb,
darra_setstate_done_cb done_cb, void* user);

异步状态切换,经主站串行闸排队,与同主站的 Build / SDO 互斥串行。

darra_sdo_read_async / darra_sdo_write_async

int darra_sdo_read_async(darra_async_gate_t* gate, uint16_t slave,
uint16_t index, uint8_t sub, int complete_access,
darra_sdo_read_done_cb done_cb, void* user);
int darra_sdo_write_async(darra_async_gate_t* gate, uint16_t slave,
uint16_t index, uint8_t sub,
const uint8_t* data, uint32_t len, int complete_access,
darra_sdo_write_done_cb done_cb, void* user);

异步 SDO 读写。转调父主站的同一把串行闸,绝不与同主站 PDO / 状态切换并发打邮箱。写入 data 入队时深拷贝;读返回的 native 缓冲在 done_cb 后由 FreeMemory 释放(回调内用完即可,不要长期持有指针)。

darra_scan_async / cancel / shutdown

int  darra_scan_async(const char* adapter, const char* secondary,
darra_scan_done_cb done_cb, void* user);
void darra_scan_async_cancel(void);
void darra_scan_async_shutdown(void);

异步静态扫描(不构建主站)。走独立静态扫描 worker,取消走与主站取消严格区分的独立取消通道。

四、取消语义

操作类型取消行为取消通道
主站 Build / SetState / SDO中断当前在跑的总线/邮箱操作,并复位内部取消状态主站取消通道
静态扫描 darra_scan_async中断当前扫描,并复位扫描专用的内部取消状态独立扫描取消通道
取消后内部状态会自动复位

取消操作会中断当前在跑的操作,并由隔离层自动复位内部取消状态。若不复位,残留状态会让后续 Build / SetState 误判为"被中断"而失败。隔离层已内置复位(darra_async_gate_cancel / darra_scan_async_cancel),应用层无需手动复位。

文件清单

异步隔离层随 SDK 头文件分发,原同步 EcInit / EcInitFromFile / SetStateSequence / QuickSlaveCount / dx_sdo_read[_ex] / dx_sdo_write[_ex] 全部保留,向后兼容。