Protocol376Simulator/Models/MessageAnalyzer.cs
2025-05-08 17:26:10 +08:00

374 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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');
}
}
}