Files
iAOP/core/edge-gateway/drivers/s7_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

70 lines
2.6 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 -*-
"""西门子 S7-1200 驱动 —— 参数化适配(issue #24 落点)。
连接参数(ip / rack / slot / db / offset 映射)全部来自模板配置,
点位到 DB 地址的映射通过点位字典 CSV 的 opcNode 列携带
(S7 场景下约定格式 `DB{db}.{byte}.{bit}`,如 DB100.0.0)。
依赖 `python-snap7`,未安装时给出明确提示。
"""
from __future__ import annotations
import re
from typing import Dict, List, Optional
from point_dict.loader import Point
from .base import Driver, SampleValue
_S7_NODE_RE = re.compile(r"^DB(\d+)\.(\d+)(?:\.(\d+))?$")
class S7Driver(Driver):
"""西门子 S7-1200 只读采集驱动(参数化适配)。"""
protocol = "s7"
def __init__(self, config: Optional[dict] = None):
super().__init__(config)
self.ip: str = self.config.get("ip", "")
self.rack: int = int(self.config.get("rack", 0))
self.slot: int = int(self.config.get("slot", 1))
self._client = None
def connect(self) -> None:
try:
import snap7 # type: ignore
except ImportError as exc: # pragma: no cover - 依赖缺失路径
raise ConnectionError(
"S7 驱动依赖库未安装:请 `pip install python-snap7`"
) from exc
if not self.ip:
raise ConnectionError("S7 驱动缺少配置: drivers.s7.ip")
self._client = snap7.client.Client()
self._client.connect(self.ip, self.rack, self.slot)
def read_points(self, points: List[Point]) -> Dict[str, SampleValue]:
if self._client is None:
raise ConnectionError("S7 客户端未连接,请先 connect()")
result: Dict[str, SampleValue] = {}
for p in points:
match = _S7_NODE_RE.match(p.opc_node or "")
if not match:
continue # 非 S7 点位由其它驱动采集
db, byte_, bit = int(match.group(1)), int(match.group(2)), match.group(3)
try:
if bit is not None:
result[p.point_id] = self._client.read_area(
0x84, db, byte_ * 8 + int(bit), 1
)[0]
else:
result[p.point_id] = self._client.read_area(0x84, db, byte_, 4)[0]
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