☰
DL/T698电表接入SNMP监控:协议转换与OID映射实践
2026/10/21 16:32:13 网站建设 项目流程

做运维监控这块的朋友,应该都有这种体会:网管平台、动环系统、IoT平台基本都认SNMP,设备上只要有标准MIB,一套监控体系就能管到底。但偏偏在我们用电信息采集这一侧,国内智能电表走的是DL/T 698.45协议,这套协议在电力行业里很成熟、很可靠,可它跟IT运维这边完全是两个世界。我去年就接手了一个活儿:机房里有几十块DTL698协议的多功能电表,但动环监控平台只认SNMP,两边语言不通,中间必须搭一座桥——把DTL698电表数据实时转成SNMP协议,让监控平台直接走OID拉数据。

这篇文章就是把这个项目从需求分析、协议拆解、架构选型到最终落地的全过程整理出来,内容包括OBIS数据项和OID如何映射、采集频率怎么定、net-snmp扩展和自研Agent怎么选、实际部署中踩过的坑等等。不管你是做数据采集网关的、搞动环监控的,还是单纯想把电力数据接进自家监控体系的,都可以直接抄这份实践。

1. 项目背景与实际需求拆解

先说清楚这事儿的来龙去脉。很多机房、数据中心、工厂车间里都装了多功能电表,用来统计市电总进线、各回路的分路电量。这些电表在电网侧是标准的DL/T 698.45协议设备,支持RS485总线、电力载波或者微功率无线通信。但在运维室这边,动环监控系统和网管平台普遍只支持SNMP协议,你想在同一个大屏上同时看到网络设备状态、温湿度传感器和电表电量,就必须把电表数据映射成SNMP的OID节点。说白了,这是两个“语言不通”的系统之间的翻译工作。

这个项目中我负责的方案,本质上是一条单向数据通路:电表侧是DL/T 698.45协议,监控侧是SNMP GET/GETNEXT请求。中间需要有一个“翻译器”,定时去读电表的电压、电流、功率、电能量等数据,存到本地缓存,等SNMP监控平台来查询时直接把缓存值返回。这里的关键点在于:监控平台几乎不会关心你底层用的是698还是Modbus还是DL/T 645,它只关心OID能不能通、数据准不准、刷新快不快。

从需求清单来看,这个项目主要包含了这几个硬性要求:

  • 支持读取组合式有功电能、分费率电能(尖峰平谷)、A/B/C三相电压、三相电流、有功功率、无功功率、功率因数等常用电表数据点。
  • 数据刷新周期要求1~5分钟以内,冻结数据(电能量)和实时数据(电压电流)要分开处理。
  • SNMP侧采用v2c协议即可,团体名可配置,支持标准的GET和GETNEXT遍历,MIB文件要能让监控平台直接导入。
  • 其中一台电表离线或通信超时的时候,不能影响其他电表的正常返回,也不能让监控平台产生大量误告警。
  • 采集设备本身要能长时间稳定运行,掉电重启后能自动恢复采集任务。

这里特别要说明一下,标题里写的DTL698,实际对应的规范是DL/T 698.45《电能信息采集与管理系统》中的面向对象数据交换协议。行业内口语念快了就成了“DTL698”,反正大家都能听懂。这套协议跟以前常见的DL/T 645-2007相比,最大的区别是它引入了面向对象建模,数据项都有统一的对象标识(OBIS),读取方式也变成了应用层指令的方式,而不是简单的高低字节地址。后面的核心实现里,我会拿实际报文一步步拆。

2. 协议分析与数据映射设计

2.1 读懂DL/T 698.45的报文结构

DL/T 698.45的帧格式其实很规整,底层链路层长得跟常规的电力规约差不多,但应用层使用了类似TLV的结构,整体层次清晰。一个典型的数据读取请求帧,固定部分大概是这么排的:帧起始符68H、长度域L、控制域C、地址域A(通常6个字节,包含区号和电表逻辑地址)、应用层APDU、校验和CS,最后以16H结束。

