/
latin
/
WialonIPSServer
Обзор
Документация
Войти
/
latin
/
WialonIPSServer
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
PacketParser.cs
205 строк
7 KB
latin
fix for run
26 ноя 2025, 15:24
26 ноя 2025, 15:24
f001443
Код
Авторство
О чём код?
using System.Text; namespace WialonIPSServer; /// <summary> /// Парсер пакетов протокола Wialon IPS /// </summary> public class PacketParser { private const byte StartByte = (byte)'#'; private const byte Separator = (byte)'#'; private static readonly byte[] EndMarker = { 0x0D, 0x0A }; // \r\n /// <summary> /// Парсит пакет из байтового массива /// </summary> public static WialonPacket? Parse(ReadOnlySpan<byte> data) { if (data.Length < 3) // Минимум: #PT# или $P$ return null; // Специальная обработка пингового пакета $P$ if (data[0] == (byte)'$' && data.Length >= 3) { if (data[1] == (byte)'P' && data[2] == (byte)'$') { return new WialonPacket { Type = PacketType.P, Message = string.Empty, Crc = null, IsValid = true }; } } if (data[0] != StartByte) return null; if (data.Length < 4) // Минимум: #PT# return null; // Находим второй разделитель # int secondHashIndex = -1; for (int i = 1; i < data.Length; i++) { if (data[i] == Separator) { secondHashIndex = i; break; } } if (secondHashIndex == -1) return null; // Извлекаем тип пакета string packetTypeStr = Encoding.ASCII.GetString(data.Slice(1, secondHashIndex - 1)); if (!Enum.TryParse<PacketType>(packetTypeStr, out var packetType)) { packetType = PacketType.Unknown; } // Ищем конец пакета (\r\n) int endIndex = -1; for (int i = secondHashIndex + 1; i < data.Length - 1; i++) { if (data[i] == EndMarker[0] && data[i + 1] == EndMarker[1]) { endIndex = i; break; } } if (endIndex == -1) return null; // Извлекаем сообщение (между вторым # и CRC или концом) int messageStart = secondHashIndex + 1; int messageEnd = endIndex; // Проверяем наличие CRC16 (4 символа hex) string message = Encoding.ASCII.GetString(data.Slice(messageStart, messageEnd - messageStart)); ushort? crc = null; byte[]? binaryData = null; // Для некоторых пакетов (I, US, UC, T) может быть бинарная часть if (packetType == PacketType.I || packetType == PacketType.US || packetType == PacketType.UC || packetType == PacketType.T) { // Ищем разделитель между текстовой и бинарной частью int binStart = messageEnd + 2; // После \r\n if (binStart < data.Length) { binaryData = data.Slice(binStart).ToArray(); } } // Пытаемся извлечь CRC из конца сообщения if (message.Length >= 4) { string last4Chars = message.Substring(message.Length - 4); if (ushort.TryParse(last4Chars, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedCrc)) { crc = parsedCrc; message = message.Substring(0, message.Length - 4); } } var packet = new WialonPacket { Type = packetType, Message = message, Crc = crc, BinaryData = binaryData }; // Проверяем CRC для пакетов, которые его содержат if (crc.HasValue && ShouldValidateCrc(packetType)) { packet.IsValid = ValidateCrc(packet, data); } else { packet.IsValid = true; // Для пакетов без CRC считаем валидными } return packet; } /// <summary> /// Проверяет, нужно ли валидировать CRC для данного типа пакета /// </summary> private static bool ShouldValidateCrc(PacketType type) { return type == PacketType.L || type == PacketType.SD || type == PacketType.D || type == PacketType.B || type == PacketType.M || type == PacketType.IT || type == PacketType.US || type == PacketType.UC || type == PacketType.I || type == PacketType.T; } /// <summary> /// Валидирует CRC пакета /// </summary> private static bool ValidateCrc(WialonPacket packet, ReadOnlySpan<byte> originalData) { if (!packet.Crc.HasValue) return false; ushort calculatedCrc; // Для пакетов с бинарными данными (I, US, UC, T) CRC считается только для бинарной части if (packet.BinaryData != null && packet.BinaryData.Length > 0) { calculatedCrc = Crc16.Calculate(packet.BinaryData); return calculatedCrc == packet.Crc.Value; } // Для остальных пакетов CRC считается для части сообщения между #PT# и CRC16 // Находим начало сообщения (после второго #) int secondHashIndex = -1; for (int i = 1; i < originalData.Length; i++) { if (originalData[i] == Separator) { secondHashIndex = i; break; } } if (secondHashIndex == -1) return false; // Данные для проверки CRC - от начала сообщения до CRC (исключая сам CRC) int crcStart = originalData.Length - 6; // 4 символа CRC + \r\n if (crcStart <= secondHashIndex) return false; var dataForCrc = originalData.Slice(secondHashIndex + 1, crcStart - secondHashIndex - 1); calculatedCrc = Crc16.Calculate(dataForCrc); return calculatedCrc == packet.Crc.Value; } /// <summary> /// Формирует ответный пакет /// </summary> public static byte[] BuildResponse(PacketType responseType, string message, ushort? crc = null) { var sb = new StringBuilder(); sb.Append('#'); sb.Append(responseType.ToString()); sb.Append('#'); sb.Append(message); if (crc.HasValue) { sb.Append(crc.Value.ToString("X4")); } sb.Append("\r\n"); return Encoding.ASCII.GetBytes(sb.ToString()); } }