三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

威胜102协议解析:从报文结构到总电量查询实战

威胜102协议解析:从报文结构到总电量查询实战

1. 项目背景与核心需求:为什么需要解析“总电量”?

在能源计量、工业自动化以及电力物联网领域,我们经常需要从智能电表、数据采集终端等设备中获取关键的能耗数据。其中,“总电量”无疑是最核心、最基础的数据指标之一。它直接关系到能耗统计、费用结算、负荷分析等一系列业务。然而,这些设备与上位机系统(如能源管理平台、SCADA系统)之间的通信,并非简单的“一问一答”,而是遵循着特定的行业通信规约。

威胜102协议,就是国内电力行业,特别是电能计量领域广泛应用的一种通信规约。它定义了数据终端设备(DTU、集中器等)与主站系统之间进行数据交换的帧格式、传输规则以及功能码含义。当你需要从一个支持威胜102协议的电表或集中器中查询“总电量”时,你并不是发送一句“把总电量发给我”的明文指令,而是需要构造一个符合102协议规范的数据报文,设备在接收到这个正确的报文后,才会回应一个包含你所需要数据的响应报文。

因此,理解并掌握“查询总电量”的完整报文交互流程,是进行数据采集、系统集成乃至故障排查的基石。这不仅仅是知道几个十六进制数,更是理解设备“语言”的过程。下面,我将以一个典型的实例,拆解从主站发起查询到从站返回数据的全流程,并深入每个字节的含义。

2. 威胜102协议报文基础框架解析

在深入具体流程之前,我们必须先理解威胜102协议报文的基本骨架。一个完整的102协议报文帧,通常由以下几个部分组成,这与很多常见的串行通信规约(如Modbus RTU)有相似之处,但也有其独特之处。

帧结构:起始符 + 长度域 + 控制域 + 地址域 + 链路用户数据(应用层) + 帧校验和 + 结束符

这是一个典型的面向字节的传输帧结构。我们逐一来看:

  1. 起始符 (Start Character): 通常为固定值0x68(十进制104),标识一帧报文的开始。接收方通过扫描这个特定字符来同步并开始接收一帧数据。

  2. 长度域 (Length Field): 指示本帧报文中,从“长度域”之后到“校验和”之前(或到“结束符”之前,具体看协议版本)的所有字节数。这是非常关键的一点,它决定了接收方应该接收多少数据。长度域本身可能占1个或2个字节,在早期的102协议中常见为1字节,意味着后续数据最大长度为255字节。

  3. 控制域 (Control Field): 这是报文的“大脑”,通常为1个字节。它定义了本帧报文的传输方向和功能属性。例如:

    • Bit位定义: 最低位(Bit0)常用来区分“主站到从站”还是“从站到主站”。
    • 功能码: 控制域的高几位或整个字节的取值,对应了不同的链路功能,如“发送/确认”(SEND/CONFIRM)、“请求/响应”(REQUEST/RESPOND)等。查询总电量通常属于“请求/响应”模式。
  4. 地址域 (Address Field): 标识通信对象的地址。在多点通信中(如一个主站带多个电表),主站通过地址域来指定要与哪个从站设备通信。地址域的长度可变,常见为1-7个字节,具体格式(如BCD码、纯二进制)需根据具体设备规约说明书确定。

  5. 链路用户数据 (Link User Data): 这是报文的核心载荷,即“应用层”数据。它本身也是一个结构化的数据单元,通常包括:

    • 应用层功能码 (AFN): 1字节,指明应用层的操作,如“总召”(即读取各类数据)、“读数据”等。查询总电量通常对应“读数据”或“总召”中的特定数据项。
    • 数据单元标识 (DA/DT): 2字节,用于进一步标识要读/写的数据对象。它通常由数据地址(DA)和数据类型(DT)组成。“总电量”这个数据项,在102协议中有其特定的DA/DT编码
    • 数据单元 (DATA): 可变长度,对于“读”命令,此部分可能为空或包含限定条件;对于“读响应”,则包含具体的电量数据值。
  6. 帧校验和 (Frame Check Sequence, FCS): 用于验证报文在传输过程中是否出错。常见的校验方式包括纵向冗余校验(LRC)或循环冗余校验(CRC)。计算范围通常覆盖从“长度域”到“链路用户数据”结束的所有字节。接收方会重新计算校验和并与报文中的校验和比对,不一致则丢弃该帧。

  7. 结束符 (End Character): 通常为固定值0x16(十进制22),标识一帧报文的结束。