应用层APDU里最关键的是“对象属性描述(OAD)”,一个OAD由几个字节组成,具体的字节含义:前两个字节是对象标识(OBIS),后面还有属性ID、数据选择等。读电表数据的请求通常会带上一个“读取请求”的操作码(比如GET请求),电表收到后回复GET-RESPONSE帧,里面的数据区就是对应的值。

你不一定需要把协议栈完整看懂,但至少要知道这几个点:帧头是68,帧尾是16,应用层最前面的字节决定操作类型(0x30左右通常是请求/响应),OBIS标识就藏在APDU的固定偏移里。如果只想快速验证链路通不通,可以先发一个身份识别请求,看电表有没有响应,这个比直接读数据简单得多。

实际项目中,我不建议从零手写完整的698协议栈。现在很多采集器主控芯片的SDK里有698协议库,或者可以直接找成熟的数据采集模块,自己在上面做业务逻辑。如果一定要自己写,重点放在帧校验、超时重传和响应解析这三块。

2.2 OBIS数据项与SNMP OID的映射关系

DL/T 698.45的数据项用OBIS标识来命名。比如“1-0:1.8.0”表示组合式正向有功电能,单位是kWh;“1-0:2.8.0”表示组合式反向有功电能。分费率电能则是把第三个数字换成费率号,比如1-0:1.8.1代表尖时正向有功、1.8.2代表峰时、1.8.3代表平时、1.8.4代表谷时。三相电压则类似“1-0:32.7.0”(A相电压),三相电流对应“1-0:31.7.0”(A相电流)。

SNMP这边又是另一套命名体系。监控平台通过OID来标识一个监控对象,通常企业私有MIB放在1.3.6.1.4.1这个节点下面。我做映射的时候,习惯是把OBIS的“1-0”省略,直接用后面的数字排布成OID。比如企业号假设是9527,那么根节点就是1.3.6.1.4.1.9527.dtl698。再往下,我用一个二维结构:第一层是表计编号,第二层是数据点编号。

映射表大概长这样:

数据点名称OBIS标识对应OID(表计编号为1)SNMP类型
总正向有功电能1-0:1.8.01.3.6.1.4.1.9527.dtl698.1.1.0Counter64
尖时正向有功电能1-0:1.8.11.3.6.1.4.1.9527.dtl698.1.2.0Counter64
峰时正向有功电能1-0:1.8.21.3.6.1.4.1.9527.dtl698.1.3.0Counter64
平时正向有功电能1-0:1.8.31.3.6.1.4.1.9527.dtl698.1.4.0Counter64
谷时正向有功电能1-0:1.8.41.3.6.1.4.1.9527.dtl698.1.5.0Counter64
A相电压1-0:32.7.01.3.6.1.4.1.9527.dtl698.1.6.0Gauge32
B相电压1-0:52.7.01.3.6.1.4.1.9527.dtl698.1.7.0Gauge32
C相电压1-0:72.7.01.3.6.1.4.1.9527.dtl698.1.8.0Gauge32
A相电流1-0:31.7.01.3.6.1.4.1.9527.dtl698.1.9.0Gauge32
总有功功率1-0:1.9.01.3.6.1.4.1.9527.dtl698.1.10.0Gauge32
功率因数1-0:13.7.01.3.6.1.4.1.9527.dtl698.1.11.0Gauge32
数据状态自定义1.3.6.1.4.1.9527.dtl698.1.12.0INTEGER

这个表是项目里的核心资产,后续MIB文件、采集程序、Agent应答全都要跟这个表保持一致。设计的时候有几个细节要提前想清楚:电能累加值建议用Counter64,因为电表的累计电量很容易超过32位整数范围;电压、电流这些实时量用Gauge32就够了;数据状态这个节点是我额外加的,用来上报“正常、通信超时、无效数据”等状态,监控平台可以基于它做告警屏蔽或者触发通知,这一个节点在后面救了我好几次。

