跳到主要内容

主动异步隔离 (Async Isolation)

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

实现要点 —— 每主站串行闸(单 worker 线程 + FIFO 队列,二值信号量语义)、关闭守门、取消复位 abort latch、独立静态扫描闸。命名遵循 C 惯例(darra_* 前缀 + _async 后缀 + 回调)。旧同步 API 全部保留。

头文件与 FFI

异步隔离层随 SDK 头文件提供(utils/async_isolate.h),直接调底层运行时库的同名函数(阻塞操作 EcInit / SetStateSequence / QuickSlaveCount / dx_sdo_read[_ex] / dx_sdo_write[_ex]);不收 DLL 句柄、不设函数指针分发表,回调是普通 C 函数指针。每主站一个 darra_async_gate_t。

一、设计要点​

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

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

二、API 总览​

回调 typedef 共 6 个:darra_async_progress_cb + darra_async_build_done_cb / darra_async_setstate_done_cb / darra_async_scan_done_cb / darra_async_sdo_read_done_cb / darra_async_sdo_write_done_cb,均在 worker 线程触发。返回错误码见 错误码。

类别函数返回类型读写说明
闸生命周期darra_async_gate_create()darra_async_gate_t只读创建串行闸(单 worker + FIFO 队列),不收主站参数
闸生命周期darra_async_gate_cancel(gate, token)int只写取消:token=NULL 广播取消在跑+排队任务;token 非空精确取消该操作
闸生命周期darra_async_gate_destroy(gate)void只写置 shutting_down + 关闭闸 + 回收残余任务
闸生命周期darra_async_gate_busy(gate)int只读1=有任务在排队/执行,0=空闲(诊断观测用)
操作令牌darra_async_token_cancel(token)int只写单操作取消主入口(token 内部记住自己的 gate)
操作令牌darra_async_token_release(token)void只写释放调用方持有的 token 引用(引用计数 -1)
主站一条龙darra_build_async_json(gate, json, progress_cb, done_cb, user_data, out_token)int只写异步从 JSON 配置 连接→扫描→配置→进 OP
主站一条龙darra_build_async_file(gate, path, progress_cb, done_cb, user_data, out_token)int只写异步从配置文件构建
状态切换darra_set_state_async(gate, master, target_state, timeout_ms, progress_cb, done_cb, user_data, out_token)int只写异步状态切换,经串行闸排队
从站 SDOdarra_sdo_read_async(gate, master, slave, index, subindex, complete_access, done_cb, user_data, out_token)int只读异步读 SDO,转调父主站同一把闸
从站 SDOdarra_sdo_write_async(gate, master, slave, index, subindex, data, len, complete_access, done_cb, user_data, out_token)int只写异步写 SDO(入队深拷贝 data)
静态扫描darra_scan_async(adapter, done_cb, user_data, out_token)int只写异步静态扫描,走独立扫描闸 + 独立 AbortScan
静态扫描darra_scan_async_cancel(token)int只写取消扫描(token=NULL 广播取消)
静态扫描darra_scan_async_shutdown(void)void只写关闭静态扫描 worker

错误码 (DARRA_ASYNC_*)​

常量值说明
DARRA_ASYNC_OK0已成功排队(操作本身的结果在 done_cb)
DARRA_ASYNC_ERR_PARAM-1参数非法(gate / 必需入参为空等)
DARRA_ASYNC_ERR_OOM-2内存分配失败(任务/队列节点)
DARRA_ASYNC_ERR_SHUTDOWN-3闸已置 shutting_down,拒绝新提交
DARRA_ASYNC_ERR_OS-4底层 OS 调用失败(建线程/锁)

三、函数详解​

回调 typedef 与所有权​

typedef void (*darra_async_progress_cb)(const char* message, void* user_data);
typedef void (*darra_async_build_done_cb)(uint16_t result_master, int canceled, void* user_data);
typedef void (*darra_async_setstate_done_cb)(int success, int canceled, void* user_data);
typedef void (*darra_async_scan_done_cb)(int slave_count, int canceled, void* user_data);
typedef void (*darra_async_sdo_read_done_cb)(const uint8_t* data, int len,
uint32_t abort_code, int canceled, void* user_data);
typedef void (*darra_async_sdo_write_done_cb)(int success, uint32_t abort_code,
int canceled, void* user_data);
  • progress_cb 的 message 仅回调期间有效(UTF-8),需保留请自己 strdup
  • build_done 的 result_master:成功返回主站索引(>0),失败返回 0
  • sdo_read_done 的 data 由 SDK 内部分配,回调返回后由 SDK 自动释放(失败/取消时为 NULL);需保留请在回调内 memcpy 走,不要长期持有指针
  • abort_code 为 SDO Abort Code(0=无 abort)
  • canceled:1=操作被取消,0=正常完成

darra_async_gate_create / destroy​

darra_async_gate_t darra_async_gate_create(void);
void darra_async_gate_destroy(darra_async_gate_t gate);
int darra_async_gate_busy(darra_async_gate_t gate);
int darra_async_gate_cancel(darra_async_gate_t gate, darra_async_token_t token);