注意: 不同厂家、不同时期的威胜102协议实施细则可能存在差异,尤其是在地址域长度、校验和算法、以及应用层数据单元的具体定义上。在实际操作前,务必获取并查阅你所对接设备的详细通信规约说明书,这是唯一权威的依据。本文基于通用和常见的实践进行阐述。

3. “查询总电量”请求报文实例拆解

现在,我们假设一个场景:主站(地址为1)要向地址为 16(十进制)的从站电表查询“正向有功总电量”。我们基于一个常见的102协议变体来构造请求报文。

步骤一:确定应用层数据单元

首先,我们需要明确“正向有功总电量”在协议中的“坐标”。

  • 应用层功能码 (AFN)0x0A0x0C常被用于“读数据”操作。我们假设为0x0A
  • 数据单元标识 (DA/DT): 总电量通常被定义为一种“电能量数据”。假设其数据标识为0x00 0x01(此处仅为示例,真实值可能是0x90 0x01或其他,必须查手册)。DA(数据地址)可能为0x00,DT(数据类型,表示正向有功总)可能为0x01
  • 数据单元 (DATA): 对于“读”请求,通常不需要附加数据,或者附加一个数据长度或限定信息。简单情况下为空。

因此,链路用户数据部分可能为:AFN+DA+DT=0x0A+0x00+0x01=0x0A 0x00 0x01(共3字节)。

步骤二:组装完整链路帧

  1. 起始符0x68
  2. 长度域 (L): 需要计算。长度域之后的部分包括:控制域(C)、地址域(A)、链路用户数据(U)。假设:
    • 控制域(C) = 1字节 (例如0x73,表示主站发送的请求帧)
    • 地址域(A) = 2字节 (地址16表示为0x10 0x00,低位在前)
    • 用户数据(U) = 3字节 (0x0A 0x00 0x01)
    • 校验和(CS) = 1字节 (暂未计算)
    • 结束符不参与长度计算。
    • 那么,从C到U的字节数 = 1 + 2 + 3 = 6字节。有些协议规定长度域包含自身,有些不包含,这里假设不包含自身,则 L = 6 (0x06)。
  3. 控制域 (C)0x73(假设:二进制 0111 0011,含义可能是:主站到从站,帧计数位有效,功能码为“请求”等)。
  4. 地址域 (A): 从站地址16->0x10 0x00(注意字节序,可能低位在前)。
  5. 链路用户数据 (U)0x0A 0x00 0x01
  6. 帧校验和 (CS): 计算从L到U所有字节的校验和。即对0x06 0x73 0x10 0x00 0x0A 0x00 0x01这7个字节进行累加和(或CRC8等)计算。假设使用累加和,忽略进位:0x06+0x73+0x10+0x00+0x0A+0x00+0x01 = 0x94。取低8位0x94作为校验和。
  7. 结束符0x16

完整的请求报文(十六进制)68 06 73 10 00 0A 00 01 94 16

字节含义逐项解析表

字节位置值(十六进制)含义说明
10x68帧起始符
20x06长度域,后续(C到U)有6个字节
30x73控制域,主站发送的请求帧
4-50x10 0x00地址域,从站地址为16 (0x0010)
6-80x0A 0x00 0x01链路用户数据:AFN=0x0A(读数据),DA=0x00, DT=0x01(总电量)
90x94帧校验和(L到U的累加和)
100x16帧结束符