2.3 数据刷新策略与时序考量

频繁去读电表没有意义,SNMP监控平台通常60秒轮询一次就够了。对于电能表数据,我把数据项分成了两类:一类是冻结数据(电能量),这类数据变化慢,一般15分钟或者30分钟读一次就行,读的是电表内部在整点时刻冻结的历史记录;另一类是实时数据(电压、电流、功率),这类数据变化快,按1分钟或者5分钟的周期读取。

经验值是:一台采集终端下挂32块表,RS485总线波特率9600bps的情况下,每块表轮询一遍大约需要5到15秒。如果所有数据项都要读,采集周期会拉长到1分钟以上。所以我的做法是分轮次:电压和功率这类实时数据每60秒读一轮,电能量冻结数据每900秒读一轮,两轮并发交错执行,这样既不占用太多总线时间,又能保证数据新鲜度。

还有一个需要注意的点:电表的“实时值”和“冻结值”读法完全不同。实时值直接GET请求就能返回当前时刻的瞬时值;但冻结值要指定“数据选择”的时间标签,比如请求读取最近一次日冻结或月冻结数据。如果你只是想看“当前累计电量”,直接读实时电能值即可,不用去翻冻结记录,更容易实现。

3. 整体架构与工具链选型

3.1 三层架构:采集、缓存、暴露

这个系统的整体架构并不复杂,核心思路就是分层解耦。第一层是采集层,负责通过RS485/串口或者网络与电表通信,向电表发送DL/T 698.45读数据请求,解析响应帧,提取出我们关心的数据点。第二层是缓存层,把最近一轮采集到的数据连同时间戳、数据状态一起放进内存数据结构中,同时写入SQLite做历史留存。第三层是暴露层,实现SNMP Agent逻辑,监听UDP 161端口,响应监控平台的GET和GETNEXT请求,从缓存层取数并返回。

这样分层有一个明显的好处:采集频率和SNMP查询频率完全解耦。就算监控平台发疯一样地高频轮询,Agent也只是读内存缓存,不会去骚扰电表。反过来,电表通信慢、超时重试,也不会阻塞SNMP的响应。我在最初的原型里曾经试图做到“来一个SNMP请求就去读一次电表”,结果某品牌监控系统一次性批量GET几十个OID,电表直接通信超时,整个链路卡死。后来改成缓存模式,问题就消失了。

3.2 方案A:net-snmp的extend和pass_persist快速实现

如果不想自己写完整的SNMP Agent,最快的一条路是Linux下装net-snmp(snmpd),利用它的extend或者pass_persist指令把数据暴露成OID。extend适合那种简单的、一次取一个值的脚本场景,比如在snmpd.conf里写一行配置,把某个OID对应到一条shell命令,OID被查询时就执行命令并把输出当成SNMP值返回。这种方式优点是配置简单,缺点是每个OID都要起一个进程或执行一次脚本,32块表、每块表12个数据点就意味着几百次进程启动,性能撑不住。

更实用的是pass_persist方式,它的逻辑是snmpd启动时拉起一个子进程,子进程保持运行,等待snmpd传过来的查询指令,然后返回对应的OID和值。这样一来脚本只启动一次,后续查询都是内存里的读写操作。我的快速原型就是这么做的:采集程序独立跑,把最新值写入临时文件;pass_persist脚本收到SNMP查询时读文件、查映射表、返回结果。这个方案半小时就能搭起来,适合验证OID映射正确性。

不过pass_persist有它的局限:它要求的输出格式是固定的“OID 类型 值”多行形式,而且GETNEXT的实现逻辑要自己处理好,不然遍历时会乱。另外snmpd本身是单进程多线程模型,如果你的监控平台对同一批OID做了大量并发查询,pass_persist脚本也会成为瓶颈。所以我最终在实际部署时没有采用pass_persist,而是直接用Python实现了一个轻量级SNMP Agent,原因下面说。