创建/销毁串行闸(每 master 一个闸,由调用方持有;不收主站索引参数)。darra_async_gate_destroy 先置 shutting_down,再关闭 —— 残余任务以 canceled=1 回调掉,不丢用户回调,防 use-after-free。darra_async_gate_cancel(gate, NULL) 为广播取消(在跑 + 排队全部取消),非 NULL token 则精确取消该操作。

darra_build_async_json / darra_build_async_file​

int darra_build_async_json(darra_async_gate_t gate, const char* json,
darra_async_progress_cb progress_cb,
darra_async_build_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);
int darra_build_async_file(darra_async_gate_t gate, const char* path,
darra_async_progress_cb progress_cb,
darra_async_build_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);

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

参数:

  • gate — 主站串行闸
  • json / path — 配置(JSON 串 / 文件路径)
  • progress_cb — 进度回调,worker 线程触发,自行 marshal(可空)
  • done_cb — 完成回调(携带 canceled 标志,可空)
  • user_data — 透传用户指针
  • out_token — 输出取消令牌(可空 = 不需要取消能力);排队成功写入 token,排队失败写 NULL,用完调 darra_async_token_release

返回值:

  • int — DARRA_ASYNC_OK(0)入队成功;负值入队失败(参数非法 / OOM / 闸正在关闭)

示例:

static void on_progress(const char* msg, void* user) {
/* worker 线程: 推消息队列回主线程, 不直接动 UI */
}
static void on_build_done(uint16_t result_master, int canceled, void* user) {
/* worker 线程: 成功时 result_master>0, 失败=0 */
}

darra_async_gate_t gate = darra_async_gate_create();
darra_async_token_t token = NULL;
darra_build_async_json(gate, cfg_json, on_progress, on_build_done, NULL, &token);
/* ... 调用方线程立刻往下跑 ... */
/* 需要取消时: darra_async_token_cancel(token); 用完: darra_async_token_release(token); */

darra_set_state_async​

int darra_set_state_async(darra_async_gate_t gate, uint16_t master,
int target_state, uint32_t timeout_ms,
darra_async_progress_cb progress_cb,
darra_async_setstate_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);

异步状态切换(底层走 DarraEcat_SetStateSequence,含重试/启动参数/进 OP 等待),经主站串行闸排队,与同主站的 Build / SDO 互斥串行。master 为目标主站索引(Build 成功后拿到),target_state 取 EC_STATE_*,timeout_ms 为切换超时(0 用底层默认)。

darra_sdo_read_async / darra_sdo_write_async​

int darra_sdo_read_async(darra_async_gate_t gate, uint16_t master, uint16_t slave,
uint16_t index, uint8_t subindex, int complete_access,
darra_async_sdo_read_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);
int darra_sdo_write_async(darra_async_gate_t gate, uint16_t master, uint16_t slave,
uint16_t index, uint8_t subindex,
const uint8_t* data, int len, int complete_access,
darra_async_sdo_write_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);

异步 SDO 读写。转调父主站的同一把串行闸,绝不与同主站 PDO / 状态切换并发打邮箱。写入 data 入队时深拷贝(调用方可立即释放原 buf);读返回的缓冲由 SDK 内部持有,回调返回后自动释放(回调内用完即可,不要长期持有指针)。底层优先走带 abort code 的 _ex 入口,不可用则回退基础读写。

darra_scan_async / cancel / shutdown​

int  darra_scan_async(const char* adapter,
darra_async_scan_done_cb done_cb, void* user_data,
darra_async_token_t* out_token);
int darra_scan_async_cancel(darra_async_token_t token);
void darra_scan_async_shutdown(void);

异步静态扫描(不构建主站,只扫 adapter 指定网卡)。走独立静态扫描 worker(共享扫描闸串行化并发扫描请求),取消走与主站取消严格区分的独立取消通道。darra_scan_async_cancel(token) 非空 token 精确取消该次扫描,NULL 广播取消当前在跑 + 排队扫描。

四、取消语义​

取消按 token 精确定位:每个 *_async 可选输出 out_token,token 由 SDK 内部引用计数管理(提交时 SDK 持一份,任务跑完自动释放;调用方那份用完必须 darra_async_token_release)。

操作类型取消行为取消入口
主站 Build / SetState / SDO中断当前在跑的总线/邮箱操作,并复位内部取消状态darra_async_token_cancel(token) 或 darra_async_gate_cancel(gate, token)
静态扫描 darra_scan_async中断当前扫描,并复位扫描专用的内部取消状态darra_scan_async_cancel(token)
  • token == NULL:广播取消 —— 当前正在跑的任务 + 所有排队任务
  • token != NULL:只取消该 token 对应的那一个任务;任务已结束 / 闸已销毁时取消是安全空操作
取消后内部状态会自动复位

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

文件清单​

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

邮箱事务级的异步管线(ec_mbx_submit_async / ec_mbx_wait / ec_mbx_cancel,ticket 取结果)见 邮箱网关;线程原语 thread_create / thread_join 的 ASYNC_* 错误码见 错误处理。