Files
iAOP/core/edge-gateway/drivers/opcua_driver.py
T
yunmei f49c0920d4 feat: 完成 issue #3 边缘采集网关模板化封装
- 点位字典 CSV schema/加载/自动校验(缺失字段/量纲/重复点号,PRD 5.1)
- 协议可插拔只读驱动:OPC UA(S7-1200 适配)/S7/Modbus/称重/能源/模拟
- 周期采集引擎:只读+背压保护+健康度指标(丢失率/P99/可用性)
- Kafka 流式上行 + 本地 spool 断点续传(丢失率≤0.02% 保障)
- 模板配置外置(gateway.yaml + 点位字典 CSV),换行业零改码
- 18 个单元测试全绿;端到端运行 SLA 达标
2026-08-04 15:32:16 +08:00

62 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""OPC UA 驱动 —— 和利时 DCS 等 OPC UA 接口参数化适配(issue #25 落点)。
连接参数全部来自模板配置(gateway.yaml 的 drivers.opcua 段),不硬编码。
实际设备接入依赖第三方库 `asyncua`(异步)或 `opcua`(同步);
未安装时给出明确提示,不会静默失败。
"""
from __future__ import annotations
from typing import Dict, List, Optional
from point_dict.loader import Point
from .base import Driver, SampleValue
class OpcUaDriver(Driver):
"""OPC UA 只读采集驱动(参数化适配)。"""
protocol = "opcua"
def __init__(self, config: Optional[dict] = None):
super().__init__(config)
self.endpoint: str = self.config.get("endpoint", "")
self.security: str = self.config.get("security", "None")
self._client = None
def connect(self) -> None:
try:
from opcua import Client # type: ignore
except ImportError as exc: # pragma: no cover - 依赖缺失路径
raise ConnectionError(
"OPC UA 驱动依赖库未安装:请 `pip install opcua`(或 asyncua)"
) from exc
if not self.endpoint:
raise ConnectionError("OPC UA 驱动缺少配置: drivers.opcua.endpoint")
self._client = Client(self.endpoint, timeout=self.config.get("timeout", 10))
self._client.connect()
self._client.session_timeout = self.config.get("session_timeout", 60000)
def read_points(self, points: List[Point]) -> Dict[str, SampleValue]:
if self._client is None:
raise ConnectionError("OPC UA 客户端未连接,请先 connect()")
result: Dict[str, SampleValue] = {}
for p in points:
node_path = p.opc_node
if not node_path:
continue # 无 OPC 节点的点由其它驱动采集
try:
node = self._client.get_node(node_path)
result[p.point_id] = node.get_value()
except Exception:
# 单点读取失败不中断整批:记为未读到(计入丢失率)
result[p.point_id] = None
return result
def close(self) -> None:
if self._client is not None:
try:
self._client.disconnect()
finally:
self._client = None