3.3 方案B:用Python自研SNMP Agent

自研SNMP Agent听起来高大上,其实并不复杂。用pysnmp这个库,几十行代码就能起一个支持GET和GETNEXT的Agent,核心要处理的无非是MIB节点注册、请求分发、OID字典序这3件事。

我选择自研的原因有三点。第一,Agent需要跟采集模块共用缓存数据,直接写成同一个进程的两个线程,用一份内存字典就完事了,不用搞进程间通信和文件锁,省去很多麻烦。第二,数据状态节点可以做得很灵活,电表通信失败时Agent能返回一个自定义错误码,这在pass_persist里实现会绕。第三,自研程序可以加一个HTTP调试接口,随时用浏览器看每个电表的采集状态和最新值,排查问题的时候太方便了。

当然,pysnmp库也不是没有坑。不同版本之间API有差异,比如v2c的CommunityData设置、UDP传输域的写法会有细微变化。建议直接锁定一个自己熟悉的版本,并且在代码里封装好Agent的启动逻辑,后续升级库的时候只动一个文件。

3.4 各方案对比参考

我把两种方案放在一起对比一下,方便你结合自己项目的情况选择:

对比项net-snmp extend/pass_persistPython自研Agent
开发工作量低,半小时可跑通中等,需要写MIB和Agent逻辑
性能上限受限于脚本通信和snmpd调度,适合小规模内存直接读取,更适合大规模或高频查询
调试便利性依赖snmpd日志,较难定位可加HTTP接口和日志,调试方便
部署依赖需要net-snmp环境,几乎所有Linux都有需要Python环境和依赖库
灵活度OID结构受pass_persist格式限制可完全自定义,支持复杂逻辑
适合场景快速原型、数据点少、临时项目正式部署、数据点较多、需要长期维护

我的建议是:如果你是第一次做类似项目,先用方案A跑通全链路,对MIB和OID有个感性认识;等确认数据映射没问题了,再上方案B做正式部署。我自己就是这么干的,原型用了2天,正式Agent又花了3天,总共一周搞定。

4. 核心实现与关键代码解析

4.1 698串口采集模块的骨架

采集模块是整个系统里最贴近硬件的一层。如果是RS485通信,操作串口需要设置波特率、数据位、校验位、停止位,常见配置是9600-8-E-1,但不同厂家电表可能默认2400或者4800,上线前最好用厂商工具读一下确认。

下面是一段简化后的采集框架代码,重点看思路,不用纠结库的细节:

import serial import time from typing import Dict, Any class Meter698Reader: # 表地址、数据映射表、串口句柄等初始化略 def __init__(self, port: str, address: str): self.ser = serial.Serial(port, baudrate=9600, bytesize=8, parity='E', stopbits=1, timeout=2) self.address = address def build_get_frame(self, oad: bytes) -> bytes: # 按DL/T 698.45组装一个GET请求帧 # 这里只列出关键字节顺序,实际代码需要加上控制域、地址域、校验和等 frame = bytearray() frame.append(0x68) # length、control、address 部分省略 frame.append(0x40) # APCI: GET请求 frame.extend(oad) # 对象属性描述 # CS校验和与结束符0x16省略 return bytes(frame) def read_value(self, oad: bytes) -> Dict[str, Any]: req = self.build_get_frame(oad) self.ser.write(req) resp = self.ser.read(128) # 解析响应帧,提取APDU中的value部分 # 注意要校验CS,并判断返回的操作码是0x30还是错误码 value = self.parse_apdu_value(resp) return {"value": value, "status": "ok"} def read_energy(self): # 1-0:1.8.0 组合式正向有功电能 oad = bytes([0x00, 0x01, 0x08, 0x00, 0x00]) # OAD示意,实际需要再细化 return self.read_value(oad)

