diff --git a/CoreAgent.ProtocolClient/Context/CellParameterManager.cs b/CoreAgent.ProtocolClient/Context/CellParameterManager.cs new file mode 100644 index 0000000..cc9cff5 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/CellParameterManager.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Models; +using Newtonsoft.Json; + +namespace CoreAgent.ProtocolClient.Context +{ + /// + /// 参数管理 + /// + public class CellParameterManager + { + public Dictionary Parameters { get; set; } = new(); + public List RanIds { get; set; } = new(); + + public void SetRanId(string ranId) + { + RanIds.Add(ranId); + } + + public void AddCell(int id, CellConfig config) + { + if (Parameters.ContainsKey(id)) + { + Parameters[id] = config; + } + else + { + Parameters.Add(id, config); + } + } + + public void SetHeaders(string[] headers, ProtocolBasicInfo basicInfo) + { + basicInfo.Headers = headers; + var cells = new List>(); + for (int i = 0; i < headers.Length; i++) + { + var header = headers[i]; + Match? info; + info = Regex.Match(header, @"lte(\w+) version ([\d-]+)", RegexOptions.IgnoreCase); + if (info.Success) + { + basicInfo.Model = info.Groups[1].Value.ToUpper(); + basicInfo.Version = info.Groups[2].Value; + } + else if (Regex.IsMatch(header, @"Licensed to")) + { + basicInfo.License = header; + } + else if ((info = Regex.Match(header, @"Metadata:(.+)$")).Success) + { + var metadata = JsonConvert.DeserializeObject>(info.Groups[1].Value); + } + else if ((info = Regex.Match(header, @"(global_(ran_node|enb)_id)=([\d\.]+)")).Success) + { + this.SetRanId(info.Groups[3].Value); + } + else if ((info = Regex.Match(header, @"Cell 0x(\d+): (.*)")).Success) + { + var cell = new Dictionary + { + ["cell_id"] = int.Parse(info.Groups[1].Value, System.Globalization.NumberStyles.HexNumber), + ["sib1Decoded"] = true, + ["sib2Decoded"] = true + }; + var list = info.Groups[2].Value.Split(' '); + foreach (var param in list) + { + var parts = param.Split('='); + if (parts.Length == 2) + { + cell[parts[0]] = HeaderCellParam(parts[0], parts[1]); + } + } + cells.Add(cell); + } + else if (cells.Count > 0) + { + info = Regex.Match(header, @"([UD]L): (.*)"); + if (info.Success) + { + var cell = cells[cells.Count - 1]; + var dir = info.Groups[1].Value; + var list = info.Groups[2].Value.Split(' '); + foreach (var param in list) + { + var parts = param.Split('='); + if (parts.Length == 2) + { + cell[parts[0]] = HeaderCellParam(parts[0], parts[1]); + } + } + } + } + } + } + + private object HeaderCellParam(string param, string value) + { + switch (param) + { + case "br_dl_sf_bitmap": + case "nb_dl_sf_bitmap": + case "label": + return value; + default: + if (int.TryParse(value, out var intValue)) + return intValue; + return value; + } + } + } +} diff --git a/CoreAgent.ProtocolClient/Context/ProtocolBasicInfo.cs b/CoreAgent.ProtocolClient/Context/ProtocolBasicInfo.cs new file mode 100644 index 0000000..a42ed07 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/ProtocolBasicInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Context +{ + /// + /// 基础信息 + /// + public class ProtocolBasicInfo + { + public string? Name { get; set; } + public string? Version { get; set; } + public string? License { get; set; } + public string? Model { get; set; } + public string[]? Headers { get; set; } + } +} diff --git a/CoreAgent.ProtocolClient/Context/ProtocolClientContext.cs b/CoreAgent.ProtocolClient/Context/ProtocolClientContext.cs new file mode 100644 index 0000000..a8974d0 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/ProtocolClientContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Context.UeStateManager; +using CoreAgent.ProtocolClient.Enums; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context +{ + public class ProtocolClientContext + { + // 常量和正则表达式已迁移至 ProtocolLogPatterns 静态类,便于统一管理和调用。 + + private readonly ILoggerFactory _loggerFactory; + + public ProtocolBasicInfo BasicInfo { get; set; } = new(); + public ProtocolFeatureFlags FeatureFlags { get; set; } = new(); + public ProtocolLogContext LogContext { get; set; } + public UeIdentifierManager UeIdentifier { get; set; } + public CellParameterManager CellParameterManager { get; set; } = new(); + public ClientState State { get; set; } = ClientState.Stop; + + public ProtocolClientContext(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + + // 使用ILoggerFactory为各个组件创建正确的Logger + var ueIdentifierLogger = _loggerFactory.CreateLogger(); + var logContextLogger = _loggerFactory.CreateLogger(); + + // 先创建UeIdentifierManager + UeIdentifier = new UeIdentifierManager(ueIdentifierLogger); + + // 然后创建ProtocolLogContext,传入this引用 + LogContext = new ProtocolLogContext(logContextLogger, this); + } + } +} diff --git a/CoreAgent.ProtocolClient/Context/ProtocolFeatureFlags.cs b/CoreAgent.ProtocolClient/Context/ProtocolFeatureFlags.cs new file mode 100644 index 0000000..975ab72 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/ProtocolFeatureFlags.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Context +{ + /// + /// 功能标志 + /// + public class ProtocolFeatureFlags + { + public bool HasCell { get; set; } + public bool HasPhy { get; set; } + public bool HasData { get; set; } + public bool HasRnti { get; set; } + public bool HasRb { get; set; } + public bool HasSignalRecord { get; set; } + public bool IsFirstGetLogs { get; set; } = true; + } +} diff --git a/CoreAgent.ProtocolClient/Context/ProtocolLogContext.cs b/CoreAgent.ProtocolClient/Context/ProtocolLogContext.cs new file mode 100644 index 0000000..1019458 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/ProtocolLogContext.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Context.UeStateManager; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context +{ + /// + /// 日志相关 + /// + public class ProtocolLogContext + { + private static int _logIdCounter; + private readonly ILogger _logger; + private readonly ProtocolClientContext _context; + + public List Logs { get; set; } = new(); + public int LogCount => Logs.Count; + + public ProtocolLogContext(ILogger logger, ProtocolClientContext context) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _context = context ?? throw new ArgumentNullException(nameof(context)); + } + public void ResetLogs() + { + if (LogCount > 0) + { + Logs.Clear(); + _context.FeatureFlags.HasCell = false; + _context.FeatureFlags.HasPhy = false; + _context.FeatureFlags.HasData = false; + _context.FeatureFlags.HasRnti = false; + _context.FeatureFlags.HasRb = false; + _context.FeatureFlags.HasSignalRecord = false; + } + } + + + /// + /// 更新日志的时间戳并分配唯一ID。 + /// 检查并处理时间戳回绕,详细记录回绕前后各关键字段,包括: + /// 1. 原始日志时间戳(log.Timestamp) + /// 2. 当前偏移量(parser.TimestampOffset) + /// 3. 计算得到的当前时间戳(timestamp) + /// 4. 上次处理的时间戳(parser.LastTimestamp) + /// 5. 回绕后新的时间戳(timestamp + 86400000) + /// 6. 本次回绕的增量(86400000) + /// 7. 日志分配的唯一ID(log.Id) + /// + public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log) + { + // 检查时间戳回绕 + var parser = _context.UeIdentifier; + var timestamp = log.Timestamp + parser.TimestampOffset; + if (timestamp < parser.LastTimestamp - 100) + { + _logger.LogInformation($"[Log回绕] 原始时间戳: {log.Timestamp}, 偏移量: {parser.TimestampOffset}, 回绕前: {timestamp}, parser.LastTimestamp(回绕前): {parser.LastTimestamp}, 回绕后: {timestamp + 86400000}, parser.LastTimestamp(回绕后): {parser.LastTimestamp}, 增量: 86400000, 日志ID(回绕前): {log.Id}"); + timestamp += 86400000; // 24小时 + parser.TimestampOffset += 86400000; + _logger.LogInformation($"[Log回绕后] 新时间戳: {timestamp}, parser.LastTimestamp(回绕后): {parser.LastTimestamp}, 新偏移量: {parser.TimestampOffset}, 日志ID(回绕后): {log.Id}"); + } + var lastTimestampBefore = parser.LastTimestamp; + parser.LastTimestamp = log.Timestamp = timestamp; + log.Id = GenerateLogId(); + _logger.LogDebug($"[Log分配ID] 日志最终时间戳: {log.Timestamp}, 分配ID: {log.Id}, 当前偏移量: {parser.TimestampOffset}, parser.LastTimestamp(分配前): {lastTimestampBefore}, parser.LastTimestamp(分配后): {parser.LastTimestamp}"); + } + + + private static int GenerateLogId() + { + return Interlocked.Increment(ref _logIdCounter); + } + + } +} diff --git a/CoreAgent.ProtocolClient/Context/ProtocolLogPatterns.cs b/CoreAgent.ProtocolClient/Context/ProtocolLogPatterns.cs new file mode 100644 index 0000000..4ffaf4c --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/ProtocolLogPatterns.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Context +{ + /// + /// 协议日志相关常量与正则表达式,便于全局统一管理和调用。 + /// + public static class ProtocolLogPatterns + { + #region 常量定义 + + /// + /// HFN回绕阈值 + /// + public const int HFN_WRAP_THRESHOLD = 512; + + /// + /// 最大日志数量 + /// + public const int LOGS_MAX = 2000000; + + /// + /// BSR表 - 对应JavaScript中的_bsr_table + /// + public static readonly int[] BsrTable = { + 0, 10, 12, 14, 17, 19, 22, 26, 31, 36, 42, 49, 57, 67, 78, 91, 107, 125, 146, 171, 200, 234, 274, 321, 376, 440, 515, 603, 706, 826, 967, 1132, 1326, 1552, 1817, 2127, 2490, 2915, 3413, 3995, 4677, 5476, 6411, 7505, 8787, 10287, 12043, 14099, 16507, 19325, 22624, 26487, 31009, 36304, 42502, 49759, 58255, 68201, 79846, 93479, 109439, 128125, 150000, 500000 + }; + + #endregion + + #region 正则表达式 + + public static readonly Regex RegExpPhy = new(@"^([a-f0-9\-]+)\s+([a-f0-9\-]+)\s+([\d\.\-]+) (\w+): (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpInfo1 = new(@"^([\w\-]+): (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpInfo2 = new(@"^([\w]+) (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpIP = new(@"^(len=\d+)\s+(\S+)\s+(.*)", RegexOptions.Compiled); + public static readonly Regex RegExpIPsec = new(@"^len=(\d+)\s+(.*)", RegexOptions.Compiled); + public static readonly Regex RegExpSDULen = new(@"SDU_len=(\d+)", RegexOptions.Compiled); + public static readonly Regex RegExpSIP = new(@"^([:\.\[\]\da-f]+)\s+(\S+) (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpMediaReq = new(@"^(\S+) (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpSignalRecord = new(@"Link:\s([\w\d]+)@(\d+)", RegexOptions.Compiled); + public static readonly Regex RegExpCellID = new(@"^([a-f0-9\-]+) (.+)", RegexOptions.Compiled); + public static readonly Regex RegExpRRC_UE_ID = new(@"Changing UE_ID to 0x(\d+)", RegexOptions.Compiled); + public static readonly Regex RegExpRRC_TMSI = new(@"(5G|m)-TMSI '([\dA-F]+)'H", RegexOptions.Compiled); + public static readonly Regex RegExpRRC_NEW_ID = new(@"newUE-Identity (['\dA-FH]+)", RegexOptions.Compiled); + public static readonly Regex RegExpRRC_CRNTI = new(@"c-RNTI '([\dA-F]+)'H", RegexOptions.Compiled); + public static readonly Regex RegExpNAS_TMSI = new(@"[Mm]-TMSI = 0x([\da-f]+)", RegexOptions.Compiled); + public static readonly Regex RegExpNAS_5GTMSI = new(@"5G-TMSI = 0x([\da-f]+)", RegexOptions.Compiled); + public static readonly Regex RegExpRRC_BC = new(@"(EUTRA|MRDC|NR|NRDC) band combinations", RegexOptions.Compiled); + public static readonly Regex RegExpPDCCH = new(@"^\s*(.+)=(\d+)$", RegexOptions.Compiled); + public static readonly Regex RegExpS1NGAP = new(@"^([\da-f\-]+)\s+([\da-f\-]+) (([^\s]+) .+)", RegexOptions.Compiled); + public static readonly Regex RegExpHexDump = new(@"^[\da-f]+:(\s+[\da-f]{2}){1,16}\s+.{1,16}$", RegexOptions.Compiled); + public static readonly Regex RegMccPattern = new Regex(@"MCC\s*=\s*(\d+)", RegexOptions.Compiled); + public static readonly Regex RegMncPattern = new Regex(@"MNC\s*=\s*(\d+)", RegexOptions.Compiled); + public static readonly Regex RegMSINPattern = new Regex(@"MSIN\s*=\s*(\d+)", RegexOptions.Compiled); + public static readonly Regex RegIMSIPattern = new Regex(@"IMSI = \s*(\d+)", RegexOptions.Compiled); + + public static Match? FindData(this Regex regex, List data) + { + return data?.FirstOrDefault(line => regex.IsMatch(line)) is string matchedLine + ? regex.Match(matchedLine) + : null; + } + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/ProtocolParsing.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/ProtocolParsing.cs new file mode 100644 index 0000000..e1a3b2b --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/ProtocolParsing.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Enums; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager 协议解析部分 + /// + public partial class UeIdentifierManager + { + #region 协议解析方法 + + /// + /// 解析提示信息 + /// + /// 协议日志 + /// 解析配置 + public void ParseHints(BuildProtocolLog log, Dictionary config) + { + if (log?.Info != (int)LogChannelId.ID_HINTS) + { + _logger?.LogDebug("跳过非提示信息日志: Info={Info}", log?.Info); + return; + } + + try + { + var hints = ParseArguments(log.Message, config); + _logger?.LogDebug("解析提示信息成功,共解析 {Count} 个参数", hints.Count); + // TODO: 处理解析后的提示信息 + } + catch (Exception ex) + { + _logger?.LogError(ex, "解析提示信息失败: {Message}", log.Message); + throw new ParsingException("Hints", log?.Message ?? "", $"解析提示信息失败: {ex.Message}", ex); + } + } + + /// + /// 解析参数字符串为字典 + /// 性能优化:使用编译后的正则表达式 + /// + /// 参数字符串,格式如 "a=1 b=2 c=hello" + /// + /// 配置字典,key为参数名,value为类型(Type)或处理函数(Func<string, object>) + /// 例如:{ "a", typeof(int) }, { "b", (Func<string, object>)(s => s.ToUpper()) } + /// + /// 解析后的参数字典 + private Dictionary ParseArguments(string arguments, Dictionary config) + { + if (string.IsNullOrEmpty(arguments)) + { + return new Dictionary(); + } + + var result = new Dictionary(); + // 使用编译后的正则表达式提高性能 + var whitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled); + + try + { + foreach (var argument in whitespaceRegex.Split(arguments)) + { + if (string.IsNullOrWhiteSpace(argument)) + continue; + + var keyValuePair = argument.Split('=', 2); + if (keyValuePair.Length != 2) + { + _logger?.LogWarning("跳过格式无效的参数: {Argument}", argument); + continue; + } + + var key = keyValuePair[0]; + var value = keyValuePair[1]; + + result[key] = ParseValue(value, key, config); + } + + return result; + } + catch (Exception ex) + { + _logger?.LogError(ex, "解析参数字符串失败: {Arguments}", arguments); + throw new ParsingException("Arguments", arguments, $"解析参数字符串失败: {ex.Message}", ex); + } + } + + /// + /// 根据配置解析参数值 + /// + /// 原始值字符串 + /// 参数键 + /// 解析配置 + /// 解析后的值 + private object ParseValue(string value, string key, Dictionary config) + { + if (!config.TryGetValue(key, out var typeOrFunc)) + return value; + + try + { + return typeOrFunc switch + { + Type type when type == typeof(int) => int.TryParse(value, out var intValue) ? intValue : 0, + Type type when type == typeof(double) => double.TryParse(value, out var doubleValue) ? doubleValue : 0.0, + Func func => func(value), + _ => value + }; + } + catch (Exception ex) + { + _logger?.LogError(ex, "解析参数值失败: Key={Key}, Value={Value}", key, value); + return value; // 返回原始值作为回退 + } + } + + /// + /// 批量解析参数 + /// 性能优化:批量处理减少重复计算 + /// + /// 参数字符串列表 + /// 解析配置 + /// 解析结果列表 + public List> ParseArgumentsBatch( + IEnumerable argumentsList, + Dictionary config) + { + if (argumentsList == null) + { + throw new ArgumentNullException(nameof(argumentsList)); + } + + var results = new List>(); + foreach (var arguments in argumentsList) + { + results.Add(ParseArguments(arguments, config)); + } + + _logger?.LogDebug("批量解析参数完成,共处理 {Count} 个参数字符串", results.Count); + return results; + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/StringIdManagement.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/StringIdManagement.cs new file mode 100644 index 0000000..de4d9e4 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/StringIdManagement.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager 字符串ID管理部分 + /// + public partial class UeIdentifierManager + { + #region 字符串ID管理方法 + + /// + /// 将字符串转换为ID,如果不存在则创建新的ID + /// 性能优化:使用缓存减少重复计算 + /// + /// 要转换的字符串 + /// 对应的ID + /// 字符串为空时抛出 + public int StringToId(string str) + { + if (string.IsNullOrEmpty(str)) + { + _logger?.LogWarning("尝试转换空字符串为ID"); + return 0; + } + + lock (_lockObject) + { + IncrementCacheStat("StringToId"); + + if (_stringIdCache.TryGetValue(str, out int existingId)) + { + IncrementCacheStat("CacheHits"); + return existingId; + } + + IncrementCacheStat("CacheMisses"); + int newId = _stringList.Count + 1; + _stringIdCache[str] = newId; + _stringList.Add(str); + + _logger?.LogDebug("创建新的字符串ID映射: '{String}' -> {Id}", str, newId); + return newId; + } + } + + /// + /// 根据ID获取对应的字符串 + /// 性能优化:使用缓存减少重复计算 + /// + /// 字符串ID + /// 对应的字符串,如果ID无效则返回空字符串 + public string IdToString(int id) + { + if (id <= 0) + { + _logger?.LogWarning("尝试获取无效ID的字符串: {Id}", id); + return string.Empty; + } + + lock (_lockObject) + { + IncrementCacheStat("IdToString"); + + if (id <= _stringList.Count) + { + IncrementCacheStat("CacheHits"); + return _stringList[id - 1]; + } + + IncrementCacheStat("CacheMisses"); + _logger?.LogWarning("ID超出范围: {Id}, 最大ID: {MaxId}", id, _stringList.Count); + return string.Empty; + } + } + + /// + /// 批量转换字符串为ID + /// 性能优化:批量处理减少锁开销 + /// + /// 字符串列表 + /// 对应的ID列表 + public List StringToIdBatch(IEnumerable strings) + { + if (strings == null) + { + throw new ArgumentNullException(nameof(strings)); + } + + var results = new List(); + lock (_lockObject) + { + foreach (var str in strings) + { + results.Add(StringToId(str)); + } + } + + _logger?.LogDebug("批量转换字符串为ID,共处理 {Count} 个字符串", results.Count); + return results; + } + + /// + /// 获取字符串缓存信息 + /// + /// 缓存信息 + public (int CacheSize, int ListSize) GetStringCacheInfo() + { + lock (_lockObject) + { + return (_stringIdCache.Count, _stringList.Count); + } + } + + #endregion + + #region 私有辅助方法 + + /// + /// 增加缓存统计计数 + /// + /// 统计名称 + private void IncrementCacheStat(string statName) + { + if (_cacheStats.ContainsKey(statName)) + { + _cacheStats[statName]++; + } + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs new file mode 100644 index 0000000..53fc378 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager TMSI匹配部分 + /// + public partial class UeIdentifierManager + { + #region TMSI匹配方法 + + /// + /// 根据TMSI匹配生成请求UE ID和接收UE ID的对应关系 + /// 使用TmsiMatchProcessor处理匹配逻辑 + /// + /// TMSI匹配结果列表 + public List GenerateTmsiMatches() + { + try + { + var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId); + var results = processor.GenerateTmsiMatches(); + _logger?.LogDebug("生成TMSI匹配结果,共 {Count} 个匹配", results.Count); + return results; + } + catch (Exception ex) + { + _logger?.LogError(ex, "生成TMSI匹配失败"); + throw new GeneralUeIdentifierManagerException("GenerateTmsiMatches", "生成TMSI匹配失败", ex); + } + } + + /// + /// 根据UE ID获取对应的IMSI + /// 优先查找当前UE ID,如果未找到则查找整个UE链中的IMSI + /// + /// UE ID + /// 对应的IMSI,如果未找到则返回空字符串 + private string GetImsiForUeId(int ueId) + { + if (ueId <= 0) + { + _logger?.LogWarning("尝试获取无效UE ID的IMSI: {UeId}", ueId); + return string.Empty; + } + + try + { + // 1. 首先查找当前UE ID对应的IMSI + var currentImsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(ueId)); + if (!string.IsNullOrEmpty(currentImsiMapping.Key)) + { + return currentImsiMapping.Key; + } + + // 2. 如果当前UE ID没有IMSI,查找整个UE链中的IMSI + var anyImsiMapping = ImsiToUeId.FirstOrDefault(kvp => !string.IsNullOrEmpty(kvp.Key)); + return anyImsiMapping.Key ?? string.Empty; + } + catch (Exception ex) + { + _logger?.LogError(ex, "获取UE {UeId} 的IMSI失败", ueId); + return string.Empty; + } + } + + /// + /// 获取UE链的统计信息 + /// + /// UE链统计信息 + public UeChainStats GetUeChainStats() + { + try + { + var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId); + var stats = processor.GetUeChainStats(); + _logger?.LogDebug("获取UE链统计信息成功"); + return stats; + } + catch (Exception ex) + { + _logger?.LogError(ex, "获取UE链统计信息失败"); + throw new GeneralUeIdentifierManagerException("GetUeChainStats", "获取UE链统计信息失败", ex); + } + } + + /// + /// 获取所有UE链的详细信息 + /// + /// UE链详细信息列表 + public List GetUeChainDetails() + { + try + { + var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId); + var details = processor.GetUeChainDetails(); + _logger?.LogDebug("获取UE链详细信息成功,共 {Count} 个链", details.Count); + return details; + } + catch (Exception ex) + { + _logger?.LogError(ex, "获取UE链详细信息失败"); + throw new GeneralUeIdentifierManagerException("GetUeChainDetails", "获取UE链详细信息失败", ex); + } + } + + /// + /// 将 SrTmsiToUeId 平铺成 SrTmsiMapping 列表,并与 GenerateTmsiMatches 结果匹配更新 IMSI、Root UE ID 和 PLMN + /// 性能优化:使用LINQ提高查找效率 + /// + /// 平铺后的 SrTmsiMapping 列表,包含匹配的 IMSI、Root UE ID 和 PLMN 信息 + public List GenerateSrTmsiMappings(List tmsiMatches) + { + if (tmsiMatches == null) + { + throw new ArgumentNullException(nameof(tmsiMatches)); + } + + try + { + var srTmsiMappings = new List(); + + // 1. 平铺 SrTmsiToUeId 字典数据 + foreach (var kvp in SrTmsiToUeId) + { + uint tmsi = kvp.Key; + var ueIds = kvp.Value; + + foreach (int ueId in ueIds) + { + var mapping = new SrTmsiMapping(string.Empty, tmsi, ueId); + srTmsiMappings.Add(mapping); + } + } + + // 2. 获取 UE 链信息以获取最外层的 Root UE ID + var ueChainDetails = GetUeChainDetails(); + + // 3. 根据 TMSI 匹配结果更新 SrTmsiMapping 的 IMSI 和 Root UE ID + foreach (var srMapping in srTmsiMappings) + { + UpdateSrTmsiMapping(srMapping, tmsiMatches, ueChainDetails); + } + + // 4. 根据 RootUeId 从 PlmnToUeId 映射表更新 PLMN 信息 + foreach (var srMapping in srTmsiMappings) + { + UpdateSrTmsiMappingPlmn(srMapping, ueChainDetails); + } + + _logger?.LogDebug("生成Service Request TMSI映射成功,共 {Count} 个映射", srTmsiMappings.Count); + return srTmsiMappings; + } + catch (Exception ex) + { + _logger?.LogError(ex, "生成Service Request TMSI映射失败"); + throw new GeneralUeIdentifierManagerException("GenerateSrTmsiMappings", "生成Service Request TMSI映射失败", ex); + } + } + + #endregion + + #region 私有辅助方法 + + /// + /// 更新 SrTmsiMapping 的 IMSI 和 Root UE ID + /// + /// Service Request TMSI映射 + /// TMSI匹配结果 + /// UE链详细信息 + private void UpdateSrTmsiMapping(SrTmsiMapping srMapping, List tmsiMatches, List ueChainDetails) + { + // 查找匹配的 TMSI 结果 + var matchingResult = tmsiMatches.FirstOrDefault(match => match.Tmsi == srMapping.Tmsi); + + if (matchingResult != null && !string.IsNullOrEmpty(matchingResult.Imsi)) + { + // 更新 IMSI + srMapping.Imsi = matchingResult.Imsi; + + // 查找对应的最外层 Root UE ID + var chainInfo = ueChainDetails.FirstOrDefault(chain => + chain.UeIds.Contains(srMapping.UeId)); + + if (chainInfo != null) + { + srMapping.RootUeId = chainInfo.RootUeId; + } + else + { + // 如果当前 UE ID 不在任何链中,尝试从匹配结果中查找 + chainInfo = ueChainDetails.FirstOrDefault(chain => + chain.UeIds.Contains(matchingResult.RequestUeId) || + chain.UeIds.Contains(matchingResult.ReceiveUeId)); + + if (chainInfo != null) + { + srMapping.RootUeId = chainInfo.RootUeId; + } + } + } + else + { + // 如果 TMSI 匹配中没有找到,尝试从 ImsiToUeId 映射中查找 + string imsi = GetImsiForUeId(srMapping.UeId); + if (!string.IsNullOrEmpty(imsi)) + { + srMapping.Imsi = imsi; + + // 查找对应的最外层 Root UE ID + var chainInfo = ueChainDetails.FirstOrDefault(chain => + chain.UeIds.Contains(srMapping.UeId)); + + if (chainInfo != null) + { + srMapping.RootUeId = chainInfo.RootUeId; + } + } + } + } + + /// + /// 更新 SrTmsiMapping 的 PLMN 信息 + /// + /// Service Request TMSI映射 + private void UpdateSrTmsiMappingPlmn(SrTmsiMapping srMapping, List ueChainDetails) + { + if (srMapping.RootUeId > 0) + { + // 查找包含 RootUeId 的 PLMN 映射 + var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(srMapping.RootUeId)); + if (!string.IsNullOrEmpty(plmnMapping.Key)) + { + srMapping.Plmn = plmnMapping.Key; + } + } + else if (srMapping.UeId > 0) + { + var Filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi); + // 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN + var chainInfo = ueChainDetails.FirstOrDefault(chain => + chain.UeIds.Contains(Filter.Value)); + + var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(Filter.Value)); + if (!string.IsNullOrEmpty(plmnMapping.Key)) + { + srMapping.Plmn = plmnMapping.Key; + srMapping.RootUeId = chainInfo.RootUeId; + } + } + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierException.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierException.cs new file mode 100644 index 0000000..0eb4e9b --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierException.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager 相关异常基类 + /// + public abstract class UeIdentifierManagerException : Exception + { + protected UeIdentifierManagerException(string message) : base(message) { } + protected UeIdentifierManagerException(string message, Exception innerException) : base(message, innerException) { } + } + + /// + /// 标识符格式异常 + /// + public class IdentifierFormatException : UeIdentifierManagerException + { + public string Identifier { get; } + public string ExpectedFormat { get; } + + public IdentifierFormatException(string identifier, string expectedFormat, string message = null) + : base(message ?? $"标识符 '{identifier}' 格式无效,期望格式: {expectedFormat}") + { + Identifier = identifier; + ExpectedFormat = expectedFormat; + } + } + + /// + /// UE信息异常 + /// + public class UeInfoException : UeIdentifierManagerException + { + public int UeId { get; } + + public UeInfoException(int ueId, string message) + : base(message) + { + UeId = ueId; + } + } + + /// + /// 映射关系异常 + /// + public class MappingException : UeIdentifierManagerException + { + public string MappingType { get; } + public object Key { get; } + + public MappingException(string mappingType, object key, string message) + : base(message) + { + MappingType = mappingType; + Key = key; + } + } + + /// + /// 缓存异常 + /// + public class CacheException : UeIdentifierManagerException + { + public string CacheType { get; } + + public CacheException(string cacheType, string message) + : base(message) + { + CacheType = cacheType; + } + + public CacheException(string cacheType, string message, Exception innerException) + : base(message, innerException) + { + CacheType = cacheType; + } + } + + /// + /// 解析异常 + /// + public class ParsingException : UeIdentifierManagerException + { + public string ParsingType { get; } + public string Input { get; } + + public ParsingException(string parsingType, string input, string message) + : base(message) + { + ParsingType = parsingType; + Input = input; + } + + public ParsingException(string parsingType, string input, string message, Exception innerException) + : base(message, innerException) + { + ParsingType = parsingType; + Input = input; + } + } + + /// + /// 通用UE标识符管理器异常 + /// + public class GeneralUeIdentifierManagerException : UeIdentifierManagerException + { + public string Operation { get; } + + public GeneralUeIdentifierManagerException(string operation, string message) + : base(message) + { + Operation = operation; + } + + public GeneralUeIdentifierManagerException(string operation, string message, Exception innerException) + : base(message, innerException) + { + Operation = operation; + } + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierManager.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierManager.cs new file mode 100644 index 0000000..bcb0db7 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierManager.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Enums; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UE标识符管理类 + /// 负责管理UE信息、标识符映射、时间戳等状态 + /// 优化版本:增强异常处理、性能优化、代码拆分 + /// + public partial class UeIdentifierManager + { + #region 公共属性 - 映射表 + + /// + /// TMSI到UE ID的映射表 + /// + public Dictionary TmsiToUeId { get; set; } = new(); + + /// + /// 请求TMSI到UE ID的映射表 + /// + public Dictionary RequestTmsiToUeId { get; set; } = new(); + + /// + /// IMSI到UE ID的映射表 + /// + public Dictionary> ImsiToUeId { get; set; } = new(); + + /// + /// PLMN到UE ID的映射表 + /// + public Dictionary> PlmnToUeId { get; set; } = new(); + + /// + /// Service Request TMSI到UE ID的映射表 + /// + public Dictionary> SrTmsiToUeId { get; set; } = new(); + + /// + /// RNTI到UE ID的映射表 + /// + public Dictionary RntiToUeId { get; set; } = new(); + + /// + /// UE信息列表,以UE ID为键 + /// + public Dictionary UeList { get; set; } = new(); + + #endregion + + #region 公共属性 - 时间戳和小区信息 + + /// + /// 最后处理的时间戳 + /// + public long LastTimestamp { get; set; } + + /// + /// 时间戳偏移量 + /// + public long TimestampOffset { get; set; } + + /// + /// 最后处理的小区ID + /// + public int? LastCell { get; set; } + + #endregion + + #region 私有字段 - 字符串缓存和性能优化 + + /// + /// 字符串到ID的映射缓存 + /// + private readonly Dictionary _stringIdCache = new(); + + /// + /// ID到字符串的列表缓存 + /// + private readonly List _stringList = new(); + + /// + /// 日志记录器 + /// + private readonly ILogger _logger; + + /// + /// 缓存统计信息 + /// + private readonly Dictionary _cacheStats = new(); + + /// + /// 线程锁对象 + /// + private readonly object _lockObject = new object(); + + #endregion + + #region 构造函数 + + /// + /// 初始化UE标识符管理器 + /// + /// 日志记录器 + public UeIdentifierManager(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + InitializeProtocolTypeStrings(); + InitializeCacheStats(); + } + + #endregion + + #region 私有初始化方法 + + /// + /// 初始化协议类型字符串缓存 + /// + private void InitializeProtocolTypeStrings() + { + try + { + string[] protocolTypeNames = Enum.GetNames(typeof(LogChannelId)); + foreach (var protocolTypeName in protocolTypeNames) + { + StringToId(protocolTypeName); + } + _logger.LogDebug("协议类型字符串缓存初始化完成,共 {Count} 个类型", protocolTypeNames.Length); + } + catch (Exception ex) + { + _logger.LogError(ex, "初始化协议类型字符串缓存失败"); + throw new CacheException("ProtocolTypeStrings", "初始化协议类型字符串缓存失败", ex); + } + } + + /// + /// 初始化缓存统计信息 + /// + private void InitializeCacheStats() + { + _cacheStats["StringToId"] = 0; + _cacheStats["IdToString"] = 0; + _cacheStats["CacheHits"] = 0; + _cacheStats["CacheMisses"] = 0; + } + + #endregion + + #region 公共方法 - 统计和监控 + + /// + /// 获取缓存统计信息 + /// + /// 缓存统计信息字典 + public Dictionary GetCacheStats() + { + lock (_lockObject) + { + return new Dictionary(_cacheStats); + } + } + + /// + /// 获取映射表统计信息 + /// + /// 映射表统计信息 + public Dictionary GetMappingStats() + { + return new Dictionary + { + ["TmsiToUeId"] = TmsiToUeId.Count, + ["RequestTmsiToUeId"] = RequestTmsiToUeId.Count, + ["ImsiToUeId"] = ImsiToUeId.Count, + ["PlmnToUeId"] = PlmnToUeId.Count, + ["SrTmsiToUeId"] = SrTmsiToUeId.Count, + ["RntiToUeId"] = RntiToUeId.Count, + ["UeList"] = UeList.Count + }; + } + + /// + /// 清理缓存 + /// + public void ClearCache() + { + lock (_lockObject) + { + _stringIdCache.Clear(); + _stringList.Clear(); + InitializeCacheStats(); + _logger.LogInformation("字符串缓存已清理"); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierMapping.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierMapping.cs new file mode 100644 index 0000000..009d6e9 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierMapping.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Enums; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager UE标识符映射部分 + /// + public partial class UeIdentifierManager + { + #region UE标识符映射方法 + + /// + /// 设置IMSI与UE ID的映射关系 + /// 性能优化:使用HashSet提高查找效率 + /// + /// UE ID + /// IMSI标识符 + /// UE ID无效时抛出 + public void SetImsi(int ueId, string imsi) + { + if (string.IsNullOrEmpty(imsi)) + { + _logger?.LogWarning("尝试设置空的IMSI"); + return; + } + + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + try + { + lock (_lockObject) + { + if (!ImsiToUeId.TryGetValue(imsi, out var existingUeIds)) + { + ImsiToUeId[imsi] = new List { ueId }; + _logger?.LogDebug("创建新的IMSI映射: {Imsi} -> UE {UeId}", imsi, ueId); + } + else if (!existingUeIds.Contains(ueId)) + { + existingUeIds.Add(ueId); + _logger?.LogDebug("添加IMSI映射: {Imsi} -> UE {UeId}", imsi, ueId); + } + } + } + catch (Exception ex) + { + _logger?.LogError(ex, "设置IMSI映射失败: UE {UeId}, IMSI {Imsi}", ueId, imsi); + throw new MappingException("IMSI", imsi, $"设置IMSI映射失败: {ex.Message}"); + } + } + + /// + /// 设置TMSI与UE ID的映射关系 + /// 性能优化:使用缓存减少重复转换 + /// + /// UE ID + /// TMSI标识符(十六进制字符串) + /// TMSI格式无效时抛出 + public void SetTmsi(int ueId, string tmsi) + { + if (string.IsNullOrEmpty(tmsi)) + { + _logger?.LogWarning("尝试设置空的TMSI"); + return; + } + + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + try + { + uint tmsiKey = Convert.ToUInt32(tmsi, 16); + + lock (_lockObject) + { + if (TmsiToUeId.TryGetValue(tmsiKey, out int existingUeId) && existingUeId > 0) + { + // 同步IMSI信息 + SyncImsiFromExistingUe(existingUeId, ueId); + // 合并UE信息 + MergeUeInfo(existingUeId, ueId); + _logger?.LogDebug("更新TMSI映射: {Tmsi} -> UE {UeId} (原UE {ExistingUeId})", tmsi, ueId, existingUeId); + } + else + { + TmsiToUeId[tmsiKey] = ueId; + _logger?.LogDebug("创建新的TMSI映射: {Tmsi} -> UE {UeId}", tmsi, ueId); + } + } + } + catch (FormatException ex) + { + var errorMessage = $"TMSI格式无效: '{tmsi}',期望十六进制格式"; + _logger?.LogError(ex, errorMessage); + throw new IdentifierFormatException(tmsi, "十六进制格式", errorMessage); + } + catch (Exception ex) + { + _logger?.LogError(ex, "设置TMSI映射失败: UE {UeId}, TMSI {Tmsi}", ueId, tmsi); + throw new MappingException("TMSI", tmsi, $"设置TMSI映射失败: {ex.Message}"); + } + } + + /// + /// 设置请求TMSI与UE ID的映射关系 + /// + /// UE ID + /// 请求TMSI标识符(十六进制字符串) + public void SetRequestTmsi(int ueId, string tmsi) + { + if (string.IsNullOrEmpty(tmsi)) + { + _logger?.LogWarning("尝试设置空的请求TMSI"); + return; + } + + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + try + { + uint tmsiKey = Convert.ToUInt32(tmsi, 16); + + lock (_lockObject) + { + RequestTmsiToUeId[tmsiKey] = ueId; + _logger?.LogDebug("设置请求TMSI映射: {Tmsi} -> UE {UeId}", tmsi, ueId); + } + } + catch (FormatException ex) + { + var errorMessage = $"请求TMSI格式无效: '{tmsi}',期望十六进制格式"; + _logger?.LogError(ex, errorMessage); + throw new IdentifierFormatException(tmsi, "十六进制格式", errorMessage); + } + } + + /// + /// 设置RNTI与UE ID的映射关系 + /// + /// 协议日志 + /// RNTI标识符(十六进制字符串) + public void SetRnti(BuildProtocolLog log, string rnti) + { + if (log?.UeId == null || string.IsNullOrEmpty(rnti)) + { + _logger?.LogWarning("设置RNTI映射失败:日志或RNTI为空"); + return; + } + + try + { + int rntiId = int.Parse(rnti, System.Globalization.NumberStyles.HexNumber); + + lock (_lockObject) + { + RntiToUeId[rntiId] = log.UeId.Value; + _logger?.LogDebug("设置RNTI映射: {Rnti} -> UE {UeId}", rnti, log.UeId.Value); + } + } + catch (FormatException ex) + { + var errorMessage = $"RNTI格式无效: '{rnti}',期望十六进制格式"; + _logger?.LogError(ex, errorMessage); + throw new IdentifierFormatException(rnti, "十六进制格式", errorMessage); + } + } + + /// + /// 设置PLMN与UE ID的映射关系 + /// + /// UE ID + /// PLMN标识符 + public void SetPlmn(int ueId, string plmn) + { + if (string.IsNullOrEmpty(plmn)) + { + _logger?.LogWarning("尝试设置空的PLMN"); + return; + } + + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + try + { + lock (_lockObject) + { + if (!PlmnToUeId.TryGetValue(plmn, out var existingUeIds)) + { + PlmnToUeId[plmn] = new List { ueId }; + _logger?.LogDebug("创建新的PLMN映射: {Plmn} -> UE {UeId}", plmn, ueId); + } + else if (!existingUeIds.Contains(ueId)) + { + existingUeIds.Add(ueId); + _logger?.LogDebug("添加PLMN映射: {Plmn} -> UE {UeId}", plmn, ueId); + } + } + } + catch (Exception ex) + { + _logger?.LogError(ex, "设置PLMN映射失败: UE {UeId}, PLMN {Plmn}", ueId, plmn); + throw new MappingException("PLMN", plmn, $"设置PLMN映射失败: {ex.Message}"); + } + } + + /// + /// 设置Service Request TMSI与UE ID的映射关系 + /// + /// UE ID + /// Service Request TMSI标识符(十六进制字符串) + public void SetSrTmsi(int ueId, string srTmsi) + { + if (string.IsNullOrEmpty(srTmsi)) + { + _logger?.LogWarning("尝试设置空的Service Request TMSI"); + return; + } + + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + try + { + uint srTmsiKey = Convert.ToUInt32(srTmsi, 16); + + lock (_lockObject) + { + if (!SrTmsiToUeId.TryGetValue(srTmsiKey, out var existingUeIds)) + { + SrTmsiToUeId[srTmsiKey] = new List { ueId }; + _logger?.LogDebug("创建新的Service Request TMSI映射: {SrTmsi} -> UE {UeId}", srTmsi, ueId); + } + else if (!existingUeIds.Contains(ueId)) + { + existingUeIds.Add(ueId); + _logger?.LogDebug("添加Service Request TMSI映射: {SrTmsi} -> UE {UeId}", srTmsi, ueId); + } + } + } + catch (FormatException ex) + { + var errorMessage = $"Service Request TMSI格式无效: '{srTmsi}',期望十六进制格式"; + _logger?.LogError(ex, errorMessage); + throw new IdentifierFormatException(srTmsi, "十六进制格式", errorMessage); + } + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Context/UeStateManager/UeInfoManagement.cs b/CoreAgent.ProtocolClient/Context/UeStateManager/UeInfoManagement.cs new file mode 100644 index 0000000..e9defb1 --- /dev/null +++ b/CoreAgent.ProtocolClient/Context/UeStateManager/UeInfoManagement.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.ProtocolClient.Context.UeStateManager +{ + /// + /// UeIdentifierManager UE信息管理部分 + /// + public partial class UeIdentifierManager + { + #region UE信息管理方法 + + /// + /// 合并两个UE的信息 + /// 性能优化:减少不必要的对象创建 + /// + /// 第一个UE ID + /// 第二个UE ID + public void MergeUeInfo(int ueId1, int ueId2) + { + if (ueId1 <= 0 || ueId2 <= 0) + { + throw new UeInfoException(Math.Min(ueId1, ueId2), "UE ID必须大于0"); + } + + try + { + lock (_lockObject) + { + UeList.TryGetValue(ueId1, out var ue1); + UeList.TryGetValue(ueId2, out var ue2); + + if (ReferenceEquals(ue1, ue2)) + { + if (ue2 == null) + { + ue2 = CreateUeInfo(ueId2); + UeList[ueId1] = ue2; + } + } + else if (ue1 == null) + { + UeList[ueId1] = ue2; + } + else if (ue2 == null) + { + UeList[ueId2] = ue1; + SyncPlmnFromExistingUe(ueId1, ueId2); + } + else + { + UeList[ueId2] = ue1; + SyncPlmnFromExistingUe(ueId1, ueId2); + } + + _logger?.LogDebug("合并UE信息: UE {UeId1} 和 UE {UeId2}", ueId1, ueId2); + } + } + catch (Exception ex) + { + _logger?.LogError(ex, "合并UE信息失败: UE {UeId1} 和 UE {UeId2}", ueId1, ueId2); + throw new UeInfoException(Math.Min(ueId1, ueId2), $"合并UE信息失败: {ex.Message}"); + } + } + + /// + /// 创建新的UE信息 + /// + /// UE ID + /// 创建的UE信息 + private UEInfo CreateUeInfo(int ueId) + { + var ueInfo = new UEInfo + { + UeId = ueId, + Caps = new ProtocolCaps { UeId = ueId } + }; + + UeList[ueId] = ueInfo; + _logger?.LogDebug("创建新的UE信息: UE {UeId}", ueId); + return ueInfo; + } + + /// + /// 获取UE信息 + /// + /// UE ID + /// UE信息,如果不存在则返回null + public UEInfo? GetUeInfo(int ueId) + { + if (ueId <= 0) + { + _logger?.LogWarning("尝试获取无效UE ID的信息: {UeId}", ueId); + return null; + } + + lock (_lockObject) + { + return UeList.TryGetValue(ueId, out var ueInfo) ? ueInfo : null; + } + } + + /// + /// 获取UE能力信息,如果不存在则创建 + /// 同时根据ImsiToUeId映射表更新IMSI信息 + /// + /// UE ID + /// UE能力信息 + public ProtocolCaps GetUeCaps(int ueId) + { + if (ueId <= 0) + { + throw new UeInfoException(ueId, "UE ID必须大于0"); + } + + lock (_lockObject) + { + if (!UeList.TryGetValue(ueId, out var ueInfo) || ueInfo == null) + { + ueInfo = CreateUeInfo(ueId); + } + + if (ueInfo.Caps == null) + { + ueInfo.Caps = new ProtocolCaps { UeId = ueId }; + } + + // 根据ImsiToUeId映射表更新IMSI信息 + UpdateImsiFromMapping(ueId, ueInfo); + + return ueInfo.Caps; + } + } + + /// + /// 根据ImsiToUeId映射表更新UE的IMSI信息 + /// 性能优化:使用LINQ提高查找效率 + /// + /// UE ID + /// UE信息对象 + private void UpdateImsiFromMapping(int ueId, UEInfo ueInfo) + { + var imsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(ueId)); + if (!string.IsNullOrEmpty(imsiMapping.Key)) + { + ueInfo.Imsi = imsiMapping.Key; + _logger?.LogDebug("更新UE {UeId} 的IMSI信息: {Imsi}", ueId, imsiMapping.Key); + } + } + + /// + /// 从现有UE ID同步IMSI信息到新的UE ID + /// + /// 现有UE ID + /// 新的UE ID + private void SyncImsiFromExistingUe(int existingUeId, int newUeId) + { + var imsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(existingUeId)); + if (!string.IsNullOrEmpty(imsiMapping.Key)) + { + SetImsi(newUeId, imsiMapping.Key); + _logger?.LogDebug("同步IMSI信息: 从UE {ExistingUeId} 到 UE {NewUeId}: {Imsi}", + existingUeId, newUeId, imsiMapping.Key); + } + } + + /// + /// 从现有UE ID同步PLMN信息到新的UE ID + /// + /// 现有UE ID + /// 新的UE ID + private void SyncPlmnFromExistingUe(int existingUeId, int newUeId) + { + var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(existingUeId)); + if (!string.IsNullOrEmpty(plmnMapping.Key)) + { + SetPlmn(newUeId, plmnMapping.Key); + _logger?.LogDebug("同步PLMN信息: 从UE {ExistingUeId} 到 UE {NewUeId}: {Plmn}", + existingUeId, newUeId, plmnMapping.Key); + } + } + + /// + /// 获取UeList中UEInfo.UeId与Caps.UeId组合的字典 + /// + /// 包含UE ID和Caps UeId组合的字典,键为UEInfo.UeId,值为Caps.UeId + public Dictionary GetUeIdToCapsUeIdMapping() + { + lock (_lockObject) + { + var mapping = new Dictionary(); + + foreach (var kvp in UeList) + { + var ueId = kvp.Key; + var ueInfo = kvp.Value; + + if (ueInfo?.Caps != null) + { + mapping[ueId] = ueInfo.Caps.UeId; + } + } + + return mapping; + } + } + + /// + /// 批量获取UE信息 + /// 性能优化:批量处理减少锁开销 + /// + /// UE ID列表 + /// UE信息字典 + public Dictionary GetUeInfoBatch(IEnumerable ueIds) + { + if (ueIds == null) + { + throw new ArgumentNullException(nameof(ueIds)); + } + + var result = new Dictionary(); + lock (_lockObject) + { + foreach (var ueId in ueIds) + { + if (UeList.TryGetValue(ueId, out var ueInfo)) + { + result[ueId] = ueInfo; + } + } + } + + _logger?.LogDebug("批量获取UE信息,共获取 {Count} 个UE的信息", result.Count); + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj b/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj index 398bd41..d949a47 100644 --- a/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj +++ b/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj @@ -12,8 +12,4 @@ - - - - diff --git a/CoreAgent.ProtocolClient/Docs/MessageIdManager.md b/CoreAgent.ProtocolClient/Docs/MessageIdManager.md new file mode 100644 index 0000000..20ba274 --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/MessageIdManager.md @@ -0,0 +1,509 @@ +# MessageIdManager.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/MessageIdManager.cs +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.HandlerEventArgs; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +namespace CoreAgent.ProtocolClient.Managers +{ + /// + /// 消息ID管理器 - 改进版 + /// + /// 修复的问题: + /// 1. 使用long类型防止ID溢出 + /// 2. 完善消息处理器清理机制 + /// 3. 改进LogGet ID管理逻辑 + /// 4. 增强参数验证和异常处理 + /// 5. 优化性能,减少字符串操作 + /// 6. 添加线程安全保护 + /// 7. 改进日志记录格式 + /// + /// 设计原则: + /// - 单一职责:专门负责消息ID管理 + /// - 开闭原则:支持扩展不同类型的消息ID管理 + /// - 线程安全:所有操作都是线程安全的 + /// - 性能优化:减少不必要的内存分配 + /// - 错误处理:完善的异常处理和参数验证 + /// + public class MessageIdManager : IDisposable + { + #region 私有字段 + + private readonly ILogger _logger; + private readonly string _clientName; + private readonly ConcurrentDictionary _messageHandlers; + private readonly ConcurrentDictionary _messageHandlersByName; + + // 使用long类型防止溢出 + private long _generalMessageId; + private long _logGetMessageId; + + // 状态管理 + private bool _disposed; + + // 性能优化:缓存字符串构建器 + private readonly StringBuilder _logBuilder = new StringBuilder(256); + + #endregion + + #region 事件 + + /// + /// 日志获取ID变化事件 + /// + public event EventHandler? LogGetIdChanged; + + /// + /// 消息处理器清理事件 + /// + public event EventHandler? HandlerCleanup; + + #endregion + + #region 属性 + + /// + /// 当前通用消息ID + /// + public long CurrentGeneralMessageId => Interlocked.Read(ref _generalMessageId); + + /// + /// 当前日志获取消息ID + /// + public long CurrentLogGetMessageId => Interlocked.Read(ref _logGetMessageId); + + /// + /// 是否已释放 + /// + public bool IsDisposed => _disposed; + + /// + /// 消息处理器数量 + /// + public int MessageHandlerCount => _messageHandlers.Count; + + /// + /// 按名称的消息处理器数量 + /// + public int NamedMessageHandlerCount => _messageHandlersByName.Count; + + /// + /// 总处理器数量 + /// + public int TotalHandlerCount => MessageHandlerCount + NamedMessageHandlerCount; + + #endregion + + #region 构造函数 + + /// + /// 构造函数 + /// + /// 客户端名称 + /// 日志记录器 + public MessageIdManager(string clientName, ILogger logger) + { + _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _messageHandlers = new ConcurrentDictionary(); + _messageHandlersByName = new ConcurrentDictionary(); + + _generalMessageId = 0; + _logGetMessageId = -1; // 初始化为-1,表示未开始 + + _logger.LogInformation("[{ClientName}] 创建消息ID管理器,初始LogGet ID: {LogGetId}", _clientName, _logGetMessageId); + } + + #endregion + + #region 公共方法 + + /// + /// 生成通用消息ID + /// + /// 消息对象 + /// 回调函数 + /// 是否为错误处理器 + /// 消息ID + public long GenerateGeneralMessageId(JObject message, Action? callback = null, bool errorHandler = false) + { + ThrowIfDisposed(); + ValidateMessage(message); + + var id = GetNextMessageId(); + message["message_id"] = id; + + // 记录log_get消息的发送 + var messageType = message["message"]?.ToString(); + if (messageType == "log_get") + { + LogLogGetMessage(id, message); + } + + // 注册回调处理器 + if (callback != null) + { + _messageHandlers[id] = new MessageHandler + { + Callback = callback, + ErrorHandler = errorHandler, + CreatedAt = DateTime.UtcNow + }; + } + + _logger.LogDebug("[{ClientName}] 生成通用消息ID: {MessageId}", _clientName, id); + return id; + } + + /// + /// 生成日志获取消息ID - 改进版 + /// + /// 消息对象 + /// 回调函数 + /// 消息ID + public long GenerateLogGetMessageId(JObject message, Action callback) + { + ThrowIfDisposed(); + ValidateMessage(message); + + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + // 生成新的消息ID + var newLogGetId = GetNextMessageId(); + message["message_id"] = newLogGetId; + + // 注册回调处理器 + _messageHandlers[newLogGetId] = new MessageHandler + { + Callback = callback, + ErrorHandler = false, + CreatedAt = DateTime.UtcNow, + IsLogGetHandler = true + }; + + // 设置新的LogGet ID + var oldLogGetId = Interlocked.Exchange(ref _logGetMessageId, newLogGetId); + + // 触发事件 + LogGetIdChanged?.Invoke(this, new LogGetIdChangedEventArgs(oldLogGetId, newLogGetId)); + + _logger.LogDebug("[{ClientName}] LogGet ID变化: {OldId} -> {NewId}", _clientName, oldLogGetId, newLogGetId); + return newLogGetId; + } + + /// + /// 处理消息响应 - 改进版 + /// + /// 响应消息 + /// 错误处理回调 + /// 是否找到并处理了消息处理器 + public bool HandleMessageResponse(JObject response, Action? errorHandler = null) + { + ThrowIfDisposed(); + + if (response == null) + return false; + + // 检查消息处理器 + var id = response["message_id"]?.Value(); + if (id.HasValue && _messageHandlers.TryGetValue(id.Value, out var handler)) + { + return HandleMessageHandler(id.Value, handler, response, errorHandler); + } + + // 检查按名称的消息处理器 + var name = response["message"]?.ToString(); + if (!string.IsNullOrEmpty(name) && _messageHandlersByName.TryGetValue(name, out var nameHandler)) + { + return HandleNamedMessageHandler(name, nameHandler, response); + } + + return false; + } + + /// + /// 设置消息处理器 + /// + /// 消息名称数组 + /// 处理器 + public void SetMessageHandler(string[] names, MessageHandler handler) + { + ThrowIfDisposed(); + + if (names == null || names.Length == 0) + throw new ArgumentException("消息名称不能为空", nameof(names)); + + if (handler == null) + throw new ArgumentNullException(nameof(handler)); + + foreach (var name in names) + { + if (!string.IsNullOrEmpty(name)) + { + _messageHandlersByName[name] = handler; + } + } + + _logger.LogDebug("[{ClientName}] 设置消息处理器: {Names}", _clientName, string.Join(", ", names)); + } + + /// + /// 取消消息处理器 + /// + /// 消息名称数组 + public void UnsetMessageHandler(string[] names) + { + ThrowIfDisposed(); + + if (names == null || names.Length == 0) + return; + + foreach (var name in names) + { + if (!string.IsNullOrEmpty(name)) + { + _messageHandlersByName.TryRemove(name, out _); + } + } + + _logger.LogDebug("[{ClientName}] 取消消息处理器: {Names}", _clientName, string.Join(", ", names)); + } + + /// + /// 检查是否为当前日志获取消息 + /// + /// 消息ID + /// 是否为当前日志获取消息 + public bool IsCurrentLogGetMessage(long messageId) + { + var currentLogGetId = Interlocked.Read(ref _logGetMessageId); + return messageId == currentLogGetId; + } + + /// + /// 重置日志获取ID + /// + public void ResetLogGetId() + { + var oldLogGetId = Interlocked.Exchange(ref _logGetMessageId, -1); + LogGetIdChanged?.Invoke(this, new LogGetIdChangedEventArgs(oldLogGetId, -1)); + _logger.LogDebug("[{ClientName}] 重置LogGet ID: {OldId} -> -1", _clientName, oldLogGetId); + } + + /// + /// 清理过期的消息处理器 - 改进版 + /// + /// 最大年龄(毫秒) + /// 清理的处理器数量 + public int CleanupExpiredHandlers(int maxAge = 30000) // 默认30秒 + { + ThrowIfDisposed(); + + var now = DateTime.UtcNow; + var expiredKeys = new List(); + + foreach (var kvp in _messageHandlers) + { + if (kvp.Value.CreatedAt.AddMilliseconds(maxAge) < now) + { + expiredKeys.Add(kvp.Key); + } + } + + var cleanedCount = 0; + foreach (var key in expiredKeys) + { + if (_messageHandlers.TryRemove(key, out _)) + { + cleanedCount++; + } + } + + if (cleanedCount > 0) + { + _logger.LogDebug("[{ClientName}] 清理了 {CleanedCount} 个过期的消息处理器", _clientName, cleanedCount); + + // 触发清理事件 + HandlerCleanup?.Invoke(this, new HandlerCleanupEventArgs( + _messageHandlers.Count, + _messageHandlersByName.Count, + cleanedCount)); + } + + return cleanedCount; + } + + #endregion + + #region 私有方法 + + /// + /// 获取下一个消息ID + /// + /// 消息ID + private long GetNextMessageId() + { + var id = Interlocked.Increment(ref _generalMessageId); + + // 检查溢出 + if (id <= 0) + { + _logger.LogWarning("[{ClientName}] 消息ID溢出,重置为1", _clientName); + Interlocked.Exchange(ref _generalMessageId, 1); + return 1; + } + + return id; + } + + /// + /// 验证消息对象 + /// + /// 消息对象 + private void ValidateMessage(JObject message) + { + if (message == null) + throw new ArgumentNullException(nameof(message)); + } + + /// + /// 处理消息处理器 + /// + /// 消息ID + /// 处理器 + /// 响应消息 + /// 错误处理回调 + /// 是否处理成功 + private bool HandleMessageHandler(long id, MessageHandler handler, JObject response, Action? errorHandler) + { + // 如果不是通知消息,则移除处理器 + if (response["notification"]?.Value() != true) + { + _messageHandlers.TryRemove(id, out _); + } + + // 处理错误 + if (response["error"] != null) + { + if (!handler.ErrorHandler) + { + errorHandler?.Invoke(response["error"]?.ToString() ?? "未知错误"); + } + else + { + handler.Callback?.Invoke(response); + } + return true; + } + + // 正常处理 + handler.Callback?.Invoke(response); + return true; + } + + /// + /// 处理按名称的消息处理器 + /// + /// 消息名称 + /// 处理器 + /// 响应消息 + /// 是否处理成功 + private bool HandleNamedMessageHandler(string name, MessageHandler handler, JObject response) + { + handler.Callback?.Invoke(response); + return true; + } + + /// + /// 记录LogGet消息 + /// + /// 消息ID + /// 消息对象 + private void LogLogGetMessage(long id, JObject message) + { + _logBuilder.Clear(); + _logBuilder.AppendFormat("[{0}] 发送log_get消息: message_id={1}", _clientName, id); + + if (message["timeout"] != null) + _logBuilder.AppendFormat(", timeout={0}", message["timeout"]); + + if (message["headers"] != null) + _logBuilder.AppendFormat(", headers={0}", message["headers"]); + + _logBuilder.AppendFormat(", ThreadId={0}", Thread.CurrentThread.ManagedThreadId); + + _logger.LogDebug(_logBuilder.ToString()); + } + + /// + /// 检查是否已释放 + /// + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MessageIdManager)); + } + } + + #endregion + + #region IDisposable + + /// + /// 释放资源 + /// + public void Dispose() + { + if (_disposed) return; + + _disposed = true; + + // 清理所有消息处理器 + _messageHandlers.Clear(); + _messageHandlersByName.Clear(); + + _logger.LogInformation("[{ClientName}] 释放消息ID管理器", _clientName); + } + + #endregion + } +} +``` + +## 文档说明 + +### 类概述 +`MessageIdManager` 是一个专门负责消息ID管理的类,用于替代原始实现中的 `_messageId` 和 `_logGetId` 字段,提供更统一和强大的消息ID管理功能。 + +### 主要功能 +1. **消息ID生成**:统一管理通用消息ID和日志获取消息ID +2. **消息处理器管理**:管理消息回调处理器,支持按ID和按名称两种方式 +3. **线程安全**:所有操作都是线程安全的 +4. **自动清理**:支持自动清理过期的消息处理器 +5. **事件通知**:提供LogGet ID变化和处理器清理的事件通知 + +### 设计改进 +- 使用long类型防止ID溢出 +- 完善的异常处理和参数验证 +- 性能优化,减少字符串操作 +- 标准的Dispose模式实现 +- 详细的日志记录 + +### 使用场景 +- 在WebSocket消息管理器中统一管理消息ID +- 处理消息响应和回调 +- 管理日志获取流程 +- 提供消息处理器的生命周期管理 \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Enums/TmsiType.cs b/CoreAgent.ProtocolClient/Enums/TmsiType.cs new file mode 100644 index 0000000..4cc52eb --- /dev/null +++ b/CoreAgent.ProtocolClient/Enums/TmsiType.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Enums +{ + /// + /// TMSI类型 + /// + public enum TmsiType + { + /// + /// 接受类型 + /// + Accept, + + /// + /// 请求类型 + /// + Request, + + /// + /// 服务请求类型 + /// + ServiceRequest + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs b/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs index c782eb3..8fd30df 100644 --- a/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs +++ b/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs @@ -12,29 +12,13 @@ using Newtonsoft.Json.Linq; namespace CoreAgent.ProtocolClient.Managers { /// - /// 消息ID管理器 - 改进版 - /// - /// 修复的问题: - /// 1. 使用long类型防止ID溢出 - /// 2. 完善消息处理器清理机制 - /// 3. 改进LogGet ID管理逻辑 - /// 4. 增强参数验证和异常处理 - /// 5. 优化性能,减少字符串操作 - /// 6. 添加线程安全保护 - /// 7. 改进日志记录格式 - /// - /// 设计原则: - /// - 单一职责:专门负责消息ID管理 - /// - 开闭原则:支持扩展不同类型的消息ID管理 - /// - 线程安全:所有操作都是线程安全的 - /// - 性能优化:减少不必要的内存分配 - /// - 错误处理:完善的异常处理和参数验证 + /// 消息ID管理器 /// public class MessageIdManager : IDisposable { #region 私有字段 - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly string _clientName; private readonly ConcurrentDictionary _messageHandlers; private readonly ConcurrentDictionary _messageHandlersByName; @@ -104,9 +88,7 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 构造函数 /// - /// 客户端名称 - /// 日志记录器 - public MessageIdManager(string clientName, ILogger logger) + public MessageIdManager(string clientName, ILogger logger) { _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -127,10 +109,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 生成通用消息ID /// - /// 消息对象 - /// 回调函数 - /// 是否为错误处理器 - /// 消息ID public long GenerateGeneralMessageId(JObject message, Action? callback = null, bool errorHandler = false) { ThrowIfDisposed(); @@ -162,11 +140,8 @@ namespace CoreAgent.ProtocolClient.Managers } /// - /// 生成日志获取消息ID - 改进版 + /// 生成日志获取消息ID /// - /// 消息对象 - /// 回调函数 - /// 消息ID public long GenerateLogGetMessageId(JObject message, Action callback) { ThrowIfDisposed(); @@ -199,11 +174,8 @@ namespace CoreAgent.ProtocolClient.Managers } /// - /// 处理消息响应 - 改进版 + /// 处理消息响应 /// - /// 响应消息 - /// 错误处理回调 - /// 是否找到并处理了消息处理器 public bool HandleMessageResponse(JObject response, Action? errorHandler = null) { ThrowIfDisposed(); @@ -231,8 +203,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 设置消息处理器 /// - /// 消息名称数组 - /// 处理器 public void SetMessageHandler(string[] names, MessageHandler handler) { ThrowIfDisposed(); @@ -257,7 +227,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 取消消息处理器 /// - /// 消息名称数组 public void UnsetMessageHandler(string[] names) { ThrowIfDisposed(); @@ -279,8 +248,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 检查是否为当前日志获取消息 /// - /// 消息ID - /// 是否为当前日志获取消息 public bool IsCurrentLogGetMessage(long messageId) { var currentLogGetId = Interlocked.Read(ref _logGetMessageId); @@ -298,10 +265,8 @@ namespace CoreAgent.ProtocolClient.Managers } /// - /// 清理过期的消息处理器 - 改进版 + /// 清理过期的消息处理器 /// - /// 最大年龄(毫秒) - /// 清理的处理器数量 public int CleanupExpiredHandlers(int maxAge = 30000) // 默认30秒 { ThrowIfDisposed(); @@ -340,7 +305,6 @@ namespace CoreAgent.ProtocolClient.Managers return cleanedCount; } - #endregion #region 私有方法 @@ -348,7 +312,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 获取下一个消息ID /// - /// 消息ID private long GetNextMessageId() { var id = Interlocked.Increment(ref _generalMessageId); @@ -367,7 +330,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 验证消息对象 /// - /// 消息对象 private void ValidateMessage(JObject message) { if (message == null) @@ -377,11 +339,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 处理消息处理器 /// - /// 消息ID - /// 处理器 - /// 响应消息 - /// 错误处理回调 - /// 是否处理成功 private bool HandleMessageHandler(long id, MessageHandler handler, JObject response, Action? errorHandler) { // 如果不是通知消息,则移除处理器 @@ -412,10 +369,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 处理按名称的消息处理器 /// - /// 消息名称 - /// 处理器 - /// 响应消息 - /// 是否处理成功 private bool HandleNamedMessageHandler(string name, MessageHandler handler, JObject response) { handler.Callback?.Invoke(response); @@ -425,8 +378,6 @@ namespace CoreAgent.ProtocolClient.Managers /// /// 记录LogGet消息 /// - /// 消息ID - /// 消息对象 private void LogLogGetMessage(long id, JObject message) { _logBuilder.Clear(); diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs index 9285813..3522936 100644 --- a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs @@ -15,40 +15,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr { #region 构造函数 - /// - /// 构造函数 - 对应LTEClientWebSocket构造函数中的WebSocket相关初始化 - /// - /// 初始化说明: - /// 1. 创建MessageIdManager替代原始的_messageId和_logGetId字段 - /// 2. 创建消息队列_messageFifo,保持与原始实现一致 - /// 3. 不初始化WebSocket实例,在Connect方法中创建 - /// 4. 移除_sentMessages和_receivedMessages的初始化 - /// - /// 对应关系: - /// - 参数clientName:对应LTEClientWebSocket构造函数中的config.Name - /// - 参数logger:对应LTEClientWebSocket构造函数中的logger参数 - /// - _messageIdManager:替代原始的_messageId和_logGetId字段 - /// - _messageFifo:对应原始的_messageFifo初始化 - /// - _disposed:对应原始的_disposed初始化 - /// - 日志记录:对应原始的构造函数日志记录 - /// - /// 重构改进: - /// - 参数验证:增加了对clientName和logger的null检查 - /// - 职责分离:专注于WebSocket相关初始化 - /// - 功能增强:通过MessageIdManager提供更好的消息ID管理 - /// - 移除冗余:移除了消息缓存相关的初始化 - /// - /// 客户端名称,对应LTEClientWebSocket._config.Name - /// 日志记录器,对应LTEClientWebSocket._logger - public WebSocketMessageManager(string clientName, ILogger logger) + public WebSocketMessageManager(string clientName, ILoggerFactory loggerFactory) { + // 参数验证 _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + var factory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - // 创建MessageIdManager,替代原始的_messageId和_logGetId字段 - _messageIdManager = new MessageIdManager(clientName, logger); + // 创建自己的Logger + _logger = factory.CreateLogger(); - // 创建消息队列,使用BlockingCollection优化线程安全性和性能 + // 初始化消息ID管理器 - 使用ILoggerFactory创建正确的logger类型 + var messageIdLogger = factory.CreateLogger(); + _messageIdManager = new MessageIdManager(clientName, messageIdLogger); + + // 创建消息队列 _messageFifo = new BlockingCollection(); _logger.LogInformation($"[{_clientName}] 创建WebSocket消息管理器"); diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs index e99f0d3..cb92155 100644 --- a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs @@ -16,27 +16,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr #region IDisposable实现 /// - /// 释放资源 - 对应LTEClientWebSocket.Dispose()方法 - /// - /// 功能说明: - /// 1. 释放WebSocket连接和相关资源 - /// 2. 清理消息队列和定时器 - /// 3. 设置释放标志,防止重复释放 - /// 4. 调用MessageIdManager的Dispose方法 - /// - /// 对应关系: - /// - 资源释放:对应原始实现中的Dispose()方法 - /// - 连接关闭:对应原始实现中的WebSocket关闭逻辑 - /// - 定时器清理:对应原始实现中的定时器释放逻辑 - /// - 队列清理:对应原始实现中的队列清理逻辑 - /// - 释放标志:对应原始实现中的_disposed设置 - /// - 日志记录:对应原始实现中的释放日志记录 - /// - /// 重构改进: - /// - 更清晰的资源释放顺序 - /// - 更完善的异常处理 - /// - 更详细的日志记录 - /// - 保持了完全一致的释放逻辑 + /// 释放资源 /// public void Dispose() { @@ -45,25 +25,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 释放资源的受保护方法 - 实现标准的Dispose模式 - /// - /// 功能说明: - /// 1. 实现标准的Dispose模式,支持手动释放和垃圾回收 - /// 2. 确保资源只被释放一次 - /// 3. 按照正确的顺序释放资源 - /// - /// 对应关系: - /// - 释放模式:对应.NET标准的Dispose模式 - /// - 资源清理:对应原始实现中的资源清理逻辑 - /// - 异常处理:对应原始实现中的异常处理 - /// - /// 重构改进: - /// - 标准的Dispose模式实现 - /// - 更安全的资源管理 - /// - 更好的异常处理 - /// - 保持了完全一致的清理逻辑 + /// 释放资源的受保护方法 /// - /// 是否为手动释放 protected virtual void Dispose(bool disposing) { if (_disposed) @@ -75,23 +38,23 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr { _logger.LogInformation($"[{_clientName}] 释放WebSocket消息管理器资源"); - // 停止消息发送定时器 - 对应原始实现中的定时器释放 + // 停止消息发送定时器 StopMessageDeferTimer(); - // 清空消息队列 - 对应原始实现中的队列清理 + // 清空消息队列 ClearMessageQueue(); - // 释放BlockingCollection资源 - 优化:确保BlockingCollection正确释放 + // 释放BlockingCollection资源 _messageFifo?.Dispose(); - // 关闭WebSocket连接 - 对应原始实现中的WebSocket关闭 + // 关闭WebSocket连接 if (_webSocket != null) { _webSocket.Close(); _webSocket = null; } - // 释放MessageIdManager - 对应原始实现中的相关资源释放 + // 释放MessageIdManager _messageIdManager?.Dispose(); } catch (Exception ex) diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs index 370199a..e2ca335 100644 --- a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs @@ -16,22 +16,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr #region 私有方法 /// - /// 检查对象是否已释放,如果已释放则抛出异常 - /// - /// 功能说明: - /// 1. 检查_disposed字段,如果为true则抛出ObjectDisposedException - /// 2. 在所有公共方法开始时调用,确保对象状态正确 - /// 3. 提供统一的释放状态检查逻辑 - /// - /// 对应关系: - /// - 检查逻辑:对应原始实现中的_disposed检查 - /// - 异常类型:ObjectDisposedException,与.NET标准一致 - /// - 使用场景:在所有公共方法开始时调用 - /// - /// 重构改进: - /// - 统一的释放状态检查 - /// - 更清晰的异常信息 - /// - 更好的代码复用 + /// 检查对象是否已释放 /// private void ThrowIfDisposed() { @@ -40,25 +25,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// WebSocket连接打开事件处理器 - 对应LTEClientWebSocket.OnSocketOpened - /// - /// 功能说明: - /// 1. 处理WebSocket连接成功建立事件 - /// 2. 记录连接成功日志 - /// 3. 触发ConnectionOpened事件 - /// - /// 对应关系: - /// - 事件处理:对应原始实现中的OnSocketOpened方法 - /// - 日志记录:对应原始实现中的连接成功日志 - /// - 事件触发:对应原始实现中的ConnectionOpened事件触发 - /// - /// 重构改进: - /// - 更清晰的日志记录 - /// - 更好的异常处理 - /// - 保持了完全一致的事件处理逻辑 + /// WebSocket连接打开事件处理器 /// - /// 事件发送者 - /// 事件参数 private void OnSocketOpened(object? sender, EventArgs e) { try @@ -73,25 +41,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// WebSocket连接关闭事件处理器 - 对应LTEClientWebSocket.OnSocketClosed - /// - /// 功能说明: - /// 1. 处理WebSocket连接关闭事件 - /// 2. 记录连接关闭日志 - /// 3. 触发ConnectionClosed事件 - /// - /// 对应关系: - /// - 事件处理:对应原始实现中的OnSocketClosed方法 - /// - 日志记录:对应原始实现中的连接关闭日志 - /// - 事件触发:对应原始实现中的ConnectionClosed事件触发 - /// - /// 重构改进: - /// - 更清晰的日志记录 - /// - 更好的异常处理 - /// - 保持了完全一致的事件处理逻辑 + /// WebSocket连接关闭事件处理器 /// - /// 事件发送者 - /// 事件参数 private void OnSocketClosed(object? sender, EventArgs e) { try @@ -106,27 +57,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// WebSocket消息接收事件处理器 - 对应LTEClientWebSocket.OnSocketMessage - /// - /// 功能说明: - /// 1. 处理WebSocket消息接收事件 - /// 2. 解析接收到的消息 - /// 3. 触发MessageReceived事件 - /// 4. 调用HandleReceivedMessage处理消息 - /// - /// 对应关系: - /// - 事件处理:对应原始实现中的OnSocketMessage方法 - /// - 消息解析:对应原始实现中的消息解析逻辑 - /// - 事件触发:对应原始实现中的MessageReceived事件触发 - /// - 消息处理:对应原始实现中的消息处理逻辑 - /// - /// 重构改进: - /// - 更清晰的错误处理 - /// - 更详细的日志记录 - /// - 保持了完全一致的事件处理逻辑 + /// WebSocket消息接收事件处理器 /// - /// 事件发送者 - /// 消息接收事件参数 private void OnSocketMessageReceived(object? sender, MessageReceivedEventArgs e) { try @@ -134,7 +66,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr var messageText = e.Message; _logger.LogDebug($"[{_clientName}] 接收到消息: {messageText}"); - // 解析消息 - 对应原始实现中的消息解析逻辑 + // 解析消息 JObject? message = null; try { @@ -147,10 +79,10 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr return; } - // 触发MessageReceived事件 - 对应原始实现中的事件触发 + // 触发MessageReceived事件 MessageReceived?.Invoke(this, message); - // 处理消息 - 对应原始实现中的消息处理逻辑 + // 处理消息 HandleReceivedMessage(message, error => ConnectionError?.Invoke(this, error)); } catch (Exception ex) @@ -161,25 +93,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// WebSocket错误事件处理器 - 对应LTEClientWebSocket.OnSocketError - /// - /// 功能说明: - /// 1. 处理WebSocket连接错误事件 - /// 2. 记录错误日志 - /// 3. 触发ConnectionError事件 - /// - /// 对应关系: - /// - 事件处理:对应原始实现中的OnSocketError方法 - /// - 错误记录:对应原始实现中的错误日志记录 - /// - 事件触发:对应原始实现中的ConnectionError事件触发 - /// - /// 重构改进: - /// - 更详细的错误信息记录 - /// - 更好的异常处理 - /// - 保持了完全一致的事件处理逻辑 + /// WebSocket错误事件处理器 /// - /// 事件发送者 - /// 错误事件参数 private void OnSocketError(object? sender, SuperSocket.ClientEngine.ErrorEventArgs e) { try @@ -195,23 +110,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 启动消息延迟发送定时器 - 对应LTEClientWebSocket.StartMessageDeferTimer - /// - /// 功能说明: - /// 1. 启动消息延迟发送定时器,实现批量发送优化 - /// 2. 当队列中消息少于100条时,延迟1毫秒发送 - /// 3. 当队列中消息达到100条时,立即发送 - /// - /// 对应关系: - /// - 定时器创建:对应原始实现中的定时器创建逻辑 - /// - 延迟策略:1毫秒延迟,与原始实现完全一致 - /// - 批处理大小:100条消息,与原始实现完全一致 - /// - 回调函数:对应原始实现中的定时器回调逻辑 - /// - /// 重构改进: - /// - 更清晰的定时器管理 - /// - 更好的异常处理 - /// - 保持了完全一致的批处理策略 + /// 启动消息延迟发送定时器 /// private void StartMessageDeferTimer() { @@ -230,22 +129,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 停止消息延迟发送定时器 - 对应LTEClientWebSocket.StopMessageDeferTimer - /// - /// 功能说明: - /// 1. 停止消息延迟发送定时器 - /// 2. 释放定时器资源 - /// 3. 确保线程安全的定时器管理 - /// - /// 对应关系: - /// - 定时器停止:对应原始实现中的定时器停止逻辑 - /// - 资源释放:对应原始实现中的定时器释放逻辑 - /// - 线程安全:使用锁确保线程安全 - /// - /// 重构改进: - /// - 更清晰的资源管理 - /// - 更好的线程安全保证 - /// - 保持了完全一致的停止逻辑 + /// 停止消息延迟发送定时器 /// private void StopMessageDeferTimer() { @@ -253,35 +137,18 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 消息延迟发送定时器回调 - 对应LTEClientWebSocket.OnMessageDeferTimer - /// - /// 功能说明: - /// 1. 处理消息延迟发送定时器回调 - /// 2. 批量发送队列中的消息 - /// 3. 实现消息发送优化 - /// - /// 对应关系: - /// - 定时器回调:对应原始实现中的OnMessageDeferTimer方法 - /// - 批量发送:对应原始实现中的批量发送逻辑 - /// - 批处理大小:100条消息,与原始实现完全一致 - /// - 发送逻辑:对应原始实现中的SendMessageNow调用 - /// - /// 重构改进: - /// - 更清晰的批量发送逻辑 - /// - 更好的异常处理 - /// - 保持了完全一致的批处理策略 + /// 消息延迟发送定时器回调 /// - /// 定时器状态参数 private void OnMessageDeferTimer(object? state) { try { - // 批量发送消息 - 对应原始实现中的批量发送逻辑 + // 批量发送消息 var messages = new List(); var count = 0; - const int batchSize = 100; // 与原始实现完全一致 + const int batchSize = 100; - // 从队列中取出消息 - 对应原始实现中的队列处理逻辑 + // 从队列中取出消息 while (count < batchSize && _messageFifo.TryTake(out var message)) { messages.Add(message); @@ -290,11 +157,11 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr if (messages.Count > 0) { - // 发送消息 - 对应原始实现中的SendMessageNow调用 + // 发送消息 SendMessageNow(messages); } - // 如果队列中还有消息,继续启动定时器 - 对应原始实现中的定时器重启逻辑 + // 如果队列中还有消息,继续启动定时器 if (_messageFifo.Count > 0) { StartMessageDeferTimer(); @@ -307,26 +174,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 立即发送消息 - 对应LTEClientWebSocket.SendMessageNow - /// - /// 功能说明: - /// 1. 立即发送消息列表到WebSocket - /// 2. 处理发送异常和错误 - /// 3. 触发MessageSent事件 - /// - /// 对应关系: - /// - 消息发送:对应原始实现中的SendMessageNow方法 - /// - 异常处理:对应原始实现中的发送异常处理 - /// - 事件触发:对应原始实现中的事件触发逻辑 - /// - 日志记录:对应原始实现中的发送日志记录 - /// - /// 重构改进: - /// - 更清晰的发送逻辑 - /// - 更详细的错误处理 - /// - 新增MessageSent事件触发 - /// - 保持了完全一致的发送逻辑 + /// 立即发送消息 /// - /// 要发送的消息列表 private void SendMessageNow(List messages) { if (messages == null || messages.Count == 0) @@ -345,7 +194,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr var messageText = JsonConvert.SerializeObject(message); _webSocket?.Send(messageText); - // 触发MessageSent事件 - 新增功能,提供更完整的消息生命周期通知 + // 触发MessageSent事件 MessageSent?.Invoke(this, message); _logger.LogDebug($"[{_clientName}] 消息已发送: {messageText}"); @@ -359,21 +208,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 清空消息队列 - 对应LTEClientWebSocket中的队列清理逻辑 - /// - /// 功能说明: - /// 1. 清空消息队列中的所有消息 - /// 2. 在断开连接时调用,确保资源清理 - /// - /// 对应关系: - /// - 队列清理:对应原始实现中的队列清理逻辑 - /// - 调用时机:在Disconnect()方法中调用 - /// - 日志记录:对应原始实现中的清理日志记录 - /// - /// 重构改进: - /// - 更清晰的清理逻辑 - /// - 更详细的日志记录 - /// - 保持了完全一致的清理逻辑 + /// 清空消息队列 /// private void ClearMessageQueue() { diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs index 6138f31..b1fcf48 100644 --- a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs @@ -17,37 +17,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr #region 公共方法 /// - /// 连接到WebSocket服务器 - 对应LTEClientWebSocket.Start()方法 - /// - /// 功能说明: - /// 1. 建立WebSocket连接,对应原始Start()方法的核心逻辑 - /// 2. 构建WebSocket URL,支持SSL和非SSL连接 - /// 3. 绑定事件处理器,对应原始的事件绑定逻辑 - /// 4. 提供更严格的参数验证和异常处理 - /// - /// 与原始实现的差异: - /// - 方法名从Start()改为Connect(),更明确表达功能 - /// - 移除了状态管理逻辑(SetState),专注连接管理 - /// - 增加了参数验证,提供更好的错误处理 - /// - /// 详细对应关系: - /// - 参数url:对应原始实现中的config.Address - /// - 参数ssl:对应原始实现中的config.Ssl - /// - URL构建:对应原始实现中的URL构建逻辑 - /// - WebSocket创建:对应原始实现中的_webSocket = new WebSocket(url) - /// - 事件绑定:对应原始实现中的事件绑定逻辑 - /// - 连接打开:对应原始实现中的_webSocket.Open() - /// - 异常处理:对应原始实现中的异常处理逻辑 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 更明确的参数验证 - /// - 更详细的异常处理 - /// - 更清晰的错误信息 - /// - 保持了完全一致的连接逻辑 + /// 连接到WebSocket服务器 /// - /// WebSocket URL,对应LTEClientWebSocket._config.Address - /// 是否使用SSL,对应LTEClientWebSocket._config.Ssl public void Connect(string url, bool ssl = false) { ThrowIfDisposed(); @@ -59,20 +30,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr { _logger.LogInformation($"[{_clientName}] 尝试连接: {url}"); - // 构建WebSocket URL - 对应原始实现中的URL构建逻辑 + // 构建WebSocket URL var fullUrl = (ssl ? "wss://" : "ws://") + url; - // 创建WebSocket实例 - 对应原始实现中的_webSocket创建 + // 创建WebSocket实例 _webSocket = new WebSocket(fullUrl); _webSocket.EnableAutoSendPing = false; - // 绑定事件处理器 - 对应原始实现中的事件绑定 + // 绑定事件处理器 _webSocket.Opened += OnSocketOpened!; _webSocket.Closed += OnSocketClosed!; - _webSocket.MessageReceived += OnSocketMessageReceived!; // 对应OnSocketMessage0 + _webSocket.MessageReceived += OnSocketMessageReceived!; _webSocket.Error += OnSocketError!; - // 打开连接 - 对应原始实现中的_webSocket.Open() + // 打开连接 _webSocket.Open(); } catch (Exception ex) @@ -84,31 +55,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 断开WebSocket连接 - 对应LTEClientWebSocket.Stop()方法中的WebSocket相关逻辑 - /// - /// 功能说明: - /// 1. 关闭WebSocket连接,对应原始Stop()方法的核心逻辑 - /// 2. 清理消息队列和定时器,对应原始的资源清理逻辑 - /// 3. 提供更完善的异常处理 - /// - /// 与原始实现的差异: - /// - 方法名从Stop()改为Disconnect(),更明确表达功能 - /// - 移除了状态管理逻辑(SetState),专注连接管理 - /// - 移除了重连逻辑,专注连接断开 - /// - /// 详细对应关系: - /// - 定时器停止:对应原始StopTimers()中的_messageDeferTimer处理 - /// - 队列清理:对应原始实现中的队列清理逻辑 - /// - WebSocket关闭:对应原始实现中的_webSocket.Close() - /// - 资源清理:对应原始实现中的资源清理逻辑 - /// - 异常处理:对应原始实现中的异常处理 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 更清晰的资源清理顺序 - /// - 更完善的异常处理 - /// - 更详细的日志记录 - /// - 保持了完全一致的清理逻辑 + /// 断开WebSocket连接 /// public void Disconnect() { @@ -118,13 +65,13 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr { _logger.LogInformation($"[{_clientName}] 断开连接"); - // 停止消息发送定时器 - 对应原始StopTimers()中的_messageDeferTimer处理 + // 停止消息发送定时器 StopMessageDeferTimer(); - // 清空消息队列 - 对应原始实现中的队列清理 + // 清空消息队列 ClearMessageQueue(); - // 关闭WebSocket连接 - 对应原始实现中的_webSocket.Close() + // 关闭WebSocket连接 if (_webSocket != null) { _webSocket.Close(); @@ -138,41 +85,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 发送消息 - 对应LTEClientWebSocket.SendMessage()方法 - /// - /// 功能说明: - /// 1. 发送通用消息,对应原始SendMessage()方法的核心逻辑 - /// 2. 使用MessageIdManager生成消息ID,替代原始的Interlocked.Increment(ref _messageId) - /// 3. 将消息加入队列,对应原始的_messageFifo.Enqueue(message) - /// 4. 启动延迟发送定时器,对应原始的定时器逻辑 - /// - /// 与原始实现的差异: - /// - 消息ID生成通过MessageIdManager,提供更好的管理 - /// - 移除了消息缓存逻辑(_sentMessages),专注传输 - /// - 增加了更严格的参数验证 - /// - 保持了完全一致的队列和定时器逻辑 - /// - /// 详细对应关系: - /// - 参数message:对应原始方法中的message参数 - /// - 参数callback:对应原始方法中的callback参数 - /// - 参数errorHandler:对应原始方法中的errorHandler参数 - /// - 连接状态检查:对应原始实现中的连接状态检查 - /// - 消息ID生成:对应原始的Interlocked.Increment(ref _messageId) - /// - 队列操作:对应原始的_messageFifo.Enqueue(message) - /// - 定时器启动:对应原始的定时器启动逻辑 - /// - 返回值:对应原始方法的返回值 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 更统一的消息ID管理 - /// - 更严格的参数验证 - /// - 更详细的日志记录 - /// - 保持了完全一致的发送逻辑 + /// 发送消息 /// - /// 消息对象,对应原始方法中的message参数 - /// 回调函数,对应原始方法中的callback参数 - /// 是否为错误处理器,对应原始方法中的errorHandler参数 - /// 消息ID,对应原始方法的返回值 public long SendMessage(JObject message, Action? callback = null, bool errorHandler = false) { ThrowIfDisposed(); @@ -180,20 +94,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr if (message == null) throw new ArgumentNullException(nameof(message)); - // 检查连接状态 - 对应原始实现中的连接状态检查 + // 检查连接状态 if (!IsConnected) { _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息"); return -1L; } - // 使用MessageIdManager生成ID - 替代原始的Interlocked.Increment(ref _messageId) + // 使用MessageIdManager生成ID var messageId = _messageIdManager.GenerateGeneralMessageId(message, callback, errorHandler); - // 添加到消息队列 - 对应原始实现中的_messageFifo.Enqueue(message) + // 添加到消息队列 _messageFifo.Add(message); - // 启动消息发送定时器 - 对应原始实现中的定时器启动逻辑 + // 启动消息发送定时器 StartMessageDeferTimer(); _logger.LogDebug($"[{_clientName}] 消息已加入队列: message_id={messageId}"); @@ -201,36 +115,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 发送日志获取消息 - 对应LTEClientWebSocket.LogGet()方法中的消息发送部分 - /// - /// 功能说明: - /// 1. 专门用于发送日志获取消息,对应原始LogGet()方法的核心逻辑 - /// 2. 使用MessageIdManager生成LogGet ID,替代原始的_logGetId管理 - /// 3. 委托给SendMessage方法,保持代码一致性 - /// - /// 与原始实现的差异: - /// - 专门处理日志获取消息,提供更清晰的接口 - /// - 使用MessageIdManager管理LogGet ID,提供更好的跟踪 - /// - 委托给SendMessage方法,避免代码重复 - /// - 保持了完全一致的发送逻辑 - /// - /// 详细对应关系: - /// - 参数message:对应原始LogGet()方法中构建的message - /// - 参数callback:对应原始LogGet()方法中的LogGetParse回调 - /// - 委托给SendMessage:对应原始实现中的SendMessage调用 - /// - LogGet ID生成:对应原始的_logGetId管理逻辑 - /// - 返回值:对应原始方法的返回值 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 更专门的日志获取消息处理 - /// - 更统一的LogGet ID管理 - /// - 避免代码重复,委托给SendMessage - /// - 保持了完全一致的发送逻辑 + /// 发送日志获取消息 /// - /// 消息对象,对应原始LogGet()方法中构建的message - /// 回调函数,对应原始LogGet()方法中的LogGetParse回调 - /// 消息ID,对应原始方法的返回值 public long SendLogGetMessage(JObject message, Action callback) { ThrowIfDisposed(); @@ -241,19 +127,17 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr if (callback == null) throw new ArgumentNullException(nameof(callback)); - // 检查连接状态 - 对应原始实现中的连接状态检查 + // 检查连接状态 if (!IsConnected) { _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送日志获取消息"); return -1L; } - // 使用MessageIdManager生成LogGet ID - 替代原始的_logGetId管理 + // 使用MessageIdManager生成LogGet ID var messageId = _messageIdManager.GenerateLogGetMessageId(message, callback); - // 委托给SendMessage方法,避免代码重复 - 对应原始实现中的SendMessage调用 - // 注意:这里不需要再次调用SendMessage,因为GenerateLogGetMessageId已经处理了消息ID和回调注册 - // 只需要将消息加入队列并启动定时器 + // 将消息加入队列并启动定时器 _messageFifo.Add(message); StartMessageDeferTimer(); @@ -262,38 +146,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 处理接收到的消息 - 对应LTEClientWebSocket.OnSocketMessage()方法中的消息处理逻辑 - /// - /// 功能说明: - /// 1. 处理接收到的WebSocket消息,对应原始OnSocketMessage()方法的核心逻辑 - /// 2. 使用MessageIdManager处理消息响应,替代原始的消息处理器查找逻辑 - /// 3. 触发MessageReceived事件,对应原始的事件触发 - /// 4. 提供完善的错误处理 - /// - /// 与原始实现的差异: - /// - 使用MessageIdManager处理消息响应,提供更好的管理 - /// - 移除了消息缓存逻辑(_receivedMessages),专注处理 - /// - 移除了业务逻辑处理(log_get、stats等),专注消息路由 - /// - 保持了完全一致的事件触发逻辑 - /// - /// 详细对应关系: - /// - 参数message:对应原始方法中的msg参数 - /// - 参数errorHandler:对应原始方法中的错误处理逻辑 - /// - 消息处理器查找:对应原始的消息处理器查找逻辑 - /// - 事件触发:对应原始的事件触发逻辑 - /// - 错误处理:对应原始的错误处理逻辑 - /// - 返回值:新增返回值提供处理状态反馈 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 更统一的消息响应处理 - /// - 更清晰的错误处理 - /// - 更详细的日志记录 - /// - 保持了完全一致的处理逻辑 + /// 处理接收到的消息 /// - /// 接收到的消息,对应原始方法中的msg参数 - /// 错误处理回调,对应原始方法中的错误处理逻辑 - /// 是否成功处理,新增返回值提供处理状态反馈 public bool HandleReceivedMessage(JObject message, Action? errorHandler = null) { ThrowIfDisposed(); @@ -303,7 +157,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr try { - // 使用MessageIdManager处理消息响应 - 替代原始的消息处理器查找逻辑 + // 使用MessageIdManager处理消息响应 var handled = _messageIdManager.HandleMessageResponse(message, errorHandler); if (handled) @@ -312,8 +166,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr return true; } - // 处理特定消息类型 - 对应原始实现中的特定消息类型处理 - // 注意:这里不处理log_get和stats等业务逻辑,因为重构版本专注于消息传输 + // 处理特定消息类型 var name = message["message"]?.ToString(); if (!string.IsNullOrEmpty(name)) { @@ -331,26 +184,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 设置消息处理器 - 对应LTEClientWebSocket.SetMessageHandler()方法 - /// - /// 功能说明: - /// 1. 设置按名称的消息处理器,对应原始SetMessageHandler()方法的核心逻辑 - /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 - /// 3. 支持多个消息名称的处理器设置 - /// - /// 详细对应关系: - /// - 参数names:对应原始方法中的names参数,消息名称数组 - /// - 参数handler:对应原始方法中的handler参数,消息处理器 - /// - 处理器注册:对应原始的_messageHandlersByName注册逻辑 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 委托给MessageIdManager,提供统一管理 - /// - 保持了完全一致的接口和功能 - /// - 更好的错误处理和参数验证 + /// 设置消息处理器 /// - /// 消息名称数组,对应原始方法中的names参数 - /// 消息处理器,对应原始方法中的handler参数 public void SetMessageHandler(string[] names, MessageHandler handler) { ThrowIfDisposed(); @@ -358,24 +193,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 取消设置消息处理器 - 对应LTEClientWebSocket.UnsetMessageHandler()方法 - /// - /// 功能说明: - /// 1. 取消按名称的消息处理器,对应原始UnsetMessageHandler()方法的核心逻辑 - /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 - /// 3. 支持多个消息名称的处理器取消 - /// - /// 详细对应关系: - /// - 参数names:对应原始方法中的names参数,消息名称数组 - /// - 处理器移除:对应原始的_messageHandlersByName移除逻辑 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 委托给MessageIdManager,提供统一管理 - /// - 保持了完全一致的接口和功能 - /// - 更好的错误处理和参数验证 + /// 取消设置消息处理器 /// - /// 消息名称数组,对应原始方法中的names参数 public void UnsetMessageHandler(string[] names) { ThrowIfDisposed(); @@ -383,25 +202,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 检查是否为当前日志获取消息 - 对应LTEClientWebSocket中的_logGetId检查逻辑 - /// - /// 功能说明: - /// 1. 检查指定的消息ID是否为当前的日志获取消息ID - /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 - /// 3. 用于日志获取流程的状态检查 - /// - /// 详细对应关系: - /// - 参数messageId:对应原始实现中的消息ID检查 - /// - 返回值:true表示是当前LogGet消息,false表示不是,对应原始逻辑 - /// - 检查逻辑:对应原始的_logGetId比较逻辑 - /// - /// 重构改进: - /// - 委托给MessageIdManager,提供统一管理 - /// - 保持了完全一致的检查逻辑 - /// - 更好的线程安全性 + /// 检查是否为当前日志获取消息 /// - /// 要检查的消息ID - /// 是否为当前日志获取消息 public bool IsCurrentLogGetMessage(long messageId) { ThrowIfDisposed(); @@ -409,22 +211,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 重置日志获取ID - 对应LTEClientWebSocket中的_logGetId重置逻辑 - /// - /// 功能说明: - /// 1. 重置日志获取消息ID,对应原始实现中的_logGetId重置逻辑 - /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 - /// 3. 用于日志获取流程的重置操作 - /// - /// 详细对应关系: - /// - 重置逻辑:对应原始的_logGetId = -1操作 - /// - 日志记录:对应原始实现中的日志记录 - /// - 事件触发:对应原始实现中的状态变化通知 - /// - /// 重构改进: - /// - 委托给MessageIdManager,提供统一管理 - /// - 保持了完全一致的重置逻辑 - /// - 更好的事件通知机制 + /// 重置日志获取ID /// public void ResetLogGetId() { @@ -433,24 +220,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr } /// - /// 清理过期的消息处理器 - 对应LTEClientWebSocket中的处理器清理逻辑 - /// - /// 功能说明: - /// 1. 清理过期的消息处理器,防止内存泄漏 - /// 2. 委托给MessageIdManager处理,提供统一的处理器管理 - /// 3. 支持可配置的过期时间 - /// - /// 详细对应关系: - /// - 参数maxAge:对应原始实现中的过期时间配置 - /// - 清理逻辑:对应原始的处理器清理逻辑 - /// - 日志记录:对应原始实现中的日志记录 - /// - /// 重构改进: - /// - 委托给MessageIdManager,提供统一管理 - /// - 保持了完全一致的清理逻辑 - /// - 更好的内存管理 + /// 清理过期的消息处理器 /// - /// 最大存活时间(毫秒),默认30000毫秒 public void CleanupExpiredHandlers(int maxAge = 30000) { ThrowIfDisposed(); diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs index 0bef973..44c7657 100644 --- a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs @@ -12,331 +12,53 @@ using WebSocket4Net; namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr { /// - /// WebSocket消息管理器 - 专门处理WebSocket的收发业务 - /// - /// 重构说明: - /// 1. 对应LTEClientWebSocket中的WebSocket连接和消息传输功能 - /// 2. 集成MessageIdManager统一管理消息ID和回调 - /// 3. 移除_sentMessages和_receivedMessages消息缓存,专注传输 - /// 4. 移除业务逻辑功能(统计更新、日志解析等),实现职责分离 - /// - /// 主要功能: - /// - WebSocket连接管理(对应LTEClientWebSocket.Start()和Stop()) - /// - 消息发送和接收(对应LTEClientWebSocket.SendMessage()和OnSocketMessage()) - /// - 消息队列和批量发送(对应LTEClientWebSocket._messageFifo和SendMessageNow()) - /// - 事件通知(对应LTEClientWebSocket的事件系统) - /// - /// 与LTEClientWebSocket的详细对应关系: - /// - /// 1. 连接管理对应关系: - /// - Connect() 对应 Start() 方法中的WebSocket连接建立逻辑 - /// - Disconnect() 对应 Stop() 方法中的WebSocket关闭逻辑 - /// - OnSocketOpened/OnSocketClosed/OnSocketError 对应原始的事件处理器 - /// - /// 2. 消息发送对应关系: - /// - SendMessage() 对应 SendMessage() 方法的核心逻辑 - /// - SendLogGetMessage() 对应 LogGet() 方法中的消息发送部分 - /// - _messageFifo 对应原始的 _messageFifo 队列 - /// - SendMessageNow() 对应原始的 SendMessageNow() 方法 - /// - StartMessageDeferTimer() 对应原始的定时器启动逻辑 - /// - /// 3. 消息接收对应关系: - /// - OnSocketMessageReceived() 对应 OnSocketMessage() 方法 - /// - HandleReceivedMessage() 对应 OnSocketMessage() 中的消息处理逻辑 - /// - MessageReceived 事件对应原始的 MessageReceived 事件 - /// - /// 4. 消息ID管理对应关系: - /// - MessageIdManager 替代原始的 _messageId 和 _logGetId 字段 - /// - GenerateGeneralMessageId() 对应 Interlocked.Increment(ref _messageId) - /// - GenerateLogGetMessageId() 对应 _logGetId 的管理逻辑 - /// - /// 5. 事件系统对应关系: - /// - ConnectionOpened 对应原始的 ConnectionOpened 事件 - /// - ConnectionClosed 对应原始的 ConnectionClosed 事件 - /// - ConnectionError 对应原始的 ConnectionError 事件 - /// - MessageReceived 对应原始的 MessageReceived 事件 - /// - MessageSent 新增事件,提供更完整的消息生命周期通知 - /// - /// 6. 资源管理对应关系: - /// - Dispose() 对应原始的 Dispose() 方法 - /// - _disposed 字段对应原始的 _disposed 字段 - /// - 定时器管理对应原始的定时器清理逻辑 - /// - /// 重构优势: - /// 1. 职责分离:专注WebSocket传输,移除业务逻辑 - /// 2. 代码复用:可在多个地方复用WebSocket管理器 - /// 3. 测试友好:更容易进行单元测试 - /// 4. 维护简单:更清晰的代码结构 - /// 5. 功能增强:通过MessageIdManager提供更好的消息管理 + /// WebSocket消息管理器 - 处理WebSocket连接和消息传输 /// public partial class WebSocketMessageManager : IDisposable { #region 私有字段 - /// - /// WebSocket实例 - 对应LTEClientWebSocket._webSocket - /// 负责底层的WebSocket连接和通信 - /// - /// 对应关系: - /// - 创建:Connect()方法中创建,对应Start()方法中的_webSocket = new WebSocket(url) - /// - 配置:EnableAutoSendPing = false,对应原始实现 - /// - 事件绑定:绑定Opened/Closed/MessageReceived/Error事件,对应原始事件绑定 - /// - 连接:调用Open()方法,对应原始的_webSocket.Open() - /// - 关闭:调用Close()方法,对应原始的_webSocket.Close() - /// - 清理:在Disconnect()中设置为null,对应原始的资源清理 - /// + // WebSocket连接实例 private WebSocket? _webSocket; - /// - /// 消息ID管理器 - 对应LTEClientWebSocket._messageId和_logGetId - /// 统一管理通用消息ID和日志获取消息ID,提供更好的消息跟踪和回调管理 - /// - /// 对应关系: - /// - _messageId 对应 MessageIdManager.GenerateGeneralMessageId() - /// - _logGetId 对应 MessageIdManager.GenerateLogGetMessageId() - /// - _messageHandlers 对应 MessageIdManager的内部处理器管理 - /// - _messageHandlersByName 对应 MessageIdManager的按名称处理器管理 - /// - /// 功能增强: - /// - 线程安全的ID生成,替代原始的Interlocked.Increment - /// - 统一的处理器管理,提供更好的回调跟踪 - /// - 自动清理过期处理器,防止内存泄漏 - /// - 事件通知机制,提供ID变化通知 - /// - private readonly MessageIdManager _messageIdManager; + // 客户端名称 + private readonly string _clientName; - /// - /// 日志记录器 - 对应LTEClientWebSocket._logger - /// 用于记录WebSocket操作和错误信息 - /// - /// 对应关系: - /// - 构造函数参数:对应LTEClientWebSocket构造函数中的logger参数 - /// - 日志记录:对应原始实现中的所有_logger.LogXXX调用 - /// - 日志格式:保持与原始实现一致的日志格式 - /// - /// 功能增强: - /// - 更详细的错误日志记录 - /// - 更好的异常堆栈跟踪 - /// - 统一的日志格式和级别 - /// - private readonly ILogger _logger; + // 日志记录器 + private readonly ILogger _logger; - /// - /// 客户端名称 - 对应LTEClientWebSocket._config.Name - /// 用于日志记录和事件标识 - /// - /// 对应关系: - /// - 构造函数参数:对应LTEClientWebSocket构造函数中的config.Name - /// - 日志前缀:对应原始实现中所有日志的[{_config.Name}]前缀 - /// - 事件标识:用于区分不同客户端的事件 - /// - /// 功能增强: - /// - 参数验证:确保clientName不为null - /// - 统一标识:在所有日志和事件中使用一致的客户端标识 - /// - private readonly string _clientName; + // 消息ID管理器 + private readonly MessageIdManager _messageIdManager; - /// - /// 消息队列 - 对应LTEClientWebSocket._messageFifo - /// 线程安全的阻塞集合,用于批量发送优化 - /// 优化说明:从ConcurrentQueue改为BlockingCollection,提供更好的线程安全性和阻塞能力 - /// - /// 对应关系: - /// - 队列类型:BlockingCollection,优化后的线程安全集合 - /// - 入队操作:Add(message),对应原始的_messageFifo.Enqueue(message) - /// - 出队操作:TryTake(out message),对应原始的队列处理逻辑 - /// - 批量处理:支持批量消息发送,对应原始的批处理逻辑 - /// - /// 功能增强: - /// - 线程安全:使用BlockingCollection保证线程安全 - /// - 阻塞能力:支持阻塞式出队操作,提高性能 - /// - 批量优化:支持批量发送减少网络开销 - /// - 延迟发送:配合_messageDeferTimer实现延迟发送 - /// - 资源管理:自动处理集合的完成状态 - /// - /// 重构改进: - /// - 移除了消息缓存功能,专注传输 - /// - 优化了队列操作逻辑,提供更好的性能 - /// - 增强了线程安全性和资源管理 - /// + // 消息队列 private readonly BlockingCollection _messageFifo; - /// - /// 消息延迟发送定时器 - 对应LTEClientWebSocket._messageDeferTimer - /// 用于实现消息的批量发送和延迟发送机制 - /// 保持与原始实现完全一致的逻辑 - /// - /// 对应关系: - /// - 定时器类型:Timer,与原始实现完全一致 - /// - 启动逻辑:StartMessageDeferTimer(),对应原始的定时器启动 - /// - 停止逻辑:StopMessageDeferTimer(),对应原始的定时器停止 - /// - 延迟策略:1毫秒延迟,与原始实现完全一致 - /// - 批处理大小:100条消息,与原始实现完全一致 - /// - /// 功能保持: - /// - 批量发送:当队列中消息少于100条时,延迟1毫秒发送 - /// - 立即发送:当队列中消息达到100条时,立即发送 - /// - 资源管理:在Disconnect()和Dispose()中正确释放 - /// - /// 重构改进: - /// - 更清晰的定时器管理逻辑 - /// - 更好的异常处理 - /// - 保持了完全一致的批处理策略 - /// + // 消息延迟发送定时器 private Timer? _messageDeferTimer; - /// - /// 释放标志 - 对应LTEClientWebSocket._disposed - /// 防止重复释放和已释放对象的操作 - /// - /// 对应关系: - /// - 字段类型:bool,对应原始的_disposed字段 - /// - 返回值:true表示已释放,false表示未释放,对应原始实现 - /// - 使用场景:外部检查对象释放状态,对应原始实现 - /// - 线程安全:直接返回_disposed字段值,对应原始实现 - /// - /// 功能保持: - /// - 释放状态检查:外部可以检查对象是否已释放 - /// - 资源保护:防止对已释放对象的操作 - /// - 状态查询:提供对象状态的查询接口 - /// - /// 重构改进: - /// - 保持了完全一致的检查逻辑 - /// - 保持了完全一致的返回值语义 - /// - 保持了完全一致的使用场景 - /// + // 释放标志 private bool _disposed; - /// - /// 同步锁对象 - /// 用于确保线程安全的操作 - /// - /// 对应关系: - /// - 新增功能:原始实现中没有显式的同步锁 - /// - 用途:确保关键操作的线程安全 - /// - 使用场景:在需要线程安全的地方使用lock语句 - /// - /// 功能增强: - /// - 线程安全:确保关键操作的原子性 - /// - 死锁预防:使用细粒度锁避免死锁 - /// - 性能优化:最小化锁的持有时间 - /// + // 同步锁对象 private readonly object _lockObject = new object(); #endregion #region 事件 - /// - /// 连接打开事件 - 对应LTEClientWebSocket.ConnectionOpened - /// 当WebSocket连接成功建立时触发 - /// - /// 对应关系: - /// - 事件类型:EventHandler,与原始实现完全一致 - /// - 触发时机:在OnSocketOpened()中触发,对应原始的OnSocketOpened事件处理 - /// - 触发条件:WebSocket连接成功建立时 - /// - 事件参数:无参数,与原始实现完全一致 - /// - /// 功能保持: - /// - 连接状态通知:通知外部连接已建立 - /// - 事件订阅:支持多个订阅者 - /// - 异步触发:事件触发不会阻塞WebSocket操作 - /// - /// 重构改进: - /// - 更清晰的触发时机 - /// - 更好的错误处理 - /// - 保持了完全一致的事件接口 - /// + // 连接打开事件 public event EventHandler? ConnectionOpened; - /// - /// 连接关闭事件 - 对应LTEClientWebSocket.ConnectionClosed - /// 当WebSocket连接关闭时触发 - /// - /// 对应关系: - /// - 事件类型:EventHandler,与原始实现完全一致 - /// - 触发时机:在OnSocketClosed()中触发,对应原始的OnSocketClosed事件处理 - /// - 触发条件:WebSocket连接关闭时 - /// - 事件参数:无参数,与原始实现完全一致 - /// - /// 功能保持: - /// - 连接状态通知:通知外部连接已关闭 - /// - 事件订阅:支持多个订阅者 - /// - 异步触发:事件触发不会阻塞WebSocket操作 - /// - /// 重构改进: - /// - 更清晰的触发时机 - /// - 更好的资源清理 - /// - 保持了完全一致的事件接口 - /// + // 连接关闭事件 public event EventHandler? ConnectionClosed; - /// - /// 连接错误事件 - 对应LTEClientWebSocket.ConnectionError - /// 当WebSocket连接发生错误时触发 - /// - /// 对应关系: - /// - 事件类型:EventHandler,与原始实现完全一致 - /// - 触发时机:在OnSocketError()和异常处理中触发,对应原始的错误处理 - /// - 触发条件:WebSocket连接错误、消息处理错误等 - /// - 事件参数:错误信息字符串,与原始实现完全一致 - /// - /// 功能保持: - /// - 错误通知:通知外部连接或处理错误 - /// - 事件订阅:支持多个订阅者 - /// - 异步触发:事件触发不会阻塞WebSocket操作 - /// - /// 重构改进: - /// - 更详细的错误信息 - /// - 更好的异常处理 - /// - 保持了完全一致的事件接口 - /// + // 连接错误事件 public event EventHandler? ConnectionError; - /// - /// 消息接收事件 - 对应LTEClientWebSocket.MessageReceived - /// 当接收到WebSocket消息时触发 - /// - /// 对应关系: - /// - 事件类型:EventHandler,与原始实现完全一致 - /// - 触发时机:在OnSocketMessageReceived()中触发,对应原始的OnSocketMessage事件处理 - /// - 触发条件:接收到WebSocket消息并解析成功后 - /// - 事件参数:解析后的JObject消息,与原始实现完全一致 - /// - /// 功能保持: - /// - 消息通知:通知外部接收到新消息 - /// - 事件订阅:支持多个订阅者 - /// - 异步触发:事件触发不会阻塞消息处理 - /// - 触发顺序:在消息处理开始时就触发,与原始实现完全一致 - /// - /// 重构改进: - /// - 更清晰的触发时机 - /// - 更好的消息解析 - /// - 保持了完全一致的事件接口和触发顺序 - /// + // 消息接收事件 public event EventHandler? MessageReceived; - /// - /// 消息发送事件 - 新增功能,LTEClientWebSocket中没有对应事件 - /// 当消息成功发送时触发,提供更完整的消息生命周期通知 - /// - /// 对应关系: - /// - 事件类型:EventHandler,与MessageReceived保持一致 - /// - 触发时机:在SendMessageNow()中触发,对应消息发送成功时 - /// - 触发条件:消息成功发送到WebSocket时 - /// - 事件参数:发送的JObject消息,与MessageReceived保持一致 - /// - /// 功能增强: - /// - 消息生命周期:提供完整的消息发送通知 - /// - 调试支持:便于调试消息发送流程 - /// - 监控支持:便于监控消息发送状态 - /// - 事件订阅:支持多个订阅者 - /// - /// 重构优势: - /// - 更完整的消息生命周期管理 - /// - 更好的调试和监控支持 - /// - 与MessageReceived事件形成对称的事件系统 - /// + // 消息发送事件 public event EventHandler? MessageSent; #endregion @@ -344,113 +66,27 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr #region 属性 /// - /// 是否已连接 - 对应LTEClientWebSocket.IsConnected - /// 检查WebSocket连接状态 - /// - /// 对应关系: - /// - 属性类型:bool,与原始实现完全一致 - /// - 检查逻辑:_webSocket?.State == WebSocketState.Open,与原始实现完全一致 - /// - 使用场景:在SendMessage()中检查连接状态,对应原始实现 - /// - 返回值:true表示已连接,false表示未连接,与原始实现完全一致 - /// - /// 功能保持: - /// - 连接状态检查:快速检查WebSocket连接状态 - /// - 空安全:使用?.操作符避免空引用异常 - /// - 实时状态:反映WebSocket的实时连接状态 - /// - /// 重构改进: - /// - 保持了完全一致的检查逻辑 - /// - 保持了完全一致的返回值语义 - /// - 保持了完全一致的使用场景 + /// 是否已连接 /// public bool IsConnected => _webSocket?.State == WebSocketState.Open; /// - /// WebSocket状态 - 对应LTEClientWebSocket._webSocket?.State - /// 获取详细的WebSocket连接状态 - /// - /// 对应关系: - /// - 属性类型:WebSocketState,对应原始的_webSocket?.State - /// - 返回值:WebSocket的详细状态,对应原始实现 - /// - 空安全:使用??操作符提供默认值,对应原始实现 - /// - 使用场景:提供更详细的连接状态信息 - /// - /// 功能保持: - /// - 详细状态:提供WebSocket的详细连接状态 - /// - 空安全:当_webSocket为null时返回WebSocketState.None - /// - 实时状态:反映WebSocket的实时状态 - /// - /// 重构改进: - /// - 保持了完全一致的状态获取逻辑 - /// - 保持了完全一致的默认值处理 - /// - 保持了完全一致的使用场景 + /// WebSocket状态 /// public WebSocketState State => _webSocket?.State ?? WebSocketState.None; /// - /// 是否已释放 - 对应LTEClientWebSocket._disposed - /// 检查对象是否已被释放 - /// - /// 对应关系: - /// - 属性类型:bool,对应原始的_disposed字段 - /// - 返回值:true表示已释放,false表示未释放,对应原始实现 - /// - 使用场景:外部检查对象释放状态,对应原始实现 - /// - 线程安全:直接返回_disposed字段值,对应原始实现 - /// - /// 功能保持: - /// - 释放状态检查:外部可以检查对象是否已释放 - /// - 资源保护:防止对已释放对象的操作 - /// - 状态查询:提供对象状态的查询接口 - /// - /// 重构改进: - /// - 保持了完全一致的检查逻辑 - /// - 保持了完全一致的返回值语义 - /// - 保持了完全一致的使用场景 + /// 是否已释放 /// public bool IsDisposed => _disposed; /// - /// 消息队列数量 - 对应LTEClientWebSocket._messageFifo.Count - /// 获取当前待发送消息的数量 - /// - /// 对应关系: - /// - 属性类型:int,对应原始的_messageFifo.Count - /// - 返回值:队列中待发送消息的数量,对应原始实现 - /// - 使用场景:监控消息队列状态,对应原始实现 - /// - 线程安全:BlockingCollection.Count是线程安全的,对应原始实现 - /// - /// 功能保持: - /// - 队列监控:监控当前待发送消息的数量 - /// - 性能监控:便于监控消息发送性能 - /// - 调试支持:便于调试消息队列状态 - /// - /// 重构改进: - /// - 保持了完全一致的计数逻辑 - /// - 保持了完全一致的返回值语义 - /// - 保持了完全一致的使用场景 - /// - 优化:使用BlockingCollection提供更好的线程安全性 + /// 消息队列数量 /// public int MessageQueueCount => _messageFifo.Count; /// - /// 消息ID管理器 - 提供对MessageIdManager的访问 - /// 允许外部访问消息ID管理功能 - /// - /// 对应关系: - /// - 属性类型:MessageIdManager,对应原始的_messageId和_logGetId管理 - /// - 返回值:内部的消息ID管理器实例,对应原始实现 - /// - 使用场景:外部访问消息ID管理功能,对应原始实现 - /// - 封装性:提供对内部MessageIdManager的访问,对应原始实现 - /// - /// 功能保持: - /// - 功能访问:外部可以访问消息ID管理功能 - /// - 封装性:保持内部实现的封装性 - /// - 扩展性:支持外部扩展消息ID管理功能 - /// - /// 重构改进: - /// - 更统一的消息ID管理接口 - /// - 更好的功能封装 - /// - 保持了完全一致的功能访问方式 + /// 消息ID管理器 /// public MessageIdManager MessageIdManager => _messageIdManager; diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLog.cs b/CoreAgent.ProtocolClient/Models/BuildProtocolLog.cs similarity index 89% rename from CoreAgent.ProtocolClient/Models/ProtocolLog.cs rename to CoreAgent.ProtocolClient/Models/BuildProtocolLog.cs index 50a536d..d6da262 100644 --- a/CoreAgent.ProtocolClient/Models/ProtocolLog.cs +++ b/CoreAgent.ProtocolClient/Models/BuildProtocolLog.cs @@ -7,7 +7,10 @@ using System.Threading.Tasks; namespace CoreAgent.ProtocolClient.Models { - public class ProtocolLog + /// + /// 构建协议日志模型 + /// + public class BuildProtocolLog { #region 基础属性 /// @@ -65,20 +68,20 @@ namespace CoreAgent.ProtocolClient.Models #endregion #region PHY层相关属性 - public PhyFields Phy { get; set; } = new(); + public PhyLayerFields Phy { get; set; } = new(); #endregion #region 数据相关属性 - public DataFields DataInfo { get; set; } = new(); + public DataLayerFields DataInfo { get; set; } = new(); #endregion #region MAC层相关属性 - public MacFields Mac { get; set; } = new(); + public MacLayerFields Mac { get; set; } = new(); #endregion } // PHY层分组 - public class PhyFields + public class PhyLayerFields { /// /// 物理信道类型 @@ -135,7 +138,7 @@ namespace CoreAgent.ProtocolClient.Models } // 数据相关分组 - public class DataFields + public class DataLayerFields { /// /// IP长度 @@ -156,7 +159,7 @@ namespace CoreAgent.ProtocolClient.Models } // MAC层分组 - public class MacFields + public class MacLayerFields { /// /// 功率余量报告(PHR) @@ -194,17 +197,17 @@ namespace CoreAgent.ProtocolClient.Models /// - /// ProtocolLog扩展方法类 - /// 提供ProtocolLog相关的工具方法和扩展功能 + /// BuildProtocolLog扩展方法类 + /// 提供BuildProtocolLog相关的工具方法和扩展功能 /// - public static class ProtocolLogExtensions + public static class BuildProtocolLogExtensions { /// /// 获取协议日志数据的字符串表示 /// /// 协议日志对象 /// 格式化的日志数据字符串 - public static string GetProtocolLogData(this ProtocolLog log) + public static string GetProtocolLogData(this BuildProtocolLog log) { return string.Join("\n", log.Data); } diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs b/CoreAgent.ProtocolClient/Models/SourceProtocolLog.cs similarity index 82% rename from CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs rename to CoreAgent.ProtocolClient/Models/SourceProtocolLog.cs index 7d6c0bc..6b82b89 100644 --- a/CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs +++ b/CoreAgent.ProtocolClient/Models/SourceProtocolLog.cs @@ -8,7 +8,10 @@ using System.Threading.Tasks; namespace CoreAgent.ProtocolClient.Models { - public record class ProtocolLogJson + /// + /// 源协议日志模型 + /// + public record class SourceProtocolLog { /// /// 消息ID @@ -41,17 +44,9 @@ namespace CoreAgent.ProtocolClient.Models public JToken? Logs { get; set; } /// - /// 初始化协议日志实体类的新实例 + /// 初始化源协议日志的新实例 /// - /// 消息内容 - /// 消息类型 - /// 协议版本 - /// 时间戳 - /// UTC时间戳 - /// 日志明细 - /// 消息ID - /// 消息头信息 - public ProtocolLogJson( + public SourceProtocolLog( string message, string? type, string? version, @@ -71,9 +66,9 @@ namespace CoreAgent.ProtocolClient.Models /// - /// 协议日志明细 + /// 源协议日志明细模型 /// - public class ProtocolLogDetailJson + public class SourceProtocolLogDetail { /// /// 源信息 @@ -153,15 +148,14 @@ namespace CoreAgent.ProtocolClient.Models [JsonProperty("rnti")] public int? Rnti { get; set; } - /// /// 深拷贝当前对象 /// - public ProtocolLogDetailJson DeepClone() + public SourceProtocolLogDetail DeepClone() { // 通过序列化和反序列化实现深拷贝 var json = JsonConvert.SerializeObject(this); - return JsonConvert.DeserializeObject(json) + return JsonConvert.DeserializeObject(json) ?? throw new InvalidOperationException("深拷贝失败,反序列化结果为 null"); } } diff --git a/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs b/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs index 65c2879..b2b9768 100644 --- a/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs +++ b/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs @@ -64,7 +64,7 @@ namespace CoreAgent.ProtocolClient.Models { // 从UE链中获取IMSI string imsi = GetImsiFromUeChain(receiveUeId, ueChains); - + // 创建匹配结果 var matchResult = new TmsiMatchResult(tmsi, requestUeId, receiveUeId, imsi); matches.Add(matchResult); @@ -85,7 +85,7 @@ namespace CoreAgent.ProtocolClient.Models var processedUeIds = new HashSet(); // 遍历所有TMSI匹配关系,构建链式结构 - foreach (var requestKvp in _tmsiToUeId) + foreach (var requestKvp in _tmsiToUeId) { uint tmsi = requestKvp.Key; int requestUeId = requestKvp.Value; @@ -101,7 +101,7 @@ namespace CoreAgent.ProtocolClient.Models // 使用链的根节点(最外层节点)作为键 int rootUeId = chain[0]; ueChains[rootUeId] = chain; - + // 标记所有UE ID为已处理 foreach (int ueId in chain) { @@ -205,7 +205,7 @@ namespace CoreAgent.ProtocolClient.Models return imsiMapping.Key; } } - + // 2. 如果当前UE ID没有IMSI,查找整个UE链中的IMSI // 遍历所有IMSI映射,查找是否有任何UE ID对应的IMSI foreach (var imsiMapping in _imsiToUeId) @@ -215,7 +215,7 @@ namespace CoreAgent.ProtocolClient.Models return imsiMapping.Key; } } - + return string.Empty; } @@ -226,7 +226,7 @@ namespace CoreAgent.ProtocolClient.Models public UeChainStats GetUeChainStats() { var ueChains = BuildUeChains(); - + return new UeChainStats { TotalChains = ueChains.Count, @@ -321,4 +321,4 @@ namespace CoreAgent.ProtocolClient.Models /// public string Imsi { get; set; } = string.Empty; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs b/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs index 11d1ce6..5379289 100644 --- a/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs +++ b/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs @@ -53,4 +53,65 @@ namespace CoreAgent.ProtocolClient.Models return $"TMSI: 0x{Tmsi:X8}, RequestUE: {RequestUeId}, ReceiveUE: {ReceiveUeId}{imsiInfo}"; } } -} \ No newline at end of file + + /// + /// Service Request TMSI映射实体 + /// 用于平铺 SrTmsiToUeId 字典数据,包含 IMSI、TMSI、UE ID、Root UE ID 四个字段 + /// + public class SrTmsiMapping + { + /// + /// IMSI标识符 + /// + public string Imsi { get; set; } = string.Empty; + + /// + /// Service Request TMSI标识符 + /// + public uint Tmsi { get; set; } + + /// + /// UE ID + /// + public int UeId { get; set; } + + /// + /// Root UE ID(链的根节点) + /// + public int RootUeId { get; set; } + + /// + /// PLMN标识符 + /// + public string Plmn { get; set; } = string.Empty; + + /// + /// 构造函数 + /// + /// IMSI标识符 + /// Service Request TMSI标识符 + /// UE ID + /// Root UE ID + /// PLMN标识符 + public SrTmsiMapping(string imsi, uint tmsi, int ueId, int rootUeId = 0, string plmn = "") + { + Imsi = imsi; + Tmsi = tmsi; + UeId = ueId; + RootUeId = rootUeId; + Plmn = plmn; + } + + /// + /// 重写ToString方法,提供友好的字符串表示 + /// + /// 格式化的字符串 + public override string ToString() + { + var imsiInfo = string.IsNullOrEmpty(Imsi) ? "" : $", IMSI: {Imsi}"; + var rootInfo = RootUeId > 0 ? $", Root UE: {RootUeId}" : ""; + var plmnInfo = string.IsNullOrEmpty(Plmn) ? "" : $", PLMN: {Plmn}"; + return $"SR-TMSI: 0x{Tmsi:X8}, UE ID: {UeId}{imsiInfo}{rootInfo}{plmnInfo}"; + } + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs b/CoreAgent.ProtocolClient/Models/TransferProtocolLog.cs similarity index 95% rename from CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs rename to CoreAgent.ProtocolClient/Models/TransferProtocolLog.cs index 895a304..5dc754c 100644 --- a/CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs +++ b/CoreAgent.ProtocolClient/Models/TransferProtocolLog.cs @@ -5,11 +5,11 @@ using CoreAgent.ProtocolClient.Enums; namespace CoreAgent.ProtocolClient.Models { /// - /// 协议日志详情实体 - /// 用于存储解析后的协议日志详细信息到数据库 + /// 传输协议日志模型 + /// 用于传输给上层的协议日志数据 /// [Table("ProtocolLogDetails")] - public class ProtocolLogDetail + public class TransferProtocolLog { /// /// 主键ID diff --git a/CoreAgent.ProtocolClient/Models/UeInfo.cs b/CoreAgent.ProtocolClient/Models/UeInfo.cs index f6e6a41..8c81d82 100644 --- a/CoreAgent.ProtocolClient/Models/UeInfo.cs +++ b/CoreAgent.ProtocolClient/Models/UeInfo.cs @@ -10,7 +10,7 @@ namespace CoreAgent.ProtocolClient.Models /// 用户设备(UE)信息模型 /// 用于存储用户设备的基本信息,包括标识符、设备能力等 /// - public class UeInfo + public class UEInfo { /// /// 用户设备唯一标识符 diff --git a/CoreAgent.ProtocolClient/modify.md b/CoreAgent.ProtocolClient/modify.md index 8e8c6d2..a2be3c9 100644 --- a/CoreAgent.ProtocolClient/modify.md +++ b/CoreAgent.ProtocolClient/modify.md @@ -195,4 +195,1129 @@ public static class ProtocolCapsExtensions 6. **其他辅助方法** - 完善了消息处理器管理和清理相关方法 **提交信息:** 重构WebSocket消息管理器:完善PublicMethods.cs文档和实现 -**提交哈希:** 219118d \ No newline at end of file +**提交哈希:** 219118d + +## 2024-12-19 +### 优化Constructor.cs注释和修复字段声明顺序 + +**修改内容:** +1. **Constructor.cs注释优化** + - 简化了构造函数注释,提高可读性 + - 移除了冗余的详细说明,保留核心要点 + - 优化了参数和异常说明的表述 + +2. **WebSocketMessageManager.cs字段顺序修复** + - 修复了字段声明顺序问题 + - 将`_clientName`字段移到`_logger`字段之前 + - 确保字段声明顺序与构造函数中的使用顺序一致 + +**涉及文件:** +- `Managers/WebSocketMgr/Constructor.cs` - 优化注释,提高可读性 +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 修复字段声明顺序 + +**主要改进:** +1. **注释优化** + - 简化了XML文档注释,去除冗余信息 + - 保留了核心功能说明和参数描述 + - 提高了代码的可读性和维护性 + +2. **字段顺序修复** + - 修复了潜在的初始化顺序问题 + - 确保字段声明顺序与使用顺序一致 + - 提高了代码的逻辑性和可读性 + +**提交信息:** 优化Constructor.cs注释和修复字段声明顺序 + +## 2024-12-19 +### 大幅优化注释,提高代码可读性 + +**修改内容:** +1. **Constructor.cs注释优化** + - 完全移除了XML文档注释,保留简洁的行内注释 + - 简化了所有注释内容,只保留核心要点 + - 提高了代码的可读性和简洁性 + +2. **WebSocketMessageManager.cs注释大幅简化** + - 简化了类级别的XML文档注释,从冗长的重构说明简化为一行描述 + - 将所有字段的详细XML文档注释改为简洁的行内注释 + - 将所有事件的详细XML文档注释改为简洁的行内注释 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/WebSocketMgr/Constructor.cs` - 完全简化注释 +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 大幅简化所有注释 + +**主要改进:** +1. **注释风格统一** + - 统一使用简洁的行内注释风格 + - 移除了冗余的XML文档注释 + - 保持了注释的核心信息 + +2. **可读性大幅提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +3. **维护性改进** + - 减少了注释维护的工作量 + - 降低了注释过时的风险 + - 提高了代码的实用性 + +**示例对比:** +```csharp +// 优化前:冗长的XML文档注释 +/// +/// WebSocket消息管理器 - 专门处理WebSocket的收发业务 +/// 重构说明:1. 对应LTEClientWebSocket中的WebSocket连接和消息传输功能... +/// + +// 优化后:简洁的XML文档注释 +/// +/// WebSocket消息管理器 - 处理WebSocket连接和消息传输 +/// + +// 优化前:详细的字段注释 +/// +/// WebSocket实例 - 对应LTEClientWebSocket._webSocket... +/// + +// 优化后:简洁的行内注释 +// WebSocket连接实例 +``` + +**提交信息:** 大幅优化注释,提高代码可读性 + +## 2024-12-19 +### 优化PublicMethods.cs注释,完成WebSocket管理器注释简化 + +**修改内容:** +1. **PublicMethods.cs注释大幅简化** + - 简化了所有方法的XML文档注释,从冗长的重构说明简化为一行描述 + - 移除了所有详细的参数说明和返回值描述 + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/WebSocketMgr/PublicMethods.cs` - 大幅简化所有方法注释 + +**主要改进:** +1. **方法注释简化** + - 所有方法的XML文档注释简化为一行描述 + - 移除了冗长的功能说明、对应关系、重构改进等详细描述 + - 移除了所有参数和返回值的详细说明 + +2. **行内注释优化** + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保留了核心的功能描述 + - 提高了代码的简洁性 + +3. **可读性提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +**示例对比:** +```csharp +// 优化前:冗长的方法注释 +/// +/// 连接到WebSocket服务器 - 对应LTEClientWebSocket.Start()方法 +/// 功能说明:1. 建立WebSocket连接,对应原始Start()方法的核心逻辑... +/// +/// WebSocket URL,对应LTEClientWebSocket._config.Address + +// 优化后:简洁的方法注释 +/// +/// 连接到WebSocket服务器 +/// + +// 优化前:详细的行内注释 +// 构建WebSocket URL - 对应原始实现中的URL构建逻辑 + +// 优化后:简洁的行内注释 +// 构建WebSocket URL +``` + +**完成状态:** +- ✅ Constructor.cs - 注释完全简化 +- ✅ WebSocketMessageManager.cs - 字段和事件注释简化 +- ✅ PublicMethods.cs - 方法注释简化 +- ✅ 所有WebSocket管理器相关文件的注释优化完成 + +**提交信息:** 优化PublicMethods.cs注释,完成WebSocket管理器注释简化 + +## 2024-12-19 +### 优化PrivateMethods.cs注释,完成所有WebSocket管理器文件注释简化 + +**修改内容:** +1. **PrivateMethods.cs注释大幅简化** + - 简化了所有私有方法的XML文档注释,从冗长的重构说明简化为一行描述 + - 移除了所有详细的参数说明和功能描述 + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/WebSocketMgr/PrivateMethods.cs` - 大幅简化所有私有方法注释 + +**主要改进:** +1. **私有方法注释简化** + - 所有私有方法的XML文档注释简化为一行描述 + - 移除了冗长的功能说明、对应关系、重构改进等详细描述 + - 移除了所有参数和返回值的详细说明 + +2. **行内注释优化** + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保留了核心的功能描述 + - 提高了代码的简洁性 + +3. **可读性提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +**示例对比:** +```csharp +// 优化前:冗长的私有方法注释 +/// +/// WebSocket连接打开事件处理器 - 对应LTEClientWebSocket.OnSocketOpened +/// 功能说明:1. 处理WebSocket连接成功建立事件... +/// +/// 事件发送者 + +// 优化后:简洁的私有方法注释 +/// +/// WebSocket连接打开事件处理器 +/// + +// 优化前:详细的行内注释 +// 解析消息 - 对应原始实现中的消息解析逻辑 + +// 优化后:简洁的行内注释 +// 解析消息 +``` + +**完成状态:** +- ✅ Constructor.cs - 注释完全简化 +- ✅ WebSocketMessageManager.cs - 字段和事件注释简化 +- ✅ PublicMethods.cs - 方法注释简化 +- ✅ PrivateMethods.cs - 私有方法注释简化 +- ✅ 所有WebSocket管理器相关文件的注释优化完成 + +**提交信息:** 优化PrivateMethods.cs注释,完成所有WebSocket管理器文件注释简化 + +## 2024-12-19 +### 优化Dispose.cs注释,完成WebSocket管理器所有文件注释简化 + +**修改内容:** +1. **Dispose.cs注释大幅简化** + - 简化了所有方法的XML文档注释,从冗长的重构说明简化为一行描述 + - 移除了所有详细的参数说明和功能描述 + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/WebSocketMgr/Dispose.cs` - 大幅简化所有方法注释 + +**主要改进:** +1. **Dispose方法注释简化** + - 所有方法的XML文档注释简化为一行描述 + - 移除了冗长的功能说明、对应关系、重构改进等详细描述 + - 移除了所有参数和返回值的详细说明 + +2. **行内注释优化** + - 简化了所有行内注释,去除冗余的对应关系说明 + - 保留了核心的功能描述 + - 提高了代码的简洁性 + +3. **可读性提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +**示例对比:** +```csharp +// 优化前:冗长的Dispose方法注释 +/// +/// 释放资源 - 对应LTEClientWebSocket.Dispose()方法 +/// 功能说明:1. 释放WebSocket连接和相关资源... +/// + +// 优化后:简洁的Dispose方法注释 +/// +/// 释放资源 +/// + +// 优化前:详细的行内注释 +// 停止消息发送定时器 - 对应原始实现中的定时器释放 + +// 优化后:简洁的行内注释 +// 停止消息发送定时器 +``` + +**完成状态:** +- ✅ Constructor.cs - 注释完全简化 +- ✅ WebSocketMessageManager.cs - 字段和事件注释简化 +- ✅ PublicMethods.cs - 公共方法注释简化 +- ✅ PrivateMethods.cs - 私有方法注释简化 +- ✅ Dispose.cs - Dispose方法注释简化 +- ✅ 所有WebSocket管理器相关文件的注释优化完成 + +**提交信息:** 优化Dispose.cs注释,完成WebSocket管理器所有文件注释简化 + +## 2024-12-19 +### 优化WebSocketMessageManager.cs属性注释,完成所有注释简化 + +**修改内容:** +1. **WebSocketMessageManager.cs属性注释简化** + - 简化了所有属性的XML文档注释,从冗长的重构说明简化为一行描述 + - 移除了所有详细的对应关系说明和功能描述 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 简化所有属性注释 + +**主要改进:** +1. **属性注释简化** + - 所有属性的XML文档注释简化为一行描述 + - 移除了冗长的对应关系、功能保持、重构改进等详细描述 + - 保持了简洁明了的功能说明 + +2. **可读性提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +**示例对比:** +```csharp +// 优化前:冗长的属性注释 +/// +/// 是否已连接 - 对应LTEClientWebSocket.IsConnected +/// 检查WebSocket连接状态 +/// 对应关系:- 属性类型:bool,与原始实现完全一致... +/// + +// 优化后:简洁的属性注释 +/// +/// 是否已连接 +/// +``` + +**完成状态:** +- ✅ Constructor.cs - 注释完全简化 +- ✅ WebSocketMessageManager.cs - 字段、事件和属性注释简化 +- ✅ PublicMethods.cs - 公共方法注释简化 +- ✅ PrivateMethods.cs - 私有方法注释简化 +- ✅ Dispose.cs - Dispose方法注释简化 +- ✅ 所有WebSocket管理器相关文件的注释优化完成 + +**提交信息:** 优化WebSocketMessageManager.cs属性注释,完成所有注释简化 + +## 2024-12-19 +### 创建MessageIdManager.md文档文件 + +**修改内容:** +1. **创建MessageIdManager.md文档** + - 在Docs目录下创建了MessageIdManager.md文档文件 + - 包含完整的MessageIdManager.cs代码和详细注释 + - 添加了类概述、主要功能、设计改进和使用场景说明 + - 遵循与Constructor.md相同的文档格式 + +**涉及文件:** +- `Docs/MessageIdManager.md` - 新创建的MessageIdManager文档 + +**文档内容:** +1. **完整代码展示** + - 包含MessageIdManager.cs的完整代码 + - 保留所有详细的XML文档注释 + - 展示完整的类结构和实现 + +2. **文档说明部分** + - 类概述:说明MessageIdManager的作用和定位 + - 主要功能:列出核心功能点 + - 设计改进:说明相对于原始实现的改进 + - 使用场景:描述适用场景 + +3. **文档格式** + - 遵循与Constructor.md相同的格式 + - 使用Markdown语法 + - 包含代码高亮和结构化说明 + +**文档特点:** +- 提供完整的代码参考 +- 包含详细的功能说明 +- 便于开发人员理解和使用 +- 与现有文档体系保持一致 + +**提交信息:** 创建MessageIdManager.md文档文件 + +## 2024-12-19 +### 优化MessageIdManager.cs注释,提高可读性 + +**修改内容:** +1. **MessageIdManager.cs注释大幅简化** + - 简化了类级别的XML文档注释,从冗长的改进说明简化为一行描述 + - 移除了所有方法的详细参数说明和返回值描述 + - 简化了所有方法的XML文档注释,去除冗余信息 + - 保持了代码的功能性,但大幅提高了可读性 + +**涉及文件:** +- `Managers/MessageIdManager.cs` - 大幅简化所有注释 + +**主要改进:** +1. **类注释简化** + - 类级别的XML文档注释简化为一行描述 + - 移除了冗长的修复问题、设计原则等详细描述 + - 保持了简洁明了的功能说明 + +2. **方法注释简化** + - 所有方法的XML文档注释简化为一行描述 + - 移除了所有参数和返回值的详细说明 + - 去除了"改进版"等冗余标识 + +3. **可读性提升** + - 代码更加简洁明了 + - 减少了视觉干扰 + - 提高了代码扫描效率 + +**示例对比:** +```csharp +// 优化前:冗长的类注释 +/// +/// 消息ID管理器 - 改进版 +/// 修复的问题:1. 使用long类型防止ID溢出... +/// 设计原则:- 单一职责:专门负责消息ID管理... +/// + +// 优化后:简洁的类注释 +/// +/// 消息ID管理器 +/// + +// 优化前:详细的方法注释 +/// +/// 生成日志获取消息ID - 改进版 +/// +/// 消息对象 +/// 回调函数 +/// 消息ID + +// 优化后:简洁的方法注释 +/// +/// 生成日志获取消息ID +/// +``` + +**完成状态:** +- ✅ Constructor.cs - 注释完全简化 +- ✅ WebSocketMessageManager.cs - 字段、事件和属性注释简化 +- ✅ PublicMethods.cs - 公共方法注释简化 +- ✅ PrivateMethods.cs - 私有方法注释简化 +- ✅ Dispose.cs - Dispose方法注释简化 +- ✅ MessageIdManager.cs - 所有注释简化 +- ✅ 所有WebSocket管理器相关文件的注释优化完成 + +**提交信息:** 优化MessageIdManager.cs注释,提高可读性 + +## 2024-12-19 +### 重命名协议日志模型,规范命名体系 + +**修改内容:** +1. **ProtocolLogJson.cs → SourceProtocolLog.cs** + - `ProtocolLogJson` → `SourceProtocolLog` (源协议日志) + - `ProtocolLogDetailJson` → `SourceProtocolLogDetail` (源协议日志明细) + - 简化了构造函数注释,提高可读性 + +2. **ProtocolLog.cs → BuildProtocolLog.cs** + - `ProtocolLog` → `BuildProtocolLog` (构建协议日志) + - `PhyFields` → `PhyLayerFields` (物理层字段) + - `DataFields` → `DataLayerFields` (数据层字段) + - `MacFields` → `MacLayerFields` (MAC层字段) + - `ProtocolLogExtensions` → `BuildProtocolLogExtensions` (扩展方法类) + +3. **ProtocolLogDetail.cs → TransferProtocolLog.cs** + - `ProtocolLogDetail` → `TransferProtocolLog` (传输协议日志) + - 更新了类注释,明确表示用于传输给上层 + +**涉及文件:** +- `Models/ProtocolLogJson.cs` - 重命名类为SourceProtocolLog +- `Models/ProtocolLog.cs` - 重命名类为BuildProtocolLog +- `Models/ProtocolLogDetail.cs` - 重命名类为TransferProtocolLog + +**命名规范说明:** +1. **Source** - 源数据,原始JSON序列化模型 +2. **Build** - 构建层,构建完整的业务模型 +3. **Transfer** - 传输层,用于传输给上层的数据模型 + +**数据流向:** +``` +源数据 → SourceProtocolLog (解析原始JSON) + ↓ +构建业务模型 → BuildProtocolLog (构建业务字段) + ↓ +传输给上层 → TransferProtocolLog (传输给上层存储) +``` + +**主要改进:** +1. **职责清晰**:每个类的作用一目了然 +2. **层次分明**:从源数据到构建再到传输的清晰层次 +3. **易于维护**:代码结构更清晰,便于理解和维护 +4. **符合架构**:体现了分层架构的设计思想 + +**提交信息:** 重命名协议日志模型,规范命名体系 + +## 2024-12-19 +### 完成文件名重命名,与类名保持一致 + +**修改内容:** +1. **文件重命名完成** + - `ProtocolLogJson.cs` → `SourceProtocolLog.cs` + - `ProtocolLog.cs` → `BuildProtocolLog.cs` + - `ProtocolLogDetail.cs` → `TransferProtocolLog.cs` + +2. **文件名与类名完全一致** + - `SourceProtocolLog.cs` 包含 `SourceProtocolLog` 和 `SourceProtocolLogDetail` 类 + - `BuildProtocolLog.cs` 包含 `BuildProtocolLog`、`PhyLayerFields`、`DataLayerFields`、`MacLayerFields`、`LinkIds`、`BuildProtocolLogExtensions` 类 + - `TransferProtocolLog.cs` 包含 `TransferProtocolLog` 类 + +**涉及文件:** +- ✅ `Models/SourceProtocolLog.cs` - 新文件名,包含源协议日志模型 +- ✅ `Models/BuildProtocolLog.cs` - 新文件名,包含构建协议日志模型 +- ✅ `Models/TransferProtocolLog.cs` - 新文件名,包含传输协议日志模型 +- ❌ `Models/ProtocolLogJson.cs` - 已删除 +- ❌ `Models/ProtocolLog.cs` - 已删除 +- ❌ `Models/ProtocolLogDetail.cs` - 已删除 + +**命名规范完成状态:** +1. **Source** - 源数据层 ✅ + - 文件名:`SourceProtocolLog.cs` + - 类名:`SourceProtocolLog`、`SourceProtocolLogDetail` + +2. **Build** - 构建层 ✅ + - 文件名:`BuildProtocolLog.cs` + - 类名:`BuildProtocolLog`、`PhyLayerFields`、`DataLayerFields`、`MacLayerFields`、`LinkIds`、`BuildProtocolLogExtensions` + +3. **Transfer** - 传输层 ✅ + - 文件名:`TransferProtocolLog.cs` + - 类名:`TransferProtocolLog` + +**主要改进:** +1. **命名一致性**:文件名与类名完全一致 +2. **结构清晰**:每个文件都有明确的职责和用途 +3. **易于维护**:文件结构更加规范,便于查找和维护 +4. **符合规范**:遵循C#命名约定和最佳实践 + +**提交信息:** 完成文件名重命名,与类名保持一致 + +## 2024-12-19 +### 优化ILogger使用,采用泛型版本 + +**修改内容:** +1. **MessageIdManager.cs ILogger优化** + - `ILogger` → `ILogger` + - 字段声明和构造函数参数都更新为泛型版本 + +2. **WebSocketMessageManager.cs ILogger优化** + - `ILogger` → `ILogger` + - 字段声明和构造函数参数都更新为泛型版本 + +**涉及文件:** +- `Managers/MessageIdManager.cs` - 更新ILogger为泛型版本 +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 更新ILogger为泛型版本 +- `Managers/WebSocketMgr/Constructor.cs` - 更新构造函数参数 + +**ILogger vs ILogger 区别说明:** + +### ILogger (非泛型版本) +```csharp +private readonly ILogger _logger; +``` +- 日志输出时不显示具体类名 +- 更通用,性能稍好 +- 不符合最佳实践 + +### ILogger (泛型版本) ✅ 推荐 +```csharp +private readonly ILogger _logger; +``` +- 日志输出时自动包含类名信息 +- 符合 Microsoft.Extensions.Logging 最佳实践 +- 便于日志过滤和分类 +- 更好的调试体验 + +**主要改进:** +1. **日志可读性**:日志输出时会显示具体的类名 +2. **调试便利**:更容易识别日志来源 +3. **最佳实践**:符合 Microsoft.Extensions.Logging 推荐用法 +4. **日志过滤**:支持按类名进行日志过滤 + +**示例对比:** +``` +// 修改前:ILogger +[2024-12-19 10:30:15] 创建消息ID管理器,初始LogGet ID: -1 + +// 修改后:ILogger +[2024-12-19 10:30:15] [MessageIdManager] 创建消息ID管理器,初始LogGet ID: -1 +``` + +**提交信息:** 优化ILogger使用,采用泛型版本 + +## 2024-12-19 +### 修复ILogger类型不匹配问题 + +**修改内容:** +1. **MessageIdManager.cs ILogger类型调整** + - 将 `ILogger` 改回 `ILogger` + - 原因:MessageIdManager 作为独立组件,应该接受通用的 ILogger + - 避免与其他组件的 ILogger 类型冲突 + +2. **WebSocketMessageManager.cs 保持泛型版本** + - 继续使用 `ILogger` + - 作为主要组件,使用泛型版本更符合最佳实践 + +**涉及文件:** +- `Managers/MessageIdManager.cs` - 改回使用非泛型 ILogger +- `Managers/WebSocketMgr/Constructor.cs` - 简化构造函数代码 + +**设计原则说明:** + +### 主要组件使用 ILogger +```csharp +// WebSocketMessageManager - 主要组件 +private readonly ILogger _logger; +``` +- 主要业务组件使用泛型版本 +- 日志输出显示具体类名 +- 便于调试和日志过滤 + +### 工具组件使用 ILogger +```csharp +// MessageIdManager - 工具组件 +private readonly ILogger _logger; +``` +- 工具类、辅助组件使用非泛型版本 +- 更灵活,避免类型冲突 +- 适合被多个组件复用的场景 + +**主要改进:** +1. **解决编译错误**:修复了类型不匹配的编译错误 +2. **设计合理性**:根据组件职责选择合适的 ILogger 类型 +3. **代码简洁性**:简化了构造函数中的复杂类型转换逻辑 +4. **维护性提升**:避免了不必要的类型转换和复杂性 + +**提交信息:** 修复ILogger类型不匹配问题 + +## 2024-12-19 +### 实现方案2:使用ILoggerFactory创建正确的Logger类型 + +**修改内容:** +1. **WebSocketMessageManager构造函数优化** + - 添加 `ILoggerFactory` 参数 + - 使用 `loggerFactory.CreateLogger()` 创建正确的logger类型 + - 添加 `_loggerFactory` 字段用于存储ILoggerFactory实例 + +2. **MessageIdManager恢复泛型版本** + - 恢复使用 `ILogger` + - 保持最佳实践,日志输出显示具体类名 + +**涉及文件:** +- `Managers/WebSocketMgr/Constructor.cs` - 添加ILoggerFactory参数 +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 添加_loggerFactory字段 +- `Managers/MessageIdManager.cs` - 恢复使用ILogger + +**方案2优势:** + +### 使用ILoggerFactory的最佳实践 +```csharp +// 构造函数接收ILoggerFactory +public WebSocketMessageManager(string clientName, ILogger logger, ILoggerFactory loggerFactory) + +// 为子组件创建正确的Logger类型 +var messageIdLogger = loggerFactory.CreateLogger(); +_messageIdManager = new MessageIdManager(clientName, messageIdLogger); +``` + +**主要优势:** +1. **类型安全**:每个组件都有正确的泛型Logger类型 +2. **最佳实践**:符合Microsoft.Extensions.Logging的设计模式 +3. **日志分类**:每个组件的日志都有明确的类名标识 +4. **依赖注入友好**:便于在DI容器中配置和管理 +5. **扩展性好**:可以轻松为其他子组件创建Logger + +**日志输出示例:** +``` +[2024-12-19 10:30:15] [WebSocketMessageManager] 创建WebSocket消息管理器 +[2024-12-19 10:30:15] [MessageIdManager] 创建消息ID管理器,初始LogGet ID: -1 +``` + +**主要改进:** +1. **解决类型冲突**:使用ILoggerFactory正确创建Logger实例 +2. **保持最佳实践**:所有组件都使用泛型Logger +3. **日志可读性**:每个组件的日志都有明确的类名标识 +4. **架构清晰**:符合依赖注入和日志管理的最佳实践 + +**提交信息:** 实现方案2:使用ILoggerFactory创建正确的Logger类型 + +## 2024-12-19 +### 优化构造函数参数,只使用ILoggerFactory + +**修改内容:** +1. **WebSocketMessageManager构造函数简化** + - 移除 `ILogger logger` 参数 + - 只保留 `ILoggerFactory loggerFactory` 参数 + - 使用 `loggerFactory.CreateLogger()` 创建自己的Logger + - 使用 `loggerFactory.CreateLogger()` 创建子组件的Logger + +**涉及文件:** +- `Managers/WebSocketMgr/Constructor.cs` - 简化构造函数参数 + +**优化后的设计:** + +### 简化后的构造函数 +```csharp +// 优化前:需要传递两个参数 +public WebSocketMessageManager(string clientName, ILogger logger, ILoggerFactory loggerFactory) + +// 优化后:只需要传递ILoggerFactory +public WebSocketMessageManager(string clientName, ILoggerFactory loggerFactory) +``` + +### 内部Logger创建 +```csharp +// 创建自己的Logger +_logger = loggerFactory.CreateLogger(); + +// 创建子组件的Logger +var messageIdLogger = loggerFactory.CreateLogger(); +_messageIdManager = new MessageIdManager(clientName, messageIdLogger); +``` + +**主要优势:** +1. **参数简化**:只需要传递一个ILoggerFactory参数 +2. **统一管理**:所有Logger都通过ILoggerFactory创建 +3. **依赖减少**:减少了构造函数的参数数量 +4. **更清晰**:明确了Logger的创建方式 +5. **符合最佳实践**:遵循依赖注入的设计模式 + +**使用示例:** +```csharp +// 调用方只需要传递ILoggerFactory +var webSocketManager = new WebSocketMessageManager("Client1", loggerFactory); +``` + +**主要改进:** +1. **简化接口**:构造函数参数更简洁 +2. **统一模式**:所有Logger都通过ILoggerFactory创建 +3. **减少冗余**:避免了重复传递Logger参数 +4. **更好的封装**:组件内部管理自己的Logger创建 + +**提交信息:** 优化构造函数参数,只使用ILoggerFactory + +## 2024-12-19 +### 移除不必要的_loggerFactory字段 + +**修改内容:** +1. **移除_loggerFactory字段** + - 删除 `private readonly ILoggerFactory _loggerFactory;` 字段声明 + - 原因:ILoggerFactory只在构造函数中使用一次,不需要作为字段存储 + +2. **优化构造函数中的使用** + - 将 `loggerFactory` 参数赋值给局部变量 `factory` + - 在构造函数中使用局部变量,避免存储不必要的字段 + +**涉及文件:** +- `Managers/WebSocketMgr/WebSocketMessageManager.cs` - 移除_loggerFactory字段 +- `Managers/WebSocketMgr/Constructor.cs` - 优化构造函数中的使用 + +**优化后的设计:** + +### 字段声明优化 +```csharp +// 优化前:存储不必要的字段 +private readonly ILoggerFactory _loggerFactory; + +// 优化后:只保留必要的字段 +// 移除了_loggerFactory字段 +``` + +### 构造函数优化 +```csharp +public WebSocketMessageManager(string clientName, ILoggerFactory loggerFactory) +{ + // 参数验证 + _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); + var factory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + + // 创建自己的Logger + _logger = factory.CreateLogger(); + + // 初始化消息ID管理器 + var messageIdLogger = factory.CreateLogger(); + _messageIdManager = new MessageIdManager(clientName, messageIdLogger); + + // 创建消息队列 + _messageFifo = new BlockingCollection(); + + _logger.LogInformation($"[{_clientName}] 创建WebSocket消息管理器"); +} +``` + +**主要优势:** +1. **内存优化**:减少不必要的字段存储 +2. **代码简洁**:移除冗余的字段声明 +3. **职责清晰**:ILoggerFactory只在初始化时使用 +4. **性能提升**:减少对象的内存占用 +5. **设计合理**:遵循最小化原则 + +**设计原则:** +- **最小化存储**:只存储真正需要的字段 +- **局部变量优先**:如果变量只在方法内使用,优先使用局部变量 +- **避免冗余**:不存储只在初始化时使用的对象 + +**提交信息:** 移除不必要的_loggerFactory字段 + +## 2024-12-19 +### 优化UeIdentifierManager的ILogger类型 + +**修改内容:** +1. **UeIdentifierManager.cs ILogger优化** + - `ILogger` → `ILogger` + - 字段声明和构造函数参数都更新为泛型版本 + +**涉及文件:** +- `Context/UeStateManager/UeIdentifierManager.cs` - 更新ILogger为泛型版本 + +**ILogger vs ILogger 区别说明:** + +### ILogger (非泛型版本) +```csharp +private readonly ILogger _logger; +``` +- 日志输出时不显示具体类名 +- 更通用,性能稍好 +- 不符合最佳实践 + +### ILogger (泛型版本) ✅ 推荐 +```csharp +private readonly ILogger _logger; +``` +- 日志输出时自动包含类名信息 +- 符合 Microsoft.Extensions.Logging 最佳实践 +- 便于日志过滤和分类 +- 更好的调试体验 + +**主要改进:** +1. **日志可读性**:日志输出时会显示具体的类名 +2. **调试便利**:更容易识别日志来源 +3. **最佳实践**:符合 Microsoft.Extensions.Logging 推荐用法 +4. **日志过滤**:支持按类名进行日志过滤 + +**示例对比:** +``` +// 修改前:ILogger +[2024-12-19 10:30:15] 协议类型字符串缓存初始化完成,共 10 个类型 + +// 修改后:ILogger +[2024-12-19 10:30:15] [UeIdentifierManager] 协议类型字符串缓存初始化完成,共 10 个类型 +``` + +**提交信息:** 优化UeIdentifierManager的ILogger类型 + +## 2024-12-19 +### 将UeIdentifierManager的ILogger参数改为必填 + +**修改内容:** +1. **构造函数参数修改** + - 移除 `= null` 默认值,使 `ILogger logger` 成为必填参数 + - 添加参数验证:`_logger = logger ?? throw new ArgumentNullException(nameof(logger));` + +2. **日志调用优化** + - 将所有 `_logger?.` 调用改为 `_logger.` 直接调用 + - 因为现在 `_logger` 不可能为 null,所以不需要空值检查 + +**涉及文件:** +- `Context/UeStateManager/UeIdentifierManager.cs` - 修改构造函数和日志调用 + +**主要改进:** +1. **参数验证**:确保 logger 参数不能为 null,提高代码健壮性 +2. **性能优化**:移除不必要的空值检查,提高日志调用性能 +3. **代码简洁**:简化日志调用代码,提高可读性 +4. **设计一致性**:与其他组件的设计保持一致 + +**修改对比:** +```csharp +// 修改前:可选参数,需要空值检查 +public UeIdentifierManager(ILogger logger = null) +{ + _logger = logger; + // ... +} +_logger?.LogDebug("初始化完成"); + +// 修改后:必填参数,直接调用 +public UeIdentifierManager(ILogger logger) +{ + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + // ... +} +_logger.LogDebug("初始化完成"); +``` + +**设计原则:** +- **依赖注入**:强制要求提供日志记录器,确保组件功能完整 +- **参数验证**:在构造函数中验证关键参数,及早发现问题 +- **性能优化**:移除不必要的空值检查,提高执行效率 +- **代码简洁**:简化日志调用,提高代码可读性 + +**提交信息:** 将UeIdentifierManager的ILogger参数改为必填 + +## 2024-12-19 +### 为ProtocolClientContext添加ILoggerFactory支持 + +**修改内容:** +1. **添加ILoggerFactory依赖** + - 添加 `Microsoft.Extensions.Logging` 命名空间引用 + - 添加 `private readonly ILoggerFactory _loggerFactory;` 字段 + - 添加构造函数接收 `ILoggerFactory` 参数 + +2. **UeIdentifierManager初始化优化** + - 移除 `UeIdentifier` 属性的默认初始化 `= new()` + - 在构造函数中使用 `ILoggerFactory` 创建正确的 Logger + - 使用 `_loggerFactory.CreateLogger()` 创建类型安全的 Logger + +**涉及文件:** +- `Context/ProtocolClientContext.cs` - 添加ILoggerFactory支持和UeIdentifierManager正确初始化 + +**主要改进:** +1. **依赖注入支持**:支持通过依赖注入容器管理Logger +2. **类型安全**:为UeIdentifierManager创建正确的泛型Logger类型 +3. **统一管理**:通过ILoggerFactory统一管理所有组件的Logger +4. **最佳实践**:遵循Microsoft.Extensions.Logging的设计模式 + +**修改对比:** +```csharp +// 修改前:直接初始化,没有Logger支持 +public UeIdentifierManager UeIdentifier { get; set; } = new(); + +// 修改后:通过ILoggerFactory正确初始化 +public ProtocolClientContext(ILoggerFactory loggerFactory) +{ + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + + var ueIdentifierLogger = _loggerFactory.CreateLogger(); + UeIdentifier = new UeIdentifierManager(ueIdentifierLogger); +} +``` + +**使用示例:** +```csharp +// 在依赖注入容器中注册 +services.AddSingleton(loggerFactory); + +// 创建ProtocolClientContext +var context = new ProtocolClientContext(loggerFactory); + +// UeIdentifierManager现在有正确的Logger支持 +context.UeIdentifier.GetCacheStats(); // 日志会显示 [UeIdentifierManager] 前缀 +``` + +**设计优势:** +- **依赖注入友好**:便于在DI容器中配置和管理 +- **Logger类型安全**:每个组件都有正确的泛型Logger类型 +- **统一日志管理**:通过ILoggerFactory统一管理所有Logger +- **扩展性好**:可以轻松为其他组件添加Logger支持 + +**提交信息:** 为ProtocolClientContext添加ILoggerFactory支持 + +## 2024-12-19 +### 为ProtocolLogContext添加ILogger支持 + +**修改内容:** +1. **添加ILogger依赖** + - 添加 `private readonly ILogger _logger;` 字段 + - 添加构造函数接收 `ILogger` 参数 + - 添加参数验证:`_logger = logger ?? throw new ArgumentNullException(nameof(logger));` + +2. **UpdateLogTimestampAndAssignId方法优化** + - 移除 `ILogger logger` 参数,使用内部的 `_logger` 字段 + - 将所有 `logger?.` 调用改为 `_logger.` 直接调用 + - 简化方法签名,减少参数传递 + +3. **ProtocolClientContext更新** + - 移除 `LogContext` 属性的默认初始化 `= new()` + - 在构造函数中使用 `ILoggerFactory` 创建正确的 Logger + - 使用 `_loggerFactory.CreateLogger()` 创建类型安全的 Logger + +**涉及文件:** +- `Context/ProtocolLogContext.cs` - 添加ILogger支持和优化方法 +- `Context/ProtocolClientContext.cs` - 更新LogContext初始化 + +**主要改进:** +1. **依赖注入支持**:支持通过依赖注入容器管理Logger +2. **类型安全**:为ProtocolLogContext创建正确的泛型Logger类型 +3. **方法简化**:移除不必要的logger参数传递 +4. **统一管理**:通过ILoggerFactory统一管理所有组件的Logger +5. **最佳实践**:遵循Microsoft.Extensions.Logging的设计模式 + +**修改对比:** +```csharp +// 修改前:需要传递logger参数 +public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log, UeIdentifierManager parser, ILogger logger) +{ + logger?.LogInformation("日志信息"); +} + +// 修改后:使用内部logger字段 +public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log, UeIdentifierManager parser) +{ + _logger.LogInformation("日志信息"); +} +``` + +**使用示例:** +```csharp +// 创建ProtocolClientContext +var context = new ProtocolClientContext(loggerFactory); + +// ProtocolLogContext现在有正确的Logger支持 +context.LogContext.UpdateLogTimestampAndAssignId(ref log, context.UeIdentifier); +// 日志会显示 [ProtocolLogContext] 前缀 +``` + +**设计优势:** +- **依赖注入友好**:便于在DI容器中配置和管理 +- **Logger类型安全**:每个组件都有正确的泛型Logger类型 +- **统一日志管理**:通过ILoggerFactory统一管理所有Logger +- **方法简化**:减少参数传递,提高代码简洁性 +- **扩展性好**:可以轻松为其他组件添加Logger支持 + +**提交信息:** 为ProtocolLogContext添加ILogger支持 + +## 2024-12-19 +### 为ProtocolLogContext添加ProtocolClientContext依赖 + +**修改内容:** +1. **添加ProtocolClientContext依赖** + - 添加 `private readonly ProtocolClientContext _context;` 字段 + - 修改构造函数接收 `ProtocolClientContext context` 参数 + - 添加参数验证:`_context = context ?? throw new ArgumentNullException(nameof(context));` + +2. **UpdateLogTimestampAndAssignId方法进一步优化** + - 移除 `UeIdentifierManager parser` 参数 + - 使用 `_context.UeIdentifier` 访问兄弟组件 + - 进一步简化方法签名,完全移除外部参数依赖 + +3. **ProtocolClientContext初始化顺序调整** + - 先创建 `UeIdentifierManager` + - 然后创建 `ProtocolLogContext`,传入 `this` 引用 + - 解决循环依赖问题 + +**涉及文件:** +- `Context/ProtocolLogContext.cs` - 添加ProtocolClientContext依赖和优化方法 +- `Context/ProtocolClientContext.cs` - 调整初始化顺序 + +**主要改进:** +1. **组件间依赖**:ProtocolLogContext可以直接访问兄弟组件 +2. **方法简化**:完全移除外部参数依赖,使用内部上下文 +3. **设计一致性**:组件间通过上下文进行通信 +4. **代码简洁**:减少参数传递,提高代码可读性 +5. **架构清晰**:明确了组件间的依赖关系 + +**修改对比:** +```csharp +// 修改前:需要传递parser参数 +public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log, UeIdentifierManager parser) +{ + var timestamp = log.Timestamp + parser.TimestampOffset; + // ... +} + +// 修改后:使用内部上下文访问兄弟组件 +public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log) +{ + var parser = _context.UeIdentifier; + var timestamp = log.Timestamp + parser.TimestampOffset; + // ... +} +``` + +**使用示例:** +```csharp +// 创建ProtocolClientContext +var context = new ProtocolClientContext(loggerFactory); + +// ProtocolLogContext现在可以直接访问兄弟组件 +context.LogContext.UpdateLogTimestampAndAssignId(ref log); +// 内部自动使用 context.UeIdentifier +``` + +**设计优势:** +- **组件间通信**:通过上下文直接访问兄弟组件 +- **方法简化**:移除不必要的参数传递 +- **依赖明确**:组件间的依赖关系更加清晰 +- **代码简洁**:减少方法参数,提高可读性 +- **架构合理**:符合组件化设计的最佳实践 + +**提交信息:** 为ProtocolLogContext添加ProtocolClientContext依赖 + +## 2024-12-19 +### 优化ProtocolLogContext的ResetLogs方法 + +**修改内容:** +1. **ResetLogs方法优化** + - 移除 `ProtocolFeatureFlags flags` 参数 + - 使用 `_context.FeatureFlags` 访问兄弟组件 + - 简化方法签名,移除外部参数依赖 + +**涉及文件:** +- `Context/ProtocolLogContext.cs` - 优化ResetLogs方法 + +**主要改进:** +1. **方法简化**:移除不必要的参数传递 +2. **组件间通信**:通过上下文直接访问兄弟组件 +3. **代码一致性**:与UpdateLogTimestampAndAssignId方法保持一致的风格 +4. **依赖明确**:明确了对FeatureFlags组件的依赖关系 + +**修改对比:** +```csharp +// 修改前:需要传递flags参数 +public void ResetLogs(ProtocolFeatureFlags flags) +{ + if (LogCount > 0) + { + Logs.Clear(); + flags.HasCell = false; + flags.HasPhy = false; + // ... + } +} + +// 修改后:使用内部上下文访问兄弟组件 +public void ResetLogs() +{ + if (LogCount > 0) + { + Logs.Clear(); + _context.FeatureFlags.HasCell = false; + _context.FeatureFlags.HasPhy = false; + // ... + } +} +``` + +**使用示例:** +```csharp +// 创建ProtocolClientContext +var context = new ProtocolClientContext(loggerFactory); + +// ProtocolLogContext现在可以直接访问兄弟组件 +context.LogContext.ResetLogs(); +// 内部自动使用 context.FeatureFlags +``` + +**设计优势:** +- **方法简化**:移除不必要的参数传递 +- **组件间通信**:通过上下文直接访问兄弟组件 +- **代码一致性**:所有方法都使用相同的访问模式 +- **依赖明确**:组件间的依赖关系更加清晰 +- **架构合理**:符合组件化设计的最佳实践 + +**提交信息:** 优化ProtocolLogContext的ResetLogs方法 \ No newline at end of file