Protocol376Simulator/Models/MessageAnalyzer.cs

374 lines
15 KiB
C#
Raw Normal View History

2025-05-08 17:26:10 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Protocol376Simulator.Models
{
public class MessageAnalyzer
{
// AFN功能码描述
private static readonly Dictionary<byte, string> AfnDescriptions = new Dictionary<byte, string>
{
{ 0x00, "确认/否认" },
{ 0x01, "复位" },
{ 0x02, "链路接口检测" },
{ 0x04, "设置参数" },
{ 0x05, "控制命令" },
{ 0x06, "身份认证及密钥协商" },
{ 0x07, "自定义数据请求" },
{ 0x08, "主动上报" },
{ 0x09, "读取数据" },
{ 0x0A, "查询参数" },
{ 0x0B, "软件升级" },
{ 0x0F, "文件传输" }
};
/// <summary>
/// 分析报文内容并生成详细解释
/// </summary>
/// <param name="message">原始报文字节数组</param>
/// <returns>报文分析结果</returns>
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();
}
/// <summary>
/// 解析控制码
/// </summary>
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();
}
/// <summary>
/// 获取启动站功能码描述
/// </summary>
private static string GetPrimaryFunctionCodeDesc(byte code)
{
return code switch
{
0 => "复位远方链路",
1 => "读取状态",
3 => "发送/确认用户数据",
4 => "发送/无需确认用户数据",
9 => "请求/响应链路状态",
10 => "请求/响应用户数据1",
11 => "请求/响应用户数据2",
_ => "未知功能"
};
}
/// <summary>
/// 获取从动站功能码描述
/// </summary>
private static string GetSecondaryFunctionCodeDesc(byte code)
{
return code switch
{
0 => "确认",
1 => "链路忙",
9 => "从站状态",
11 => "响应用户数据",
_ => "未知功能"
};
}
/// <summary>
/// 根据不同的AFN值分析数据域
/// </summary>
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();
}
/// <summary>
/// 计算校验和
/// </summary>
private static byte CalculateChecksum(byte[] message)
{
byte sum = 0;
// 从第二个68H开始计算校验和
for (int i = 5; i < message.Length - 2; i++)
{
sum += message[i];
}
return sum;
}
/// <summary>
/// 将BCD码转换为字符串
/// </summary>
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');
}
}
}