这里要特意提醒:上面代码只展示了结构,不能直接拿去用。DL/T 698.45的帧组装和解析细节很多,尤其是地址域的字节顺序、控制域的FCB翻转、APDU中的TLV长度编码,这些地方出错的概率最高。我的建议是拿厂商提供的协议调试工具抓一帧真实报文,对照协议文档逐字节核对,比看任何代码都有效。

4.2 协议响应解析与数据规约化

电表返回的响应帧里,数据部分可能是整数、BCD码、字符串或者带时标的复合结构。解析时最大的坑就是十进制倍率。比如某些电表的电压值存储的是实际值的10倍、100倍,电能值则可能带了互感器变比,直接读出来的数字要除以倍率才能得到真实电量。如果电表是经互感器接入的,还需要在采集配置里单独填上变比,比如800/5的互感器,综合倍率是160。

我遇到过一次非常隐蔽的问题:两块同型号电表,一块读出来的电压是2200,另一块是0220,带前导零的电表如果按整数解析会丢位。这类问题通常是因为电表返回的是字符串形式的数值,解决办法是统一按二进制整数解析再根据OBIS表确定倍率,不要依赖字符串长度。

数据进缓存之前,我还习惯先把所有值转换成统一规格:电压存浮点(单位V)、电流存浮点(单位A)、功率存浮点(单位W)、电能存整型(单位Wh或kWh,带倍率说明)。这样SNMP侧只需要做一次OID映射,不需要每个数据点单独处理单位换算。

4.3 共享缓存与状态标记

缓存层我用了一个简单的环形字典,键是“表地址+数据点编号”,值包含数值、采集时间戳、状态、倍率。采集线程每轮更新这个字典,SNMP Agent线程只读这个字典,因为Python的GIL保证了字典操作的原子性,所以这里不需要额外加锁。

状态标记这块值得多说几句。电表通信超时的时候,缓存里的旧值仍然存在,但状态要标记为“超时”。如果SNMP Agent直接返回旧值,监控平台就会把旧值当作最新值,很可能导致误判或者漏判。我在设计中增加了一个数据状态OID,状态值定义如下:

  • 0:数据正常
  • 1:当前无数据(初始化状态)
  • 2:通信超时,返回缓存旧值
  • 3:数据校验错误

监控平台可以通过阈值规则对状态值做告警:当状态值等于2持续3次以上,说明这条采集链路可能出问题了。这样即使SNMP返回的是缓存旧值,监控平台也知道“这个数据不太可信”,而不会直接当成真实值去触发告警。

4.4 SNMP Agent的OID遍历实现

用pysnmp实现Agent时,最需要细心的地方是GETNEXT的字典序递增逻辑。SNMP协议规定,OID在这个MIB树里必须是有序排列的,客户端发出GETNEXT请求时,Agent要返回比请求OID更大的下一个OID。很多自己实现的Agent在这一步会出现“遍历到一半就断了”或者“无限循环”的情况。

核心逻辑是:先把所有已注册的OID放在一个列表里排好序,收到GETNEXT请求时用二分查找找到第一个大于请求OID的节点,如果找到了就返回该节点的OID和值,找不到就返回SNMP的endOfMibView。当监控平台遍历完整个表,它就会自动停止。这个逻辑我单独封装成一个函数,方便单测。

from pysnmp.entity import engine, config from pysnmp.carrier.asyncore.dgram import udp from pysnmp.proto import rfc1902 import sortedcontainers # 或直接用列表+二分 # 简化示例:注册一个OID节点的响应回调 def get_next_oid(oid_tuple): # 在已排序的oid_list中找到下一个节点 # 返回 (next_oid, value),没有则返回endOfMibView pass def handle_get(oid): # 根据oid直接查mapping pass