实操心得: 在调试阶段,最稳妥的方式是使用串口调试助手或网络抓包工具(如Wireshark,配置正确的串口捕获或网络端口),以十六进制格式发送上述报文。同时,一定要开启接收区的十六进制显示。发送后,观察从站是否有数据返回。如果没有,首先检查物理连接(串口线、波特率、数据位、停止位、校验位)是否正确,这是最常见的问题。其次,核对地址域是否正确。最后,校验和算法是另一个常见的坑,务必确认设备使用的校验算法(累加和、CRC8、CRC16等)。

4. “查询总电量”响应报文实例与数据解析

当从站设备(电表)正确接收到主站的请求报文后,如果地址匹配、校验正确,它将执行“读数据”操作,并组织一个响应报文发回给主站。

响应报文的帧结构与请求报文类似,但有以下关键变化:

  1. 控制域 (C): 方向位会改变,表明这是从站到主站的响应。例如,可能变为0xF3(在请求帧0x73的基础上,改变方向位)。
  2. 链路用户数据 (U): 内容变为“响应”格式。通常包括:
    • 应用层功能码 (AFN): 通常与请求帧的AFN相同或为对应的确认码,如0x8A(表示对0x0A的响应)。
    • 数据单元标识 (DA/DT): 与请求帧中的DA/DT一致,表示响应对应哪个数据项。
    • 数据单元 (DATA)这里就包含了我们梦寐以求的“总电量”数据!电量数据通常以“底码”形式传输,即电表从安装开始累积的电能值,单位可能是kWh(千瓦时)或MWh(兆瓦时),并以特定的数据格式(如4字节或6字节的BCD码、或4字节的整型数)存放。

假设从站返回的正向有功总电量值为12345.67 kWh。在通信中,这个值通常被放大一定的倍数(如100倍)以消除小数,变成整数1234567。然后以BCD码或二进制形式传输。

以4字节BCD码(压缩格式)为例1234567的BCD码表示为:0x01 0x23 0x45 0x67。但需注意字节顺序(Endianness),可能是0x67 0x45 0x23 0x01(低位在前)。

假设一个响应报文实例68 0B F3 10 00 8A 00 01 67 45 23 01 2E 16

字节含义逐项解析表

字节位置值(十六进制)含义说明
10x68帧起始符
20x0B长度域,后续有11个字节(C到DATA结束)
30xF3控制域,从站发送的响应帧
4-50x10 0x00地址域,从站地址为16
6-80x8A 0x00 0x01链路用户数据:AFN=0x8A(读数据响应),DA=0x00, DT=0x01
9-120x67 0x45 0x23 0x01数据单元:总电量值,BCD码,低位在前。解读:0x01 0x23 0x45 0x67-> 十进制 1234567。假设倍率为100,则实际电量为 12345.67 kWh。
130x2E帧校验和(计算0x0B 0xF3 ... 0x01的累加和)
140x16帧结束符

数据解析的关键步骤

  1. 定位数据区: 根据长度域和已知的固定结构(起始符、长度、控制、地址、AFN、DA/DT),找到数据单元的开始位置。
  2. 识别数据类型与长度: 根据请求的DT (0x01),你知道返回的是总电量,并且从规约手册中得知其数据类型为“4字节BCD码,低位在前,倍率100”。
  3. 提取与转换
    • 提取字节:0x67, 0x45, 0x23, 0x01
    • 按正确顺序重组(低位在前,所以实际顺序就是提取的顺序):0x67 0x45 0x23 0x01
    • 将每个字节当作两个BCD数字解读:0x67-> 数字670x45->450x23->230x01->01
    • 从左到右(从最高位字节到最低位字节)拼接:01 23 45 67
    • 得到整数1234567
    • 除以倍率100,得到最终电量值:12345.67kWh。

避坑指南: 数据解析中最容易出错的就是字节顺序数据格式。同样是4字节,可能是uint32整型直接传输,也可能是BCD码。同样是BCD码,可能低位在前也可能高位在前。同样是一个值,可能有倍率(如10、100、1000倍)需要还原。这些信息必须、也只能从设备配套的通信规约说明书中找到明确定义,没有任何捷径。在代码实现解析逻辑时,务必为每种数据项(DT)单独编写解析函数,并做好详细的注释。

