browse
browse(node_id, filter = NodeClass.Unspecified) — 列出指定节点的直接子节点 (一层).
前置阅读 / 配套
- 大节点分页请用 browse_with_paging + browse_next.
- 路径式批量解析请用 translate_browse_paths.
签名
def browse(self,
node_id: str,
filter: NodeClass = NodeClass.Unspecified) -> List[Reference]: ...
def browse_many(self,
node_ids: List[str],
filter: NodeClass = NodeClass.Unspecified) -> List[List[Reference]]: ...
Reference 字段
| 字段 | 类型 | 说明 |
|---|---|---|
| node_id | str | 子节点 NodeId |
| browse_name | str | 浏览名 |
| display_name | str | 显示名 |
| node_class | NodeClass | 子节点类别 |
Reference.to_node(session) 转为可操作的 OpcUaNode.
例子
from opcua import NodeClass
# 列出 Boiler1 下所有子节点
children = ua.browse("ns=2;s=Boiler1")
for c in children:
print(f" {c.node_class.name} {c.browse_name} -> {c.node_id}")
# 只看 Variable 类型
vars_ = ua.browse("ns=2;s=Boiler1", filter=NodeClass.Variable)
# 只看 Method
methods = ua.browse("ns=2;s=Calculator", filter=NodeClass.Method)
批量浏览 (browse_many)
如果同时要浏览多个节点 (例如 GUI 初次展开树), 用 browse_many 一次 RPC:
roots = ["i=85" /* Objects */, "i=86" /* Types */]
results = ua.browse_many(roots)
for nid, refs in zip(roots, results):
print(f"{nid}:")
for c in refs:
print(f" {c.browse_name}")
如果某个节点浏览失败 (NodeId 错), 对应槽位返回空列表, 不抛异常.
NodeClass filter
| 值 | 包含 |
|---|---|
| Unspecified (0) | 全部 |
| Object (1) | 仅 Object |
| Variable (2) | 仅 Variable |
| Method (4) | 仅 Method |
| Object | Variable (按位或) | 组合 |
异常
OpcUaException(BadNodeIdUnknown) / OpcUaException(BadCommunicationError) 等. transport 失败抛, 单个子节点失败不抛.
大节点的分页 (ContinuationPoint)
如果一个节点有 1000+ 个子节点, 单次 browse 服务端可能截断. 此时 browse 只返回第一页.
要看全部, 用 browse_with_paging + browse_next:
page = ua.browse_with_paging("ns=2;s=BigFolder")
all_refs = list(page.references)
while page.continuation_point is not None:
page = ua.browse_next(page.continuation_point)
all_refs.extend(page.references)
性能
- 单次 browse: ~5-15 ms (网络往返 + 服务端遍历)
- browse_many 批量: 总耗时 ~ 单次 (省 N-1 次往返)
- 1000+ 子节点会分页, 加 browse_next 处理
browse_filtered — 方向 + 引用类型过滤
browse_filtered 在 browse 的基础上加 BrowseDirection + reference_type_id + include_subtypes + result_mask 过滤 (Part 4 §5.8.2.2).
def browse_filtered(self,
nid: str,
node_class_filter: NodeClass = NodeClass.Unspecified,
direction: BrowseDirection = BrowseDirection.FORWARD,
reference_type_id: str = None,
include_subtypes: bool = True,
result_mask: int = 0x3F) -> List[Reference]: ...
相关结构:
class BrowseDirection(IntEnum):
FORWARD = 0 # 仅正向 (出引用), 父→子
INVERSE = 1 # 仅反向 (入引用), 子→父
BOTH = 2 # 正反向都返回
示例
from opcua import BrowseDirection
# 1) 正向 + 仅 HierarchicalReferences (i=33)
children = ua.browse_filtered("ns=2;s=Boiler1",
direction=BrowseDirection.FORWARD,
reference_type_id="i=33",
include_subtypes=True)
# 2) 反向找父
parents = ua.browse_filtered("ns=2;s=Tank.Level",
direction=BrowseDirection.INVERSE)
for p in parents:
print(f"父: {p.browse_name}")
# 3) 双向
all_refs = ua.browse_filtered("ns=2;s=Robot.Joint1", direction=BrowseDirection.BOTH)
实现说明
- Stack 提供
DarraUa_Session_BrowseWithFilter时直调原生 - 否则 fallback 到 browse + 客户端按 NodeClass 过滤; reference_type_id / direction 在 fallback 模式下无法精确生效, 打 Warning
browse_all — 自动续翻
def browse_all(self, node_id: str,
filter: NodeClass = NodeClass.Unspecified,
max_pages: int = 32) -> List[Reference]: ...
自动反复调 browse_next 直到 ContinuationPoint 为空:
all_refs = ua.browse_all("ns=2;s=BigFolder")
print(f"共 {len(all_refs)} 个子节点")