实际部署中,我还加了一个启动参数:监听端口、团体名、数据文件路径都做成可配置的,避免为了改端口重新部署程序。团体名我建议不要用public,内网监控虽然风险不大,但写成自定义团体名能减少被无关扫描匹配到的概率。如果安全要求更高,直接上SNMP v3的USM认证,pysnmp也支持,不过配置会复杂一些,这个项目里没用到。

5. 部署调试与踩坑记录

5.1 硬件接线与通信参数核对

现场部署的第一步永远是物理链路。RS485接线看起来简单,但“A/B接反”是所有采集项目里的头号坑,没有之一。A线接B口,或者屏蔽层没接地,会导致要么完全不通,要么通信时好时坏。我的习惯是接线后先用串口调试工具直接发送一个698请求帧,看电表有没有回复,没有的话先对调A/B再试一次。

波特率不匹配是第二高频问题。国内很多多功能电表出厂默认波特率是2400或者4800,但采集模块可能习惯性配成9600。一旦波特率错了,抓包看到的现象就是收到一堆乱码,或者干脆没响应。所以部署时的检查顺序建议是:先确认物理接线,再确认波特率,再确认数据位和校验位,最后才是协议层面的调试。

如果在项目里是通过集中器间接抄表,那就更要注意了:集中器的下行通信方式可能是载波或者微功率无线,它抄表本身就需要时间,而且时常会有个别电表掉线。这种场景下采集程序的重试机制和超时时间必须放宽,不能按RS485直连的标准来。我的做法是把单次抄表超时设置为3秒,每块表连续失败3次后就不再重试,标记为离线,等下一个周期再尝试,而不是死磕某一台离线表导致整条总线阻塞。

5.2 读数不对的排查思路

数据读回来了,但数值不对,这是最让人抓狂的阶段。我记得有一次把所有电表的功率都读成了0,排查了半天,最后发现是OAD里的数据选择子结构没写正确,导致电表返回了一个空值。所以排查读数问题,我建议按这个顺序来:

  • 先用厂商的调试工具读同一块表,确认电表本身数据正常。
  • 用串口抓包工具看原始响应帧,确认返回的数据字节到底是什么,而不是直接信任解析代码。
  • 对照协议文档查看数据类型和长度,确认没有解析错位。
  • 核实倍率和变比配置,尤其是带互感器的回路。
  • 最后再查OAD映射表和MIB文件,确认OID对应关系没串。

这个过程听起来繁琐,但实际上大多数问题都出在解析和倍率这两个环节。建议开发阶段就把原始帧日志保存下来,每个响应都留一份hex dump,这样排查问题的时候可以直接翻日志,不用反复去现场补数据。

5.3 监控平台告警风暴的规避

监控平台通常会对每个OID设置阈值。如果电表离线时Agent仍然返回一个很大的旧值或者0值,平台可能会触发“电压过低”或“功率突变”之类告警,一告警就是几十台设备一起告,形成告警风暴。规避手段就是前面说的数据状态节点。

我在项目里实际做了两步。第一步,电表离线时,Agent对数据类OID返回缓存旧值,但对状态OID返回2。第二步,在监控平台上把“状态OID等于2”设置为所有数据类OID告警的屏蔽条件。这样离线时平台看到的还是最后一次正常值,且不会因为超时连续产生误告警。等采集恢复了,状态OID变回0,平台自动恢复正常告警。这个设计当时跟监控平台的对接方确认了很久,但上线后效果非常好,几乎没人再因为“电表数据异常”半夜打电话。

5.4 多费率电量的特殊处理

分费率电量的OBIS跟总电量不一样,每个费率时段单独累计,这本身没什么难度。但要注意一点:电表内部费率时段是通过当地电网的时段方案设置的,比如尖峰平谷的时段划分可能随季节调整。你读数据时,如果只关心“当前总电量”,读组合式正向有功电能就行;如果业务上需要分费率统计,就要把尖峰平谷分别映射到不同的OID。