5. 完整通信流程与异常处理实战

理解了单个请求和响应报文后,我们将其置于完整的通信上下文中,并讨论常见的异常情况。

一个健壮的查询流程应包含以下步骤

  1. 链路建立与维护: 在开始数据查询前,有些102协议变体需要先进行“链路测试”或“登录”操作,以确保物理链路和基础协议是通的。这通常通过发送一帧固定的“链路测试”报文并等待确认来实现。
  2. 发送请求帧: 如第3节所述,构造并发送格式正确的查询请求报文。
  3. 等待与接收响应: 设置一个合理的超时时间(如3-5秒),等待从站回应。使用串口或Socket的读取函数,持续读取数据直到超时。
  4. 响应帧验证: 收到数据后,进行逐层验证:
    • 帧完整性: 检查是否以0x68开头,是否包含0x16结束符。
    • 长度校验: 根据长度域L,检查收到的数据长度是否足够。如果收到的数据比L指示的少,说明帧不完整,可能是通信中断。
    • 校验和验证: 重新计算从长度域到数据区结束的校验和,与报文中的CS字节比较。不一致则丢弃,说明传输过程中有误码。
    • 地址匹配: 检查响应帧中的地址域是否与请求的从站地址一致,防止收到其他设备的应答。
    • AFN/DA/DT匹配: 检查应用层标识是否与之前的请求对应。
  5. 数据解析与处理: 验证通过后,跳转到第4节的数据解析流程,提取并计算最终的电量值。
  6. 错误处理与重试: 如果任何一步验证失败,或发生超时,应进入错误处理流程。常见的策略是记录日志,并在短暂的延迟后进行重试(例如,最多重试3次)。

常见异常与排查思路

  • 无响应(超时)

    • 物理层: 检查串口线/网线、波特率(9600, 19200等)、数据位(8)、停止位(1)、校验位(偶校验EVEN/奇校验ODD/无校验NONE)。波特率和校验位错误是最常见原因
    • 链路层: 确认请求报文的地址域完全正确。地址可能是BCD码、纯二进制,甚至可能包含区域码、表号等组合。用设备提供的测试软件(如果有)先试通一次,并抓取报文对比。
    • 报文格式: 确认帧起始符、结束符是否正确。确认校验和算法是否正确。一个快速验证的方法是,先发送一个已知正确的、简单的链路测试帧,看设备是否有回复。
  • 响应帧校验错误

    • 大概率是校验和算法用错了。仔细阅读规约书,确认是累加和(ADD)、异或和(XOR)、CRC8还是CRC16。累加和是忽略进位还是取补码?这些细节至关重要。
    • 也可能是长度域计算范围理解有误(是否包含自身?是否包含校验和?)。
  • 解析出的数据值明显不合理(如极大、极小或负数):

    • 字节顺序错误: 尝试将字节数组反向后再解析。
    • 数据格式错误: 确认是BCD码还是二进制整数。对于BCD码,是压缩格式(一个字节两个数字)还是非压缩格式(一个字节一个数字,用ASCII码表示)?
    • 倍率错误: 确认数值是否需要除以一个倍率(10, 100, 1000, 10000)才能得到真实值。
    • 数据类型错误: 确认你请求的DT (0x01) 是否真的对应“正向有功总电量”?有时电量数据分为“当前电量”和“上月电量”等,需要不同的DT。

个人经验: 在开发这类规约解析模块时,“报文日志”功能是救命稻草。务必在代码中,将每次发送和接收的原始字节数组,以十六进制字符串的形式详细打印或保存到日志文件中。格式最好类似于Tx: 68 06 73 ...Rx: 68 0B F3 ...。当出现问题时,对比日志和规约手册,可以快速定位是发送不对还是解析不对。另外,准备一个像“串口调试助手”这样的可视化工具进行手动测试和对比,在项目初期能极大提升效率。

← 返回列表