跳到主要内容

BrowseCrawler — 整树离线爬取

概述

IDE 启动 / HMI 联机时常需要把服务端 AddressSpace 子树拉到本地, 后续节点搜索 / 自动补全无需再发 Browse RPC。BrowseCrawler 用 BFS 队列遍历, 已访问 set 去重 (服务端节点图允许有环), 双重保护 (maxDepth + maxNodes) 防爆。

对应规范段: Part 4 §5.8 (Browse 服务集)。

API

成员类别读写说明
new BrowseCrawler(ua, maxDepth, maxNodes, nodeClassFilter)构造构造爬取器
CrawlAsync(rootNodeId, progress, ct)方法异步 BFS 爬取, 返回 CrawlResult
CrawlResult.AllNodes属性扁平节点列表 (List<CrawlNode>)
CrawlResult.ChildrenByParent属性父子关系字典
CrawlResult.Elapsed属性总耗时
CrawlNode.NodeId / BrowseName / DisplayName / NodeClass / Depth属性节点字段

构造参数:

参数默认说明
maxDepth10最大递归深度 (防深度爆)
maxNodes100,000最大节点数 (防总数爆)
nodeClassFilterUnspecified仅返回此 NodeClass (BFS 仍遍历, 不写入结果)

代码示例

using DarraOpcUa_Client;

using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();

// 1) 爬整棵 ObjectsFolder
var crawler = new BrowseCrawler(ua, maxDepth: 8, maxNodes: 50_000);

var result = await crawler.CrawlAsync(
WellKnownNodes.ObjectsFolder,
progress: new Progress<(int count, string nid)>(p =>
Console.Write($"\r已抓 {p.count} 个, 当前 {p.nid}")));

Console.WriteLine($"\n共 {result.AllNodes.Count} 节点, 耗时 {result.Elapsed.TotalSeconds:F1}s");

// 2) 仅抓 Variable 节点 (建本地 Tag 字典)
var c2 = new BrowseCrawler(ua, nodeClassFilter: NodeClass.Variable);
var r2 = await c2.CrawlAsync("ns=2;s=Boilers");
foreach (var n in r2.AllNodes)
Console.WriteLine($" {n.BrowseName} ({n.NodeId})");

// 3) 取消支持 (用户关闭对话框)
using var cts = new CancellationTokenSource();
cancelButton.Click += (_, _) => cts.Cancel();
var r3 = await crawler.CrawlAsync("i=85", null, cts.Token);
Console.WriteLine($"取消时已抓 {r3.AllNodes.Count} 个");

实现细节

  • BrowseSafe 调底层, 单点失败不中断整棵爬取
  • 每抓 100 个节点汇报一次 Progress, 避免 UI 线程被刷屏
  • 已访问 set 用 StringComparer.Ordinal 严格匹配 NodeId
  • Task.Run 跑后台, 不阻塞调用线程

性能基准

节点规模耗时 (千兆 LAN)
1,000~0.5s
10,000~5s
50,000~25s

实际取决于服务端节点图深度 + Browse RPC 单次返回量。

最佳实践

  • 限定 root — 不要从 i=84 (Root) 开始, 改从 i=85 (Objects) 或具体子树
  • nodeClassFilter — 只关心 Variable 时过滤, 节省内存
  • 启动时一次性爬 — 后续编辑器 / 树面板从 AllNodes 本地查
  • 取消支持 — 给用户"取消加载"按钮, CrawlAsync 会立即返回当前已抓部分
  • 不要爬死循环maxDepth / maxNodes 必设, 防"无限节点服务端"卡死

跨语言对照

C#PythonJavaC++RustC
BrowseCrawlerBrowseCrawlerBrowseCrawlerBrowseCrawlerBrowseCrawlerDarraUa_BrowseCrawler_*
CrawlAsynccrawl_asynccrawlAsyncCrawlAsynccrawl_asyncDarraUa_BrowseCrawler_Crawl

下一步