还有一个实际场景需要注意:电表在换费率方案或者校时之后,分费率累计值可能短暂出现异常甚至清零。如果你对接的是计费或者报表系统,建议除了实时值之外,还要定时把冻结数据里的历史值读出来存档,防止电表内部数据变动导致历史曲线断档。

6. 常见问题速查表与容量参考

6.1 高频问题排查一览

现象可能原因解决方案
串口完全无数据A/B接反、波特率错误、串口占用对调A/B,核对波特率,关闭其它串口调试工具
收到乱码校验位/停止位配置错误、干扰严重配置8E1或8N1,检查屏蔽层接地
有响应但解析报错帧不完整、CS校验失败、地址域错误抓包核对,确认长度域含义,检查表地址字节序
个别表读不到表离线、载波信号弱、表地址配置错误现场确认表状态,按块表单独重试,检查地址编码
电能值明显偏大或偏小倍率/变比配置错误核实互感器变比,统一数值缩放逻辑
SNMP GET返回超时缓存未更新、端口被防火墙拦截检查采集线程状态,测试UDP 161连通性
GETNEXT遍历中断MIB树排序错误、未处理endOfMibView检查OID排序逻辑,补齐结束标记
监控平台导入MIB报错MIB语法不兼容、节点命名不规范用标准MIB编译器校验,避免使用非法字符

6.2 性能与容量参考

我在现场测过一组数据:一台采集终端通过RS485挂32块电表,9600bps波特率,轮询电压、电流、功率等实时数据一轮大约需要25~40秒;叠加电能量冻结数据后,30分钟一个循环,完全能满足监控需求。SNMP侧,监控平台60秒轮询一次,每次批量GET大约200个OID,Agent的内存缓存模式下,平均单次响应时间在10毫秒以内,CPU占用可以忽略不计。

如果数据规模更大,比如上百块表,建议不要把数据点全部展开成一个OID列表,而是考虑在Agent里加一层“按表索引动态生成OID”的逻辑,这样当表数量变化时只需要改配置文件,不用改MIB。另外一个思路是用多个采集进程分担不同回路的电表,Agent层做聚合,这在高并发场景下会更稳。

6.3 这个方案的扩展方向

项目上线后,团队还给它扩展了几个能力。一个是把采集到的数据同时写入本地时序数据库,这样监控平台除了实时告警之外,还能直接从数据库里拉取历史曲线,避免完全依赖SNMP轮询历史数据。另一个是加了简单的HTTP接口,运维人员直接访问网页就能看到每块表的通信状态和最近数值,排查问题效率提升了很多。

还有同事建议把数据上报到能源管理平台做能耗分析。因为底层的采集层已经是统一的“数据点字典”,上层对接任何平台都只需要写一个适配器,不需要改动采集逻辑。这个架构上的好处,是我当初分层设计时没想到的,算是无心插柳。

最后分享几个实在的经验

这项目做完,我自己沉淀了几条经验。第一,对接电力协议设备,千万别迷信“通用协议库”,每个厂家的实现细节都有差异,现场调试工具和抓包能力是必须的,没有这两样,你会被各种玄学问题折磨到怀疑人生。第二,做协议转换类项目,数据状态的设计比数据值本身还重要。一个明确的“数据不可信”标记,能省掉后期无数告警和误判的麻烦。第三,能在内存里解决的事,就不要加中间文件或者数据库,解码和通信已经够复杂了,能少一个变量就少一个变量。

要说遗憾,也有:这项目里的电表没有支持SNMP的直连功能,否则也不用折腾这个转换程序。听说现在已经有部分高端表计直接内置SNMP Agent,但价格嘛,懂的都懂。所以至少在很长一段时间里,用软件做协议转换依然是最经济实惠的方案。你要是也在做类似的电表接入项目,希望这篇实践记录能帮你少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询