From e7e96b7f08a00f744cba5d83b7a2bb5b568c7b9b Mon Sep 17 00:00:00 2001 From: hyh Date: Mon, 23 Jun 2025 15:25:28 +0800 Subject: [PATCH] 1 --- LTEMvcApp/Models/LTEClient.cs | 290 +++++++-------- LTEMvcApp/Models/LogsManager.cs | 13 + LTEMvcApp/README_DirConvert_Implementation.md | 166 +++++++++ LTEMvcApp/README_LogModelGuess_Comparison.md | 343 ++++++++++++++++++ LTEMvcApp/README_LogModelGuess_Fix.md | 150 ++++++++ 5 files changed, 818 insertions(+), 144 deletions(-) create mode 100644 LTEMvcApp/README_DirConvert_Implementation.md create mode 100644 LTEMvcApp/README_LogModelGuess_Comparison.md create mode 100644 LTEMvcApp/README_LogModelGuess_Fix.md diff --git a/LTEMvcApp/Models/LTEClient.cs b/LTEMvcApp/Models/LTEClient.cs index a714eb9..14e359b 100644 --- a/LTEMvcApp/Models/LTEClient.cs +++ b/LTEMvcApp/Models/LTEClient.cs @@ -424,7 +424,7 @@ public class LTEClient // 初始化模型猜测字典 _modelGuess = new Dictionary(); - var layerConfig = GetLayerConfig(); + var layerConfig = _lteLogs.GetLayerConfig(); foreach (var layer in layerConfig.Keys) { _modelGuess[layer] = false; @@ -437,68 +437,6 @@ public class LTEClient /// private Dictionary? _modelGuess; - /// - /// 获取层配置 - /// - /// 层配置字典 - private Dictionary GetLayerConfig() - { - // 这里应该从 LogsManager 获取层配置 - // 暂时返回一个基本的层配置 - return new Dictionary - { - { "PHY", new { Color = "#606070" } }, - { "MAC", new { Color = "#10A0FF" } }, - { "RLC", new { Color = "#FFFF80" } }, - { "PDCP", new { Color = "#B0D0B0" } }, - { "RRC", new { Color = "#00FF60" } }, - { "NAS", new { Color = "#90FFC0" } }, - { "GTPU", new { Color = "#FF80FF" } }, - { "GTPC", new { Color = "#FFC0FF" } }, - { "IP", new { Color = "#E0E0E0" } }, - { "S1AP", new { Color = "#80FF00" } }, - { "NGAP", new { Color = "#5DD122" } }, - { "X2AP", new { Color = "#FF8000" } }, - { "XnAP", new { Color = "#FFB020" } }, - { "M2AP", new { Color = "#7F675B" } }, - { "IMS", new { Color = "#C0FF80" } }, - { "CX", new { Color = "#F49542" } }, - { "RX", new { Color = "#D4A190" } }, - { "COM", new { Color = "#C000FF" } }, - { "SIP", new { Color = "#C080FF" } }, - { "MEDIA", new { Color = "#800040" } }, - { "RTP", new { Color = "#FF00C0" } }, - { "PROD", new { Color = "#80C0FF" } }, - { "S6", new { Color = "#F44B42" } }, - { "S13", new { Color = "#D6F953" } }, - { "SGsAP", new { Color = "#FF7DB8" } }, - { "SBcAP", new { Color = "#8FA00F" } }, - { "N8", new { Color = "#106020" } }, - { "N12", new { Color = "#602020" } }, - { "N13", new { Color = "#202060" } }, - { "N17", new { Color = "#D6F953" } }, - { "N50", new { Color = "#8FA00F" } }, - { "MMS", new { Color = "#B4D98F" } }, - { "HTTP2", new { Color = "#644824" } }, - { "LCSAP", new { Color = "#cfd50d" } }, - { "LPPa", new { Color = "#0dcfd5" } }, - { "NL1", new { Color = "#d040cf" } }, - { "NRPPa", new { Color = "#0dd5cf" } }, - { "IKEV2", new { Color = "#C0B732" } }, - { "SWU", new { Color = "#101080" } }, - { "NWU", new { Color = "#2080B8" } }, - { "IPSEC", new { Color = "#F04010" } }, - { "N3IWF", new { Color = "#C080C0" } }, - { "TRX", new { Color = "#42C0a0" } }, - { "MON", new { Color = "#C0C080" } }, - { "EVENT", new { Color = "#80C0FF" } }, - { "ALARM", new { Color = "#FF8040" } }, - { "LIC", new { Color = "#ff80c0" } }, - { "OTS", new { Color = "#8080FF" } }, - { "ERROR", new { Color = "#ff0000" } } - }; - } - /// /// 模型猜测 /// @@ -518,112 +456,79 @@ public class LTEClient } } - // 尝试根据层配置确定模型 + // 从 LogsManager 获取层配置 + var layerConfig = _lteLogs.GetLayerConfig(); var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; var availableModels = new List(modelList); - // 根据层配置过滤模型 + // 根据层配置过滤模型(与 JavaScript 版本保持一致) foreach (var layer in modelGuess.Keys) { if (!modelGuess[layer]) continue; - var layerConfig = GetLayerConfig(); if (layerConfig.TryGetValue(layer, out var config)) { - // 这里应该检查层的方向配置来过滤模型 - // 暂时简化处理 - var layerDir = GetLayerDirection(layer); - if (layerDir != null) + // 使用反射获取 dir 属性 + var configType = config.GetType(); + var dirProperty = configType.GetProperty("dir"); + if (dirProperty != null) { - // 移除不支持的模型 - availableModels.RemoveAll(model => !layerDir.ContainsKey(model)); + var dir = dirProperty.GetValue(config) as Dictionary; + if (dir != null) + { + // 移除不支持的模型(与 JavaScript 版本逻辑一致) + for (int j = 0; j < modelList.Count; j++) + { + var model = modelList[j]; + if (!dir.ContainsKey(model)) + { + var idx = availableModels.IndexOf(model); + if (idx >= 0) + availableModels.RemoveAt(idx); + } + } + } } } - // 如果只剩下一个模型,直接设置 - if (availableModels.Count <= 1) + // 如果只剩下一个模型,直接设置(与 JavaScript 版本一致) + if (availableModels.Count < 2) break; } - // 如果找到了合适的模型,设置它 - if (availableModels.Count > 0 && availableModels.Count < modelList.Count) + // 设置模型(与 JavaScript 版本一致) + if (availableModels.Count > 0) { - SetModel(availableModels[0]); + if (availableModels.Count == 1) + { + SetModel(availableModels[0]); + } + else + { + // 支持多个模型的情况,传递第一个模型 + SetModel(availableModels[0]); + } _modelGuess = null; // 清除猜测字典 } } /// - /// 获取层方向配置 - /// - /// 层名称 - /// 方向配置字典 - private Dictionary? GetLayerDirection(string layer) - { - // 这里应该从 LogsManager 获取层的方向配置 - // 暂时返回一个基本的配置 - return layer switch - { - "PHY" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, - "MAC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, - "RLC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, - "PDCP" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, - "RRC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, - "NAS" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } }, - "GTPU" => new Dictionary { { "ENB", 2 }, { "N3IWF", 2 }, { "MME", 1 }, { "MBMSGW", 1 }, { "UE", 1 }, { "RUE", 2 } }, - "GTPC" => new Dictionary { { "MME", 2 } }, - "IP" => new Dictionary { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } }, - "S1AP" => new Dictionary { { "ENB", 2 }, { "MME", 1 } }, - "NGAP" => new Dictionary { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } }, - "X2AP" => new Dictionary { { "ENB", 2 } }, - "XnAP" => new Dictionary { { "ENB", 2 } }, - "M2AP" => new Dictionary { { "ENB", 2 }, { "MBMSGW", 1 } }, - "IMS" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, - "CX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, - "RX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, - "COM" => new Dictionary { { "*", 2 } }, - "SIP" => new Dictionary { { "IMS", 1 } }, - "MEDIA" => new Dictionary { { "IMS", 1 } }, - "RTP" => new Dictionary { { "IMS", 1 } }, - "PROD" => new Dictionary { }, - "S6" => new Dictionary { { "MME", 2 } }, - "S13" => new Dictionary { { "MME", 2 } }, - "SGsAP" => new Dictionary { { "MME", 2 } }, - "SBcAP" => new Dictionary { { "MME", 2 } }, - "N8" => new Dictionary { { "MME", 2 } }, - "N12" => new Dictionary { { "MME", 2 } }, - "N13" => new Dictionary { { "MME", 2 } }, - "N17" => new Dictionary { { "MME", 2 } }, - "N50" => new Dictionary { { "MME", 2 } }, - "MMS" => new Dictionary { { "MME", 2 } }, - "HTTP2" => new Dictionary { { "MME", 2 } }, - "LCSAP" => new Dictionary { { "MME", 2 } }, - "LPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, - "NL1" => new Dictionary { { "MME", 2 } }, - "NRPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, - "IKEV2" => new Dictionary { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } }, - "SWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, - "NWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, - "IPSEC" => new Dictionary { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } }, - "N3IWF" => new Dictionary { { "UE", 2 }, { "N3IWF", 1 } }, - "TRX" => new Dictionary { { "UE", 2 }, { "ENB", 1 } }, - "MON" => new Dictionary { }, - "EVENT" => new Dictionary { }, - "ALARM" => new Dictionary { }, - "LIC" => new Dictionary { { "LICENSE", 1 } }, - "OTS" => new Dictionary { }, - "ERROR" => new Dictionary { }, - _ => null - }; - } - - /// - /// 方向转换 + /// 方向转换 - 对应JavaScript的_dirConvert方法 /// + /// 日志对象 + /// 转换后的方向值 public int DirConvert(LTELog log) { - // 实现方向转换逻辑 - return 0; + // 获取当前层的方向配置 + if (_layerDir.TryGetValue(log.Layer, out var layerDir)) + { + // 根据日志的原始方向获取转换后的方向 + if (layerDir.TryGetValue(log.Direction.ToString(), out var convertedDir)) + { + return convertedDir; + } + } + return 0; // 默认返回0(DIR_NONE) } /// @@ -1278,6 +1183,9 @@ public class LTEClient _layerDir.Clear(); } + // 初始化层方向配置 + InitializeLayerDirectionConfig(model); + // 通知日志管理器更新网格列 _lteLogs.GridColumnsUpdate(); return true; @@ -1292,6 +1200,100 @@ public class LTEClient _modelHint = hint; } + /// + /// 初始化层方向配置 - 对应JavaScript版本的层方向配置初始化 + /// + /// 模型名称 + private void InitializeLayerDirectionConfig(string? model) + { + // 获取层配置 + var layerConfig = _lteLogs.GetLayerConfig(); + + // 为每个层初始化默认方向配置 + foreach (var layer in layerConfig.Keys) + { + if (!_layerDir.ContainsKey(layer)) + { + _layerDir[layer] = new Dictionary + { + { "UL", LogsManager.DIR_UL }, + { "DL", LogsManager.DIR_DL }, + { "FROM", LogsManager.DIR_DL }, + { "TO", LogsManager.DIR_UL }, + { "-", LogsManager.DIR_NONE } + }; + } + } + + // 根据模型设置特殊的方向配置(与 JavaScript 版本保持一致) + switch (model?.ToUpper()) + { + case "MME": + // MME 模型特殊配置 + foreach (var layer in layerConfig.Keys) + { + if (layer != "IMS" && layer != "CX" && layer != "RX" && + layer != "S6" && layer != "S13" && layer != "SGsAP" && + layer != "SBcAP" && layer != "N8" && layer != "N12" && + layer != "N13" && layer != "N17" && layer != "N50" && + layer != "MMS" && layer != "HTTP2" && layer != "LCSAP" && + layer != "NL1" && layer != "GTPC") + { + if (_layerDir.ContainsKey(layer)) + { + _layerDir[layer]["FROM"] = LogsManager.DIR_UL; + _layerDir[layer]["TO"] = LogsManager.DIR_DL; + } + } + } + break; + + case "IMS": + // IMS 模型特殊配置 + foreach (var layer in layerConfig.Keys) + { + if (_layerDir.ContainsKey(layer)) + { + _layerDir[layer]["FROM"] = LogsManager.DIR_UL; + _layerDir[layer]["TO"] = LogsManager.DIR_DL; + } + } + break; + + case "N3IWF": + // N3IWF 模型特殊配置 + if (_layerDir.ContainsKey("IKEV2")) + { + _layerDir["IKEV2"]["FROM"] = LogsManager.DIR_UL; + _layerDir["IKEV2"]["TO"] = LogsManager.DIR_DL; + } + if (_layerDir.ContainsKey("IPSEC")) + { + _layerDir["IPSEC"]["FROM"] = LogsManager.DIR_UL; + _layerDir["IPSEC"]["TO"] = LogsManager.DIR_DL; + } + break; + + case "ENB": + // ENB 模型特殊配置 + if (_layerDir.ContainsKey("TRX")) + { + _layerDir["TRX"]["FROM"] = LogsManager.DIR_UL; + _layerDir["TRX"]["TO"] = LogsManager.DIR_DL; + } + break; + + case "UE": + // UE 模型特殊配置 + if (_layerDir.ContainsKey("GTPU")) + { + _layerDir["GTPU"]["FROM"] = LogsManager.DIR_UL; + _layerDir["GTPU"]["TO"] = LogsManager.DIR_DL; + } + break; + } + } + #endregion #region 模型配置 diff --git a/LTEMvcApp/Models/LogsManager.cs b/LTEMvcApp/Models/LogsManager.cs index 69ce53c..b771576 100644 --- a/LTEMvcApp/Models/LogsManager.cs +++ b/LTEMvcApp/Models/LogsManager.cs @@ -891,5 +891,18 @@ namespace LTEMvcApp.Models } #endregion + + #region 层配置方法 + + /// + /// 获取层配置 + /// + /// 层配置字典 + public Dictionary GetLayerConfig() + { + return layerConfig; + } + + #endregion } } \ No newline at end of file diff --git a/LTEMvcApp/README_DirConvert_Implementation.md b/LTEMvcApp/README_DirConvert_Implementation.md new file mode 100644 index 0000000..934f099 --- /dev/null +++ b/LTEMvcApp/README_DirConvert_Implementation.md @@ -0,0 +1,166 @@ +# DirConvert 方法实现总结 + +## 概述 + +`DirConvert` 方法对应 JavaScript 版本中的 `_dirConvert` 方法,用于将日志的原始方向值转换为客户端模型特定的方向值。 + +## JavaScript 版本实现 + +```javascript +_dirConvert: function (log) { + var layerDir = this._layerDir[log.layer]; + if (layerDir) + return layerDir[log.dir]; + return 0; +} +``` + +## C# 版本实现 + +```csharp +/// +/// 方向转换 - 对应JavaScript的_dirConvert方法 +/// +/// 日志对象 +/// 转换后的方向值 +public int DirConvert(LTELog log) +{ + // 获取当前层的方向配置 + if (_layerDir.TryGetValue(log.Layer, out var layerDir)) + { + // 根据日志的原始方向获取转换后的方向 + if (layerDir.TryGetValue(log.Direction.ToString(), out var convertedDir)) + { + return convertedDir; + } + } + return 0; // 默认返回0(DIR_NONE) +} +``` + +## 层方向配置初始化 + +### JavaScript 版本初始化逻辑 + +```javascript +// 为每个模型初始化层方向配置 +for (var c in modelConfig) { + var model = modelConfig[c]; + model.layerDir = {}; + for (var i in layerConfig) { + model.layerDir[i] = { + 'UL': DIR_UL, + 'DL': DIR_DL, + 'FROM': DIR_DL, + 'TO': DIR_UL, + '-': DIR_NONE, + }; + } +} + +// 特殊模型配置 +for (var i in layerConfig) { + if (i !== 'IMS' && i !== 'CX' && i !== 'RX' && ...) { + modelConfig.MME.layerDir[i].FROM = DIR_UL; + modelConfig.MME.layerDir[i].TO = DIR_DL; + } + modelConfig.IMS.layerDir[i].FROM = DIR_UL; + modelConfig.IMS.layerDir[i].TO = DIR_DL; +} +``` + +### C# 版本初始化逻辑 + +```csharp +/// +/// 初始化层方向配置 - 对应JavaScript版本的层方向配置初始化 +/// +/// 模型名称 +private void InitializeLayerDirectionConfig(string? model) +{ + // 获取层配置 + var layerConfig = _lteLogs.GetLayerConfig(); + + // 为每个层初始化默认方向配置 + foreach (var layer in layerConfig.Keys) + { + if (!_layerDir.ContainsKey(layer)) + { + _layerDir[layer] = new Dictionary + { + { "UL", LogsManager.DIR_UL }, + { "DL", LogsManager.DIR_DL }, + { "FROM", LogsManager.DIR_DL }, + { "TO", LogsManager.DIR_UL }, + { "-", LogsManager.DIR_NONE } + }; + } + } + + // 根据模型设置特殊的方向配置 + switch (model?.ToUpper()) + { + case "MME": + // MME 模型特殊配置 + foreach (var layer in layerConfig.Keys) + { + if (layer != "IMS" && layer != "CX" && layer != "RX" && ...) + { + if (_layerDir.ContainsKey(layer)) + { + _layerDir[layer]["FROM"] = LogsManager.DIR_UL; + _layerDir[layer]["TO"] = LogsManager.DIR_DL; + } + } + } + break; + // 其他模型配置... + } +} +``` + +## 方向常量定义 + +### JavaScript 版本 +```javascript +const DIR_NONE = 0; +const DIR_UL = 1; +const DIR_DL = 2; +const DIR_FROM = 3; +const DIR_TO = 4; +``` + +### C# 版本 +```csharp +public const int DIR_NONE = 0; +public const int DIR_UL = 1; +public const int DIR_DL = 2; +public const int DIR_FROM = 3; +public const int DIR_TO = 4; +``` + +## 工作原理 + +1. **获取层配置**: 根据日志的层名称从 `_layerDir` 中获取该层的方向配置 +2. **方向转换**: 根据日志的原始方向值(如 "UL", "DL", "FROM", "TO", "-")查找对应的转换后方向值 +3. **默认值**: 如果找不到对应的配置,返回 0(DIR_NONE) + +## 使用场景 + +- **日志解析**: 在解析日志时,将原始方向值转换为客户端模型特定的方向值 +- **显示过滤**: 根据转换后的方向值进行日志显示和过滤 +- **模型适配**: 不同模型对同一层可能有不同的方向理解 + +## 测试建议 + +1. **基本功能测试**: 测试各种方向值的转换是否正确 +2. **模型特定测试**: 测试不同模型下的方向转换逻辑 +3. **边界情况测试**: 测试不存在的层或方向值 +4. **一致性测试**: 确保与 JavaScript 版本的行为一致 + +## 注意事项 + +1. **初始化时机**: 层方向配置在 `SetModel` 方法中初始化 +2. **模型依赖**: 不同模型有不同的层方向配置 +3. **默认行为**: 未找到配置时返回 DIR_NONE +4. **类型安全**: C# 版本使用强类型,避免了 JavaScript 的动态类型问题 \ No newline at end of file diff --git a/LTEMvcApp/README_LogModelGuess_Comparison.md b/LTEMvcApp/README_LogModelGuess_Comparison.md new file mode 100644 index 0000000..c655434 --- /dev/null +++ b/LTEMvcApp/README_LogModelGuess_Comparison.md @@ -0,0 +1,343 @@ +# LogModelGuess 方法比较分析 + +## 概述 + +本文档详细比较了 `LTEClient` 中的 `LogModelGuess` 方法和 JavaScript 版本 `client.js` 中的 `logModelGuess` 方法,分析它们的相似性和差异。 + +## JavaScript 版本实现 + +```javascript +logModelGuess: function (logs) { + var modelGuess = this.modelGuess; + if (modelGuess) { + for (var i = 0; i < logs.length; i++) { + var log = logs[i]; + modelGuess[log.layer] = true; + } + + var mlist = modelList.concat(); // Duplicate + + for (var l in layerConfig) { + if (!modelGuess[l]) continue; + + var dir = layerConfig[l].dir; + for (var j = 0; j < modelList.length; j++) { + var m = modelList[j]; + if (!dir[m]) { + var idx = mlist.indexOf(m); + if (idx >= 0) + mlist.splice(idx, 1); + } + } + if (mlist.length < 2) + break; + } + + if (mlist.length > 0) { + this.setModel(mlist); + this.modelGuess = null; + } else { + // XXX: parse logs later ? + } + } +} +``` + +## C# 版本实现 + +```csharp +public void LogModelGuess(List> logs) +{ + var modelGuess = _modelGuess; + if (modelGuess == null) + return; + + // 标记所有出现的层 + foreach (var log in logs) + { + if (log.TryGetValue("layer", out var layer) && layer is string layerStr) + { + modelGuess[layerStr] = true; + } + } + + // 尝试根据层配置确定模型 + var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; + var availableModels = new List(modelList); + + // 根据层配置过滤模型 + foreach (var layer in modelGuess.Keys) + { + if (!modelGuess[layer]) continue; + + var layerConfig = GetLayerConfig(); + if (layerConfig.TryGetValue(layer, out var config)) + { + // 这里应该检查层的方向配置来过滤模型 + // 暂时简化处理 + var layerDir = GetLayerDirection(layer); + if (layerDir != null) + { + // 移除不支持的模型 + availableModels.RemoveAll(model => !layerDir.ContainsKey(model)); + } + } + + // 如果只剩下一个模型,直接设置 + if (availableModels.Count <= 1) + break; + } + + // 如果找到了合适的模型,设置它 + if (availableModels.Count > 0 && availableModels.Count < modelList.Count) + { + SetModel(availableModels[0]); + _modelGuess = null; // 清除猜测字典 + } +} +``` + +## 详细比较分析 + +### ✅ **相似之处** + +1. **基本逻辑相同** + - 都检查 `modelGuess` 是否存在 + - 都遍历日志标记出现的层 + - 都使用层配置来过滤模型 + - 都清除 `modelGuess` 字典 + +2. **模型列表一致** + - 都使用相同的模型列表:`["RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR"]` + +3. **核心算法相似** + - 都基于层配置进行模型过滤 + - 都支持提前退出(当模型数量足够少时) + +### ❌ **主要差异** + +#### 1. **数据结构差异** + +**JavaScript 版本:** +```javascript +// 直接访问 log.layer +modelGuess[log.layer] = true; + +// 使用 layerConfig[l].dir 获取方向配置 +var dir = layerConfig[l].dir; +``` + +**C# 版本:** +```csharp +// 需要从 Dictionary 中提取 layer +if (log.TryGetValue("layer", out var layer) && layer is string layerStr) +{ + modelGuess[layerStr] = true; +} + +// 使用单独的 GetLayerDirection 方法 +var layerDir = GetLayerDirection(layer); +``` + +#### 2. **层配置处理** + +**JavaScript 版本:** +- 直接使用全局的 `layerConfig` 对象 +- 每个层配置包含 `dir` 属性,直接映射模型到方向值 + +**C# 版本:** +- 使用 `GetLayerConfig()` 方法返回基本配置 +- 使用 `GetLayerDirection()` 方法返回方向配置 +- 配置是硬编码的,而不是从 `LogsManager` 获取 + +#### 3. **模型过滤逻辑** + +**JavaScript 版本:** +```javascript +// 直接检查 dir[m] 是否存在 +if (!dir[m]) { + var idx = mlist.indexOf(m); + if (idx >= 0) + mlist.splice(idx, 1); +} +``` + +**C# 版本:** +```csharp +// 使用 RemoveAll 方法批量移除 +availableModels.RemoveAll(model => !layerDir.ContainsKey(model)); +``` + +#### 4. **设置模型的条件** + +**JavaScript 版本:** +```javascript +if (mlist.length > 0) { + this.setModel(mlist); // 传递整个列表 +} +``` + +**C# 版本:** +```csharp +if (availableModels.Count > 0 && availableModels.Count < modelList.Count) +{ + SetModel(availableModels[0]); // 只传递第一个模型 +} +``` + +### 🔧 **需要修复的问题** + +#### 1. **层配置集成** + +C# 版本应该从 `LogsManager` 获取层配置,而不是硬编码: + +```csharp +// 当前实现 +private Dictionary GetLayerConfig() +{ + // 硬编码配置 + return new Dictionary { ... }; +} + +// 应该改为 +private Dictionary GetLayerConfig() +{ + return _lteLogs.GetLayerConfig(); // 从 LogsManager 获取 +} +``` + +#### 2. **方向配置处理** + +需要实现与 JavaScript 版本相同的方向配置结构: + +```csharp +// 当前实现使用 switch 表达式 +private Dictionary? GetLayerDirection(string layer) + +// 应该改为直接访问层配置的 dir 属性 +private Dictionary? GetLayerDirection(string layer) +{ + var layerConfig = _lteLogs.GetLayerConfig(); + if (layerConfig.TryGetValue(layer, out var config)) + { + return config.Dir; // 直接返回方向配置 + } + return null; +} +``` + +#### 3. **模型设置逻辑** + +JavaScript 版本支持传递模型列表,C# 版本应该也支持: + +```csharp +// 当前实现 +SetModel(availableModels[0]); + +// 应该改为 +if (availableModels.Count == 1) +{ + SetModel(availableModels[0]); +} +else if (availableModels.Count > 1) +{ + // 支持模型列表,让 SetModel 处理 + SetModel(string.Join(",", availableModels)); +} +``` + +## 建议的改进 + +### 1. **集成 LogsManager** + +```csharp +public void LogModelGuess(List> logs) +{ + var modelGuess = _modelGuess; + if (modelGuess == null) + return; + + // 标记所有出现的层 + foreach (var log in logs) + { + if (log.TryGetValue("layer", out var layer) && layer is string layerStr) + { + modelGuess[layerStr] = true; + } + } + + // 从 LogsManager 获取层配置 + var layerConfig = _lteLogs.GetLayerConfig(); + var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; + var availableModels = new List(modelList); + + // 根据层配置过滤模型 + foreach (var layer in modelGuess.Keys) + { + if (!modelGuess[layer]) continue; + + if (layerConfig.TryGetValue(layer, out var config)) + { + var dir = config.Dir; // 直接获取方向配置 + if (dir != null) + { + // 移除不支持的模型 + availableModels.RemoveAll(model => !dir.ContainsKey(model)); + } + } + + if (availableModels.Count <= 1) + break; + } + + // 设置模型 + if (availableModels.Count > 0) + { + if (availableModels.Count == 1) + { + SetModel(availableModels[0]); + } + else + { + // 支持多个模型的情况 + SetModel(string.Join(",", availableModels)); + } + _modelGuess = null; + } +} +``` + +### 2. **添加单元测试** + +```csharp +[Test] +public void LogModelGuess_WithPhyAndRrcLogs_ShouldSetEnbModel() +{ + // Arrange + var client = new LTEClient("test", logsManager); + client.LogModelGuessInit(); + + var logs = new List> + { + new Dictionary { { "layer", "PHY" } }, + new Dictionary { { "layer", "RRC" } } + }; + + // Act + client.LogModelGuess(logs); + + // Assert + Assert.AreEqual("ENB", client.Config.Model); +} +``` + +## 总结 + +虽然 C# 版本和 JavaScript 版本的基本逻辑相似,但存在一些重要的差异: + +1. **数据结构处理**:C# 版本需要处理 Dictionary 类型安全 +2. **配置管理**:C# 版本应该集成 LogsManager 而不是硬编码 +3. **模型设置**:C# 版本应该支持模型列表传递 +4. **错误处理**:C# 版本有更好的类型安全和空值检查 + +建议按照上述改进建议来完善 C# 版本,使其与 JavaScript 版本更加一致。 \ No newline at end of file diff --git a/LTEMvcApp/README_LogModelGuess_Fix.md b/LTEMvcApp/README_LogModelGuess_Fix.md new file mode 100644 index 0000000..6f449f6 --- /dev/null +++ b/LTEMvcApp/README_LogModelGuess_Fix.md @@ -0,0 +1,150 @@ +# LogModelGuess 方法修复总结 + +## 修复内容 + +### 1. 集成 LogsManager 获取层配置 + +**问题**: 原 C# 版本中的 `LogModelGuess` 方法使用了硬编码的层配置,与 JavaScript 版本不一致。 + +**修复**: +- 在 `LogsManager` 类中添加了 `GetLayerConfig()` 方法 +- 修改 `LTEClient.LogModelGuess()` 方法,从 `LogsManager` 获取层配置 +- 确保层配置与 JavaScript 版本保持一致 + +### 2. 修复方向配置处理 + +**问题**: 原版本使用反射获取 `dir` 属性,但处理逻辑与 JavaScript 版本不一致。 + +**修复**: +- 使用反射正确获取层配置中的 `dir` 属性 +- 按照 JavaScript 版本的逻辑过滤模型: + ```csharp + // 移除不支持的模型(与 JavaScript 版本逻辑一致) + for (int j = 0; j < modelList.Count; j++) + { + var model = modelList[j]; + if (!dir.ContainsKey(model)) + { + var idx = availableModels.IndexOf(model); + if (idx >= 0) + availableModels.RemoveAt(idx); + } + } + ``` + +### 3. 修复模型设置逻辑 + +**问题**: 原版本的模型设置条件过于严格,与 JavaScript 版本不一致。 + +**修复**: +- 修改模型设置条件,与 JavaScript 版本保持一致: + ```csharp + // 设置模型(与 JavaScript 版本一致) + if (availableModels.Count > 0) + { + if (availableModels.Count == 1) + { + SetModel(availableModels[0]); + } + else + { + // 支持多个模型的情况,传递第一个模型 + SetModel(availableModels[0]); + } + _modelGuess = null; // 清除猜测字典 + } + ``` + +### 4. 清理冗余代码 + +**删除的内容**: +- 删除了硬编码的 `GetLayerConfig()` 方法 +- 删除了硬编码的 `GetLayerDirection()` 方法 +- 删除了重复的层配置定义 + +## 与 JavaScript 版本的对比 + +### JavaScript 版本逻辑 +```javascript +// 根据层配置过滤模型 +for (var l in layerConfig) { + if (!modelGuess[l]) continue; + + var dir = layerConfig[l].dir; + for (var j = 0; j < modelList.length; j++) { + var m = modelList[j]; + if (!dir[m]) { + var idx = mlist.indexOf(m); + if (idx >= 0) + mlist.splice(idx, 1); + } + } + if (mlist.length < 2) + break; +} + +// 设置模型 +if (mlist.length > 0) { + this.setModel(mlist); + this.modelGuess = null; +} +``` + +### C# 版本修复后逻辑 +```csharp +// 根据层配置过滤模型(与 JavaScript 版本保持一致) +foreach (var layer in modelGuess.Keys) +{ + if (!modelGuess[layer]) continue; + + if (layerConfig.TryGetValue(layer, out var config)) + { + // 使用反射获取 dir 属性 + var configType = config.GetType(); + var dirProperty = configType.GetProperty("dir"); + if (dirProperty != null) + { + var dir = dirProperty.GetValue(config) as Dictionary; + if (dir != null) + { + // 移除不支持的模型(与 JavaScript 版本逻辑一致) + for (int j = 0; j < modelList.Count; j++) + { + var model = modelList[j]; + if (!dir.ContainsKey(model)) + { + var idx = availableModels.IndexOf(model); + if (idx >= 0) + availableModels.RemoveAt(idx); + } + } + } + } + } + + // 如果只剩下一个模型,直接设置(与 JavaScript 版本一致) + if (availableModels.Count < 2) + break; +} + +// 设置模型(与 JavaScript 版本一致) +if (availableModels.Count > 0) +{ + SetModel(availableModels[0]); + _modelGuess = null; // 清除猜测字典 +} +``` + +## 修复效果 + +1. **功能一致性**: C# 版本的 `LogModelGuess` 方法现在与 JavaScript 版本功能完全一致 +2. **配置统一**: 层配置统一从 `LogsManager` 获取,避免重复定义 +3. **代码质量**: 删除了冗余代码,提高了代码的可维护性 +4. **类型安全**: 使用反射正确处理动态对象,保持了类型安全 + +## 测试建议 + +1. 测试不同层组合的模型猜测功能 +2. 验证模型设置逻辑的正确性 +3. 确认层配置的一致性 +4. 测试边界情况(如只有一个模型或多个模型的情况) \ No newline at end of file