using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Protocol376Simulator.Models { public class MessageAnalyzer { // AFN功能码描述 private static readonly Dictionary AfnDescriptions = new Dictionary { { 0x00, "确认/否认" }, { 0x01, "复位" }, { 0x02, "链路接口检测" }, { 0x04, "设置参数" }, { 0x05, "控制命令" }, { 0x06, "身份认证及密钥协商" }, { 0x07, "自定义数据请求" }, { 0x08, "主动上报" }, { 0x09, "读取数据" }, { 0x0A, "查询参数" }, { 0x0B, "软件升级" }, { 0x0F, "文件传输" } }; /// /// 分析报文内容并生成详细解释 /// /// 原始报文字节数组 /// 报文分析结果 public static string AnalyzeMessage(byte[] message) { var sb = new StringBuilder(); sb.AppendLine("====== 报文详细分析 ======"); // 检查报文合法性 if (message.Length < 13 || message[0] != 0x68 || message[5] != 0x68 || message[message.Length - 1] != 0x16) { sb.AppendLine("❌ 报文格式不合法!"); sb.AppendLine($"原始报文: {BitConverter.ToString(message).Replace("-", " ")}"); return sb.ToString(); } // 报文基本信息 sb.AppendLine($"原始报文: {BitConverter.ToString(message).Replace("-", " ")}"); sb.AppendLine($"报文长度: {message.Length} 字节"); // 头部分析 sb.AppendLine("\n【帧头结构】"); sb.AppendLine($"起始字节1: 0x{message[0]:X2}"); sb.AppendLine($"长度域1: 0x{message[1]:X2} 0x{message[2]:X2}"); sb.AppendLine($"长度域2: 0x{message[3]:X2} 0x{message[4]:X2}"); sb.AppendLine($"起始字节2: 0x{message[5]:X2}"); // 控制域分析 byte controlCode = message[6]; sb.AppendLine("\n【控制域】"); sb.AppendLine($"控制码: 0x{controlCode:X2}"); // 解析控制码 sb.AppendLine(AnalyzeControlCode(controlCode)); // 地址域分析 byte[] address = message.Skip(7).Take(4).ToArray(); sb.AppendLine("\n【地址域】"); sb.AppendLine($"地址域: {BitConverter.ToString(address).Replace("-", " ")}"); sb.AppendLine($"BCD编码地址: {BcdToString(address)}"); // 数据域分析 int dataLength = message.Length - 13; // 减去帧头、控制域、地址域、校验和、结束符 if (dataLength > 0) { byte[] data = message.Skip(11).Take(dataLength).ToArray(); sb.AppendLine("\n【数据域】"); sb.AppendLine($"数据域: {BitConverter.ToString(data).Replace("-", " ")}"); sb.AppendLine($"数据长度: {dataLength} 字节"); // AFN分析 if (data.Length > 0) { byte afn = data[0]; string afnDesc = AfnDescriptions.ContainsKey(afn) ? AfnDescriptions[afn] : "未知功能"; sb.AppendLine($"应用功能码(AFN): 0x{afn:X2} - {afnDesc}"); } // 数据单元标识分析 if (data.Length > 2) { sb.AppendLine($"数据单元标识: 0x{data[1]:X2} 0x{data[2]:X2}"); } // 根据AFN进一步分析数据内容 if (data.Length > 0) { sb.AppendLine(AnalyzeDataByAfn(data)); } } else { sb.AppendLine("\n【数据域】"); sb.AppendLine("无数据域内容"); } // 校验和分析 byte calculatedCs = CalculateChecksum(message); byte actualCs = message[message.Length - 2]; sb.AppendLine("\n【校验和】"); sb.AppendLine($"校验和: 0x{actualCs:X2} ({(calculatedCs == actualCs ? "✓ 正确" : "❌ 错误,应为 0x" + calculatedCs.ToString("X2"))})"); // 结束符 sb.AppendLine("\n【结束符】"); sb.AppendLine($"结束字节: 0x{message[message.Length - 1]:X2}"); return sb.ToString(); } /// /// 解析控制码 /// private static string AnalyzeControlCode(byte controlCode) { var sb = new StringBuilder(); // 方向位D7 (DIR) bool isDownlink = (controlCode & 0x80) != 0; sb.AppendLine($"方向位(D7): {(isDownlink ? "下行 (主站→集中器)" : "上行 (集中器→主站)")}"); // 启动标志位D6 (PRM) bool isPrimaryStation = (controlCode & 0x40) != 0; sb.AppendLine($"启动标志位(D6): {(isPrimaryStation ? "启动站" : "从动站")}"); // 帧计数位D5 (FCB) bool fcb = (controlCode & 0x20) != 0; // 功能码D0-D3 byte functionCode = (byte)(controlCode & 0x0F); // 根据PRM和功能码解析不同的含义 if (isPrimaryStation) { sb.AppendLine($"帧计数位(D5): {fcb}"); sb.AppendLine($"功能码(D0-D3): 0x{functionCode:X1} - {GetPrimaryFunctionCodeDesc(functionCode)}"); } else { bool dfc = (controlCode & 0x10) != 0; sb.AppendLine($"数据流控制位(D4): {dfc} - {(dfc ? "从站接收缓冲区已满" : "从站可以接收数据")}"); sb.AppendLine($"功能码(D0-D3): 0x{functionCode:X1} - {GetSecondaryFunctionCodeDesc(functionCode)}"); } return sb.ToString(); } /// /// 获取启动站功能码描述 /// private static string GetPrimaryFunctionCodeDesc(byte code) { return code switch { 0 => "复位远方链路", 1 => "读取状态", 3 => "发送/确认用户数据", 4 => "发送/无需确认用户数据", 9 => "请求/响应链路状态", 10 => "请求/响应用户数据1", 11 => "请求/响应用户数据2", _ => "未知功能" }; } /// /// 获取从动站功能码描述 /// private static string GetSecondaryFunctionCodeDesc(byte code) { return code switch { 0 => "确认", 1 => "链路忙", 9 => "从站状态", 11 => "响应用户数据", _ => "未知功能" }; } /// /// 根据不同的AFN值分析数据域 /// private static string AnalyzeDataByAfn(byte[] data) { if (data.Length == 0) return "数据为空"; var sb = new StringBuilder(); byte afn = data[0]; sb.AppendLine("\n【数据域解析】"); switch (afn) { case 0x00: // 确认/否认 sb.AppendLine("确认/否认报文"); if (data.Length > 5) { byte resultCode = data[5]; sb.AppendLine($"结果码: 0x{resultCode:X2} - {(resultCode == 0 ? "成功" : "失败")}"); } break; case 0x02: // 链路接口检测 sb.AppendLine("链路接口检测报文"); break; case 0x04: // 设置参数 sb.AppendLine("设置参数报文"); if (data.Length > 5) { byte paramType = data[5]; sb.AppendLine($"参数类型: 0x{paramType:X2}"); // 解析参数内容 if (data.Length > 7) { byte paramLength = data[6]; byte[] paramValue = data.Skip(7).Take(paramLength).ToArray(); sb.AppendLine($"参数长度: {paramLength} 字节"); sb.AppendLine($"参数值: {BitConverter.ToString(paramValue).Replace("-", " ")}"); // 尝试转换为ASCII显示 try { string asciiValue = System.Text.Encoding.ASCII.GetString(paramValue) .Replace('\0', '.') .Replace('\r', '.') .Replace('\n', '.'); sb.AppendLine($"参数值(ASCII): \"{asciiValue}\""); } catch { // 忽略无法转换的情况 } } } break; case 0x07: // 自定义数据 sb.AppendLine("自定义数据报文"); if (data.Length > 2) { byte dataFlag = data[1]; if (dataFlag == 0x01) // 登录请求 { sb.AppendLine("登录请求"); } else if (dataFlag == 0x02) // 心跳 { sb.AppendLine("心跳报文"); if (data.Length > 8) { byte[] statusInfo = data.Skip(7).Take(2).ToArray(); sb.AppendLine($"自检状态信息: 0x{statusInfo[0]:X2} 0x{statusInfo[1]:X2}"); } } else if (dataFlag == 0x03) // 阀控 { sb.AppendLine("阀控操作报文"); if (data.Length > 5) { byte operation = data[5]; string operationDesc = operation switch { 0x01 => "开阀", 0x02 => "关阀", 0x03 => "查询状态", _ => $"未知操作(0x{operation:X2})" }; sb.AppendLine($"阀控操作: {operationDesc}"); } } } break; case 0x08: // 主动上报 sb.AppendLine("数据上传报文"); if (data.Length > 5) { byte dataType = data[5]; byte dataLen = data.Length > 6 ? data[6] : (byte)0; string dataTypeDesc = dataType switch { 0x01 => "水表数据", 0x02 => "电表数据", 0x03 => "气表数据", 0x04 => "状态数据", _ => $"未知类型(0x{dataType:X2})" }; sb.AppendLine($"数据类型: {dataTypeDesc}"); sb.AppendLine($"数据长度: {dataLen} 字节"); if (data.Length > 7 && dataLen > 0) { byte[] meterData = data.Skip(7).Take(dataLen).ToArray(); sb.AppendLine($"表计数据: {BitConverter.ToString(meterData).Replace("-", " ")}"); // 转换为数值显示(大端序) if (meterData.Length == 4) { uint value = ((uint)meterData[0] << 24) | ((uint)meterData[1] << 16) | ((uint)meterData[2] << 8) | meterData[3]; sb.AppendLine($"数值: {value}"); } } } break; case 0x09: // 读取数据 sb.AppendLine("数据读取报文"); if (data.Length > 5) { byte dataType = data[5]; string dataTypeDesc = dataType switch { 0x01 => "水表数据", 0x02 => "电表数据", 0x03 => "气表数据", 0x04 => "状态数据", _ => $"未知类型(0x{dataType:X2})" }; sb.AppendLine($"请求的数据类型: {dataTypeDesc}"); } break; default: sb.AppendLine($"未知AFN(0x{afn:X2})报文,无法解析具体内容"); break; } return sb.ToString(); } /// /// 计算校验和 /// private static byte CalculateChecksum(byte[] message) { byte sum = 0; // 从第二个68H开始计算校验和 for (int i = 5; i < message.Length - 2; i++) { sum += message[i]; } return sum; } /// /// 将BCD码转换为字符串 /// private static string BcdToString(byte[] bcd) { var sb = new StringBuilder(); foreach (byte b in bcd) { sb.Append((b >> 4) & 0x0F); sb.Append(b & 0x0F); } return sb.ToString().TrimStart('0'); } } }