using System.Collections.Immutable; using System.Text.RegularExpressions; using Newtonsoft.Json; namespace LTEMvcApp.Models; /// /// LTE客户端类 - 对应JavaScript中的lte.client对象 /// public class LTEClient { #region 常量定义 /// /// HFN回绕阈值 /// private const int HFN_WRAP_THRESHOLD = 512; /// /// 最大日志数量 /// private const int LOGS_MAX = 2000000; #endregion #region 正则表达式 /// /// PHY层日志正则表达式 /// private static readonly Regex RegExpPhy = new(@"^([a-f0-9\-]+)\s+([a-f0-9\-]+)\s+([\d\.\-]+) (\w+): (.+)", RegexOptions.Compiled); /// /// 信息1正则表达式 /// private static readonly Regex RegExpInfo1 = new(@"^([\w\-]+): (.+)", RegexOptions.Compiled); /// /// 信息2正则表达式 /// private static readonly Regex RegExpInfo2 = new(@"^([\w]+) (.+)", RegexOptions.Compiled); /// /// IP日志正则表达式 /// private static readonly Regex RegExpIP = new(@"^(len=\d+)\s+(\S+)\s+(.*)", RegexOptions.Compiled); /// /// IPsec日志正则表达式 /// private static readonly Regex RegExpIPsec = new(@"^len=(\d+)\s+(.*)", RegexOptions.Compiled); /// /// SDU长度正则表达式 /// private static readonly Regex RegExpSDULen = new(@"SDU_len=(\d+)", RegexOptions.Compiled); /// /// SIP日志正则表达式 /// private static readonly Regex RegExpSIP = new(@"^([:\.\[\]\da-f]+)\s+(\S+) (.+)", RegexOptions.Compiled); /// /// 媒体请求正则表达式 /// private static readonly Regex RegExpMediaReq = new(@"^(\S+) (.+)", RegexOptions.Compiled); /// /// 信号记录正则表达式 /// private static readonly Regex RegExpSignalRecord = new(@"Link:\s([\w\d]+)@(\d+)", RegexOptions.Compiled); /// /// 小区ID正则表达式 /// private static readonly Regex RegExpCellID = new(@"^([a-f0-9\-]+) (.+)", RegexOptions.Compiled); /// /// RRC UE ID正则表达式 /// private static readonly Regex RegExpRRC_UE_ID = new(@"Changing UE_ID to 0x(\d+)", RegexOptions.Compiled); /// /// RRC TMSI正则表达式 /// private static readonly Regex RegExpRRC_TMSI = new(@"(5G|m)-TMSI '([\dA-F]+)'H", RegexOptions.Compiled); /// /// RRC新ID正则表达式 /// private static readonly Regex RegExpRRC_NEW_ID = new(@"newUE-Identity (['\dA-FH]+)", RegexOptions.Compiled); /// /// RRC CRNTI正则表达式 /// private static readonly Regex RegExpRRC_CRNTI = new(@"c-RNTI '([\dA-F]+)'H", RegexOptions.Compiled); /// /// NAS TMSI正则表达式 /// private static readonly Regex RegExpNAS_TMSI = new(@"m-TMSI = 0x([\da-f]+)", RegexOptions.Compiled); /// /// NAS 5G TMSI正则表达式 /// private static readonly Regex RegExpNAS_5GTMSI = new(@"5G-TMSI = 0x([\da-f]+)", RegexOptions.Compiled); /// /// RRC频段组合正则表达式 /// private static readonly Regex RegExpRRC_BC = new(@"(EUTRA|MRDC|NR|NRDC) band combinations", RegexOptions.Compiled); /// /// PDCCH正则表达式 /// private static readonly Regex RegExpPDCCH = new(@"^\s*(.+)=(\d+)$", RegexOptions.Compiled); /// /// S1/NGAP正则表达式 /// private static readonly Regex RegExpS1NGAP = new(@"^([\da-f\-]+)\s+([\da-f\-]+) (([^\s]+) .+)", RegexOptions.Compiled); /// /// 十六进制转储正则表达式 /// private static readonly Regex RegExpHexDump = new(@"^[\da-f]+:(\s+[\da-f]{2}){1,16}\s+.{1,16}$", RegexOptions.Compiled); #endregion #region 基础属性 /// /// 客户端ID /// public int ClientId { get; set; } /// /// 客户端名称 /// public string Name { get; set; } = string.Empty; /// /// 客户端配置 /// public ClientConfig Config { get; set; } = new(); /// /// 客户端状态 /// public ClientState State { get; set; } = ClientState.Stop; /// /// 版本信息 /// public string? Version { get; set; } /// /// 许可证信息 /// public string? License { get; set; } /// /// 模型类型 /// public string? Model { get; set; } /// /// 是否有信号记录 /// public bool HasSignalRecord { get; set; } #endregion #region 日志相关属性 /// /// 日志列表 /// public List Logs { get; set; } = new(); /// /// 日志数量 /// public int LogCount => Logs.Count; /// /// 是否已过滤 /// public bool Filtered { get; set; } /// /// 重置标志 /// public bool ResetFlag { get; set; } #endregion #region 解析器状态 /// /// 最后HARQ信息 /// public Dictionary> LastHarq { get; set; } = new(); /// /// 最后NB HARQ信息 /// public Dictionary>> LastNbHarq { get; set; } = new(); /// /// 帧信息 /// public FrameInfo Frame { get; set; } = new(); /// /// TMSI到UE ID的映射 /// public Dictionary TmsiToUeId { get; set; } = new(); /// /// RNTI到UE ID的映射 /// public Dictionary RntiToUeId { get; set; } = new(); /// /// UE列表 /// public Dictionary UeList { get; set; } = new(); /// /// 最后时间戳 /// public long LastTimestamp { get; set; } /// /// 时间戳偏移 /// public long TimestampOffset { get; set; } /// /// 最后小区 /// public int? LastCell { get; set; } #endregion #region 功能标志 /// /// 是否有小区信息 /// public bool HasCell { get; set; } /// /// 是否有物理层信息 /// public bool HasPhy { get; set; } /// /// 是否有数据 /// public bool HasData { get; set; } /// /// 是否有RNTI /// public bool HasRnti { get; set; } /// /// 是否有资源块 /// public bool HasRb { get; set; } #endregion #region 参数和组件 /// /// 参数信息 /// public Dictionary Parameters { get; set; } = new(); /// /// 组件列表 /// public Dictionary Components { get; set; } = new(); #endregion #region 依赖注入 /// /// 日志管理器 /// private readonly LogsManager _lteLogs; /// /// 获取日志管理器实例 /// public LogsManager LogsManager => _lteLogs; #endregion #region 构造函数 public LTEClient(ClientConfig config, LogsManager logsManager) { Config = config; Name = config.Name; ClientId = GenerateClientId(); _lteLogs = logsManager; ResetParserState(); } /// /// 使用名称创建客户端 /// /// 客户端名称 /// 日志管理器 public LTEClient(string name, LogsManager logsManager) { Config = new ClientConfig { Name = name, Enabled = true }; Name = name; ClientId = GenerateClientId(); _lteLogs = logsManager; ResetParserState(); } #endregion #region 客户端控制方法 /// /// 启动客户端 /// public void Start() { SetState(ClientState.Start); } /// /// 停止客户端 /// public void Stop() { SetState(ClientState.Stop); } /// /// 销毁客户端 /// public void Destroy() { SetState(ClientState.Destroy); } #endregion #region 日志管理方法 /// /// 重置日志 /// public void ResetLogs() { if (LogCount > 0) { ResetFlag = true; Logs.Clear(); ResetParserState(); HasCell = false; HasPhy = false; HasData = false; HasRnti = false; HasRb = false; HasSignalRecord = false; } } /// /// 添加日志 /// public void AddLog(LTELog log) { // 检查时间戳回绕 var timestamp = log.Timestamp + TimestampOffset; if (timestamp < LastTimestamp - 100) { Console.WriteLine($"Log wrap by {LastTimestamp - timestamp}"); timestamp += 86400000; // 24小时 TimestampOffset += 86400000; } LastTimestamp = log.Timestamp = timestamp; log.Client = this; log.Id = GenerateLogId(); } #endregion #region 工具方法 /// /// 设置头信息 /// public void SetHeaders(string[] headers) { // 实现头信息设置逻辑 } /// /// 初始化模型猜测 /// public void LogModelGuessInit() { if (string.IsNullOrEmpty(Config.Model)) { // 尝试所有模型 var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; if (!TryModelHint(modelList)) { SetModel("MME"); // 默认模型 } // 初始化模型猜测字典 _modelGuess = new Dictionary(); var layerConfig = GetLayerConfig(); foreach (var layer in layerConfig.Keys) { _modelGuess[layer] = false; } } } /// /// 模型猜测字典 /// private Dictionary? _modelGuess; /// /// 获取层配置 /// /// 层配置字典 private Dictionary GetLayerConfig() { // 这里应该从 LogsManager 获取层配置 // 暂时返回一个基本的层配置 return new Dictionary { { "PHY", new { Color = "#606070" } }, { "MAC", new { Color = "#10A0FF" } }, { "RLC", new { Color = "#FFFF80" } }, { "PDCP", new { Color = "#B0D0B0" } }, { "RRC", new { Color = "#00FF60" } }, { "NAS", new { Color = "#90FFC0" } }, { "GTPU", new { Color = "#FF80FF" } }, { "GTPC", new { Color = "#FFC0FF" } }, { "IP", new { Color = "#E0E0E0" } }, { "S1AP", new { Color = "#80FF00" } }, { "NGAP", new { Color = "#5DD122" } }, { "X2AP", new { Color = "#FF8000" } }, { "XnAP", new { Color = "#FFB020" } }, { "M2AP", new { Color = "#7F675B" } }, { "IMS", new { Color = "#C0FF80" } }, { "CX", new { Color = "#F49542" } }, { "RX", new { Color = "#D4A190" } }, { "COM", new { Color = "#C000FF" } }, { "SIP", new { Color = "#C080FF" } }, { "MEDIA", new { Color = "#800040" } }, { "RTP", new { Color = "#FF00C0" } }, { "PROD", new { Color = "#80C0FF" } }, { "S6", new { Color = "#F44B42" } }, { "S13", new { Color = "#D6F953" } }, { "SGsAP", new { Color = "#FF7DB8" } }, { "SBcAP", new { Color = "#8FA00F" } }, { "N8", new { Color = "#106020" } }, { "N12", new { Color = "#602020" } }, { "N13", new { Color = "#202060" } }, { "N17", new { Color = "#D6F953" } }, { "N50", new { Color = "#8FA00F" } }, { "MMS", new { Color = "#B4D98F" } }, { "HTTP2", new { Color = "#644824" } }, { "LCSAP", new { Color = "#cfd50d" } }, { "LPPa", new { Color = "#0dcfd5" } }, { "NL1", new { Color = "#d040cf" } }, { "NRPPa", new { Color = "#0dd5cf" } }, { "IKEV2", new { Color = "#C0B732" } }, { "SWU", new { Color = "#101080" } }, { "NWU", new { Color = "#2080B8" } }, { "IPSEC", new { Color = "#F04010" } }, { "N3IWF", new { Color = "#C080C0" } }, { "TRX", new { Color = "#42C0a0" } }, { "MON", new { Color = "#C0C080" } }, { "EVENT", new { Color = "#80C0FF" } }, { "ALARM", new { Color = "#FF8040" } }, { "LIC", new { Color = "#ff80c0" } }, { "OTS", new { Color = "#8080FF" } }, { "ERROR", new { Color = "#ff0000" } } }; } /// /// 模型猜测 /// /// 日志列表 public void LogModelGuess(List> logs) { var modelGuess = _modelGuess; if (modelGuess == null) return; // 标记所有出现的层 foreach (var log in logs) { if (log.TryGetValue("layer", out var layer) && layer is string layerStr) { modelGuess[layerStr] = true; } } // 尝试根据层配置确定模型 var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; var availableModels = new List(modelList); // 根据层配置过滤模型 foreach (var layer in modelGuess.Keys) { if (!modelGuess[layer]) continue; var layerConfig = GetLayerConfig(); if (layerConfig.TryGetValue(layer, out var config)) { // 这里应该检查层的方向配置来过滤模型 // 暂时简化处理 var layerDir = GetLayerDirection(layer); if (layerDir != null) { // 移除不支持的模型 availableModels.RemoveAll(model => !layerDir.ContainsKey(model)); } } // 如果只剩下一个模型,直接设置 if (availableModels.Count <= 1) break; } // 如果找到了合适的模型,设置它 if (availableModels.Count > 0 && availableModels.Count < modelList.Count) { SetModel(availableModels[0]); _modelGuess = null; // 清除猜测字典 } } /// /// 获取层方向配置 /// /// 层名称 /// 方向配置字典 private Dictionary? GetLayerDirection(string layer) { // 这里应该从 LogsManager 获取层的方向配置 // 暂时返回一个基本的配置 return layer switch { "PHY" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, "MAC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, "RLC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, "PDCP" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, "RRC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, "NAS" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } }, "GTPU" => new Dictionary { { "ENB", 2 }, { "N3IWF", 2 }, { "MME", 1 }, { "MBMSGW", 1 }, { "UE", 1 }, { "RUE", 2 } }, "GTPC" => new Dictionary { { "MME", 2 } }, "IP" => new Dictionary { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } }, "S1AP" => new Dictionary { { "ENB", 2 }, { "MME", 1 } }, "NGAP" => new Dictionary { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } }, "X2AP" => new Dictionary { { "ENB", 2 } }, "XnAP" => new Dictionary { { "ENB", 2 } }, "M2AP" => new Dictionary { { "ENB", 2 }, { "MBMSGW", 1 } }, "IMS" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, "CX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, "RX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, "COM" => new Dictionary { { "*", 2 } }, "SIP" => new Dictionary { { "IMS", 1 } }, "MEDIA" => new Dictionary { { "IMS", 1 } }, "RTP" => new Dictionary { { "IMS", 1 } }, "PROD" => new Dictionary { }, "S6" => new Dictionary { { "MME", 2 } }, "S13" => new Dictionary { { "MME", 2 } }, "SGsAP" => new Dictionary { { "MME", 2 } }, "SBcAP" => new Dictionary { { "MME", 2 } }, "N8" => new Dictionary { { "MME", 2 } }, "N12" => new Dictionary { { "MME", 2 } }, "N13" => new Dictionary { { "MME", 2 } }, "N17" => new Dictionary { { "MME", 2 } }, "N50" => new Dictionary { { "MME", 2 } }, "MMS" => new Dictionary { { "MME", 2 } }, "HTTP2" => new Dictionary { { "MME", 2 } }, "LCSAP" => new Dictionary { { "MME", 2 } }, "LPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, "NL1" => new Dictionary { { "MME", 2 } }, "NRPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, "IKEV2" => new Dictionary { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } }, "SWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, "NWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, "IPSEC" => new Dictionary { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } }, "N3IWF" => new Dictionary { { "UE", 2 }, { "N3IWF", 1 } }, "TRX" => new Dictionary { { "UE", 2 }, { "ENB", 1 } }, "MON" => new Dictionary { }, "EVENT" => new Dictionary { }, "ALARM" => new Dictionary { }, "LIC" => new Dictionary { { "LICENSE", 1 } }, "OTS" => new Dictionary { }, "ERROR" => new Dictionary { }, _ => null }; } /// /// 方向转换 /// public int DirConvert(LTELog log) { // 实现方向转换逻辑 return 0; } /// /// 字符串转ID - 委托给LogsManager /// public int StringToId(string str) { return _lteLogs.String2Id(str); } /// /// ID转字符串 - 委托给LogsManager /// public string IdToString(int id) { return _lteLogs.Id2String(id); } #endregion #region 日志解析方法 /// /// 解析日志列表 - 对应JavaScript的_logListParse方法 /// /// 日志列表 /// 是否为WebSocket消息 public void ParseLogList(List logs, bool isWebSocket = false) { var length = logs.Count; for (int i = 0; i < length; i++) { var log = logs[i]; log.Message = log.Message + ""; AddLog(log); // 解析消息 switch (log.Layer) { case "PHY": if (!ParsePhyLog(log, log.Message, isWebSocket)) continue; ProcessPhyLog(log); break; case "RRC": ParseCellId(log, isWebSocket); var rrcUeIdMatch = RegExpRRC_UE_ID.Match(log.Message); if (rrcUeIdMatch.Success) { SetSameUe(log, int.Parse(rrcUeIdMatch.Groups[1].Value, System.Globalization.NumberStyles.HexNumber)); continue; } var infoMatch = RegExpInfo1.Match(log.Message); if (infoMatch.Success) { if (!SetLogInfo(log, infoMatch.Groups[1].Value)) continue; log.Message = infoMatch.Groups[2].Value; ProcessRrcLog(log); } var bcMatch = RegExpRRC_BC.Match(log.Message); if (bcMatch.Success) { try { var data = log.GetDataString(); var jsonData = JsonConvert.DeserializeObject(data); // 处理频段组合信息 } catch { // 忽略JSON解析错误 } } break; case "NAS": ParseCellId(log, isWebSocket); var nasTmsiMatch = RegExpNAS_TMSI.Match(log.Message); if (nasTmsiMatch.Success) { SetTmsi(log, nasTmsiMatch.Groups[1].Value); continue; } var nas5gTmsiMatch = RegExpNAS_5GTMSI.Match(log.Message); if (nas5gTmsiMatch.Success) { SetTmsi(log, nas5gTmsiMatch.Groups[1].Value); continue; } var nasInfoMatch = RegExpInfo2.Match(log.Message); if (nasInfoMatch.Success) { if (!SetLogInfo(log, nasInfoMatch.Groups[1].Value)) continue; log.Message = nasInfoMatch.Groups[2].Value; ProcessNasLog(log); } break; case "MAC": ParseCellId(log, isWebSocket); ParseMacLog(log); break; case "IP": var ipMatch = RegExpIP.Match(log.Message); if (ipMatch.Success) { var lenPart = ipMatch.Groups[1].Value; log.IpLen = int.Parse(lenPart.Split('=')[1]); if (!SetLogInfo(log, ipMatch.Groups[2].Value)) continue; log.Message = ipMatch.Groups[3].Value; HasData = true; } break; case "GTPU": var sduLenMatch = RegExpSDULen.Match(log.Message); if (sduLenMatch.Success) { log.SduLen = int.Parse(sduLenMatch.Groups[1].Value); HasData = true; } break; case "S1AP": case "NGAP": var s1ngMatch = RegExpS1NGAP.Match(log.Message); if (s1ngMatch.Success) { log.Message = s1ngMatch.Groups[3].Value; var coreId = int.TryParse(s1ngMatch.Groups[1].Value, System.Globalization.NumberStyles.HexNumber, null, out var core) ? core : (int?)null; var ranId = int.TryParse(s1ngMatch.Groups[2].Value, System.Globalization.NumberStyles.HexNumber, null, out var ran) ? ran : (int?)null; log.LinkIds = new LinkIds { Core = coreId, Ran = ranId }; } break; case "SIP": var sipMatch = RegExpSIP.Match(log.Message); if (sipMatch.Success) { if (!SetLogInfo(log, sipMatch.Groups[2].Value)) continue; log.Message = sipMatch.Groups[3].Value; } break; case "MEDIA": var mediaMatch = RegExpMediaReq.Match(log.Message); if (mediaMatch.Success) { if (!SetLogInfo(log, mediaMatch.Groups[1].Value)) continue; log.Message = mediaMatch.Groups[2].Value; } break; case "IPsec": var ipsecMatch = RegExpIPsec.Match(log.Message); if (ipsecMatch.Success) { log.IpLen = int.Parse(ipsecMatch.Groups[1].Value); log.Message = ipsecMatch.Groups[2].Value; } break; default: break; } // 处理提示信息 ProcessHints(log); } // 限制日志数量 if (LogCount > LOGS_MAX) { var excess = LogCount - LOGS_MAX; Logs.RemoveRange(0, excess); } } #endregion #region PHY层解析方法 /// /// 解析PHY日志 /// private bool ParsePhyLog(LTELog log, string message, bool isWebSocket) { if (!isWebSocket) { var phyMatch = RegExpPhy.Match(message); if (!phyMatch.Success) { Console.WriteLine($"Bad PHY log: {log}"); return false; } log.Cell = int.Parse(phyMatch.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); log.Rnti = int.Parse(phyMatch.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); log.Channel = phyMatch.Groups[4].Value; log.Message = phyMatch.Groups[5].Value; } HasPhy = true; HasCell = true; HasRnti = true; // 解析PHY参数 var lines = log.GetData(); foreach (var line in lines) { var parts = line.Split('='); if (parts.Length == 2) { ParsePhyParameter(log, parts[0].Trim(), parts[1].Trim()); } } return true; } /// /// 解析PHY参数 /// private void ParsePhyParameter(LTELog log, string param, string value) { switch (param.ToLower()) { case "frame": if (int.TryParse(value, out var frame)) { log.Frame = frame; HasPhy = true; } break; case "subframe": if (int.TryParse(value, out var subframe)) { log.Subframe = subframe; } break; case "slot": if (int.TryParse(value, out var slot)) { log.Slot = slot; } break; case "symbol": if (int.TryParse(value, out var symbol)) { log.Symbol = symbol; } break; case "ant": if (int.TryParse(value, out var ant)) { log.AntennaPort = ant; } break; case "rb_start": if (int.TryParse(value, out var rbStart)) { log.RbStart = rbStart; HasRb = true; } break; case "rb_count": if (int.TryParse(value, out var rbCount)) { log.RbCount = rbCount; HasRb = true; } break; case "mcs": if (int.TryParse(value, out var mcs)) { log.Mcs = mcs; } break; case "tbs": if (int.TryParse(value, out var tbs)) { log.Tbs = tbs; } break; case "harq_id": if (int.TryParse(value, out var harqId)) { log.HarqId = harqId; } break; case "harq_ndi": if (bool.TryParse(value, out var harqNdi)) { log.HarqNdi = harqNdi; } break; case "harq_rv": if (int.TryParse(value, out var harqRv)) { log.HarqRedundancyVersion = harqRv; } break; } } /// /// 处理PHY日志 /// private void ProcessPhyLog(LTELog log) { // 处理HARQ信息 if (log.HarqId.HasValue) { ProcessHarqLog(log, log.HarqId.Value); } // 处理帧信息 if (log.Frame.HasValue) { var frame = log.Frame.Value; var hfn = frame >> 10; var sfn = frame & 0x3FF; if (Frame.Timestamp == -1) { Frame.Timestamp = log.Timestamp; Frame.Hfn = hfn; Frame.Last = sfn; } else { var diff = sfn - Frame.Last; if (diff < -HFN_WRAP_THRESHOLD) { Frame.Hfn++; } else if (diff > HFN_WRAP_THRESHOLD) { Frame.Hfn--; } Frame.Last = sfn; } } } /// /// 处理HARQ日志 /// private void ProcessHarqLog(LTELog log, int harq) { if (log.Cell.HasValue && log.UeId.HasValue) { var cell = log.Cell.Value; var ueId = log.UeId.Value; var key = ueId * 32 + harq; if (!LastHarq.ContainsKey(cell)) { LastHarq[cell] = new Dictionary(); } LastHarq[cell][key] = log; } } #endregion #region 其他层解析方法 /// /// 解析小区ID /// private void ParseCellId(LTELog log, bool isWebSocket) { if (!isWebSocket) { var cellMatch = RegExpCellID.Match(log.Message); if (cellMatch.Success) { log.Cell = int.Parse(cellMatch.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); log.Message = cellMatch.Groups[2].Value; HasCell = true; } } } /// /// 设置日志信息 /// private bool SetLogInfo(LTELog log, string info) { if (string.IsNullOrEmpty(info) || info == "?") return false; log.Info = StringToId(info); return true; } /// /// 设置相同UE /// private void SetSameUe(LTELog log, int ueId) { log.UeId = ueId; if (!UeList.ContainsKey(ueId)) { UeList[ueId] = new UEInfo { UeId = ueId }; } } /// /// 处理RRC日志 /// private void ProcessRrcLog(LTELog log) { // 处理RRC TMSI var tmsiMatch = RegExpRRC_TMSI.Match(log.Message); if (tmsiMatch.Success) { SetTmsi(log, tmsiMatch.Groups[2].Value); } // 处理RRC CRNTI var crntiMatch = RegExpRRC_CRNTI.Match(log.Message); if (crntiMatch.Success) { SetRnti(log, crntiMatch.Groups[1].Value); } } /// /// 处理NAS日志 /// private void ProcessNasLog(LTELog log) { // NAS日志处理逻辑 } /// /// 解析MAC日志 /// private void ParseMacLog(LTELog log) { // MAC日志解析逻辑 } /// /// 处理提示信息 /// private void ProcessHints(LTELog log, Dictionary? config = null) { // 处理提示信息逻辑 } /// /// 设置TMSI /// private void SetTmsi(LTELog log, string tmsi) { var tmsiId = int.Parse(tmsi, System.Globalization.NumberStyles.HexNumber); if (log.UeId.HasValue) { TmsiToUeId[tmsiId] = log.UeId.Value; } } /// /// 设置RNTI /// private void SetRnti(LTELog log, string rnti) { var rntiId = int.Parse(rnti, System.Globalization.NumberStyles.HexNumber); if (log.UeId.HasValue) { RntiToUeId[rntiId] = log.UeId.Value; } } #endregion #region 私有辅助方法 /// /// 设置状态 /// private void SetState(ClientState state) { if (State != state) { State = state; switch (state) { case ClientState.Stop: ResetLogs(); break; case ClientState.Start: case ClientState.Connected: break; case ClientState.Destroy: return; } } } /// /// 重置解析器状态 /// private void ResetParserState() { LastHarq.Clear(); LastNbHarq.Clear(); Frame = new FrameInfo(); TmsiToUeId.Clear(); RntiToUeId.Clear(); UeList.Clear(); LastTimestamp = 0; TimestampOffset = 0; LastCell = null; // 注意:LogsManager 的字符串ID缓存是全局的,不需要重置 // 这样可以保持字符串ID的一致性 } /// /// 生成客户端ID /// private static int GenerateClientId() { return Interlocked.Increment(ref _clientIdCounter); } /// /// 生成日志ID /// private static int GenerateLogId() { return Interlocked.Increment(ref _logIdCounter); } #endregion #region 静态字段 private static int _clientIdCounter = 0; private static int _logIdCounter = 0; #endregion #region 模型管理方法 /// /// 模型提示信息 /// private string? _modelHint; /// /// 传输方向 /// public int TxDir { get; set; } /// /// 接收方向 /// public int RxDir { get; set; } /// /// RAN ID列表 /// public List RanIds { get; set; } = new(); /// /// 层方向配置 /// private Dictionary> _layerDir = new(); /// /// 尝试模型提示 /// /// 模型列表 /// 是否成功设置模型 public bool TryModelHint(List models) { if (string.IsNullOrEmpty(_modelHint)) return false; foreach (var model in models) { if (_clientModelConfigs.TryGetValue(model, out var config)) { var modelHint = config.ModelHint; if (string.IsNullOrEmpty(modelHint)) { // 没有提示,基于模型名称 modelHint = model; } if (Regex.IsMatch(_modelHint, modelHint, RegexOptions.IgnoreCase)) { return SetModel(model); } } } return false; } /// /// 设置模型 /// /// 模型名称 /// 是否强制设置 /// 是否成功设置 public bool SetModel(string model, bool force = false) { // 如果模型相同且不强制,直接返回成功 if (model == Config.Model && !force) return true; Config.Model = model; // 根据模型设置传输方向 switch (model?.ToUpper()) { case "ENB": TxDir = 2; // DIR_DL RxDir = 1; // DIR_UL RanIds.Clear(); break; case "UE": TxDir = 1; // DIR_UL RxDir = 2; // DIR_DL break; default: break; } // 设置层方向配置 if (_clientModelConfigs.TryGetValue(model ?? "", out var config)) { _layerDir = config.LayerDir ?? new Dictionary>(); } else { _layerDir.Clear(); } // 通知日志管理器更新网格列 _lteLogs.GridColumnsUpdate(); return true; } /// /// 设置模型提示 /// /// 提示信息 public void SetModelHint(string hint) { _modelHint = hint; } #endregion #region 模型配置 /// /// 客户端模型配置类 /// public class ClientModelConfig { public string? Icon { get; set; } public string? ModelHint { get; set; } public string? Title { get; set; } public Dictionary>? LayerDir { get; set; } } /// /// 客户端模型配置字典 /// private static readonly Dictionary _clientModelConfigs = new() { { "RUE", new ClientModelConfig { Icon = "icon-ue2" } }, { "UE", new ClientModelConfig { Icon = "icon-ue" } }, { "PROBE", new ClientModelConfig() }, { "ENB", new ClientModelConfig { Icon = "icon-air", ModelHint = "enb|gnb|bbu", Title = "RAN" } }, { "N3IWF", new ClientModelConfig { Icon = "icon-air", ModelHint = "n3iwf", Title = "N3IWF" } }, { "MME", new ClientModelConfig { Icon = "icon-server", ModelHint = "epc|mme|amf", Title = "CN" } }, { "MBMSGW", new ClientModelConfig { Icon = "icon-download", ModelHint = "mbms" } }, { "IMS", new ClientModelConfig { Icon = "icon-dial" } }, { "MONITOR", new ClientModelConfig { Icon = "icon-monitor" } }, { "LICENSE", new ClientModelConfig { Icon = "icon-file" } } }; #endregion } /// /// 客户端日志配置 /// public class ClientLogsConfig { /// /// 日志层配置 /// public Dictionary Layers { get; set; } = new(); /// /// 是否启用信号日志 /// public bool? Signal { get; set; } /// /// 是否启用控制信道日志 /// public bool? Cch { get; set; } // 允许其他未明确定义的属性 [JsonExtensionData] public Dictionary? ExtensionData { get; set; } } /// /// 客户端配置 /// public class ClientConfig { /// /// 客户端名称 /// public string Name { get; set; } = string.Empty; /// /// 服务器地址 /// public string Address { get; set; } = string.Empty; /// /// 是否启用 /// public bool Enabled { get; set; } /// /// 密码 /// public string Password { get; set; } = string.Empty; /// /// 重连延迟(毫秒) /// public int ReconnectDelay { get; set; } = 5000; /// /// 是否启用SSL /// public bool Ssl { get; set; } /// /// 日志配置 /// public ClientLogsConfig Logs { get; set; } = new(); /// /// 是否暂停 /// public bool Pause { get; set; } /// /// 是否只读 /// public bool Readonly { get; set; } /// /// 是否跳过日志菜单 /// public bool SkipLogMenu { get; set; } /// /// 是否锁定 /// public bool Locked { get; set; } /// /// 是否激活 /// public bool Active { get; set; } /// /// 模型 /// public string? Model { get; set; } } /// /// 客户端状态 /// public enum ClientState { Stop, Start, Loading, Connecting, Connected, Error, Destroy } /// /// 帧信息 /// public class FrameInfo { public int Last { get; set; } public int Hfn { get; set; } public long Timestamp { get; set; } = -1; } /// /// UE信息 /// public class UEInfo { public int UeId { get; set; } public string? Imsi { get; set; } public string? Imei { get; set; } public object? Caps { get; set; } }