using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace LTEMvcApp.Models { /// /// 日志管理器 - 从JavaScript logs.js迁移 /// public class LogsManager { #region 常量定义 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; #endregion #region 静态字段 private static readonly string[] modelList = { "RUE", "UE", "PROBE", "ENB", "N3IWF", "MBMSGW", "MME", "IMS", "LICENSE", "MONITOR" }; private static readonly Dictionary modelConfig = new() { { "RUE", new { icon = "icon-ue2" } }, { "UE", new { icon = "icon-ue" } }, { "PROBE", new { } }, { "ENB", new { icon = "icon-air", modelHint = "enb|gnb|bbu", title = "RAN" } }, { "N3IWF", new { icon = "icon-air", modelHint = "n3iwf", title = "N3IWF" } }, { "MME", new { icon = "icon-server", modelHint = "epc|mme|amf", title = "CN" } }, { "MBMSGW", new { icon = "icon-download", modelHint = "mbms" } }, { "IMS", new { icon = "icon-dial" } }, { "MONITOR", new { icon = "icon-monitor" } }, { "LICENSE", new { icon = "icon-file" } } }; private static readonly Dictionary layerConfig = new() { { "PHY", new { color = "#606070", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } }, { "MAC", new { color = "#10A0FF", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } }, { "RLC", new { color = "#FFFF80", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } } } }, { "PDCP", new { color = "#B0D0B0", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } } } }, { "RRC", new { color = "#00FF60", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } }, { "NAS", new { color = "#90FFC0", dir = new Dictionary { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } }, debug = new { level = "debug", max_size = 1 } } }, { "GTPU", new { color = "#FF80FF", dir = new Dictionary { { "ENB", 2 }, { "N3IWF", 2 }, { "MME", 1 }, { "MBMSGW", 1 }, { "UE", 1 }, { "RUE", 2 } }, epc = true, max = new { max_size = 32 } } }, { "GTPC", new { color = "#FFC0FF", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "IP", new { color = "#E0E0E0", dir = new Dictionary { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } }, max = new { max_size = 32 } } }, { "S1AP", new { color = "#80FF00", dir = new Dictionary { { "ENB", 2 }, { "MME", 1 } }, epc = true, debug = new { level = "debug", max_size = 1 } } }, { "NGAP", new { color = "#5DD122", dir = new Dictionary { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } }, epc = true, debug = new { level = "debug", max_size = 1 } } }, { "X2AP", new { color = "#FF8000", dir = new Dictionary { { "ENB", 2 } } } }, { "XnAP", new { color = "#FFB020", dir = new Dictionary { { "ENB", 2 } } } }, { "M2AP", new { color = "#7F675B", dir = new Dictionary { { "ENB", 2 }, { "MBMSGW", 1 } }, epc = true } }, { "IMS", new { color = "#C0FF80", dir = new Dictionary { { "MME", 2 }, { "IMS", 1 } }, debug = new { level = "debug", max_size = 1 }, epc = true } }, { "CX", new { color = "#F49542", dir = new Dictionary { { "MME", 2 }, { "IMS", 1 } }, epc = true } }, { "RX", new { color = "#D4A190", dir = new Dictionary { { "MME", 2 }, { "IMS", 1 } }, epc = true } }, { "COM", new { color = "#C000FF", dir = new Dictionary { { "*", 2 } } } }, { "SIP", new { color = "#C080FF", dir = new Dictionary { { "IMS", 1 } }, epc = true, debug = new { level = "debug", max_size = 1 } } }, { "MEDIA", new { color = "#800040", dir = new Dictionary { { "IMS", 1 } }, epc = true } }, { "RTP", new { color = "#FF00C0", dir = new Dictionary { { "IMS", 1 } }, epc = true } }, { "PROD", new { color = "#80C0FF", dir = new Dictionary { } } }, { "S6", new { color = "#F44B42", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "S13", new { color = "#D6F953", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "SGsAP", new { color = "#FF7DB8", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "SBcAP", new { color = "#8FA00F", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "N8", new { color = "#106020", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "N12", new { color = "#602020", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "N13", new { color = "#202060", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "N17", new { color = "#D6F953", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "N50", new { color = "#8FA00F", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "MMS", new { color = "#B4D98F", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "HTTP2", new { color = "#644824", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "LCSAP", new { color = "#cfd50d", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "LPPa", new { color = "#0dcfd5", dir = new Dictionary { { "ENB", 2 }, { "MME", 3 } }, epc = true } }, { "NL1", new { color = "#d040cf", dir = new Dictionary { { "MME", 2 } }, epc = true } }, { "NRPPa", new { color = "#0dd5cf", dir = new Dictionary { { "ENB", 2 }, { "MME", 3 } }, epc = true } }, { "IKEV2", new { color = "#C0B732", dir = new Dictionary { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } } } }, { "SWU", new { color = "#101080", dir = new Dictionary { { "UE", 2 }, { "MME", 1 } } } }, { "NWU", new { color = "#2080B8", dir = new Dictionary { { "UE", 2 }, { "MME", 1 } } } }, { "IPSEC", new { color = "#F04010", dir = new Dictionary { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } }, epc = true, max = new { max_size = 32 } } }, { "N3IWF", new { color = "#C080C0", dir = new Dictionary { { "UE", 2 }, { "N3IWF", 1 } } } }, { "TRX", new { color = "#42C0a0", dir = new Dictionary { { "UE", 2 }, { "ENB", 1 } }, debug = new { level = "debug" } } }, { "MON", new { color = "#C0C080", dir = new Dictionary { } } }, { "EVENT", new { color = "#80C0FF", dir = new Dictionary { } } }, { "ALARM", new { color = "#FF8040", dir = new Dictionary { } } }, { "LIC", new { color = "#ff80c0", dir = new Dictionary { { "LICENSE", 1 } } } }, { "OTS", new { color = "#8080FF", dir = new Dictionary { } } }, { "ERROR", new { color = "#ff0000", dir = new Dictionary { } } } }; #endregion #region 实例字段 private readonly Dictionary _logs = new(); private readonly int _logDefaultLevel = 0; // localStorage.debug >> 0 private readonly string[] _logsMethods = { "error", "warn", "info", "debug", "prof" }; private readonly long _startDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); private readonly Dictionary _stringIDs = new(); private readonly List _stringList = new(); private readonly Dictionary _eventListeners = new(); private int _eventListenerId = 0; #endregion #region 属性 public string Mode { get; set; } = "web"; public string FontDef { get; set; } = "13px helvetica, arial, verdana, sans-serif"; public int LastClientId { get; set; } = 0; public int LogsMax { get; set; } = 100000; public List LogsRange { get; set; } = new(); public List LogsInfo { get; set; } = new(); public List LogsLayers { get; set; } = new(); public string ExportTimeFormat { get; set; } = "HH:mm:ss.fff"; #endregion #region 构造函数 public LogsManager() { Init(); } #endregion #region 初始化方法 public void Init() { // 初始化通道定义 var def = new Dictionary(); var ids = new Dictionary(); // 这里可以添加通道定义的初始化逻辑 // 对应JavaScript中的_channelsDef.forEach逻辑 } #endregion #region 日志相关方法 public void SetLogger(string name, object instance) { var module = new { level = _logDefaultLevel, list = new List() }; _logs[name] = module; // 为每个日志方法创建对应的函数 for (int i = 0; i < _logsMethods.Length; i++) { var methodName = _logsMethods[i]; var level = 1 << i; // 这里需要动态设置instance的方法 } } private void Log(string module, string id, object log) { // 实现日志记录逻辑 var logObj = new { ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - _startDate, level = ((dynamic)log).level, args = ((dynamic)log).args }; var moduleObj = (dynamic)_logs[module]; moduleObj.list.Add(logObj); if (moduleObj.list.Count > 20) moduleObj.list.RemoveAt(0); if ((logObj.level & moduleObj.level) != 0) { if ((logObj.level & 0x1) != 0 && moduleObj.level == 0x1) { foreach (var item in moduleObj.list) { LogDisplay(id, item); } moduleObj.list.Clear(); } else { LogDisplay(id, logObj); } } } private void LogDisplay(string id, object log) { var time = Ts2Time(((dynamic)log).ts); var level = ((dynamic)log).level; var args = ((dynamic)log).args; string style = ""; int index = 0; if ((level & 0x1) != 0) { style = "background: red"; index = 0; } else if ((level & 0x2) != 0) { style = "background: yellow"; index = 1; } else if ((level & 0x10) != 0) { style = "background: #00ff00"; index = 4; } else { index = (int)Math.Floor(Math.Log2(level)); } // 这里可以输出到控制台或日志系统 Console.WriteLine($"[{time}][{_logsMethods[index].ToUpper()}][{id}]"); } #endregion #region 时间转换方法 public string Ts2Time(long ts, bool full = false) { // 持续时间 (< 2000-01-01 00:00:00) if (ts < 946681200000) return Ts2Duration(ts); var dateTime = DateTimeOffset.FromUnixTimeMilliseconds(ts).DateTime; var prefix = ""; if (full) { var day = dateTime.Day; var month = dateTime.Month; var year = dateTime.Year; prefix = $"{year}-{month:D2}-{day:D2} "; } var h = dateTime.Hour; var m = dateTime.Minute; var s = dateTime.Second; var ms = dateTime.Millisecond; return $"{prefix}{h:D2}:{m:D2}:{s:D2}.{ms:D3}"; } public string Ts2Duration(long ts) { var prefix = ""; if (ts < 0) { prefix = "-"; ts = -ts; } var timeSpan = TimeSpan.FromMilliseconds(ts); var h = (int)timeSpan.TotalHours; var m = timeSpan.Minutes; var s = timeSpan.Seconds; var ms = timeSpan.Milliseconds; return $"{prefix}{h:D2}:{m:D2}:{s:D2}.{ms:D3}"; } public long TsDayOffset(long ts) { if (ts < 946681200000) return 0; var dateTime = DateTimeOffset.FromUnixTimeMilliseconds(ts).DateTime; var dayStart = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0); return new DateTimeOffset(dayStart).ToUnixTimeMilliseconds(); } public long? Str2Ts(string value) { var match = Regex.Match(value, @"^(((\d+):)?(\d+):)?(\d+)(\.(\d+))?$"); if (match.Success) { var h = int.Parse(match.Groups[3].Success ? match.Groups[3].Value : "0"); var m = int.Parse(match.Groups[4].Success ? match.Groups[4].Value : "0"); var s = int.Parse(match.Groups[5].Value); var msStr = match.Groups[7].Success ? match.Groups[7].Value : "0"; var ms = (int)((int.Parse(msStr) * Math.Pow(10, 3 - msStr.Length))); return h * 3600000L + m * 60000L + s * 1000L + ms; } return null; } #endregion #region 字符串处理方法 public int String2Id(string str) { if (_stringIDs.ContainsKey(str)) return (int)_stringIDs[str]; var id = _stringList.Count + 1; _stringIDs[str] = id; _stringList.Add(str); return id; } public string Id2String(int id) { if (id > 0 && id <= _stringList.Count) return _stringList[id - 1]; return ""; } public string Tooltip(string txt) { return txt?.Replace(" ", " ") ?? ""; } public T Merge(T dst, T src, bool override_ = false) where T : class { // 实现对象合并逻辑 return dst; } public T Clone(T o) where T : class { // 实现对象克隆逻辑 return o; } public string PadLeft(string txt, char c, int len) { return txt.PadLeft(len, c); } #endregion #region 哈希和工具方法 public uint Hash(string str) { uint h = 5381; for (int i = 0; i < str.Length; i++) { uint c = (uint)str[i]; h = ((h << 5) + h) ^ c; } return h; } public string[] Levels { get; } = { "none", "error", "warn", "info", "debug" }; public int GetLevel(string str) { return Array.IndexOf(Levels, str); } public string GetHtmlIcon(string c) { return $""; } public List MapFilter(T[] array, Func func) { var res = new List(); for (int i = 0; i < array.Length; i++) { var item = func(array[i], i); if (item != null) res.Add(item); } return res; } public void DrawConstellation(object canvas, byte[] constel) { // 实现星座图绘制逻辑 } public string CreateIcon(string icon, string tooltip) { return $""; } public double GetColLight(string c) { var match = Regex.Match(c, @"#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})"); if (match.Success) { var r = int.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber); var g = int.Parse(match.Groups[2].Value, System.Globalization.NumberStyles.HexNumber); var b = int.Parse(match.Groups[3].Value, System.Globalization.NumberStyles.HexNumber); var h = (r * r * 0.299 + g * g * 0.587 + b * b * 0.114) / (255.0 * 255.0); return h; } return 1; } public string GetTextColor(string bgColor) { var h = GetColLight(bgColor); return h >= 0.15 ? "black" : "white"; } #endregion #region 数组操作方法 public void ArrayIntersect(List a, List b) { for (int i = a.Count - 1; i >= 0; i--) { var index = b.IndexOf(a[i]); if (index < 0) a.RemoveAt(i); } } public long String2Size(string str) { if (string.IsNullOrEmpty(str)) return 0; var match = Regex.Match(str, @"^(\d+)([KMG])$"); if (match.Success) { var size = long.Parse(match.Groups[1].Value); var unit = match.Groups[2].Value; return unit switch { "K" => size * 1000, "M" => size * 1000000, "G" => size * 1000000000, _ => size }; } return long.Parse(str); } public Dictionary SplitArgs(string args, Dictionary cfg) { var hash = new Dictionary(); var argList = Regex.Split(args, @"\s+"); foreach (var arg in argList) { var parts = arg.Split('='); if (parts.Length == 2) { var key = parts[0]; var value = parts[1]; if (cfg.ContainsKey(key)) { var cfgType = cfg[key].GetType(); if (cfgType == typeof(int)) { hash[key] = int.Parse(value); } else if (cfgType == typeof(long)) { hash[key] = long.Parse(value); } else { hash[key] = value; } } else { hash[key] = value; } } } return hash; } #endregion #region 事件处理方法 public int RegisterEventListener(string type, Action cb) { var id = _eventListenerId++; _eventListeners[id] = new { type = type, cb = cb }; return id; } public void UnregisterEventListener(int id) { _eventListeners.Remove(id); } public void SendEvent(object eventObj) { var type = ((dynamic)eventObj).type; foreach (var kvp in _eventListeners) { var el = (dynamic)kvp.Value; if (el.type == type || el.type == "*") { el.cb(eventObj); } } } #endregion #region 鼠标事件方法 public string GetMouseWheelEvent() { // 检测浏览器类型并返回相应的事件名 return "mousewheel"; // 简化实现 } public int GetMouseWheelDelta(object eventObj) { // 实现鼠标滚轮事件处理 return 0; } #endregion #region 文件导出方法 public void ExportFile(object data, string filename, string mimetype) { // 实现文件导出逻辑 } #endregion #region 日志排序方法 public int SortLogs(LTELog a, LTELog b) { var tsCompare = a.Timestamp.CompareTo(b.Timestamp); if (tsCompare != 0) return tsCompare; return a.Id.CompareTo(b.Id); } public int GetLogIndexByTimestamp(List list, long timestamp) { var l = list.Count; var b = l - 1; var a = 0; while (a <= b) { var i = (a + b) >> 1; var ts_i = list[i].Timestamp; if (timestamp < ts_i) { b = i - 1; } else if (timestamp > ts_i) { a = i + 1; } else { while (++i < l && list[i].Timestamp == timestamp) ; return i; } } return a; } public List MergeLogs(List srcList, List dstList) { var srcLength = srcList.Count; var t0 = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var dstLength = dstList.Count; if (dstLength > 500000 && srcLength < 50000) { // 排序新数据 srcList.Sort(SortLogs); var t1 = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // 插入 var a = GetLogIndexByTimestamp(dstList, srcList[0].Timestamp); for (int i = 0; i < srcLength; i++) { var log = srcList[i]; var ts = log.Timestamp; while (a < dstLength) { var ts_a = dstList[a].Timestamp; if (ts < ts_a) break; a++; } if (a == dstLength) { while (i < srcLength) { dstList.Add(srcList[i++]); } break; } dstList.Insert(a, log); a++; dstLength++; } } else { dstList.AddRange(srcList); var t1 = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); dstList.Sort(SortLogs); dstLength = dstList.Count; } return dstList; } #endregion #region 鼠标移动注册方法 public void RegisterMouseMove(Action cb) { // 实现鼠标移动事件注册 } #endregion #region 存储更新方法 public void StoreUpdate(Dictionary cache, List store, List list, string id, bool addOnly = false) { var found = new Dictionary(); foreach (var item in list) { var prop = ((dynamic)item).GetType().GetProperty(id)?.GetValue(item)?.ToString(); if (prop != null) { if (!cache.ContainsKey(prop)) { store.Add(item); cache[prop] = item; } else { // 更新现有项 var index = store.IndexOf(cache[prop]); if (index >= 0) store[index] = item; } found[prop] = true; } } if (!addOnly) { for (int i = store.Count - 1; i >= 0; i--) { var item = store[i]; var prop = ((dynamic)item).GetType().GetProperty(id)?.GetValue(item)?.ToString(); if (prop != null && !found.ContainsKey(prop)) { store.RemoveAt(i); cache.Remove(prop); } } } } public T StoreGetById(List store, string id, object value) { foreach (var item in store) { var itemValue = ((dynamic)item).GetType().GetProperty(id)?.GetValue(item); if (Equals(itemValue, value)) return item; } return default(T); } #endregion #region 日志状态更新方法 public void LogStateUpdate(object logState, Action cb) { // 实现日志状态更新逻辑 cb?.Invoke(); } public void LogStateUpdateEnd(object logState, List logs, Action cb) { // 实现日志状态更新结束逻辑 cb?.Invoke(); } #endregion #region Canvas设置方法 public void SetCanvasSize(object canvas, int width, int height) { // 实现Canvas大小设置逻辑 } #endregion #region 文件加载方法 public void LoadFile(object options) { // 实现文件加载逻辑 } #endregion #region 警告框方法 public void WarnBox(string title, string message, Action cb = null, object scope = null) { // 实现警告框显示逻辑 } #endregion #region 窗口自动高度设置方法 public void SetWindowAutoHeight(object win, object config) { // 实现窗口自动高度设置逻辑 } #endregion #region 正则表达式方法 public Regex GetRexExp(string value) { var match = Regex.Match(value ?? "", @"^\/(.+)\/([gi]*)$"); try { if (match.Success) { var pattern = match.Groups[1].Value; var flags = match.Groups[2].Value; var options = RegexOptions.None; if (flags.Contains("i")) options |= RegexOptions.IgnoreCase; if (flags.Contains("g")) options |= RegexOptions.Multiline; return new Regex($"({pattern})", options); } return new Regex($"({value})", RegexOptions.IgnoreCase | RegexOptions.Multiline); } catch { return null; } } #endregion #region 本地存储方法 public T LocalStorageGet(string name) { // 实现本地存储获取逻辑 return default(T); } public void LocalStorageSet(string name, T value) { // 实现本地存储设置逻辑 } #endregion #region 测试方法 public void TestColor() { // 实现颜色测试逻辑 } #endregion #region 工具方法 public string FilterValueFormater(string type, object value, bool highLight = false) { // 实现过滤器值格式化逻辑 return value?.ToString() ?? ""; } public void UpdateFilter() { // 实现过滤器更新逻辑 } public void LockFilter(bool state) { // 实现过滤器锁定逻辑 } public void SelectLog(LTELog log) { // 实现日志选择逻辑 } public void GotoTime(object time) { // 实现时间跳转逻辑 } public void UpdateLogs(object parameters = null) { // 实现日志更新逻辑 } public List GetLogs(object client = null) { // 实现获取日志逻辑 return new List(); } public List GetClients() { // 实现获取客户端列表逻辑 return new List(); } public void AddClient(object cfg, bool persistent = false, bool save = true) { // 实现添加客户端逻辑 } public object GetClientById(string storeId) { // 实现根据ID获取客户端逻辑 return null; } public bool RemoveClient(object client) { // 实现移除客户端逻辑 return false; } public void SaveConfig() { // 实现配置保存逻辑 } public void RefreshClientGrid() { // 实现客户端网格刷新逻辑 } public void Load(bool loading) { // 实现加载状态设置逻辑 } public void GridColumnsUpdate() { // 实现网格列更新逻辑 } public void ContextMenu(List items, LTELog log) { // 实现上下文菜单逻辑 } public void AddTab(object tab, bool active = false) { // 实现添加标签页逻辑 } #endregion } }