Browse Source

1

feature/LteClientLogFun
hyh 1 month ago
parent
commit
e7e96b7f08
  1. 290
      LTEMvcApp/Models/LTEClient.cs
  2. 13
      LTEMvcApp/Models/LogsManager.cs
  3. 166
      LTEMvcApp/README_DirConvert_Implementation.md
  4. 343
      LTEMvcApp/README_LogModelGuess_Comparison.md
  5. 150
      LTEMvcApp/README_LogModelGuess_Fix.md

290
LTEMvcApp/Models/LTEClient.cs

@ -424,7 +424,7 @@ public class LTEClient
// 初始化模型猜测字典
_modelGuess = new Dictionary<string, bool>();
var layerConfig = GetLayerConfig();
var layerConfig = _lteLogs.GetLayerConfig();
foreach (var layer in layerConfig.Keys)
{
_modelGuess[layer] = false;
@ -437,68 +437,6 @@ public class LTEClient
/// </summary>
private Dictionary<string, bool>? _modelGuess;
/// <summary>
/// 获取层配置
/// </summary>
/// <returns>层配置字典</returns>
private Dictionary<string, object> GetLayerConfig()
{
// 这里应该从 LogsManager 获取层配置
// 暂时返回一个基本的层配置
return new Dictionary<string, object>
{
{ "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" } }
};
}
/// <summary>
/// 模型猜测
/// </summary>
@ -518,112 +456,79 @@ public class LTEClient
}
}
// 尝试根据层配置确定模型
// 从 LogsManager 获取层配置
var layerConfig = _lteLogs.GetLayerConfig();
var modelList = new List<string> { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" };
var availableModels = new List<string>(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<string, int>;
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; // 清除猜测字典
}
}
/// <summary>
/// 获取层方向配置
/// </summary>
/// <param name="layer">层名称</param>
/// <returns>方向配置字典</returns>
private Dictionary<string, int>? GetLayerDirection(string layer)
{
// 这里应该从 LogsManager 获取层的方向配置
// 暂时返回一个基本的配置
return layer switch
{
"PHY" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } },
"MAC" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } },
"RLC" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } },
"PDCP" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } },
"RRC" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } },
"NAS" => new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } },
"GTPU" => new Dictionary<string, int> { { "ENB", 2 }, { "N3IWF", 2 }, { "MME", 1 }, { "MBMSGW", 1 }, { "UE", 1 }, { "RUE", 2 } },
"GTPC" => new Dictionary<string, int> { { "MME", 2 } },
"IP" => new Dictionary<string, int> { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } },
"S1AP" => new Dictionary<string, int> { { "ENB", 2 }, { "MME", 1 } },
"NGAP" => new Dictionary<string, int> { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } },
"X2AP" => new Dictionary<string, int> { { "ENB", 2 } },
"XnAP" => new Dictionary<string, int> { { "ENB", 2 } },
"M2AP" => new Dictionary<string, int> { { "ENB", 2 }, { "MBMSGW", 1 } },
"IMS" => new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } },
"CX" => new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } },
"RX" => new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } },
"COM" => new Dictionary<string, int> { { "*", 2 } },
"SIP" => new Dictionary<string, int> { { "IMS", 1 } },
"MEDIA" => new Dictionary<string, int> { { "IMS", 1 } },
"RTP" => new Dictionary<string, int> { { "IMS", 1 } },
"PROD" => new Dictionary<string, int> { },
"S6" => new Dictionary<string, int> { { "MME", 2 } },
"S13" => new Dictionary<string, int> { { "MME", 2 } },
"SGsAP" => new Dictionary<string, int> { { "MME", 2 } },
"SBcAP" => new Dictionary<string, int> { { "MME", 2 } },
"N8" => new Dictionary<string, int> { { "MME", 2 } },
"N12" => new Dictionary<string, int> { { "MME", 2 } },
"N13" => new Dictionary<string, int> { { "MME", 2 } },
"N17" => new Dictionary<string, int> { { "MME", 2 } },
"N50" => new Dictionary<string, int> { { "MME", 2 } },
"MMS" => new Dictionary<string, int> { { "MME", 2 } },
"HTTP2" => new Dictionary<string, int> { { "MME", 2 } },
"LCSAP" => new Dictionary<string, int> { { "MME", 2 } },
"LPPa" => new Dictionary<string, int> { { "ENB", 2 }, { "MME", 3 } },
"NL1" => new Dictionary<string, int> { { "MME", 2 } },
"NRPPa" => new Dictionary<string, int> { { "ENB", 2 }, { "MME", 3 } },
"IKEV2" => new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } },
"SWU" => new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 } },
"NWU" => new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 } },
"IPSEC" => new Dictionary<string, int> { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } },
"N3IWF" => new Dictionary<string, int> { { "UE", 2 }, { "N3IWF", 1 } },
"TRX" => new Dictionary<string, int> { { "UE", 2 }, { "ENB", 1 } },
"MON" => new Dictionary<string, int> { },
"EVENT" => new Dictionary<string, int> { },
"ALARM" => new Dictionary<string, int> { },
"LIC" => new Dictionary<string, int> { { "LICENSE", 1 } },
"OTS" => new Dictionary<string, int> { },
"ERROR" => new Dictionary<string, int> { },
_ => null
};
}
/// <summary>
/// 方向转换
/// 方向转换 - 对应JavaScript的_dirConvert方法
/// </summary>
/// <param name="log">日志对象</param>
/// <returns>转换后的方向值</returns>
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)
}
/// <summary>
@ -1278,6 +1183,9 @@ public class LTEClient
_layerDir.Clear();
}
// 初始化层方向配置
InitializeLayerDirectionConfig(model);
// 通知日志管理器更新网格列
_lteLogs.GridColumnsUpdate();
return true;
@ -1292,6 +1200,100 @@ public class LTEClient
_modelHint = hint;
}
/// <summary>
/// 初始化层方向配置 - 对应JavaScript版本的层方向配置初始化
/// </summary>
/// <param name="model">模型名称</param>
private void InitializeLayerDirectionConfig(string? model)
{
// 获取层配置
var layerConfig = _lteLogs.GetLayerConfig();
// 为每个层初始化默认方向配置
foreach (var layer in layerConfig.Keys)
{
if (!_layerDir.ContainsKey(layer))
{
_layerDir[layer] = new Dictionary<string, int>
{
{ "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 模型配置

13
LTEMvcApp/Models/LogsManager.cs

@ -891,5 +891,18 @@ namespace LTEMvcApp.Models
}
#endregion
#region 层配置方法
/// <summary>
/// 获取层配置
/// </summary>
/// <returns>层配置字典</returns>
public Dictionary<string, object> GetLayerConfig()
{
return layerConfig;
}
#endregion
}
}

166
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
/// <summary>
/// 方向转换 - 对应JavaScript的_dirConvert方法
/// </summary>
/// <param name="log">日志对象</param>
/// <returns>转换后的方向值</returns>
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
/// <summary>
/// 初始化层方向配置 - 对应JavaScript版本的层方向配置初始化
/// </summary>
/// <param name="model">模型名称</param>
private void InitializeLayerDirectionConfig(string? model)
{
// 获取层配置
var layerConfig = _lteLogs.GetLayerConfig();
// 为每个层初始化默认方向配置
foreach (var layer in layerConfig.Keys)
{
if (!_layerDir.ContainsKey(layer))
{
_layerDir[layer] = new Dictionary<string, int>
{
{ "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 的动态类型问题

343
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<Dictionary<string, object>> 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<string> { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" };
var availableModels = new List<string>(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<string, object> GetLayerConfig()
{
// 硬编码配置
return new Dictionary<string, object> { ... };
}
// 应该改为
private Dictionary<string, object> GetLayerConfig()
{
return _lteLogs.GetLayerConfig(); // 从 LogsManager 获取
}
```
#### 2. **方向配置处理**
需要实现与 JavaScript 版本相同的方向配置结构:
```csharp
// 当前实现使用 switch 表达式
private Dictionary<string, int>? GetLayerDirection(string layer)
// 应该改为直接访问层配置的 dir 属性
private Dictionary<string, int>? 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<Dictionary<string, object>> 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<string> { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" };
var availableModels = new List<string>(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<Dictionary<string, object>>
{
new Dictionary<string, object> { { "layer", "PHY" } },
new Dictionary<string, object> { { "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 版本更加一致。

150
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<string, int>;
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. 测试边界情况(如只有一个模型或多个模型的情况)
Loading…
Cancel
Save