You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
908 lines
30 KiB
908 lines
30 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LTEMvcApp.Models
|
|
{
|
|
/// <summary>
|
|
/// 日志管理器 - 从JavaScript logs.js迁移
|
|
/// </summary>
|
|
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<string, object> 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<string, object> layerConfig = new()
|
|
{
|
|
{ "PHY", new { color = "#606070", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "MAC", new { color = "#10A0FF", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "RLC", new { color = "#FFFF80", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } } } },
|
|
{ "PDCP", new { color = "#B0D0B0", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } } } },
|
|
{ "RRC", new { color = "#00FF60", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 1 } }, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "NAS", new { color = "#90FFC0", dir = new Dictionary<string, int> { { "UE", 2 }, { "PROBE", 3 }, { "ENB", 3 }, { "N3IWF", 3 }, { "MME", 1 } }, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "GTPU", new { color = "#FF80FF", dir = new Dictionary<string, int> { { "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<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "IP", new { color = "#E0E0E0", dir = new Dictionary<string, int> { { "RUE", 2 }, { "UE", 2 }, { "N3IWF", 3 }, { "PROBE", 3 }, { "MME", 3 } }, max = new { max_size = 32 } } },
|
|
{ "S1AP", new { color = "#80FF00", dir = new Dictionary<string, int> { { "ENB", 2 }, { "MME", 1 } }, epc = true, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "NGAP", new { color = "#5DD122", dir = new Dictionary<string, int> { { "ENB", 2 }, { "MME", 1 }, { "N3IWF", 2 } }, epc = true, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "X2AP", new { color = "#FF8000", dir = new Dictionary<string, int> { { "ENB", 2 } } } },
|
|
{ "XnAP", new { color = "#FFB020", dir = new Dictionary<string, int> { { "ENB", 2 } } } },
|
|
{ "M2AP", new { color = "#7F675B", dir = new Dictionary<string, int> { { "ENB", 2 }, { "MBMSGW", 1 } }, epc = true } },
|
|
{ "IMS", new { color = "#C0FF80", dir = new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } }, debug = new { level = "debug", max_size = 1 }, epc = true } },
|
|
{ "CX", new { color = "#F49542", dir = new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } }, epc = true } },
|
|
{ "RX", new { color = "#D4A190", dir = new Dictionary<string, int> { { "MME", 2 }, { "IMS", 1 } }, epc = true } },
|
|
{ "COM", new { color = "#C000FF", dir = new Dictionary<string, int> { { "*", 2 } } } },
|
|
{ "SIP", new { color = "#C080FF", dir = new Dictionary<string, int> { { "IMS", 1 } }, epc = true, debug = new { level = "debug", max_size = 1 } } },
|
|
{ "MEDIA", new { color = "#800040", dir = new Dictionary<string, int> { { "IMS", 1 } }, epc = true } },
|
|
{ "RTP", new { color = "#FF00C0", dir = new Dictionary<string, int> { { "IMS", 1 } }, epc = true } },
|
|
{ "PROD", new { color = "#80C0FF", dir = new Dictionary<string, int> { } } },
|
|
{ "S6", new { color = "#F44B42", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "S13", new { color = "#D6F953", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "SGsAP", new { color = "#FF7DB8", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "SBcAP", new { color = "#8FA00F", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "N8", new { color = "#106020", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "N12", new { color = "#602020", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "N13", new { color = "#202060", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "N17", new { color = "#D6F953", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "N50", new { color = "#8FA00F", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "MMS", new { color = "#B4D98F", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "HTTP2", new { color = "#644824", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "LCSAP", new { color = "#cfd50d", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "LPPa", new { color = "#0dcfd5", dir = new Dictionary<string, int> { { "ENB", 2 }, { "MME", 3 } }, epc = true } },
|
|
{ "NL1", new { color = "#d040cf", dir = new Dictionary<string, int> { { "MME", 2 } }, epc = true } },
|
|
{ "NRPPa", new { color = "#0dd5cf", dir = new Dictionary<string, int> { { "ENB", 2 }, { "MME", 3 } }, epc = true } },
|
|
{ "IKEV2", new { color = "#C0B732", dir = new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 }, { "N3IWF", 1 } } } },
|
|
{ "SWU", new { color = "#101080", dir = new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 } } } },
|
|
{ "NWU", new { color = "#2080B8", dir = new Dictionary<string, int> { { "UE", 2 }, { "MME", 1 } } } },
|
|
{ "IPSEC", new { color = "#F04010", dir = new Dictionary<string, int> { { "UE", 2 }, { "IMS", 1 }, { "N3IWF", 1 }, { "MME", 1 } }, epc = true, max = new { max_size = 32 } } },
|
|
{ "N3IWF", new { color = "#C080C0", dir = new Dictionary<string, int> { { "UE", 2 }, { "N3IWF", 1 } } } },
|
|
{ "TRX", new { color = "#42C0a0", dir = new Dictionary<string, int> { { "UE", 2 }, { "ENB", 1 } }, debug = new { level = "debug" } } },
|
|
{ "MON", new { color = "#C0C080", dir = new Dictionary<string, int> { } } },
|
|
{ "EVENT", new { color = "#80C0FF", dir = new Dictionary<string, int> { } } },
|
|
{ "ALARM", new { color = "#FF8040", dir = new Dictionary<string, int> { } } },
|
|
{ "LIC", new { color = "#ff80c0", dir = new Dictionary<string, int> { { "LICENSE", 1 } } } },
|
|
{ "OTS", new { color = "#8080FF", dir = new Dictionary<string, int> { } } },
|
|
{ "ERROR", new { color = "#ff0000", dir = new Dictionary<string, int> { } } }
|
|
};
|
|
|
|
#endregion
|
|
|
|
#region 实例字段
|
|
|
|
private readonly Dictionary<string, object> _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<string, object> _stringIDs = new();
|
|
private readonly List<string> _stringList = new();
|
|
|
|
private readonly Dictionary<int, object> _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<int[]> LogsRange { get; set; } = new();
|
|
public List<int> LogsInfo { get; set; } = new();
|
|
public List<string> 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<string, object>();
|
|
var ids = new Dictionary<string, int>();
|
|
|
|
// 这里可以添加通道定义的初始化逻辑
|
|
// 对应JavaScript中的_channelsDef.forEach逻辑
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 日志相关方法
|
|
|
|
public void SetLogger(string name, object instance)
|
|
{
|
|
var module = new { level = _logDefaultLevel, list = new List<object>() };
|
|
_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>(T dst, T src, bool override_ = false) where T : class
|
|
{
|
|
// 实现对象合并逻辑
|
|
return dst;
|
|
}
|
|
|
|
public T Clone<T>(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 $"<img src='data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' class='x-tree-icon {c}'>";
|
|
}
|
|
|
|
public List<T> MapFilter<T>(T[] array, Func<T, int, T> func)
|
|
{
|
|
var res = new List<T>();
|
|
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 $"<img src=\"resources/images/{icon}.png\" height=12 title=\"{tooltip}\">";
|
|
}
|
|
|
|
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<T>(List<T> a, List<T> 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<string, object> SplitArgs(string args, Dictionary<string, object> cfg)
|
|
{
|
|
var hash = new Dictionary<string, object>();
|
|
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<object> 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<LTELog> 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<LTELog> MergeLogs(List<LTELog> srcList, List<LTELog> 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<object> cb)
|
|
{
|
|
// 实现鼠标移动事件注册
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 存储更新方法
|
|
|
|
public void StoreUpdate<T>(Dictionary<string, T> cache, List<T> store, List<T> list, string id, bool addOnly = false)
|
|
{
|
|
var found = new Dictionary<string, bool>();
|
|
|
|
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<T>(List<T> 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<LTELog> 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<T>(string name)
|
|
{
|
|
// 实现本地存储获取逻辑
|
|
return default(T);
|
|
}
|
|
|
|
public void LocalStorageSet<T>(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<LTELog> GetLogs(object client = null)
|
|
{
|
|
// 实现获取日志逻辑
|
|
return new List<LTELog>();
|
|
}
|
|
|
|
public List<object> GetClients()
|
|
{
|
|
// 实现获取客户端列表逻辑
|
|
return new List<object>();
|
|
}
|
|
|
|
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<object> items, LTELog log)
|
|
{
|
|
// 实现上下文菜单逻辑
|
|
}
|
|
|
|
public void AddTab(object tab, bool active = false)
|
|
{
|
|
// 实现添加标签页逻辑
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 层配置方法
|
|
|
|
/// <summary>
|
|
/// 获取层配置
|
|
/// </summary>
|
|
/// <returns>层配置字典</returns>
|
|
public Dictionary<string, object> GetLayerConfig()
|
|
{
|
|
return layerConfig;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|