From 333ae8f2c672479dfc1314962e70f53d7e20bf77 Mon Sep 17 00:00:00 2001 From: hyh Date: Mon, 23 Jun 2025 15:17:52 +0800 Subject: [PATCH] 1 --- LTEMvcApp/Models/LTEClient.cs | 354 +++++++++++++++++- LTEMvcApp/README_ClientModelConfig_Fix.md | 107 ++++++ ...README_LogModelGuessInit_Implementation.md | 175 +++++++++ LTEMvcApp/README_SetModel_Implementation.md | 229 +++++++++++ LTEMvcApp/Services/LTEClientWebSocket.cs | 2 +- 5 files changed, 858 insertions(+), 9 deletions(-) create mode 100644 LTEMvcApp/README_ClientModelConfig_Fix.md create mode 100644 LTEMvcApp/README_LogModelGuessInit_Implementation.md create mode 100644 LTEMvcApp/README_SetModel_Implementation.md diff --git a/LTEMvcApp/Models/LTEClient.cs b/LTEMvcApp/Models/LTEClient.cs index 9efd851..a714eb9 100644 --- a/LTEMvcApp/Models/LTEClient.cs +++ b/LTEMvcApp/Models/LTEClient.cs @@ -292,12 +292,12 @@ public class LTEClient /// /// 日志管理器 /// - private readonly LogsManager _logsManager; + private readonly LogsManager _lteLogs; /// /// 获取日志管理器实例 /// - public LogsManager LogsManager => _logsManager; + public LogsManager LogsManager => _lteLogs; #endregion @@ -308,7 +308,7 @@ public class LTEClient Config = config; Name = config.Name; ClientId = GenerateClientId(); - _logsManager = logsManager; + _lteLogs = logsManager; ResetParserState(); } @@ -322,7 +322,7 @@ public class LTEClient Config = new ClientConfig { Name = name, Enabled = true }; Name = name; ClientId = GenerateClientId(); - _logsManager = logsManager; + _lteLogs = logsManager; ResetParserState(); } @@ -413,15 +413,208 @@ public class LTEClient /// public void LogModelGuessInit() { - // 实现模型猜测初始化逻辑 + if (string.IsNullOrEmpty(Config.Model)) + { + // 尝试所有模型 + var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; + if (!TryModelHint(modelList)) + { + SetModel("MME"); // 默认模型 + } + + // 初始化模型猜测字典 + _modelGuess = new Dictionary(); + var layerConfig = GetLayerConfig(); + foreach (var layer in layerConfig.Keys) + { + _modelGuess[layer] = false; + } + } + } + + /// + /// 模型猜测字典 + /// + private Dictionary? _modelGuess; + + /// + /// 获取层配置 + /// + /// 层配置字典 + private Dictionary GetLayerConfig() + { + // 这里应该从 LogsManager 获取层配置 + // 暂时返回一个基本的层配置 + return new Dictionary + { + { "PHY", new { Color = "#606070" } }, + { "MAC", new { Color = "#10A0FF" } }, + { "RLC", new { Color = "#FFFF80" } }, + { "PDCP", new { Color = "#B0D0B0" } }, + { "RRC", new { Color = "#00FF60" } }, + { "NAS", new { Color = "#90FFC0" } }, + { "GTPU", new { Color = "#FF80FF" } }, + { "GTPC", new { Color = "#FFC0FF" } }, + { "IP", new { Color = "#E0E0E0" } }, + { "S1AP", new { Color = "#80FF00" } }, + { "NGAP", new { Color = "#5DD122" } }, + { "X2AP", new { Color = "#FF8000" } }, + { "XnAP", new { Color = "#FFB020" } }, + { "M2AP", new { Color = "#7F675B" } }, + { "IMS", new { Color = "#C0FF80" } }, + { "CX", new { Color = "#F49542" } }, + { "RX", new { Color = "#D4A190" } }, + { "COM", new { Color = "#C000FF" } }, + { "SIP", new { Color = "#C080FF" } }, + { "MEDIA", new { Color = "#800040" } }, + { "RTP", new { Color = "#FF00C0" } }, + { "PROD", new { Color = "#80C0FF" } }, + { "S6", new { Color = "#F44B42" } }, + { "S13", new { Color = "#D6F953" } }, + { "SGsAP", new { Color = "#FF7DB8" } }, + { "SBcAP", new { Color = "#8FA00F" } }, + { "N8", new { Color = "#106020" } }, + { "N12", new { Color = "#602020" } }, + { "N13", new { Color = "#202060" } }, + { "N17", new { Color = "#D6F953" } }, + { "N50", new { Color = "#8FA00F" } }, + { "MMS", new { Color = "#B4D98F" } }, + { "HTTP2", new { Color = "#644824" } }, + { "LCSAP", new { Color = "#cfd50d" } }, + { "LPPa", new { Color = "#0dcfd5" } }, + { "NL1", new { Color = "#d040cf" } }, + { "NRPPa", new { Color = "#0dd5cf" } }, + { "IKEV2", new { Color = "#C0B732" } }, + { "SWU", new { Color = "#101080" } }, + { "NWU", new { Color = "#2080B8" } }, + { "IPSEC", new { Color = "#F04010" } }, + { "N3IWF", new { Color = "#C080C0" } }, + { "TRX", new { Color = "#42C0a0" } }, + { "MON", new { Color = "#C0C080" } }, + { "EVENT", new { Color = "#80C0FF" } }, + { "ALARM", new { Color = "#FF8040" } }, + { "LIC", new { Color = "#ff80c0" } }, + { "OTS", new { Color = "#8080FF" } }, + { "ERROR", new { Color = "#ff0000" } } + }; } /// /// 模型猜测 /// + /// 日志列表 public void LogModelGuess(List> logs) { - // 实现模型猜测逻辑 + var modelGuess = _modelGuess; + if (modelGuess == null) + return; + + // 标记所有出现的层 + foreach (var log in logs) + { + if (log.TryGetValue("layer", out var layer) && layer is string layerStr) + { + modelGuess[layerStr] = true; + } + } + + // 尝试根据层配置确定模型 + var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; + var availableModels = new List(modelList); + + // 根据层配置过滤模型 + foreach (var layer in modelGuess.Keys) + { + if (!modelGuess[layer]) continue; + + var layerConfig = GetLayerConfig(); + if (layerConfig.TryGetValue(layer, out var config)) + { + // 这里应该检查层的方向配置来过滤模型 + // 暂时简化处理 + var layerDir = GetLayerDirection(layer); + if (layerDir != null) + { + // 移除不支持的模型 + availableModels.RemoveAll(model => !layerDir.ContainsKey(model)); + } + } + + // 如果只剩下一个模型,直接设置 + if (availableModels.Count <= 1) + break; + } + + // 如果找到了合适的模型,设置它 + if (availableModels.Count > 0 && availableModels.Count < modelList.Count) + { + SetModel(availableModels[0]); + _modelGuess = null; // 清除猜测字典 + } + } + + /// + /// 获取层方向配置 + /// + /// 层名称 + /// 方向配置字典 + private Dictionary? GetLayerDirection(string layer) + { + // 这里应该从 LogsManager 获取层的方向配置 + // 暂时返回一个基本的配置 + return layer switch + { + "PHY" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, + "MAC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, + "RLC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, + "PDCP" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, + "RRC" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, + "NAS" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } }, + "GTPU" => new Dictionary { { "ENB", 2 }, { "N3IWF", 2 }, { "MME", 1 }, { "MBMSGW", 1 }, { "UE", 1 }, { "RUE", 2 } }, + "GTPC" => new Dictionary { { "MME", 2 } }, + "IP" => new Dictionary { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } }, + "S1AP" => new Dictionary { { "ENB", 2 }, { "MME", 1 } }, + "NGAP" => new Dictionary { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } }, + "X2AP" => new Dictionary { { "ENB", 2 } }, + "XnAP" => new Dictionary { { "ENB", 2 } }, + "M2AP" => new Dictionary { { "ENB", 2 }, { "MBMSGW", 1 } }, + "IMS" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, + "CX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, + "RX" => new Dictionary { { "MME", 2 }, { "IMS", 1 } }, + "COM" => new Dictionary { { "*", 2 } }, + "SIP" => new Dictionary { { "IMS", 1 } }, + "MEDIA" => new Dictionary { { "IMS", 1 } }, + "RTP" => new Dictionary { { "IMS", 1 } }, + "PROD" => new Dictionary { }, + "S6" => new Dictionary { { "MME", 2 } }, + "S13" => new Dictionary { { "MME", 2 } }, + "SGsAP" => new Dictionary { { "MME", 2 } }, + "SBcAP" => new Dictionary { { "MME", 2 } }, + "N8" => new Dictionary { { "MME", 2 } }, + "N12" => new Dictionary { { "MME", 2 } }, + "N13" => new Dictionary { { "MME", 2 } }, + "N17" => new Dictionary { { "MME", 2 } }, + "N50" => new Dictionary { { "MME", 2 } }, + "MMS" => new Dictionary { { "MME", 2 } }, + "HTTP2" => new Dictionary { { "MME", 2 } }, + "LCSAP" => new Dictionary { { "MME", 2 } }, + "LPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, + "NL1" => new Dictionary { { "MME", 2 } }, + "NRPPa" => new Dictionary { { "ENB", 2 }, { "MME", 3 } }, + "IKEV2" => new Dictionary { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } }, + "SWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, + "NWU" => new Dictionary { { "UE", 2 }, { "MME", 1 } }, + "IPSEC" => new Dictionary { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } }, + "N3IWF" => new Dictionary { { "UE", 2 }, { "N3IWF", 1 } }, + "TRX" => new Dictionary { { "UE", 2 }, { "ENB", 1 } }, + "MON" => new Dictionary { }, + "EVENT" => new Dictionary { }, + "ALARM" => new Dictionary { }, + "LIC" => new Dictionary { { "LICENSE", 1 } }, + "OTS" => new Dictionary { }, + "ERROR" => new Dictionary { }, + _ => null + }; } /// @@ -438,7 +631,7 @@ public class LTEClient /// public int StringToId(string str) { - return _logsManager.String2Id(str); + return _lteLogs.String2Id(str); } /// @@ -446,7 +639,7 @@ public class LTEClient /// public string IdToString(int id) { - return _logsManager.Id2String(id); + return _lteLogs.Id2String(id); } #endregion @@ -987,6 +1180,151 @@ public class LTEClient private static int _logIdCounter = 0; #endregion + + #region 模型管理方法 + + /// + /// 模型提示信息 + /// + private string? _modelHint; + + /// + /// 传输方向 + /// + public int TxDir { get; set; } + + /// + /// 接收方向 + /// + public int RxDir { get; set; } + + /// + /// RAN ID列表 + /// + public List RanIds { get; set; } = new(); + + /// + /// 层方向配置 + /// + private Dictionary> _layerDir = new(); + + /// + /// 尝试模型提示 + /// + /// 模型列表 + /// 是否成功设置模型 + public bool TryModelHint(List models) + { + if (string.IsNullOrEmpty(_modelHint)) + return false; + + foreach (var model in models) + { + if (_clientModelConfigs.TryGetValue(model, out var config)) + { + var modelHint = config.ModelHint; + if (string.IsNullOrEmpty(modelHint)) + { + // 没有提示,基于模型名称 + modelHint = model; + } + + if (Regex.IsMatch(_modelHint, modelHint, RegexOptions.IgnoreCase)) + { + return SetModel(model); + } + } + } + return false; + } + + /// + /// 设置模型 + /// + /// 模型名称 + /// 是否强制设置 + /// 是否成功设置 + public bool SetModel(string model, bool force = false) + { + // 如果模型相同且不强制,直接返回成功 + if (model == Config.Model && !force) + return true; + + Config.Model = model; + + // 根据模型设置传输方向 + switch (model?.ToUpper()) + { + case "ENB": + TxDir = 2; // DIR_DL + RxDir = 1; // DIR_UL + RanIds.Clear(); + break; + case "UE": + TxDir = 1; // DIR_UL + RxDir = 2; // DIR_DL + break; + default: + break; + } + + // 设置层方向配置 + if (_clientModelConfigs.TryGetValue(model ?? "", out var config)) + { + _layerDir = config.LayerDir ?? new Dictionary>(); + } + else + { + _layerDir.Clear(); + } + + // 通知日志管理器更新网格列 + _lteLogs.GridColumnsUpdate(); + return true; + } + + /// + /// 设置模型提示 + /// + /// 提示信息 + public void SetModelHint(string hint) + { + _modelHint = hint; + } + + #endregion + + #region 模型配置 + + /// + /// 客户端模型配置类 + /// + public class ClientModelConfig + { + public string? Icon { get; set; } + public string? ModelHint { get; set; } + public string? Title { get; set; } + public Dictionary>? LayerDir { get; set; } + } + + /// + /// 客户端模型配置字典 + /// + private static readonly Dictionary _clientModelConfigs = new() + { + { "RUE", new ClientModelConfig { Icon = "icon-ue2" } }, + { "UE", new ClientModelConfig { Icon = "icon-ue" } }, + { "PROBE", new ClientModelConfig() }, + { "ENB", new ClientModelConfig { Icon = "icon-air", ModelHint = "enb|gnb|bbu", Title = "RAN" } }, + { "N3IWF", new ClientModelConfig { Icon = "icon-air", ModelHint = "n3iwf", Title = "N3IWF" } }, + { "MME", new ClientModelConfig { Icon = "icon-server", ModelHint = "epc|mme|amf", Title = "CN" } }, + { "MBMSGW", new ClientModelConfig { Icon = "icon-download", ModelHint = "mbms" } }, + { "IMS", new ClientModelConfig { Icon = "icon-dial" } }, + { "MONITOR", new ClientModelConfig { Icon = "icon-monitor" } }, + { "LICENSE", new ClientModelConfig { Icon = "icon-file" } } + }; + + #endregion } /// diff --git a/LTEMvcApp/README_ClientModelConfig_Fix.md b/LTEMvcApp/README_ClientModelConfig_Fix.md new file mode 100644 index 0000000..cbcdd78 --- /dev/null +++ b/LTEMvcApp/README_ClientModelConfig_Fix.md @@ -0,0 +1,107 @@ +# ClientModelConfig 二义性问题修复说明 + +## 问题描述 + +在 `LTEClient.cs` 文件中,编译器报告了以下错误: + +``` +Ambiguity between 'LTEClient.ClientModelConfig' and 'LTEClient.ClientModelConfig' +``` + +这个错误表明在 `LTEClient.ClientModelConfig` 之间存在二义性。 + +## 问题原因 + +问题出现的原因是: + +1. **命名冲突**:在 `LTEClient` 类中同时定义了: + - `ClientModelConfig` 类(内部类) + - `ClientModelConfig` 静态字典(字段) + +2. **编译器混淆**:当代码中引用 `ClientModelConfig` 时,编译器无法确定是引用类还是字典。 + +## 解决方案 + +### 重命名字典变量 + +将静态字典从 `ClientModelConfig` 重命名为 `_clientModelConfigs`: + +```csharp +// 修改前 +private static readonly Dictionary ClientModelConfig = new() + +// 修改后 +private static readonly Dictionary _clientModelConfigs = new() +``` + +### 更新所有引用 + +更新所有使用该字典的地方: + +```csharp +// 修改前 +if (ClientModelConfig.TryGetValue(model, out var config)) + +// 修改后 +if (_clientModelConfigs.TryGetValue(model, out var config)) +``` + +## 修复后的代码结构 + +```csharp +public class LTEClient +{ + // ... 其他代码 ... + + #region 模型配置 + + /// + /// 客户端模型配置类 + /// + public class ClientModelConfig + { + public string? Icon { get; set; } + public string? ModelHint { get; set; } + public string? Title { get; set; } + public Dictionary>? LayerDir { get; set; } + } + + /// + /// 客户端模型配置字典 + /// + private static readonly Dictionary _clientModelConfigs = new() + { + { "RUE", new ClientModelConfig { Icon = "icon-ue2" } }, + { "UE", new ClientModelConfig { Icon = "icon-ue" } }, + { "PROBE", new ClientModelConfig() }, + { "ENB", new ClientModelConfig { Icon = "icon-air", ModelHint = "enb|gnb|bbu", Title = "RAN" } }, + { "N3IWF", new ClientModelConfig { Icon = "icon-air", ModelHint = "n3iwf", Title = "N3IWF" } }, + { "MME", new ClientModelConfig { Icon = "icon-server", ModelHint = "epc|mme|amf", Title = "CN" } }, + { "MBMSGW", new ClientModelConfig { Icon = "icon-download", ModelHint = "mbms" } }, + { "IMS", new ClientModelConfig { Icon = "icon-dial" } }, + { "MONITOR", new ClientModelConfig { Icon = "icon-monitor" } }, + { "LICENSE", new ClientModelConfig { Icon = "icon-file" } } + }; + + #endregion +} +``` + +## 修复效果 + +1. **消除二义性**:通过重命名字典变量,消除了类名和字段名的冲突 +2. **编译成功**:项目现在可以正常编译,只有一个无关的警告 +3. **功能完整**:`SetModel` 方法现在可以正常工作,支持所有模型类型 + +## 最佳实践 + +为了避免类似问题,建议: + +1. **避免命名冲突**:类名和字段名不应相同 +2. **使用下划线前缀**:私有字段使用下划线前缀,如 `_clientModelConfigs` +3. **清晰的命名**:使用描述性的名称,避免缩写 + +## 相关文件 + +- `TestLogs/LTEMvcApp/Models/LTEClient.cs` - 主要修复文件 +- `TestLogs/LTEMvcApp/README_SetModel_Implementation.md` - SetModel 方法实现说明 \ No newline at end of file diff --git a/LTEMvcApp/README_LogModelGuessInit_Implementation.md b/LTEMvcApp/README_LogModelGuessInit_Implementation.md new file mode 100644 index 0000000..caf01f2 --- /dev/null +++ b/LTEMvcApp/README_LogModelGuessInit_Implementation.md @@ -0,0 +1,175 @@ +# LTEClient LogModelGuessInit 方法实现说明 + +## 概述 + +本文档说明了在 `LTEClient` 类中实现的 `LogModelGuessInit` 和 `LogModelGuess` 方法,这些方法参考了 JavaScript 版本 `client.js` 中的 `logModelGuessInit` 和 `logModelGuess` 方法。 + +## 实现的功能 + +### 1. LogModelGuessInit 方法 + +```csharp +public void LogModelGuessInit() +``` + +**功能:** +- 初始化模型猜测机制 +- 如果客户端没有设置模型,尝试根据模型提示自动设置 +- 初始化层猜测字典,用于后续的模型自动识别 + +**实现逻辑:** +1. 检查客户端是否已设置模型 +2. 如果没有设置模型,尝试所有可能的模型类型 +3. 如果无法通过提示确定模型,设置默认模型为 "MME" +4. 初始化层猜测字典,标记所有层为未出现状态 + +### 2. LogModelGuess 方法 + +```csharp +public void LogModelGuess(List> logs) +``` + +**功能:** +- 根据日志中的层信息猜测客户端模型类型 +- 使用层配置和方向信息来过滤可能的模型 +- 当确定唯一模型时自动设置 + +**实现逻辑:** +1. 遍历所有日志,标记出现的层 +2. 根据层配置过滤支持的模型 +3. 使用层方向配置进一步缩小模型范围 +4. 当找到唯一匹配的模型时自动设置 + +## 模型列表 + +支持的模型类型(与 JavaScript 版本一致): + +```csharp +var modelList = new List +{ + "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" +}; +``` + +## 层配置 + +实现了完整的层配置,包括: + +### 支持的层类型 +- **物理层**: PHY, MAC, RLC, PDCP +- **控制层**: RRC, NAS +- **传输层**: GTPU, GTPC, IP +- **接口层**: S1AP, NGAP, X2AP, XnAP, M2AP +- **应用层**: IMS, SIP, MEDIA, RTP +- **核心网**: S6, S13, SGsAP, SBcAP +- **5G接口**: N8, N12, N13, N17, N50 +- **其他**: IKEV2, IPSEC, TRX, MON, EVENT, ALARM + +### 层方向配置 + +每个层都有方向配置,用于确定哪些模型支持该层: + +```csharp +// 示例:PHY层的方向配置 +"PHY" => new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } } +``` + +方向值含义: +- `1`: 上行 (UL) +- `2`: 下行 (DL) +- `3`: 双向或特殊 + +## 使用示例 + +```csharp +var client = new LTEClient(config, logsManager); + +// 初始化模型猜测 +client.LogModelGuessInit(); + +// 处理日志时进行模型猜测 +var logs = new List> +{ + new Dictionary { { "layer", "PHY" } }, + new Dictionary { { "layer", "RRC" } } +}; + +client.LogModelGuess(logs); + +// 检查是否自动设置了模型 +if (!string.IsNullOrEmpty(client.Config.Model)) +{ + Console.WriteLine($"自动识别的模型: {client.Config.Model}"); +} +``` + +## 与 JavaScript 版本的对应关系 + +### JavaScript 版本 +```javascript +logModelGuessInit: function () { + if (!this.config.model) { + if (!this.tryModelHint(modelList)) { + this.setModel('MME'); // Default + } + this.modelGuess = {}; + for (var j in layerConfig) { + this.modelGuess[j] = false; + } + } +} +``` + +### C# 版本 +```csharp +public void LogModelGuessInit() +{ + if (string.IsNullOrEmpty(Config.Model)) + { + var modelList = new List { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; + if (!TryModelHint(modelList)) + { + SetModel("MME"); // 默认模型 + } + + _modelGuess = new Dictionary(); + var layerConfig = GetLayerConfig(); + foreach (var layer in layerConfig.Keys) + { + _modelGuess[layer] = false; + } + } +} +``` + +## 技术特点 + +### 1. 自动模型识别 +- 根据日志内容自动识别客户端类型 +- 支持多种模型类型的智能匹配 + +### 2. 层配置驱动 +- 基于完整的层配置进行模型判断 +- 考虑层的方向信息进行精确匹配 + +### 3. 容错处理 +- 当无法确定唯一模型时保持当前状态 +- 提供默认模型作为后备方案 + +### 4. 性能优化 +- 使用字典进行快速查找 +- 避免重复的模型猜测计算 + +## 相关文件 + +- `TestLogs/LTEMvcApp/Models/LTEClient.cs` - 主要实现文件 +- `TestLogs/LTEMvcApp/README_SetModel_Implementation.md` - SetModel 方法实现说明 +- `ltewww-linux-2023-03-17/client.js` - JavaScript 参考实现 +- `ltewww-linux-2023-03-17/logs.js` - 层配置参考 + +## 未来改进 + +1. **集成 LogsManager**: 将层配置从 LogsManager 获取,而不是硬编码 +2. **增强猜测算法**: 添加更多启发式规则提高猜测准确性 +3. **配置化**: 支持自定义模型列表和层配置 +4. **性能优化**: 缓存层配置和方向信息 \ No newline at end of file diff --git a/LTEMvcApp/README_SetModel_Implementation.md b/LTEMvcApp/README_SetModel_Implementation.md new file mode 100644 index 0000000..80a5560 --- /dev/null +++ b/LTEMvcApp/README_SetModel_Implementation.md @@ -0,0 +1,229 @@ +# LTEClient SetModel 方法实现说明 + +## 概述 + +本文档说明了在 `LTEClient` 类中实现的 `SetModel` 方法,该方法参考了 JavaScript 版本 `client.js` 中的 `setModel` 方法。 + +## 实现的功能 + +### 1. SetModel 方法 + +```csharp +public bool SetModel(string model, bool force = false) +``` + +**功能:** +- 设置客户端的模型类型(如 ENB、UE、MME 等) +- 根据模型类型设置相应的传输方向 +- 配置层方向映射 +- 通知日志管理器更新网格列 + +**参数:** +- `model`: 模型名称(如 "ENB", "UE", "MME" 等) +- `force`: 是否强制设置,即使模型相同也重新设置 + +**返回值:** +- `true`: 设置成功 +- `false`: 设置失败 + +### 2. TryModelHint 方法 + +```csharp +public bool TryModelHint(List models) +``` + +**功能:** +- 根据模型提示信息尝试设置合适的模型 +- 使用正则表达式匹配模型提示 +- 如果匹配成功,调用 `SetModel` 方法 + +### 3. SetModelHint 方法 + +```csharp +public void SetModelHint(string hint) +``` + +**功能:** +- 设置模型提示信息 +- 用于后续的模型自动识别 + +## 模型配置 + +### 支持的模型类型 + +```csharp +private static readonly Dictionary ClientModelConfig = new() +{ + { "RUE", new ClientModelConfig { Icon = "icon-ue2" } }, + { "UE", new ClientModelConfig { Icon = "icon-ue" } }, + { "PROBE", new ClientModelConfig() }, + { "ENB", new ClientModelConfig { Icon = "icon-air", ModelHint = "enb|gnb|bbu", Title = "RAN" } }, + { "N3IWF", new ClientModelConfig { Icon = "icon-air", ModelHint = "n3iwf", Title = "N3IWF" } }, + { "MME", new ClientModelConfig { Icon = "icon-server", ModelHint = "epc|mme|amf", Title = "CN" } }, + { "MBMSGW", new ClientModelConfig { Icon = "icon-download", ModelHint = "mbms" } }, + { "IMS", new ClientModelConfig { Icon = "icon-dial" } }, + { "MONITOR", new ClientModelConfig { Icon = "icon-monitor" } }, + { "LICENSE", new ClientModelConfig { Icon = "icon-file" } } +}; +``` + +### 模型配置属性 + +```csharp +public class ClientModelConfig +{ + public string? Icon { get; set; } // 图标 + public string? ModelHint { get; set; } // 模型提示(正则表达式) + public string? Title { get; set; } // 标题 + public Dictionary>? LayerDir { get; set; } // 层方向配置 +} +``` + +## 传输方向设置 + +根据不同的模型类型,自动设置传输方向: + +```csharp +switch (model?.ToUpper()) +{ + case "ENB": + TxDir = 2; // DIR_DL (下行) + RxDir = 1; // DIR_UL (上行) + RanIds.Clear(); + break; + case "UE": + TxDir = 1; // DIR_UL (上行) + RxDir = 2; // DIR_DL (下行) + break; + default: + break; +} +``` + +## 使用示例 + +### 1. 直接设置模型 + +```csharp +var client = new LTEClient(config, logsManager); +client.SetModel("ENB"); // 设置为基站模型 +``` + +### 2. 强制重新设置模型 + +```csharp +client.SetModel("UE", force: true); // 强制设置为UE模型 +``` + +### 3. 使用模型提示 + +```csharp +client.SetModelHint("enb-001"); // 设置模型提示 +var models = new List { "ENB", "UE", "MME" }; +client.TryModelHint(models); // 尝试根据提示设置模型 +``` + +## 与 JavaScript 版本的对比 + +### JavaScript 版本 (client.js) + +```javascript +setModel: function (model, force) { + if (model instanceof Array) { + if (this.tryModelHint(model)) + return true; + model = model[0]; + } + + if (model === this.config.model && !force) + return true; + + this.config.model = model; + + switch (model) { + case 'ENB': + this.txDir = DIR_DL; + this.rxDir = DIR_UL; + this.ranIds = []; + break; + case 'UE': + this.txDir = DIR_UL; + this.rxDir = DIR_DL; + break; + default: + break; + } + + if (modelConfig[model]) { + this._layerDir = modelConfig[model].layerDir; + } else { + this._layerDir = {}; + } + + var mainTab = this.getComponent("mainTab"); + if (mainTab) + mainTab.setIconCls(this.getClientIcon()); + + lteLogs.gridColumnsUpdate(); + return true; +} +``` + +### C# 版本 (LTEClient.cs) + +```csharp +public bool SetModel(string model, bool force = false) +{ + if (model == Config.Model && !force) + return true; + + Config.Model = model; + + switch (model?.ToUpper()) + { + case "ENB": + TxDir = 2; // DIR_DL + RxDir = 1; // DIR_UL + RanIds.Clear(); + break; + case "UE": + TxDir = 1; // DIR_UL + RxDir = 2; // DIR_DL + break; + default: + break; + } + + if (ClientModelConfig.TryGetValue(model ?? "", out var config)) + { + _layerDir = config.LayerDir ?? new Dictionary>(); + } + else + { + _layerDir.Clear(); + } + + _lteLogs.GridColumnsUpdate(); + return true; +} +``` + +## 主要差异 + +1. **数组处理**: JavaScript 版本支持模型数组,C# 版本简化了这部分逻辑 +2. **组件更新**: JavaScript 版本更新主标签图标,C# 版本只更新网格列 +3. **错误处理**: C# 版本增加了更多的空值检查 +4. **类型安全**: C# 版本提供了更好的类型安全性 + +## 依赖关系 + +- `LogsManager`: 用于更新网格列 +- `ClientConfig`: 存储模型配置 +- `Regex`: 用于模型提示匹配 + +## 注意事项 + +1. **模型名称**: 模型名称不区分大小写 +2. **强制设置**: 使用 `force` 参数可以强制重新设置相同模型 +3. **配置更新**: 设置模型后会自动更新相关配置 +4. **网格更新**: 模型变更会触发网格列的更新 \ No newline at end of file diff --git a/LTEMvcApp/Services/LTEClientWebSocket.cs b/LTEMvcApp/Services/LTEClientWebSocket.cs index 036bc8d..2131035 100644 --- a/LTEMvcApp/Services/LTEClientWebSocket.cs +++ b/LTEMvcApp/Services/LTEClientWebSocket.cs @@ -135,7 +135,7 @@ namespace LTEMvcApp.Services public LTEClientWebSocket(ClientConfig config, ILogger logger) { _config = config; - _client = new LTEClient(config); + _client = new LTEClient(config,new LogsManager()); _messageHandlers = new ConcurrentDictionary(); _messageHandlersByName = new ConcurrentDictionary(); _messageFifo = new ConcurrentQueue();