# opcua-learning **Repository Path**: chen-dongyu123/opcua-learning ## Basic Information - **Project Name**: opcua-learning - **Description**: OPCUA学习项目 - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-10 - **Last Updated**: 2026-09-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OPC UA 系列资料与实现核查清单 当前覆盖:Part 1—3;核查日期:2026-09-05。正文逐篇追加条款和固定版本实现依据。 ## 标准与官方文档 | 来源 | 修订/版本与位置 | 本篇用途 | |---|---|---| | [OPC UA Part 1](https://reference.opcfoundation.org/specs/OPC-10000-1) | 在线发布页 1.05.06;§4.1 Scope、§5.3 OPC UA Servers | 信息/通信/一致性体系;应用、服务器、地址空间及订阅实体 | | [Part 1 §4.1](https://reference.opcfoundation.org/specs/OPC-10000-1/4.1) | Scope | 标准的基础模型分工 | | [Part 1 §5.3](https://reference.opcfoundation.org/specs/OPC-10000-1/5.3) | OPC UA Servers | 服务器及相关实体概念 | | [OPC UA Part 3 §5.2.2](https://reference.opcfoundation.org/specs/OPC-10000-3/5.2.2) | 1.05.06;NodeId | 标识、命名空间与节点身份 | | [OPC UA Part 14 §5.1](https://reference.opcfoundation.org/specs/OPC-10000-14/5.1) | 在线现行页;General | PubSub 与 Client/Server 的关系;正文不使用版本相关消息字段 | | [OPC Foundation:Classic](https://opcfoundation.org/about/opc-technologies/opc-classic/) | 官网介绍,按检索日记录 | COM/DCOM、DA、A&E、HDA 背景 | | [OPC Foundation:Unified Architecture](https://opcfoundation.org/about/opc-technologies/opc-ua/) | 官网介绍,按检索日记录 | 平台独立性、统一架构及 IEC 62541 关系 | | [Python Coroutines and Tasks](https://docs.python.org/3.12/library/asyncio-task.html) | Python 3.12 文档;协程、sleep、timeout 等节 | 异步让出执行、顶层运行和超时处理 | | [uv CLI](https://docs.astral.sh/uv/reference/cli/#uv-sync) | 在线文档;uv sync / --frozen;本机 uv 0.12.1 | 固定锁文件与环境复现 | | [UaExpert 厂商页](https://www.unified-automation.com/products/development-tools/uaexpert.html) | 官网介绍,未安装本地产品 | 独立客户端复验入口,不作为已实测证据 | 规范解释最终以对应正式发布文件为准;在线页面可能更新。没有记录或验证的子功能不沿用整分册的“支持”结论。 ## Python 实现依据 发布物:[asyncua 2.0.1](https://pypi.org/project/asyncua/2.0.1/)。本地 `.venv` 与全新 `.venv-repro` 均实际安装该版本;完整依赖见 [uv.lock](./examples/uv.lock)。 本篇最小示例由教学模型和实验要求编写。上游 [server-minimal.py](https://github.com/FreeOpcUa/opcua-asyncio/blob/master/examples/server-minimal.py) 与 [client-minimal.py](https://github.com/FreeOpcUa/opcua-asyncio/blob/master/examples/client-minimal.py) 用作入口参考;这两个 master 链接会变化,因此不作为实验版本锁定依据。 实际检查安装包中以下接口的签名和调用方式: | 接口 | 2.0.1 核查结果 | L01 是否实际调用 | |---|---|---| | `Server.set_application_uri()` | 异步,需要 await | 是 | | `Server.register_namespace()` | 异步,返回索引 | 是 | | `Server.set_security_policy()` | 同步配置 | 是 | | `Server.set_identity_tokens()` | 同步配置,可选 AnonymousIdentityToken | 是 | | `Node.add_object()` / `Node.add_variable()` | 异步创建 | 是 | | `Node.write_value()` | 异步,可显式传 VariantType | 是 | | `Client(..., auto_reconnect=False)` | 构造函数存在该选项,默认 False | 是,显式关闭 | | `Client.session_state_path` 及恢复相关实现 | 源码中可见,仅作为后续核查线索 | 否,不能据此宣称恢复功能已验收 | 安装包实现文件 SHA-256 与教学代码摘要保存在 [L01 metadata.json](./evidence/L01/metadata.json)。本篇未修改上游库。 --- ## Part 2 追记:信息模型、DataValue 与 NodeSet2(核查日期 2026-09-05) ### 标准引用(正文超链接所在位置) | 来源 | 修订/位置 | 正文用途 | |---|---|---| | [OPC UA Part 3 §4.1](https://reference.opcfoundation.org/specs/OPC-10000-3/4.1) | 在线 1.05.06 | NodeClass 与属性概览 | | [OPC UA Part 3 §5.2.2 NodeId](https://reference.opcfoundation.org/specs/OPC-10000-3/5.2.2) | 1.05.06 | NodeId 与命名空间;禁止 ns 依赖做业务身份 | | [Part 3 §7 ReferenceTypes](https://reference.opcfoundation.org/specs/OPC-10000-3/) | 1.05.06 | HasComponent / HasTypeDefinition 等引用语义(页内子项) | | [OPC UA Part 8(Data Access)](https://reference.opcfoundation.org/specs/OPC-10000-8/) | 在线现行页 | EUInformation / 单位 / 范围工程质量语义;正文只按本文实测使用 V/4408652 | | DataValue / StatusCode / Timestamps | 在线 Part 4 §7 语义与 Part 8 Quality;正文以 2.0.1 实测行为引用 | 严格/宽容读差异、两条时间戳 | | NodeSet / UANodeSet | Part 6 与官网格式 | XML 资产可移植性的概念定位(不把 NodeSet2 当站点配置) | 规范化表述以对应正式发布文件为准;未逐项核对的分册不沿用“全分册支持”式结论。 ### 2.0.1 实际调用核查表(Part 2 实验用) | 接口/成员 | 2.0.1 结果 | 哪些 lab 实际调用 | |---|---|---| | `Node.set_modelling_rule(True/None)` | 存在;实例成员按 Mandatory 复制 | L02/L03/L04 模板成员 | | `Node.add_object(..., objecttype=...)` 实例化 ObjectType | B导入/生成成员(前缀 NodeId) | L02/L04 | | `ObjectType.add_variable` & `add_property`(EngineeringUnits/Content) | ok | L02/L04 | | `Node.read_type_definition()`(实例到类型引用) | 返回模板 NodeId | L02/L04 | | `Node.get_properties()`/`get_children()`/`read_node_class()` | 返回真实引用/NodeClass | L02/L03/L04 | | `EUInformation` dataclass + ExtensionObject 属性 | 可写入并复制到实例;read 得内置结构 | L02/L04 | | `Client.get_namespace_array()`/`get_namespace_index(uri)` | 返回运行时数组/索引 | L02/L03/L04 | | `read_data_value(raise_on_bad_status)` | 默认在 Bad 上抛;False 时保留 Status/两条时间、Value 可为 None | L03 | | `Node.set_writable(True)` + `write_value(varianttype)` | 双精度设点可写 | L03 | | 类型不匹配写(Int32→Double 节点) | 服务端拒 `BadTypeMismatch` | L03 | | `Server.export_xml_by_ns(...)` / `import_xml(...)` | 导出 12358 B / 导入 22 节点并重映射 | L04 | | 自建自定义 DataTypeDefinition + 兼容二进制 ExtensionObject 全程复读 | 未在 2.0.1 完整闭环验证(见 L04 README 限制) | 未宣称 | 详细判定、目录与两环境计数:见 [L02](./evidence/L02/README.md)、[L03](./evidence/L03/README.md)、[L04](./evidence/L04/README.md) 的 metadata.json/日志。Part 2 未修改 asyncua 实现。 --- ## Part 3 追记:通信分层 / Session / 核心服务(核查日期 2026-09-05) ### 标准引用与定位用途 | 来源 | 修订/位置 | 用途 | |---|---|---| | OPC UA Part 6(Mappings)UA TCP 与消息分块 | 在线现行页 → UA TCP / Secure Conversation / 分块 | 3.1/3.2 帧类型、MessageSize、F/C/A 与正文不做完整加密 payload 的界定 | | OPC UA Part 4 ServiceSet | 在线 Part 4 | CreateSession/ActivateSession、Read vs Browse、Call 参数与逐项结果、OperationLimits 概念 | | OPC UA Part 4 Method(含 InputArguments/OutputArguments/Executable) | 正文引用的服务语义 | 3.6.2 方法语义与“服务接受≠控制成立”的分层 | | OPC UA Part 12 Discovery | 概念 | GetEndpoints/FindServers 只作定位(本文实验在固定端点) | | [UaExpert/第三方对端] | 未安装 | 仍为未验收互操作项 | ### 2.0.1 实际调用核查(Part 3 实验用) | 接口 | 结论 | 实验 | |---|---|---| | 直通代理采集 UA TCP(本库 l05_capture)+ 8 字节头解析 | 可采真实帧;HEL/ACK/OPN/MSG 清晰 | L05 | | `Client.open_secure_channel / create_session`修订值经客户端属性 | `session_timeout` 服务器回落 60000;channel 3600000 | L06 | | 阶段化失败(connect vs activate_read) | L06 harness 分阶段断言 | L06 | | `add_method(...,[Input],[Output])` + Variant 参数 | 服务端回调需收 Variant 并返回 ua.Variant 列表才能正确序列化 | L07 | | `Node.call_method` 输出读回/Status | Double 设点读回、`BadTypeMismatch`、Method 后 BreakerPosition 1→2 | L07 | | String->Boolean 参数不被服务边界自动拒 | 记录为 accepted_by_service;提示实现侧 Validate | L07 | 详细计数/日志:见该 lab 目录与 metadata;Part 3 未修改 asyncua 实现。 --- ## Part 4—5 追记:订阅、恢复、证书与权限(2026-09-06) ### 一手资料与定位 | 来源 | 用途 | |---|---| | [Part 4:采样间隔](https://reference.opcfoundation.org/specs/OPC-10000-4/5.13.1.2) | 请求/修订值、源刷新与监视项采样区别 | | [Part 4:队列参数](https://reference.opcfoundation.org/specs/OPC-10000-4/5.13.1.5) | DiscardOldest=false 替换最新项、容量 1、Overflow | | [Part 4:DataChangeFilter](https://reference.opcfoundation.org/specs/OPC-10000-4/7.22.2) | 三种触发器、绝对死区比较基准、百分比量程 | | [Part 4:Subscription](https://reference.opcfoundation.org/specs/OPC-10000-4/5.14.1.1) | Publish、KeepAlive、Lifetime 的职责 | | [Part 4:Republish](https://reference.opcfoundation.org/specs/OPC-10000-4/5.14.6) | 重传缓存与 BadMessageNotAvailable | | [Part 4:重新连接](https://reference.opcfoundation.org/specs/OPC-10000-4/6.7) | 旧会话激活、转移订阅、重传与状态补读的边界 | | [Part 2:安全模型](https://reference.opcfoundation.org/specs/OPC-10000-2) | 消息保护、应用认证、用户及权限分层 | | [Part 4:证书验证](https://reference.opcfoundation.org/specs/OPC-10000-4/6.1.3) | 信任与签发者列表、链、URI/主机/时间/用途等检查 | | [Part 18:Role-Based Security](https://reference.opcfoundation.org/specs/OPC-10000-18) | 标准角色体系与实验请求级授权的区分 | | [Part 12:Discovery and Global Services](https://reference.opcfoundation.org/specs/OPC-10000-12) | GDS 的体系位置,未宣称本地实现 | | [asyncua v2.0.1 validator.py](https://github.com/FreeOpcUa/opcua-asyncio/blob/v2.0.1/asyncua/crypto/validator.py) | 内置验证选项、TrustStore 依赖、主机检查限制 | 本次在线 Part 4 页显示版本 1.05.07;不将该修订号套用于全部分册。源码观察以本地锁定安装包为运行依据,相关文件为 `server/monitored_item_service.py`、`server/internal_subscription.py`、`server/internal_session.py`、`server/uaprocessor.py`、`crypto/validator.py`、`crypto/permission_rules.py`。 ### Python API 与实际验证 | 接口/适配 | 实测结论 | 限制 | |---|---|---| | `client.uaclient.session.create_subscription/create_monitored_items` | 返回完整 Revised 参数、状态与标识 | 服务层适配锁定 2.0.1 | | DataChangeTrigger | Status/StatusValue/StatusValueTimestamp 分别 2/3/4 条(含初始) | 无死区时的隔离实验 | | QueueSize/DiscardOldest | 两种设置均收到最新两项且状态 0 | 已知偏差;不能由无 Overflow 推断无覆盖 | | DataChangeFilter | 绝对死区漏掉累计小步越限;Percent 全通过并告警 | Percent 节点无 EURange,不是能力正常验收 | | `session.republish` | 不存在序号返回 seq=0 空消息 | 不符合标准错误预期;成功恢复未验证 | | `Client(auto_reconnect=False)` + 新建对象 | 三种断线重建成功,Namespace 2→4 重解析成功 | 不验证内置自动重连、转移或旧会话复用 | | `set_certificate_validator` / `client.certificate_validator` | 双向验证、指定负向证书被拒 | 单一直接签发 CA + 自定义固定 IP,不是完整链验证 | | `set_security_policy` / `set_identity_tokens` | SignAndEncrypt 成功,匿名与 None 配置失败 | None 案例只记录实际 UaError,不捏造服务错误 | | `PermissionRuleset.check_validity` | reader/operator/engineer 权限差异有效 | 请求级白名单;不是完整 RolePermissions 模型 | | 原始 Variant Method 回调 | String 被拒 BadTypeMismatch,Boolean 模拟动作后读回成功 | 未完成通用参数矩阵及实际设备控制 | 正文:[Part 4](./Part4_OPCUA_订阅机制与断线恢复.md)、[Part 5](./Part5_OPCUA_安全证书与访问控制.md)。测试计数、原始记录、环境与 SHA-256 见 [L08—L10 证据](./evidence/part4-5/README.md)。本轮没有修改已完成的 Part 1—3 或共享模型。 --- ## Part 6—7 追记:工程运行、事件、历史与文件(2026-09-06) ### 一手资料 | 来源 | 定位 | |---|---| | [Python 3.12 asyncio tasks](https://docs.python.org/3.12/library/asyncio-task.html) | TaskGroup 失败传播、取消与 to_thread;线程中的阻塞调用不因外层取消自动停止 | | [aiosqlite 文档](https://aiosqlite.omnilib.dev/en/stable/) | 每连接线程与请求队列、异步数据库操作 | | [Part 4 EventFilter](https://reference.opcfoundation.org/specs/OPC-10000-4/7.22.3) | SelectClauses / WhereClause 与字段选择 | | [Part 9 Concepts](https://reference.opcfoundation.org/specs/OPC-10000-9/4) | Event、Condition、Alarm、确认与恢复的区别 | | [Part 9 Condition state synchronization](https://reference.opcfoundation.org/specs/OPC-10000-9/4.5) | 刷新条件视图,不等于历史事件重放 | | [Part 11 ReadRawModifiedDetails](https://reference.opcfoundation.org/specs/OPC-10000-11/6.5.3.1) | 时间范围、数量与边界参数 | | [Part 11 Read raw](https://reference.opcfoundation.org/specs/OPC-10000-11/6.5.3.2) | 原始保存值、质量、时间与分页语义 | | [Part 11 Continuation Points](https://reference.opcfoundation.org/specs/OPC-10000-11/6.3) | 不透明续传状态及释放责任 | | [Part 20 FileType](https://reference.opcfoundation.org/specs/OPC-10000-20/4.2) | 文件对象属性和方法体系 | | [Part 20 Open](https://reference.opcfoundation.org/specs/OPC-10000-20/4.2.2) | 文件句柄与 Session 生命周期 | 本轮在线页:Part 4 显示 1.05.07、Part 9 显示 1.05.06、Part 11 显示 1.05.04;不同分册不使用统一小版本号。旧的 Part 11 `/6.4.3` 路径未用作正文引用,已定位到现行 `/6.5.3.1` 与 `/6.5.3.2`。 ### 锁定版本源码核查与实测 | 对象 | 核查或实测 | 边界 | |---|---|---| | `asyncio.to_thread` + 两源更新 | 阻塞 150 ms 后抛 OSError,另一源仍更新 | 有界模拟驱动,非无限阻塞恢复 | | `data.monitored_item.Value` | 保存值/质量/两时间及接收标识 | JSON 仅覆盖本例 Double,不是全 UA 类型编码 | | `TaskGroup` + `aiosqlite` | 消费者异常传播,SQL 提交与覆盖计数守恒 | 不提供协议确认到数据库事务的 exactly-once | | `create_custom_event_type/get_event_generator` | 自定义 MeasuredCurrent 字段与生成 | 本次类型 ID 未导出为版本化 NodeSet2 | | `subscribe_events` + 显式 `EventFilter` | 3 条事件与 1 条高严重度事件 | 没有完整 A&C、事件层级传播验收 | | `HistoryDict(max_history_data_response_size=2)` | 5 条保留值分 3 页,质量与时间存在 | 内存后端;NumValuesPerNode 正数路径先截断的版本限制由源码核查 | | `Node.history_read` | 传递不透明 CP 并检查状态 | 本轮完整读完,未验收取消时释放 | | `ReadProcessedDetails` | Average 返回 BadNotImplemented | 不把原始读取成功推成聚合支持 | | `FileType` 实例 + `Server.link_method` | 自定义只读回调经真实 Call 分块与哈希核对 | 无 Session 拥有者验证,自定义租约由夹具清理 | 本地 SDK 核查文件:`server/event_generator.py`、`common/subscription.py`、`server/history.py`、`common/node.py`、`client/ua_file_transfer.py`。`subscribe_alarms_and_conditions` 存在不代表服务端实现 A&C;`UaFile` 客户端存在不代表自带完整文件服务器。 直接依赖 aiosqlite 0.22.1 已显式加入 pyproject 与锁文件;没有升级第三方包。正文:[Part 6](./Part6_OPCUA_Python服务端与采集客户端.md)、[Part 7](./Part7_OPCUA_事件报警历史访问与文件传输.md)。[证据页](./evidence/part6-7/README.md)保存环境、九项测试两环境结果、原始日志与文件指纹。 ## Part 8—9 补充核查(2026-09-06) | 一手来源 | 本轮用途 | |---|---| | [Part 14 模型协同](https://reference.opcfoundation.org/specs/OPC-10000-14/4.4) | C/S 与 PubSub 共用模型、角色不必绑定 | | [DataSetMetaData](https://reference.opcfoundation.org/specs/OPC-10000-14/5.2.3) | 元数据与带外静态契约的区别 | | [DataSetMessage](https://reference.opcfoundation.org/specs/OPC-10000-14/5.3.3) | 关键帧、增量、序号与配置版本 | | [Message mappings](https://reference.opcfoundation.org/specs/OPC-10000-14/7.2) | 编码层与传输层分开 | | [UADP NetworkMessage layout](https://reference.opcfoundation.org/specs/OPC-10000-14/7.2.4.4.2) | 核对固定 37 字节实例的标志和布局 | | [UDP mapping](https://reference.opcfoundation.org/specs/OPC-10000-14/7.3.2) | 数据报与网络配置边界 | | [Message security](https://reference.opcfoundation.org/specs/OPC-10000-14/5.3.5)、[Transport security](https://reference.opcfoundation.org/specs/OPC-10000-14/5.3.6) | 消息安全与连接安全分别验收 | | [asyncua releases](https://github.com/FreeOpcUa/opcua-asyncio/releases) | 2.0 引入初步 PubSub;实际锁定 2.0.1 | | [open62541 v1.4.11](https://github.com/open62541/open62541/releases/tag/v1.4.11) | 固定独立对端提交,非最新版推荐 | | [open62541 PubSub 配置教程](https://www.open62541.org/doc/v1.4.11/tutorial_pubsub_publish.html) | 完整公开配置 API 与本实验内部 codec 适配器的区别 | | [Part 7 Profiles](https://reference.opcfoundation.org/specs/OPC-10000-7) | 声明范围;本次在线页 1.05.02 | | [OPC Foundation certification](https://opcfoundation.org/certification/) | 工程回归和正式认证分别记录 | | [UaExpert](https://www.unified-automation.com/products/development-tools/uaexpert.html) | 工具定位;本次未运行 GUI 验收 | | [Python time](https://docs.python.org/3.12/library/time.html) | perf_counter 与 process_time 的计量对象 | | [GetProcessMemoryInfo](https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessmemoryinfo) | Windows 工作集采样 | Part 14 在线页显示 1.05.06。本次源码使用安装的 `asyncua/pubsub/uadp.py`、固定 open62541 的内部 PubSub NetworkMessage 编解码接口及公开 C/S API。L15 验证静态元数据、Variant keyframe、Double/UInt32 两字段;不是公开 Writer/Reader 配置生命周期验收。L16 独立 C/S 验证 URI 索引解析、Double 读写和错误 Int32 的状态码,NoSecurity。 性能为 asyncua 双端同进程、同事件循环、回环 TCP,与独立对端测试分别归档。三档点数各三模式三重复;原始时间数组可重算,末值工作集不当作峰值,订阅突发不当作持续容量。Windows 提前定时唤醒导致预运行 loop-lag 零值偏差,修正为 50 ms deadline 后复验;正文明确样本量限制。 新增测试在既有和新锁定 Python 环境各 8 项通过,旧实验 34 项回归通过。未升级 UA 项目依赖;图表单独使用 matplotlib 3.10.8。正文:[Part 8](./Part8_OPCUA_PubSub机制与Python验证.md)、[Part 9](./Part9_OPCUA_互操作测试与性能评估.md);[构建、原始数据与指纹](./evidence/part8-9/README.md)。 ## Part 10 补充核查(2026-09-06) | 来源 | 用途与边界 | |---|---| | [c104 项目](https://github.com/Fraunhofer-FIT-DIEN/iec104-python) | lib60870 与 Python 回调的实现定位;原生依赖、平台与许可入口 | | [c104 1.18.0 发布](https://pypi.org/project/c104/1.18.0/) | 固定 Windows CPython 3.12 wheel,不追随最新 API | | [1.18.0 Point](https://iec104-python.readthedocs.io/v1.18.0/python/point.html) | set(value,quality,timestamp_ms)、updated_at_ms、on_receive、transmit;安装版本没有后续 ShortInfo 对象 | | [DataValue](https://reference.opcfoundation.org/specs/OPC-10000-4/7.11) | 值、状态与源/服务时间的职责;Bad 值不能当有效测量 | | [Python asyncio 开发说明](https://docs.python.org/3.12/library/asyncio-dev.html) | 原生线程到事件循环的边界;本例使用有界 queue.Queue | | [ABB DNP3 flags](https://help.plc.abb.com/dnp3_quality_flag.html) | 具体厂商质量字段说明;本例转换规则不是规范映射 | | [libiec61850 Quality](https://support.mz-automation.de/doc/libiec61850/c/latest/group__QUALITY.html) | validity、test、operatorBlocked 的库级含义;本篇只模拟部分策略 | | [systemd.exec 官方文档源](https://github.com/systemd/systemd/blob/main/man/systemd.exec.xml) | 服务账号、状态目录与文件系统限制;未做 Linux 实机验收 | L17 实测 c104 双端 IEC104 TCP → asyncua Server → 独立 open62541 Read。CA47/IOA11/TI36/COT3、单点带时标浮点,数值缩放 0.1、V 单位,源质量/未来时间/源进程退出重启均有可核验输出。IEC 两端同栈,非实际 RTU 跨厂商互操作;单位另由 Python UA Client 核对。 L18 两环境各执行 60 秒目标观察,真实源协议输入与 SQLite 提交;慢消费为人为延时,SQLite query_only 触发真实写错误。未运行 24 小时,未测磁盘满、Linux/systemd、真实控制、冗余或另外两种源协议。源码与正文分别标注模拟质量规则和本地命令账本。 pyproject/uv.lock 新增可选 gateway extra,固定 c104 1.18.0,既有第三方包版本保持不变;网关实验使用 Python 3.12。新增八项测试两环境通过,服务无源正常启停另核查。正文:[Part 10](./Part10_OPCUA_电力协议网关部署与排障.md);[原始记录、环境与指纹](./evidence/part10/README.md)。