using System;
using System.Text;
using Protocol376Simulator.Interfaces;
namespace Protocol376Simulator.Models
{
///
/// 376.1协议消息类
///
public class Protocol376Message : IProtocolMessage
{
public byte StartByte { get; set; } = 0x68;
public byte Length { get; set; }
public byte ControlCode { get; set; }
public byte[] Address { get; set; } = new byte[4];
public byte[] Data { get; set; }
public byte CheckSum { get; set; }
public byte EndByte { get; set; } = 0x16;
public MessageType Type { get; set; }
///
/// 将消息转换为字节数组
///
public byte[] ToBytes()
{
// 根据模板生成报文:68 36 00 36 00 68 C9 XX XX XX XX 07 02 70 00 00 04 00 29 F4 CS 16
var dataLength = Data?.Length ?? 0;
var message = new byte[dataLength + 13]; // 计算实际长度
// 前缀部分
message[0] = 0x68; // 起始字节
message[1] = 0x36; // 长度域1
message[2] = 0x00; // 长度域2
message[3] = 0x36; // 长度域3(重复)
message[4] = 0x00; // 长度域4(重复)
message[5] = 0x68; // 起始字节(重复)
message[6] = ControlCode;
// 地址域
Array.Copy(Address, 0, message, 7, 4);
// 数据域
if (Data != null)
{
Array.Copy(Data, 0, message, 11, dataLength);
}
// 计算校验和
byte sum = 0;
for (int i = 5; i < message.Length - 2; i++) // 从第二个68H开始计算校验和
{
sum += message[i];
}
message[message.Length - 2] = sum;
// 结束字节
message[message.Length - 1] = 0x16;
return message;
}
///
/// 获取消息详细信息
///
public string GetMessageInfo()
{
var sb = new StringBuilder();
// 打印完整报文
var fullMessage = ToBytes();
sb.Append("完整报文: ");
foreach (var b in fullMessage)
{
sb.Append($"{b:X2} ");
}
sb.AppendLine();
// 打印报文内容解析
sb.AppendLine("报文解析:");
sb.AppendLine($"报文类型: {Type}");
sb.AppendLine($"控制码: 0x{ControlCode:X2}");
// 地址域
sb.Append("地址域: ");
foreach (var b in Address)
{
sb.Append($"{b:X2} ");
}
sb.AppendLine();
// 数据域
if (Data != null && Data.Length > 0)
{
sb.Append("数据域: ");
foreach (var b in Data)
{
sb.Append($"{b:X2} ");
}
sb.AppendLine();
// 根据不同报文类型解析数据域
sb.AppendLine(ParseDataByType());
}
else
{
sb.AppendLine("数据域: 无");
}
return sb.ToString();
}
private string ParseDataByType()
{
if (Data == null || Data.Length == 0)
return "数据域解析: 无数据";
var sb = new StringBuilder();
sb.AppendLine("数据域解析:");
switch (Type)
{
case MessageType.Login:
sb.AppendLine(" 登录报文");
if (Data.Length >= 7)
{
sb.AppendLine($" AFN: 0x{Data[0]:X2} - 登录请求");
sb.AppendLine($" 数据单元标识: {BitConverter.ToString(Data, 1, 2).Replace("-", " ")}");
}
break;
case MessageType.Heartbeat:
sb.AppendLine(" 心跳报文");
if (Data.Length >= 9)
{
sb.AppendLine($" AFN: 0x{Data[0]:X2} - 心跳");
sb.AppendLine($" 数据单元标识: {BitConverter.ToString(Data, 1, 2).Replace("-", " ")}");
sb.AppendLine($" 自检状态信息: {BitConverter.ToString(Data, 7, 2).Replace("-", " ")}");
}
break;
case MessageType.ValveControl:
sb.AppendLine(" 阀控操作报文");
if (Data.Length >= 7)
{
sb.AppendLine($" AFN: 0x{Data[0]:X2}");
sb.AppendLine($" 数据单元标识: {BitConverter.ToString(Data, 1, 2).Replace("-", " ")}");
sb.AppendLine($" 阀控操作码: 0x{Data[5]:X2}");
string operation = Data[5] switch
{
0x01 => "开阀",
0x02 => "关阀",
0x03 => "查询状态",
_ => "未知操作"
};
sb.AppendLine($" 操作: {operation}");
}
break;
case MessageType.DataUpload:
sb.AppendLine(" 数据上传报文");
if (Data.Length >= 5)
{
sb.AppendLine($" AFN: 0x{Data[0]:X2}");
sb.AppendLine($" 数据单元标识: {BitConverter.ToString(Data, 1, 2).Replace("-", " ")}");
sb.AppendLine($" 数据长度: {Data.Length - 5} 字节");
}
break;
case MessageType.Response:
sb.AppendLine(" 响应报文");
if (Data.Length >= 3)
{
sb.AppendLine($" AFN: 0x{Data[0]:X2}");
sb.AppendLine($" 数据单元标识: {BitConverter.ToString(Data, 1, 2).Replace("-", " ")}");
if (Data.Length > 3)
{
sb.AppendLine($" 结果码: 0x{Data[3]:X2}");
string result = Data[3] == 0 ? "成功" : "失败";
sb.AppendLine($" 操作结果: {result}");
}
}
break;
default:
sb.AppendLine(" 未知报文类型");
break;
}
return sb.ToString();
}
///
/// 转换集中器地址为字节数组
///
private static byte[] ConvertAddress(string concentratorAddress)
{
try
{
// 使用 BCD 编码转换集中器地址
var address = new byte[4];
// 假设集中器地址是9位数字,转换为4字节BCD
if (concentratorAddress.Length != 9)
{
throw new ArgumentException("集中器地址必须是9位数字");
}
// 左边高字节,右边低字节
address[0] = byte.Parse(concentratorAddress.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
address[1] = byte.Parse(concentratorAddress.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
address[2] = byte.Parse(concentratorAddress.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
address[3] = byte.Parse(concentratorAddress.Substring(6, 2) + "0", System.Globalization.NumberStyles.HexNumber);
return address;
}
catch (Exception ex)
{
throw new ArgumentException($"集中器地址转换失败: {ex.Message}", ex);
}
}
///
/// 创建登录消息
///
/// 集中器地址
public static Protocol376Message CreateLoginMessage(string concentratorAddress)
{
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
// 数据域 - 登录请求
Data = new byte[] { 0x07, 0x02, 0x70, 0x00, 0x00, 0x04, 0x00, 0x29, 0xF4 },
Type = MessageType.Login
};
return message;
}
///
/// 创建心跳消息
///
/// 集中器地址
public static Protocol376Message CreateHeartbeatMessage(string concentratorAddress)
{
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
// 数据域 - 心跳
Data = new byte[] { 0x08, 0x02, 0x70, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 },
Type = MessageType.Heartbeat
};
return message;
}
///
/// 创建阀控消息
///
/// 集中器地址
/// 阀门操作:1=开阀,2=关阀,3=查询状态
public static Protocol376Message CreateValveControlMessage(string concentratorAddress, byte valveOperation)
{
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
// 数据域 - 阀控操作
Data = new byte[] { 0x09, 0x02, 0x70, 0x00, 0x00, valveOperation },
Type = MessageType.ValveControl
};
return message;
}
///
/// 创建数据上传消息
///
/// 集中器地址
/// 表计数据
public static Protocol376Message CreateDataUploadMessage(string concentratorAddress, byte[] data)
{
if (data == null || data.Length == 0)
{
throw new ArgumentException("表计数据不能为空");
}
// 构造数据域
var dataField = new byte[5 + data.Length];
dataField[0] = 0x0A; // AFN
dataField[1] = 0x02; // 数据单元标识1
dataField[2] = 0x70; // 数据单元标识2
dataField[3] = 0x00; // 时间标签1
dataField[4] = 0x00; // 时间标签2
// 拷贝表计数据
Array.Copy(data, 0, dataField, 5, data.Length);
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
Data = dataField,
Type = MessageType.DataUpload
};
return message;
}
///
/// 创建读数据消息
///
/// 集中器地址
/// 数据类型:1=水表,2=电表,3=气表,4=状态
public static Protocol376Message CreateReadDataMessage(string concentratorAddress, byte dataType)
{
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
// 数据域 - 读数据
Data = new byte[] { 0x0B, 0x02, 0x70, 0x00, 0x00, dataType },
Type = MessageType.ReadData
};
return message;
}
///
/// 创建设置参数消息
///
/// 集中器地址
/// 参数类型
/// 参数数据
public static Protocol376Message CreateSetParameterMessage(string concentratorAddress, byte paramType, byte[] paramData)
{
if (paramData == null || paramData.Length == 0)
{
throw new ArgumentException("参数数据不能为空");
}
// 构造数据域
var dataField = new byte[6 + paramData.Length];
dataField[0] = 0x0C; // AFN
dataField[1] = 0x02; // 数据单元标识1
dataField[2] = 0x70; // 数据单元标识2
dataField[3] = 0x00; // 时间标签1
dataField[4] = 0x00; // 时间标签2
dataField[5] = paramType; // 参数类型
// 拷贝参数数据
Array.Copy(paramData, 0, dataField, 6, paramData.Length);
var message = new Protocol376Message
{
ControlCode = 0xC9, // 控制码
Address = ConvertAddress(concentratorAddress),
Data = dataField,
Type = MessageType.SetParameter
};
return message;
}
///
/// 从字节数组解析消息
///
/// 消息字节数组
/// 解析后的消息对象
public static Protocol376Message ParseFromBytes(byte[] messageBytes)
{
if (messageBytes == null || messageBytes.Length < 13)
{
throw new ArgumentException("消息字节数组格式无效");
}
// 检查起始字节和结束字节
if (messageBytes[0] != 0x68 || messageBytes[5] != 0x68 || messageBytes[messageBytes.Length - 1] != 0x16)
{
throw new ArgumentException("消息格式无效:起始或结束字节错误");
}
// 计算校验和
byte calculatedChecksum = 0;
for (int i = 5; i < messageBytes.Length - 2; i++)
{
calculatedChecksum += messageBytes[i];
}
// 验证校验和
byte receivedChecksum = messageBytes[messageBytes.Length - 2];
if (calculatedChecksum != receivedChecksum)
{
throw new ArgumentException($"校验和错误: 计算={calculatedChecksum:X2}, 接收={receivedChecksum:X2}");
}
// 提取控制码
byte controlCode = messageBytes[6];
// 提取地址域
byte[] address = new byte[4];
Array.Copy(messageBytes, 7, address, 0, 4);
// 提取数据域
int dataLength = messageBytes.Length - 13;
byte[] data = new byte[dataLength];
if (dataLength > 0)
{
Array.Copy(messageBytes, 11, data, 0, dataLength);
}
// 识别消息类型
MessageType messageType = MessageType.Response; // 默认为响应类型
if (dataLength > 0)
{
messageType = data[0] switch
{
0x07 => MessageType.Login,
0x08 => MessageType.Heartbeat,
0x09 => MessageType.ValveControl,
0x0A => MessageType.DataUpload,
0x0B => MessageType.ReadData,
0x0C => MessageType.SetParameter,
_ => MessageType.Response
};
}
// 构造消息对象
var message = new Protocol376Message
{
ControlCode = controlCode,
Address = address,
Data = data,
Type = messageType,
CheckSum = receivedChecksum
};
return message;
}
}
}