From bdb79e8f655031d0a3dcb3cf4d6d24a82cfef526 Mon Sep 17 00:00:00 2001 From: root <295172551@qq.com> Date: Sun, 29 Jun 2025 16:41:20 +0800 Subject: [PATCH] dasdad1 --- LTEMvcApp/Models/ASN1Extensions.cs | 270 +++++++++++++++++ LTEMvcApp/Models/ASN1Parser.cs | 365 +++++++++++++++++++++++ LTEMvcApp/Models/LTECapabilities.cs | 441 ++++++++++++++++++++++++++++ LTEMvcApp/Models/LTEClient.cs | 3 +- LTEMvcApp/Models/StatisticsData.cs | 11 +- 5 files changed, 1086 insertions(+), 4 deletions(-) create mode 100644 LTEMvcApp/Models/ASN1Extensions.cs create mode 100644 LTEMvcApp/Models/ASN1Parser.cs create mode 100644 LTEMvcApp/Models/LTECapabilities.cs diff --git a/LTEMvcApp/Models/ASN1Extensions.cs b/LTEMvcApp/Models/ASN1Extensions.cs new file mode 100644 index 0000000..d3908a3 --- /dev/null +++ b/LTEMvcApp/Models/ASN1Extensions.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LTEMvcApp.Models +{ + /// + /// ASN1扩展方法 + /// + public static class ASN1Extensions + { + /// + /// 安全获取字典值 + /// + public static T GetValueOrDefault(this Dictionary dict, string key, T defaultValue = default(T)) + { + if (dict != null && dict.ContainsKey(key)) + { + try + { + return (T)Convert.ChangeType(dict[key], typeof(T)); + } + catch + { + return defaultValue; + } + } + return defaultValue; + } + + /// + /// 安全获取字典值 + /// + public static object GetValueOrDefault(this Dictionary dict, string key) + { + return dict?.ContainsKey(key) == true ? dict[key] : null; + } + + /// + /// 将对象转换为字典 + /// + public static Dictionary ToDictionary(this object obj) + { + if (obj is Dictionary dict) + return dict; + + if (obj is IDictionary idict) + return new Dictionary(idict); + + return null; + } + + /// + /// 将对象转换为列表 + /// + public static List ToList(this object obj) + { + if (obj is List list) + return list; + + if (obj is IEnumerable enumerable) + return enumerable.ToList(); + + return null; + } + + /// + /// 安全转换为整数 + /// + public static int? ToInt32(this object obj) + { + if (obj == null) return null; + + try + { + return Convert.ToInt32(obj); + } + catch + { + return null; + } + } + + /// + /// 安全转换为字符串 + /// + public static string ToSafeString(this object obj) + { + return obj?.ToString(); + } + + /// + /// 安全转换为布尔值 + /// + public static bool? ToBoolean(this object obj) + { + if (obj == null) return null; + + try + { + return Convert.ToBoolean(obj); + } + catch + { + return null; + } + } + } + + /// + /// ASN1工具类 + /// + public static class ASN1Utils + { + /// + /// 创建ASN1对象 + /// + public static Dictionary CreateASN1Object() + { + return new Dictionary(); + } + + /// + /// 创建ASN1数组 + /// + public static List CreateASN1Array() + { + return new List(); + } + + /// + /// 添加属性到ASN1对象 + /// + public static void AddProperty(Dictionary obj, string name, object value) + { + if (obj == null) return; + + if (string.IsNullOrEmpty(name)) + { + if (obj.ContainsKey("")) + { + var existing = obj[""]; + if (existing is List list) + { + list.Add(value); + } + else + { + obj[""] = new List { existing, value }; + } + } + else + { + obj[""] = new List { value }; + } + } + else + { + if (obj.ContainsKey(name)) + { + var existing = obj[name]; + if (existing is List list) + { + list.Add(value); + } + else + { + obj[name] = new List { existing, value }; + } + } + else + { + obj[name] = value; + } + } + } + + /// + /// 添加元素到ASN1数组 + /// + public static void AddElement(List array, object value) + { + array?.Add(value); + } + + /// + /// 深度克隆ASN1对象 + /// + public static Dictionary DeepClone(Dictionary obj) + { + if (obj == null) return null; + + var result = new Dictionary(); + foreach (var kvp in obj) + { + if (kvp.Value is Dictionary dict) + { + result[kvp.Key] = DeepClone(dict); + } + else if (kvp.Value is List list) + { + result[kvp.Key] = DeepCloneList(list); + } + else + { + result[kvp.Key] = kvp.Value; + } + } + return result; + } + + /// + /// 深度克隆ASN1数组 + /// + public static List DeepCloneList(List list) + { + if (list == null) return null; + + var result = new List(); + foreach (var item in list) + { + if (item is Dictionary dict) + { + result.Add(DeepClone(dict)); + } + else if (item is List subList) + { + result.Add(DeepCloneList(subList)); + } + else + { + result.Add(item); + } + } + return result; + } + + /// + /// 将ASN1对象转换为JSON字符串 + /// + public static string ToJson(Dictionary obj, bool prettyPrint = false) + { + if (obj == null) return "null"; + + var settings = new Newtonsoft.Json.JsonSerializerSettings + { + Formatting = prettyPrint ? Newtonsoft.Json.Formatting.Indented : Newtonsoft.Json.Formatting.None + }; + + return Newtonsoft.Json.JsonConvert.SerializeObject(obj, settings); + } + + /// + /// 从JSON字符串创建ASN1对象 + /// + public static Dictionary FromJson(string json) + { + if (string.IsNullOrEmpty(json)) return null; + + try + { + return Newtonsoft.Json.JsonConvert.DeserializeObject>(json); + } + catch + { + return null; + } + } + } +} \ No newline at end of file diff --git a/LTEMvcApp/Models/ASN1Parser.cs b/LTEMvcApp/Models/ASN1Parser.cs new file mode 100644 index 0000000..51b34d5 --- /dev/null +++ b/LTEMvcApp/Models/ASN1Parser.cs @@ -0,0 +1,365 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using Newtonsoft.Json; + +namespace LTEMvcApp.Models +{ + /// + /// ASN1解析器,用于解析GSER格式的ASN1数据 + /// + public class ASN1Parser + { + private static readonly Regex RegExpOpen = new Regex(@"((?! )[\w-\s]+(? + /// 从GSER格式的字符串数组解析ASN1数据 + /// + /// GSER格式的字符串数组 + /// 解析后的ASN1对象 + public static Dictionary FromGSER(string[] data) + { + if (data == null || data.Length == 0) + return null; + + var root = (ASN1Node)null; + var stack = new Stack(); + + try + { + int i = 0; + // 移除十六进制转储 + for (; i < data.Length; i++) + { + var d = data[i]; + if (!RegExpHexDump.IsMatch(d)) + break; + } + + for (; i < data.Length; i++) + { + var d = data[i]; + + // 移除注释 + for (int j = 0; j < 10; j++) // 最多10次迭代 + { + var c0 = d.IndexOf("/*"); + if (c0 < 0) break; + + var c1 = d.IndexOf("*/"); + if (c1 >= 0) + { + d = d.Remove(c0, c1 + 2 - c0); + } + else + { + d = d.Substring(0, c0); + for (i++; i < data.Length; i++) + { + var d1 = data[i]; + var c2 = d1.IndexOf("*/"); + if (c2 >= 0) + { + d += d1.Substring(c2 + 2); + break; + } + } + } + } + + var match = RegExpOpen.Match(d); + if (match.Success) + { + var node = new ASN1Node { Name = match.Groups[1].Success ? match.Groups[1].Value.Trim() : null }; + if (root == null) root = node; + stack.Push(node); + continue; + } + + if (stack.Count == 0) continue; + + match = RegExpClose.Match(d); + if (match.Success) + { + var node = stack.Pop(); + if (stack.Count == 0) // 结束 + break; + + AddProperty(stack.Peek(), node.Name, node.Obj ?? new Dictionary()); + continue; + } + + // 数值常量 + match = RegExpValueNum.Match(d); + if (match.Success) + { + AddProperty(stack.Peek(), match.Groups[1].Value, int.Parse(match.Groups[2].Value)); + } + // 通用字符串 + else if ((match = RegExpValueEnumerated.Match(d)).Success) + { + var value = match.Groups[2].Value; + switch (value) + { + case "TRUE": + AddProperty(stack.Peek(), match.Groups[1].Value, true); + break; + case "FALSE": + AddProperty(stack.Peek(), match.Groups[1].Value, false); + break; + default: + AddProperty(stack.Peek(), match.Groups[1].Value, value); + break; + } + } + // 带基本值的Choice + else if ((match = RegExpValueChoice.Match(d)).Success) + { + AddProperty(stack.Peek(), match.Groups[1].Value, + new Dictionary + { + ["choice"] = match.Groups[2].Value, + ["value"] = match.Groups[3].Value + }); + } + else if ((match = RegExpValueListNum.Match(d)).Success) + { + AddProperty(stack.Peek(), null, int.Parse(match.Groups[1].Value)); + } + else if ((match = RegExpValueListMisc.Match(d)).Success) + { + AddProperty(stack.Peek(), null, match.Groups[1].Value); + } + } + + // 验证解析结果 + if (root == null) + return null; + + if (stack.Count != 0) + { + // 栈不为空说明解析不完整,返回null + return null; + } + + // 确保返回的是Dictionary类型 + if (root.Obj is Dictionary dict) + { + return dict; + } + else if (root.Obj is List list) + { + // 如果是列表,包装成字典 + return new Dictionary { ["items"] = list }; + } + else if (root.Obj != null) + { + // 如果是其他类型,包装成字典 + return new Dictionary { ["value"] = root.Obj }; + } + else + { + // 如果root.Obj为null,返回空字典 + return new Dictionary(); + } + } + catch (Exception ex) + { + // 记录异常信息(在实际应用中可以使用日志框架) + throw new InvalidOperationException($"解析GSER数据时发生错误: {ex.Message}", ex); + } + } + + /// + /// 添加属性到节点 + /// + private static void AddProperty(ASN1Node node, string name, object value) + { + if (string.IsNullOrEmpty(name)) + { + if (node.Obj == null) node.Obj = new List(); + ((List)node.Obj).Add(value); + } + else + { + if (node.Obj == null) node.Obj = new Dictionary(); + + var dict = (Dictionary)node.Obj; + if (!dict.ContainsKey(name)) + { + dict[name] = value; + } + else + { + var cur = dict[name]; + if (cur is List list) + { + list.Add(value); + } + else + { + dict[name] = new List { cur, value }; + } + } + } + } + + /// + /// 深度访问ASN1对象 + /// + /// ASN1对象 + /// 访问路径 + /// 访问到的值 + public static object Dig(Dictionary asn1, params object[] path) + { + if (asn1 == null) + return null; + + var current = (object)asn1; + var count = 1; + + for (int i = 0; i < path.Length;) + { + var arg = path[i]; + if (arg is int intArg) + { + count = intArg; + i++; + } + else + { + if (current is Dictionary dict) + { + var key = arg.ToString(); + if (!dict.ContainsKey(key)) + return null; + current = dict[key]; + } + else if (current is List list) + { + var index = int.Parse(arg.ToString()); + if (index >= list.Count) + return null; + current = list[index]; + } + else + { + return null; + } + + if (--count == 0) + { + i++; + count = 1; + } + } + } + + return current; + } + + /// + /// 映射ASN1数组 + /// + /// 返回类型 + /// ASN1对象 + /// 映射函数 + /// 映射结果 + public static List Map(object asn1, Func callback) + { + // 参数验证 + if (callback == null) + throw new ArgumentNullException(nameof(callback), "回调函数不能为空"); + + // 如果asn1为null,返回空列表 + if (asn1 == null) + return new List(); + + try + { + if (asn1 is List list) + { + // 处理列表类型,过滤掉null值并应用映射 + return list.Where(item => item != null) + .Select(callback) + .ToList(); + } + else + { + // 处理单个对象 + return new List { callback(asn1) }; + } + } + catch (Exception ex) + { + // 记录异常信息(在实际应用中可以使用日志框架) + throw new InvalidOperationException($"映射ASN1数据时发生错误: {ex.Message}", ex); + } + } + + /// + /// 映射ASN1数组(带索引) + /// + /// 返回类型 + /// ASN1对象 + /// 带索引的映射函数 + /// 映射结果 + public static List Map(object asn1, Func callback) + { + // 参数验证 + if (callback == null) + throw new ArgumentNullException(nameof(callback), "回调函数不能为空"); + + // 如果asn1为null,返回空列表 + if (asn1 == null) + return new List(); + + try + { + if (asn1 is List list) + { + // 处理列表类型,过滤掉null值并应用带索引的映射 + var result = new List(); + int index = 0; + foreach (var item in list) + { + if (item != null) + { + result.Add(callback(item, index)); + } + index++; + } + return result; + } + else + { + // 处理单个对象 + return new List { callback(asn1, 0) }; + } + } + catch (Exception ex) + { + // 记录异常信息(在实际应用中可以使用日志框架) + throw new InvalidOperationException($"映射ASN1数据时发生错误: {ex.Message}", ex); + } + } + } + + /// + /// ASN1节点 + /// + public class ASN1Node + { + public string Name { get; set; } + public object Obj { get; set; } + } +} \ No newline at end of file diff --git a/LTEMvcApp/Models/LTECapabilities.cs b/LTEMvcApp/Models/LTECapabilities.cs new file mode 100644 index 0000000..aea1fde --- /dev/null +++ b/LTEMvcApp/Models/LTECapabilities.cs @@ -0,0 +1,441 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace LTEMvcApp.Models +{ + /// + /// LTE能力解析器 + /// + public class LTECapabilities + { + public string UEId { get; set; } + public List Bands { get; set; } = new List(); + public int? Category { get; set; } + public int? CategoryDL { get; set; } + public int? CategoryUL { get; set; } + public List> ASN1Data { get; set; } = new List>(); + public Dictionary BandCombinations { get; set; } = new Dictionary(); + public List> CA { get; set; } + public List NRBands { get; set; } + public string VoNR { get; set; } + public List> MRDC { get; set; } + + private List _pendingData = new List(); + private int _count = 0; + + public static readonly Dictionary UE_CAPS_MIMO = new Dictionary + { + ["twoLayers"] = 2, + ["fourLayers"] = 4, + ["eightLayers"] = 8 + }; + + /// + /// 获取类别信息 + /// + public string GetCategory() + { + var cat = new List(); + if (CategoryDL.HasValue) + cat.Add($"DL={CategoryDL}"); + if (CategoryUL.HasValue) + cat.Add($"UL={CategoryUL}"); + + if (cat.Count > 0) + return string.Join(", ", cat); + + if (Category.HasValue) + return Category.ToString(); + + return "?"; + } + + /// + /// 添加待解析数据 + /// + public void Add(string[] data) + { + if (data != null && data.Length > 0) + { + _pendingData.Add(data); + } + } + + /// + /// 解析所有待处理数据 + /// + public LTECapabilities Parse() + { + while (_pendingData.Count > 0) + { + _count++; + ParseASN1(ASN1Parser.FromGSER(_pendingData[0])); + _pendingData.RemoveAt(0); + } + return _count > 0 ? this : null; + } + + /// + /// 解析ASN1数据 + /// + private void ParseASN1(Dictionary asn1) + { + if (asn1 == null) return; + + ASN1Data.Add(asn1); + + var ratList = ASN1Parser.Dig(asn1, "ueCapabilityInformation", "ueCapabilityInformation-r8", "ue-CapabilityRAT-ContainerList") as List ?? new List(); + var ratList1 = ASN1Parser.Dig(asn1, "ueCapabilityInformation", "ueCapabilityInformation", "ue-CapabilityRAT-ContainerList") as List; + + if (ratList1 != null) + { + ratList.AddRange(ratList1); + } + + foreach (var rat in ratList) + { + if (rat is Dictionary ratDict) + { + var ratType = ratDict.GetValueOrDefault("rat-Type")?.ToString(); + var container = ratDict.GetValueOrDefault("ueCapabilityRAT-Container") ?? ratDict.GetValueOrDefault("ue-CapabilityRAT-Container"); + + switch (ratType) + { + case "eutra": + ParseRatEUTRA(container as Dictionary); + break; + case "nr": + ParseRatNR(container as Dictionary); + break; + case "eutra-nr": + ParseRatEUTRA_NR(container as Dictionary); + break; + } + } + } + + if (Category == null) + { + ParseR13(asn1); + } + } + + /// + /// 解析R13版本 + /// + private void ParseR13(Dictionary asn1) + { + var root = ASN1Parser.Dig(asn1, "ueCapabilityInformation-r13", "ueCapabilityInformation-r13", "ue-Capability-Container-r13") as Dictionary; + if (root != null) + { + // Category + if (root.ContainsKey("ue-Category-NB-r13")) + { + Category = Convert.ToInt32(root["ue-Category-NB-r13"]); + } + + var bands = ASN1Parser.Dig(root, "rf-Parameters-r13", "supportedBandList-r13") as List; + if (bands != null) + { + var mappedBands = ASN1Parser.Map(bands, b => + { + if (b is Dictionary bandDict) + { + return new BandInfo { Band = Convert.ToInt32(bandDict["band-r13"]) }; + } + return new BandInfo(); + }); + + if (mappedBands != null && mappedBands.Count > 0) + { + //Bands = mappedBands.Cast().ToList(); + } + } + } + } + + /// + /// 解析EUTRA RAT + /// + private void ParseRatEUTRA(Dictionary asn1) + { + if (asn1 == null) return; + + if (asn1.ContainsKey("ue-Category")) + { + Category = Convert.ToInt32(asn1["ue-Category"]); + } + + var mimo = 1; + if (Category >= 5) + { + mimo = 4; + } + else if (Category >= 2) + { + mimo = 2; + } + + // Bands + var extBands = new List(); + var bands = ASN1Parser.Dig(asn1, "rf-Parameters", "supportedBandListEUTRA") as List; + if (bands != null) + { + var mappedBands = ASN1Parser.Map(bands, (b, i) => + { + if (b is Dictionary bandDict) + { + var band = Convert.ToInt32(bandDict["bandEUTRA"]); + if (band == 64) + { + extBands.Add(i); + } + return new BandInfo { Band = band }; + } + return new BandInfo(); + }); + + if (mappedBands != null && mappedBands.Count > 0) + { + //Bands = mappedBands.Cast().ToList(); + var ext = ASN1Parser.Dig(asn1, 2, "nonCriticalExtension", "lateNonCriticalExtension", 3, "nonCriticalExtension", "rf-Parameters-v9e0", "supportedBandListEUTRA-v9e0") as List; + if (ext != null) + { + foreach (var b in extBands) + { + if (b < ext.Count && ext[b] is Dictionary extBand) + { + Bands[b].Band = Convert.ToInt32(extBand["bandEUTRA-v9e0"]); + } + } + } + } + } + + // CA + var ca = ASN1Parser.Dig(asn1, 2, "nonCriticalExtension") as Dictionary; + if (ca != null) + { + try + { + extBands.Clear(); + var ca1 = ca.GetValueOrDefault("nonCriticalExtension") as Dictionary; + if (ca1 != null) + { + if (ca1.ContainsKey("ue-Category-v1020")) + { + Category = Convert.ToInt32(ca1["ue-Category-v1020"]); + } + + var rfParams = ca1.GetValueOrDefault("rf-Parameters-v1020") as Dictionary; + if (rfParams != null) + { + var supportedBandComb = rfParams.GetValueOrDefault("supportedBandCombination-r10") as List; + if (supportedBandComb != null) + { + CA = ASN1Parser.Map>(supportedBandComb, (c0, i0) => + { + if (c0 is List c0List) + { + return ASN1Parser.Map(c0List, (c1, i1) => + { + if (c1 is Dictionary c1Dict) + { + var band = Convert.ToInt32(c1Dict["bandEUTRA-r10"]); + if (band == 64) + { + //extBands.Add(new[] { i0, i1 }); + } + + var dl = c1Dict.GetValueOrDefault("bandParametersDL-r10") as List; + var ul = c1Dict.GetValueOrDefault("bandParametersUL-r10") as List; + + var result = new BandCombinationInfo + { + Band = band, + MIMO = mimo + }; + + if (dl != null && dl.Count > 0 && dl[0] is Dictionary dlDict) + { + result.DL = dlDict.GetValueOrDefault("ca-BandwidthClassDL-r10")?.ToString(); + if (dlDict.ContainsKey("supportedMIMO-CapabilityDL-r10")) + { + var mimoCap = dlDict["supportedMIMO-CapabilityDL-r10"].ToString(); + result.MIMO_9_10 = UE_CAPS_MIMO.GetValueOrDefault(mimoCap, mimo); + } + } + + if (ul != null && ul.Count > 0 && ul[0] is Dictionary ulDict) + { + result.UL = ulDict.GetValueOrDefault("ca-BandwidthClassUL-r10")?.ToString(); + } + + return result; + } + return new BandCombinationInfo(); + }); + } + return new List(); + }); + } + } + } + } + catch (Exception) { } + } + + if (Bands != null && Bands.Count > 0) + { + //Bands = Bands.OrderBy(b => b.Band).ToList(); + } + } + + /// + /// 解析NR RAT + /// + private void ParseRatNR(Dictionary asn1) + { + if (asn1 == null) return; + + // Bands + var bands = ASN1Parser.Dig(asn1, "rf-Parameters", "supportedBandListNR") as List; + if (bands != null) + { + var mappedBands = ASN1Parser.Map(bands, b => + { + if (b is Dictionary bandDict) + { + return new BandInfo { Band = Convert.ToInt32(bandDict["bandNR"]) }; + } + return new BandInfo(); + }); + + if (mappedBands != null && mappedBands.Count > 0) + { + NRBands = mappedBands; + } + } + + // VoNR + var v1540 = ASN1Parser.Dig(asn1, 2, "nonCriticalExtension") as Dictionary; + if (v1540 != null) + { + var vonr = new List(); + if (ASN1Parser.Dig(v1540, "ims-Parameters", "ims-ParametersFRX-Diff", "voiceOverNR")?.ToString() == "supported") + { + vonr.AddRange(new[] { "FR1", "FR2" }); + } + else + { + if (ASN1Parser.Dig(v1540, "fr1-Add-UE-NR-Capabilities-v1540", "ims-ParametersFRX-Diff", "voiceOverNR")?.ToString() == "supported") + vonr.Add("FR1"); + if (ASN1Parser.Dig(v1540, "fr2-Add-UE-NR-Capabilities-v1540", "ims-ParametersFRX-Diff", "voiceOverNR")?.ToString() == "supported") + vonr.Add("FR2"); + } + if (vonr.Count > 0) + { + VoNR = string.Join(", ", vonr); + } + } + } + + /// + /// 解析EUTRA-NR RAT + /// + private void ParseRatEUTRA_NR(Dictionary asn1) + { + if (asn1 == null) return; + + var bands = ASN1Parser.Dig(asn1, "rf-ParametersMRDC", "supportedBandCombinationList") as List; + if (bands != null) + { + MRDC = ASN1Parser.Map>(bands, band => + { + if (band is Dictionary bandDict) + { + var bl = bandDict.GetValueOrDefault("bandList") as Dictionary; + if (bl != null) + { + var result = new List(); + + var nr = bl.GetValueOrDefault("nr") as List; + if (nr != null) + { + result.AddRange(ASN1Parser.Map(nr, b => + { + if (b is Dictionary bDict) + { + return new BandCombinationInfo + { + Band = Convert.ToInt32(bDict["bandNR"]), + BandType = "NR", + DL = bDict.GetValueOrDefault("ca-BandwidthClassDL-NR")?.ToString(), + UL = bDict.GetValueOrDefault("ca-BandwidthClassUL-NR")?.ToString() + }; + } + return new BandCombinationInfo(); + })); + } + + var eutra = bl.GetValueOrDefault("eutra") as List; + if (eutra != null) + { + result.AddRange(ASN1Parser.Map(eutra, b => + { + if (b is Dictionary bDict) + { + return new BandCombinationInfo + { + Band = Convert.ToInt32(bDict["bandEUTRA"]), + DL = bDict.GetValueOrDefault("ca-BandwidthClassDL-EUTRA")?.ToString(), + UL = bDict.GetValueOrDefault("ca-BandwidthClassUL-EUTRA")?.ToString() + }; + } + return new BandCombinationInfo(); + })); + } + + return result; + } + } + return new List(); + }); + } + } + + /// + /// 设置频段组合 + /// + public void SetBandComb(Dictionary json, string ratType) + { + BandCombinations[ratType] = json; + } + } + + /// + /// 频段信息 + /// + public class BandInfo + { + public int Band { get; set; } + public bool DL256QAM { get; set; } + public string BandType { get; set; } = "LTE"; + } + + /// + /// 频段组合信息 + /// + public class BandCombinationInfo + { + public int Band { get; set; } + public string DL { get; set; } + public string UL { get; set; } + public int MIMO { get; set; } + public int? MIMO_9_10 { get; set; } + public bool MIMO4_3_4 { get; set; } + public string BandType { get; set; } = "LTE"; + } +} \ No newline at end of file diff --git a/LTEMvcApp/Models/LTEClient.cs b/LTEMvcApp/Models/LTEClient.cs index ddb5d0f..b21653a 100644 --- a/LTEMvcApp/Models/LTEClient.cs +++ b/LTEMvcApp/Models/LTEClient.cs @@ -1936,7 +1936,8 @@ public class LTEClient var keysToUpdate = UeList.Where(kv => kv.Value == ue1).Select(kv => kv.Key).ToList(); foreach (var key in keysToUpdate) { - UeList[key] = ue0; + int newKey =(int)key; + UeList[newKey] = ue0; } // 合并属性 if (string.IsNullOrEmpty(ue0.Imsi)) ue0.Imsi = ue1.Imsi; diff --git a/LTEMvcApp/Models/StatisticsData.cs b/LTEMvcApp/Models/StatisticsData.cs index 1da76c3..b87259a 100644 --- a/LTEMvcApp/Models/StatisticsData.cs +++ b/LTEMvcApp/Models/StatisticsData.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; + using System.Collections.Concurrent; using Newtonsoft.Json.Linq; @@ -428,7 +427,13 @@ namespace LTEMvcApp.Models { lock (_lockObject) { - return _statsQueue.ToList(); + var result = new List(); + foreach (var item in _statsQueue) + { + if (item != null) + result.Add(item); + } + return result; } }