using System.Collections.Immutable;
using System.Text.RegularExpressions;
using System.Threading;
using Newtonsoft.Json;
namespace LTEMvcApp.Models;
///
/// LTE客户端类 - 对应JavaScript中的lte.client对象
///
public class LTEClient
{
#region 常量定义
///
/// HFN回绕阈值
///
private const int HFN_WRAP_THRESHOLD = 512;
///
/// 最大日志数量
///
private const int LOGS_MAX = 2000000;
///
/// BSR表 - 对应JavaScript中的_bsr_table
///
private 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 正则表达式
///
/// PHY层日志正则表达式
///
private static readonly Regex RegExpPhy = new(@"^([a-f0-9\-]+)\s+([a-f0-9\-]+)\s+([\d\.\-]+) (\w+): (.+)", RegexOptions.Compiled);
///
/// 信息1正则表达式
///
private static readonly Regex RegExpInfo1 = new(@"^([\w\-]+): (.+)", RegexOptions.Compiled);
///
/// 信息2正则表达式
///
private static readonly Regex RegExpInfo2 = new(@"^([\w]+) (.+)", RegexOptions.Compiled);
///
/// IP日志正则表达式
///
private static readonly Regex RegExpIP = new(@"^(len=\d+)\s+(\S+)\s+(.*)", RegexOptions.Compiled);
///
/// IPsec日志正则表达式
///
private static readonly Regex RegExpIPsec = new(@"^len=(\d+)\s+(.*)", RegexOptions.Compiled);
///
/// SDU长度正则表达式
///
private static readonly Regex RegExpSDULen = new(@"SDU_len=(\d+)", RegexOptions.Compiled);
///
/// SIP日志正则表达式
///
private static readonly Regex RegExpSIP = new(@"^([:\.\[\]\da-f]+)\s+(\S+) (.+)", RegexOptions.Compiled);
///
/// 媒体请求正则表达式
///
private static readonly Regex RegExpMediaReq = new(@"^(\S+) (.+)", RegexOptions.Compiled);
///
/// 信号记录正则表达式
///
private static readonly Regex RegExpSignalRecord = new(@"Link:\s([\w\d]+)@(\d+)", RegexOptions.Compiled);
///
/// 小区ID正则表达式
///
private static readonly Regex RegExpCellID = new(@"^([a-f0-9\-]+) (.+)", RegexOptions.Compiled);
///
/// RRC UE ID正则表达式
///
private static readonly Regex RegExpRRC_UE_ID = new(@"Changing UE_ID to 0x(\d+)", RegexOptions.Compiled);
///
/// RRC TMSI正则表达式
///
private static readonly Regex RegExpRRC_TMSI = new(@"(5G|m)-TMSI '([\dA-F]+)'H", RegexOptions.Compiled);
///
/// RRC新ID正则表达式
///
private static readonly Regex RegExpRRC_NEW_ID = new(@"newUE-Identity (['\dA-FH]+)", RegexOptions.Compiled);
///
/// RRC CRNTI正则表达式
///
private static readonly Regex RegExpRRC_CRNTI = new(@"c-RNTI '([\dA-F]+)'H", RegexOptions.Compiled);
///
/// NAS TMSI正则表达式
///
private static readonly Regex RegExpNAS_TMSI = new(@"m-TMSI = 0x([\da-f]+)", RegexOptions.Compiled);
///
/// NAS 5G TMSI正则表达式
///
private static readonly Regex RegExpNAS_5GTMSI = new(@"5G-TMSI = 0x([\da-f]+)", RegexOptions.Compiled);
///
/// RRC频段组合正则表达式
///
private static readonly Regex RegExpRRC_BC = new(@"(EUTRA|MRDC|NR|NRDC) band combinations", RegexOptions.Compiled);
///
/// PDCCH正则表达式
///
private static readonly Regex RegExpPDCCH = new(@"^\s*(.+)=(\d+)$", RegexOptions.Compiled);
///
/// S1/NGAP正则表达式
///
private static readonly Regex RegExpS1NGAP = new(@"^([\da-f\-]+)\s+([\da-f\-]+) (([^\s]+) .+)", RegexOptions.Compiled);
///
/// 十六进制转储正则表达式
///
private static readonly Regex RegExpHexDump = new(@"^[\da-f]+:(\s+[\da-f]{2}){1,16}\s+.{1,16}$", RegexOptions.Compiled);
#endregion
#region 基础属性
///
/// 客户端ID
///
public int ClientId { get; set; }
///
/// 客户端名称
///
public string Name { get; set; } = string.Empty;
///
/// 客户端配置
///
public ClientConfig Config { get; set; } = new();
///
/// 客户端状态
///
public ClientState State { get; set; } = ClientState.Stop;
///
/// 版本信息
///
public string? Version { get; set; }
///
/// 许可证信息
///
public string? License { get; set; }
///
/// 模型类型
///
public string? Model { get; set; }
///
/// 是否有信号记录
///
public bool HasSignalRecord { get; set; }
///
/// 头部信息
///
public string[]? Headers { get; set; }
///
/// 模式(ran/ims)
///
public string Mode { get; set; } = "ran";
#endregion
#region 日志相关属性
///
/// 日志列表
///
public List Logs { get; set; } = new();
///
/// 日志数量
///
public int LogCount => Logs.Count;
///
/// 是否已过滤
///
public bool Filtered { get; set; }
///
/// 重置标志
///
public bool ResetFlag { get; set; }
#endregion
#region 解析器状态
///
/// 最后HARQ信息
///
public Dictionary> LastHarq { get; set; } = new();
///
/// 最后NB HARQ信息
///
public Dictionary>> LastNbHarq { get; set; } = new();
///
/// 帧信息
///
public FrameInfo Frame { get; set; } = new();
///
/// TMSI到UE ID的映射
///
public Dictionary TmsiToUeId { get; set; } = new();
///
/// RNTI到UE ID的映射
///
public Dictionary RntiToUeId { get; set; } = new();
///
/// UE列表
///
public Dictionary UeList { get; set; } = new();
///
/// 最后时间戳
///
public long LastTimestamp { get; set; }
///
/// 时间戳偏移
///
public long TimestampOffset { get; set; }
///
/// 最后小区
///
public int? LastCell { get; set; }
#endregion
#region 功能标志
///
/// 是否有小区信息
///
public bool HasCell { get; set; }
///
/// 是否有物理层信息
///
public bool HasPhy { get; set; }
///
/// 是否有数据
///
public bool HasData { get; set; }
///
/// 是否有RNTI
///
public bool HasRnti { get; set; }
///
/// 是否有资源块
///
public bool HasRb { get; set; }
#endregion
#region 参数和组件
#region
private int PDSCH;
private int PDCCH;
private int EPDCCH;
private int PUSCH;
private int PUCCH;
private int NPDSCH;
private int NPUSCH;
private int NPBCH;
private int BCCH_NR;
private int ID_HINTS;
private int SRS;
private int CSI;
private int SIM_EVENT;
private int IP_SIM;
#endregion
///
/// 参数信息
///
public Dictionary Parameters { get; set; } = new();
///
/// 组件列表
///
public Dictionary Components { get; set; } = new();
#endregion
#region 依赖注入
///
/// 日志管理器
///
private readonly LogsManager _lteLogs;
///
/// 获取日志管理器实例
///
public LogsManager LogsManager => _lteLogs;
#endregion
#region 构造函数
public LTEClient(ClientConfig config, LogsManager logsManager)
{
Config = config;
Name = config.Name;
ClientId = GenerateClientId();
_lteLogs = logsManager;
ResetParserState();
}
///
/// 使用名称创建客户端
///
/// 客户端名称
/// 日志管理器
public LTEClient(string name, LogsManager logsManager)
{
Config = new ClientConfig { Name = name, Enabled = true };
Name = name;
ClientId = GenerateClientId();
_lteLogs = logsManager;
ResetParserState();
}
#endregion
#region 客户端控制方法
///
/// 启动客户端
///
public void Start()
{
SetState(ClientState.Start);
}
///
/// 停止客户端
///
public void Stop()
{
SetState(ClientState.Stop);
}
///
/// 销毁客户端
///
public void Destroy()
{
SetState(ClientState.Destroy);
}
#endregion
#region 日志管理方法
///
/// 重置日志
///
public void ResetLogs()
{
if (LogCount > 0)
{
ResetFlag = true;
Logs.Clear();
ResetParserState();
HasCell = false;
HasPhy = false;
HasData = false;
HasRnti = false;
HasRb = false;
HasSignalRecord = false;
}
}
///
/// 添加日志
///
public void AddLog(LTELog log)
{
// 检查时间戳回绕
var timestamp = log.Timestamp + TimestampOffset;
if (timestamp < LastTimestamp - 100)
{
Console.WriteLine($"Log wrap by {LastTimestamp - timestamp}");
timestamp += 86400000; // 24小时
TimestampOffset += 86400000;
}
LastTimestamp = log.Timestamp = timestamp;
//log.Client = this;
log.Id = GenerateLogId();
}
#endregion
#region 工具方法
///
/// 设置头信息 - 对应JavaScript的setHeaders方法
///
/// 头信息数组
public void SetHeaders(string[] headers)
{
// 保存头信息
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)
{
if (string.IsNullOrEmpty(Config.Model))
{
var model = info.Groups[1].Value.ToUpper();
if (_clientModelConfigs.ContainsKey(model))
{
SetModel(model);
}
}
Version = info.Groups[2].Value;
}
// 检查许可证信息
else if (Regex.IsMatch(header, @"Licensed to"))
{
License = header;
}
// 检查元数据信息
else if ((info = Regex.Match(header, @"Metadata:(.+)$")).Success)
{
try
{
var metadata = JsonConvert.DeserializeObject>(info.Groups[1].Value);
if (metadata.TryGetValue("model", out var model))
{
SetModel(model.ToString());
}
}
catch
{
// 忽略JSON解析错误
}
}
// 检查RAN ID信息
else if ((info = Regex.Match(header, @"(global_(ran_node|enb)_id)=([\d\.]+)")).Success)
{
SetModel("ENB");
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]);
}
}
}
}
}
// 添加所有小区
foreach (var cell in cells)
{
var cellId = (int)cell["cell_id"];
AddCell(cellId, cell);
}
// 通知日志管理器刷新客户端网格
_lteLogs.RefreshClientGrid();
}
///
/// 处理头信息中的小区参数 - 对应JavaScript的_headerCellParam方法
///
/// 参数名
/// 参数值
/// 处理后的值
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;
}
}
///
/// 添加小区 - 对应JavaScript的_addCell方法
///
/// 小区ID
/// 小区配置
/// 小区对象
public LTECell AddCell(int id, Dictionary config)
{
var cell = new LTECell(id, config);
if (Parameters.ContainsKey("cells") && Parameters["cells"] is Dictionary cells)
{
cells[id.ToString()] = cell;
}
else
{
Parameters["cells"] = new Dictionary { [id.ToString()] = cell };
}
return cell;
}
///
/// 设置RAN ID - 对应JavaScript的setRanId方法
///
/// RAN ID
public void SetRanId(string ranId)
{
RanIds.Add(ranId);
// 处理RAN ID链接(简化实现,因为C#版本没有clientList)
// 在实际应用中,这里可能需要与其他客户端进行协调
}
///
/// 初始化模型猜测
///
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 = _lteLogs.GetLayerConfig();
foreach (var layer in layerConfig.Keys)
{
_modelGuess[layer] = false;
}
}
}
///
/// 模型猜测字典
///
private Dictionary? _modelGuess;
///
/// 模型猜测
///
/// 日志列表
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);
// 根据层配置过滤模型(与 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)
{
if (availableModels.Count == 1)
{
SetModel(availableModels[0]);
}
else
{
// 支持多个模型的情况,传递第一个模型
SetModel(availableModels[0]);
}
_modelGuess = null; // 清除猜测字典
}
}
///
/// 方向转换 - 对应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)
}
///
/// 字符串转ID - 委托给LogsManager
///
public int StringToId(string str)
{
return _lteLogs.String2Id(str);
}
///
/// ID转字符串 - 委托给LogsManager
///
public string IdToString(int id)
{
return _lteLogs.Id2String(id);
}
#endregion
#region 日志解析方法
///
/// 解析日志列表 - 对应JavaScript的_logListParse方法
///
/// 日志列表
/// 是否为WebSocket消息
public void ParseLogList(List logs, bool isWebSocket = false)
{
var length = logs.Count;
for (int i = 0; i < length; i++)
{
var log = logs[i];
log.Message = log.Message + "";
AddLog(log);
// 解析消息
switch (log.Layer)
{
case "PHY":
if (!ParsePhyLog(log, log.Message, isWebSocket)) continue;
ProcessPhyLog(log);
break;
case "RRC":
ParseCellId(log, isWebSocket);
var rrcUeIdMatch = RegExpRRC_UE_ID.Match(log.Message);
if (rrcUeIdMatch.Success)
{
SetSameUe(log, int.Parse(rrcUeIdMatch.Groups[1].Value, System.Globalization.NumberStyles.HexNumber));
continue;
}
var rrcInfoMatch = RegExpInfo1.Match(log.Message);
if (rrcInfoMatch.Success)
{
if (!SetLogInfo(log, rrcInfoMatch.Groups[1].Value)) continue;
log.Message = rrcInfoMatch.Groups[2].Value;
ProcessRrcLog(log);
}
var rrcBcMatch = RegExpRRC_BC.Match(log.Message);
if (rrcBcMatch.Success)
{
try
{
var data = log.GetDataString();
var jsonData = JsonConvert.DeserializeObject