Darra OPC UA Client — C SDK
Darra OPC UA Client 协议栈最底层的对外接口, 也是 C# / Java / Python / C++ / Rust 五种语言绑定的共同基座. 与其他语言不同, C 用户没有 wrapper class — 直接 #include <darra_opcua/darra_opcua.h>, 调用 DarraUa_* 系列 C ABI 函数即可.
章节导航
- 入门 → Session 会话 (创建 / 连接 / Read / Write / KeepAlive)
- 浏览 → Nodes 节点 (Browse / 路径解析 / RegisterNodes)
- 实时 → Subscription 订阅 (Subscription_Create / MonitoredItem / DataChange)
- 历史 → History 历史 (Raw / Modified / AtTime / Processed / Events / Update / Delete)
- 调用 → Method 方法 (Session_Call)
- 发现 → Discovery 发现 (Discovery_GetEndpoints / FindServers)
- 数据 → Data Types (Variant / DataValue / WellKnownNodes)
- 安全 → Security (证书 / 加密)
安装
- CMake (推荐)
- MSVC 一行编译
- GCC / Clang
cmake_minimum_required(VERSION 3.16)
project(my_opcua_app C)
set(CMAKE_C_STANDARD 99)
set(DARRA_STACK_ROOT "/path/to/Darra_OPCUA_Stack")
add_executable(my_app main.c)
target_include_directories(my_app PRIVATE "${DARRA_STACK_ROOT}/include")
target_link_directories(my_app PRIVATE "${DARRA_STACK_ROOT}/build")
if(WIN32)
target_link_libraries(my_app PRIVATE Darra.OpcUa.Core)
else()
target_link_libraries(my_app PRIVATE darra_opcua)
endif()
cl /std:c11 /utf-8 /I<stack>\include main.c /link <stack>\build\Darra.OpcUa.Core.lib
gcc -std=c99 -I<stack>/include main.c -L<stack>/build -ldarra_opcua -o my_app
系统要求
| 项 | 要求 |
|---|---|
| Compiler | C99+ (GCC 8+, Clang 10+, MSVC 2019+) |
| OS | Windows 10/11, Linux (Ubuntu 20.04+), macOS 11+ |
| 平台 | x64 |
| 协议 | OPC UA TCP (opc.tcp://) |
| 库 | Darra.OpcUa.Core.dll (Windows) / libdarra_opcua.so (Linux) |
头文件
主入口只有一个: <darra_opcua/darra_opcua.h>, 它已经包含 status / types / session / subscribe 子头. 历史与事件订阅按需加:
#include <darra_opcua/darra_opcua.h> /* Session + Browse + Subscription */
#include <darra_opcua/darra_opcua_history.h> /* HistoryRead / HistoryUpdate */
#include <darra_opcua/darra_opcua_event.h> /* Event 订阅 (Alarms & Conditions) */
主要类型:
| 类型 | 用途 |
|---|---|
DarraUa_SessionHandle | 会话句柄 (uint32_t, 0 = 非法) |
DarraUa_SubscriptionHandle | 订阅句柄 |
DarraUa_MonitoredItemHandle | MonitoredItem 句柄 |
DarraUa_Variant* | 通用值容器 (opaque) |
DarraUa_DataValue* | Read 返回的完整值 (opaque) |
DarraUa_BrowseResult* | Browse 返回的子节点列表 (opaque) |
DarraUa_Status | StatusCode (uint32_t, 0 = Good) |
DarraUa_NodeClass / DarraUa_AttributeId | OPC UA 枚举 |
跨平台 sleep 宏
C99 没有标准 sleep, 跨平台 demo 一律放这段在文件顶部:
#if defined(_WIN32)
# include <windows.h>
# define OPCUA_SLEEP_MS(ms) Sleep(ms)
# define OPCUA_SLEEP_SEC(s) Sleep((s) * 1000)
#else
# include <unistd.h>
# define OPCUA_SLEEP_MS(ms) usleep((ms) * 1000)
# define OPCUA_SLEEP_SEC(s) sleep(s)
#endif
一分钟快速开始
#include <darra_opcua/darra_opcua.h>
#include <stdio.h>
int main(int argc, char** argv)
{
const char* endpoint = (argc >= 2) ? argv[1] : "opc.tcp://localhost:4840";
/* 1. 库初始化 */
DarraUa_Initialize();
/* 2. 配置 + 创建 Session */
DarraUa_SessionConfig cfg;
DarraUa_SessionConfig_Init(&cfg);
cfg.endpoint_url = endpoint;
cfg.security_mode = DARRA_UA_MSG_SECURITY_MODE_NONE;
cfg.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None";
cfg.user_token_type = DARRA_UA_USER_TOKEN_ANONYMOUS;
DarraUa_SessionHandle h = DARRA_UA_INVALID_SESSION_HANDLE;
DarraUa_Session_Create(&cfg, &h);
DarraUa_Session_Connect(h);
/* 3. 读 ServerStatus.CurrentTime */
DarraUa_Status st = 0;
DarraUa_DataValue* dv = DarraUa_Session_ReadNode(
h, "i=2258", DARRA_UA_ATTR_VALUE, &st);
if (dv && DARRA_UA_STATUS_IS_GOOD(st)) {
const DarraUa_Variant* v = DarraUa_DataValue_GetValue(dv);
DarraUa_DateTime ft = DarraUa_Variant_GetDateTime(v);
printf("CurrentTime ft=%lld\n", (long long)ft);
DarraUa_DataValue_Delete(dv);
}
/* 4. 反向清理 */
DarraUa_Session_Disconnect(h);
DarraUa_Session_Close(h);
DarraUa_Shutdown();
return 0;
}
高级 API 概览
| 功能 | 入口 API | 说明 |
|---|---|---|
| 库初始化 / 卸载 | DarraUa_Initialize / DarraUa_Shutdown | 进程级, 内部引用计数 |
| 创建会话 | DarraUa_Session_Create(&cfg, &h) | 不连接 |
| 连接 / 断开 | DarraUa_Session_Connect / Disconnect / Close | None / Sign / SignAndEncrypt + Username / Anonymous |
| 单点读写 | DarraUa_Session_ReadNode / WriteNode | 字符串 NodeId 便捷 API |
| 批量读 | DarraUa_Session_ReadNodes / Read | 一次 RPC, N 节点 |
| 浏览 | DarraUa_Session_BrowseNode / Browse / BrowseNodes / BrowseNext | 含分页支持 |
| 路径解析 | DarraUa_Session_TranslateBrowsePaths / ResolveBrowsePath | 浏览路径 → NodeId |
| 注册节点 | DarraUa_Session_RegisterNodes / UnregisterNodes | 高频访问加速 |
| 数据订阅 | DarraUa_Subscription_Create + _AddNode / _AddNodes | publishing / sampling 双层 |
| 事件订阅 | DarraUa_Event_AddItem (darra_opcua_event.h) | 报警 & 条件 |
| 调方法 | DarraUa_Session_Call(...) | OPC UA Method 节点 |
| 历史读 | DarraUa_Session_HistoryReadRaw / Modified / AtTime / Processed / Events | 5 模式 |
| 历史写 | DarraUa_Session_HistoryUpdateData / HistoryDeleteRange | Insert / Replace / Update / Remove |
| 心跳 | cfg.keepalive_interval_ms | 后台周期探测 |
| 自动重连 | cfg.auto_reconnect = 1 | 检测断线 → 重连 |
| 服务发现 | DarraUa_Discovery_GetEndpoints / FindServers | 不需要 Session |
| 命名空间 | DarraUa_Session_GetNamespaceUri / FindNamespaceIndex | 防 ns 漂移 |
内存所有权
C 没有 RAII / GC, 所有"返回堆对象"的 API 都需要调用对应的 _Delete. 谁分配谁释放, Stack 分配的对象用 Stack 提供的 _Delete 释放, 切勿混用 free() (跨 DLL CRT 不一致会崩).
| 分配 | 释放 |
|---|---|
DarraUa_Variant_New() | DarraUa_Variant_Delete(v) |
DarraUa_Session_ReadNode(...) | DarraUa_DataValue_Delete(dv) |
DarraUa_Session_BrowseNode(...) | DarraUa_BrowseResult_Delete(br) |
DarraUa_Session_HistoryReadRaw(...) | DarraUa_DataValueArray_Delete(arr, count) |
DarraUa_Session_HistoryReadEvents(...) | DarraUa_EventArray_Delete(arr, count) |
DarraUa_Session_Call(...) | DarraUa_VariantArray_Delete(arr, count) |
DarraUa_Discovery_GetEndpoints(...) | DarraUa_GetEndpointsResult_Delete(r) |
DarraUa_Session_TranslateBrowsePaths(...) 单项 | DarraUa_Free(out_node_ids[i]) |
| Stack 内部 malloc 的"裸缓冲区" | DarraUa_Free(p) |
句柄 (uint32_t) 由 Stack pool 管理, 不要 free():
| 句柄 | 关闭 |
|---|---|
DarraUa_SessionHandle | DarraUa_Session_Close(h) |
DarraUa_SubscriptionHandle | DarraUa_Subscription_Delete(sub) |
DarraUa_MonitoredItemHandle | DarraUa_MonitoredItem_Delete(sub, mi) |
错误处理模板
C 没有异常, 每个 API 都返回 DarraUa_Status:
DarraUa_Status st = DarraUa_Session_Connect(h);
if (!DARRA_UA_STATUS_IS_GOOD(st)) {
fprintf(stderr, "connect failed: 0x%08X (%s)\n",
(unsigned)st, DarraUa_StatusName(st));
/* 走 cleanup goto label */
}
线程模型与回调
- 所有
DarraUa_*API 线程安全 (内部加锁), 可在多线程并发调用同一会话 - 订阅 / 事件回调由 Stack 内部线程池触发, 与调用线程不同
- 手动 pump 模式下 (调
DarraUa_Session_Publish), 回调在调用 Publish 的线程触发 - 调
DarraUa_Session_StartAutoPublish(h)让 SDK 后台线程持续 Publish - 回调里访问跨线程共享数据务必加锁 / 用原子, 不要做长时间计算或阻塞 IO
跨语言对齐
C SDK 暴露的函数与 C# SDK 一一对应, C# ua.Read("ns=2;s=X") 等价于 C DarraUa_Session_ReadNode(handle, "ns=2;s=X", attr, &status). 概念性内容 (协议 / 行为) 请优先参考 C# 文档, C 文档只强调 C 特有的内存所有权和回调线程语义.
| 操作 | C# (DarraOpcUa) | C |
|---|---|---|
| 库初始化 | (隐式) | DarraUa_Initialize() |
| 创建会话 | new DarraOpcUa(url) | DarraUa_Session_Create(&cfg, &h) |
| 连接 | ua.Connect() | DarraUa_Session_Connect(h) |
| 读单节点 | ua.Read("i=2258") | DarraUa_Session_ReadNode(h, "i=2258", attr, &st) |
| 写单节点 | ua.Write(id, v) | DarraUa_Session_WriteNode(h, id, attr, v) |
| 浏览 | ua.Browse("i=84") | DarraUa_Session_BrowseNode(h, "i=84", filter, &res) |
| 创建订阅 | ua.CreateSubscription(500) | DarraUa_Subscription_Create(h, &cfg, &sub) |
| 订阅一节点 | sub.Add("i=2258") | DarraUa_Subscription_AddNode(sub, "i=2258", attr, smpl, cb, ctx, &mi) |
| Pump | ua.Publish(2000) | DarraUa_Session_Publish(h, 2000) |
| 销毁 | using var ua | DarraUa_Session_Close(h) |
按左侧侧边栏顺序读完即可掌握全部 C ABI.