"""配电系统 UDP 协议封装(声明式消息定义)。 字段顺序、大小与 C++ 侧 pmSysvariable.h / ccuUdpMsg.h 严格对齐。 帧格式(配电消息,单字节校验): start1(0x40) start2(0x40) | id(uint16 LE) | length(uint16 LE) | body | checkCode(和校验 &0xFF) 说明: - 字段表(FIELDS)是唯一数据源,length、pack/unpack 均由此推导, 避免手工维护长度字段与 C++ 结构体大小不一致。 - unpack 采用固定偏移解析 + 起始字节/最小长度/校验和校验。 """ import struct DIS_START1 = 0x40 DIS_START2 = 0x40 # 断路器状态定义(与 C++ 一致) BREAKER_ON = 0x55 # 合 BREAKER_OFF = 0xAA # 分 BREAKER_FAULT = 0x5A # 故障 class Field: """协议字段定义。fmt 支持 'B'(uchar) / 'H'(ushort LE)。""" __slots__ = ("name", "fmt", "size") def __init__(self, name, fmt): self.name = name self.fmt = fmt self.size = struct.calcsize(fmt) class Message: """声明式消息基类:由 FIELDS 自动生成 pack/unpack 与长度。""" MSG_ID = 0x0000 CN = "未知消息" FIELDS = () # tuple[Field] def __init__(self): self.start1 = DIS_START1 self.start2 = DIS_START2 self.msg_id = self.MSG_ID self.length = self.body_size() self.checkCode = 0 for f in self.FIELDS: setattr(self, f.name, 0) # ---- 派生属性 ------------------------------------------------- @classmethod def body_size(cls): return sum(f.size for f in cls.FIELDS) @classmethod def field_names(cls): return [f.name for f in cls.FIELDS] def to_dict(self): return {f.name: getattr(self, f.name) for f in self.FIELDS} def __repr__(self): parts = [f"{self.__class__.__name__}(id=0x{self.MSG_ID:04X}, len={self.length}"] for f in self.FIELDS: parts.append(f"{f.name}={getattr(self, f.name)}") return ", ".join(parts) + ")" # ---- 打包/解包 ------------------------------------------------ def pack(self): body = b"" for f in self.FIELDS: body += struct.pack("<" + f.fmt, getattr(self, f.name)) self.length = self.body_size() header = struct.pack("