5 changed files with 1086 additions and 4 deletions
@ -0,0 +1,270 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace LTEMvcApp.Models |
|||
{ |
|||
/// <summary>
|
|||
/// ASN1扩展方法
|
|||
/// </summary>
|
|||
public static class ASN1Extensions |
|||
{ |
|||
/// <summary>
|
|||
/// 安全获取字典值
|
|||
/// </summary>
|
|||
public static T GetValueOrDefault<T>(this Dictionary<string, object> 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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 安全获取字典值
|
|||
/// </summary>
|
|||
public static object GetValueOrDefault(this Dictionary<string, object> dict, string key) |
|||
{ |
|||
return dict?.ContainsKey(key) == true ? dict[key] : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将对象转换为字典
|
|||
/// </summary>
|
|||
public static Dictionary<string, object> ToDictionary(this object obj) |
|||
{ |
|||
if (obj is Dictionary<string, object> dict) |
|||
return dict; |
|||
|
|||
if (obj is IDictionary<string, object> idict) |
|||
return new Dictionary<string, object>(idict); |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将对象转换为列表
|
|||
/// </summary>
|
|||
public static List<object> ToList(this object obj) |
|||
{ |
|||
if (obj is List<object> list) |
|||
return list; |
|||
|
|||
if (obj is IEnumerable<object> enumerable) |
|||
return enumerable.ToList(); |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 安全转换为整数
|
|||
/// </summary>
|
|||
public static int? ToInt32(this object obj) |
|||
{ |
|||
if (obj == null) return null; |
|||
|
|||
try |
|||
{ |
|||
return Convert.ToInt32(obj); |
|||
} |
|||
catch |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 安全转换为字符串
|
|||
/// </summary>
|
|||
public static string ToSafeString(this object obj) |
|||
{ |
|||
return obj?.ToString(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 安全转换为布尔值
|
|||
/// </summary>
|
|||
public static bool? ToBoolean(this object obj) |
|||
{ |
|||
if (obj == null) return null; |
|||
|
|||
try |
|||
{ |
|||
return Convert.ToBoolean(obj); |
|||
} |
|||
catch |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ASN1工具类
|
|||
/// </summary>
|
|||
public static class ASN1Utils |
|||
{ |
|||
/// <summary>
|
|||
/// 创建ASN1对象
|
|||
/// </summary>
|
|||
public static Dictionary<string, object> CreateASN1Object() |
|||
{ |
|||
return new Dictionary<string, object>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 创建ASN1数组
|
|||
/// </summary>
|
|||
public static List<object> CreateASN1Array() |
|||
{ |
|||
return new List<object>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加属性到ASN1对象
|
|||
/// </summary>
|
|||
public static void AddProperty(Dictionary<string, object> obj, string name, object value) |
|||
{ |
|||
if (obj == null) return; |
|||
|
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
if (obj.ContainsKey("")) |
|||
{ |
|||
var existing = obj[""]; |
|||
if (existing is List<object> list) |
|||
{ |
|||
list.Add(value); |
|||
} |
|||
else |
|||
{ |
|||
obj[""] = new List<object> { existing, value }; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
obj[""] = new List<object> { value }; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (obj.ContainsKey(name)) |
|||
{ |
|||
var existing = obj[name]; |
|||
if (existing is List<object> list) |
|||
{ |
|||
list.Add(value); |
|||
} |
|||
else |
|||
{ |
|||
obj[name] = new List<object> { existing, value }; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
obj[name] = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加元素到ASN1数组
|
|||
/// </summary>
|
|||
public static void AddElement(List<object> array, object value) |
|||
{ |
|||
array?.Add(value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 深度克隆ASN1对象
|
|||
/// </summary>
|
|||
public static Dictionary<string, object> DeepClone(Dictionary<string, object> obj) |
|||
{ |
|||
if (obj == null) return null; |
|||
|
|||
var result = new Dictionary<string, object>(); |
|||
foreach (var kvp in obj) |
|||
{ |
|||
if (kvp.Value is Dictionary<string, object> dict) |
|||
{ |
|||
result[kvp.Key] = DeepClone(dict); |
|||
} |
|||
else if (kvp.Value is List<object> list) |
|||
{ |
|||
result[kvp.Key] = DeepCloneList(list); |
|||
} |
|||
else |
|||
{ |
|||
result[kvp.Key] = kvp.Value; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 深度克隆ASN1数组
|
|||
/// </summary>
|
|||
public static List<object> DeepCloneList(List<object> list) |
|||
{ |
|||
if (list == null) return null; |
|||
|
|||
var result = new List<object>(); |
|||
foreach (var item in list) |
|||
{ |
|||
if (item is Dictionary<string, object> dict) |
|||
{ |
|||
result.Add(DeepClone(dict)); |
|||
} |
|||
else if (item is List<object> subList) |
|||
{ |
|||
result.Add(DeepCloneList(subList)); |
|||
} |
|||
else |
|||
{ |
|||
result.Add(item); |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 将ASN1对象转换为JSON字符串
|
|||
/// </summary>
|
|||
public static string ToJson(Dictionary<string, object> 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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 从JSON字符串创建ASN1对象
|
|||
/// </summary>
|
|||
public static Dictionary<string, object> FromJson(string json) |
|||
{ |
|||
if (string.IsNullOrEmpty(json)) return null; |
|||
|
|||
try |
|||
{ |
|||
return Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(json); |
|||
} |
|||
catch |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// ASN1解析器,用于解析GSER格式的ASN1数据
|
|||
/// </summary>
|
|||
public class ASN1Parser |
|||
{ |
|||
private static readonly Regex RegExpOpen = new Regex(@"((?! )[\w-\s]+(?<! ))?:?\s*{", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpClose = new Regex(@"}", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpValueNum = new Regex(@"([\w-]+)\s+(\d+),?$", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpValueEnumerated = new Regex(@"([\w-]+)\s+([\w-']+)\s*,?$", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpValueChoice = new Regex(@"([\w-]+) ([\w-]+):\s+([\w-']+)\s*,?$", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpValueListNum = new Regex(@"\s+(\d+),?$", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpValueListMisc = new Regex(@"\s+([\w-]+),?$", RegexOptions.Compiled); |
|||
private static readonly Regex RegExpHexDump = new Regex(@"^[0-9A-Fa-f\s]+$", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// 从GSER格式的字符串数组解析ASN1数据
|
|||
/// </summary>
|
|||
/// <param name="data">GSER格式的字符串数组</param>
|
|||
/// <returns>解析后的ASN1对象</returns>
|
|||
public static Dictionary<string, object> FromGSER(string[] data) |
|||
{ |
|||
if (data == null || data.Length == 0) |
|||
return null; |
|||
|
|||
var root = (ASN1Node)null; |
|||
var stack = new Stack<ASN1Node>(); |
|||
|
|||
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<string, object>()); |
|||
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<string, object> |
|||
{ |
|||
["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<string, object>类型
|
|||
if (root.Obj is Dictionary<string, object> dict) |
|||
{ |
|||
return dict; |
|||
} |
|||
else if (root.Obj is List<object> list) |
|||
{ |
|||
// 如果是列表,包装成字典
|
|||
return new Dictionary<string, object> { ["items"] = list }; |
|||
} |
|||
else if (root.Obj != null) |
|||
{ |
|||
// 如果是其他类型,包装成字典
|
|||
return new Dictionary<string, object> { ["value"] = root.Obj }; |
|||
} |
|||
else |
|||
{ |
|||
// 如果root.Obj为null,返回空字典
|
|||
return new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
// 记录异常信息(在实际应用中可以使用日志框架)
|
|||
throw new InvalidOperationException($"解析GSER数据时发生错误: {ex.Message}", ex); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加属性到节点
|
|||
/// </summary>
|
|||
private static void AddProperty(ASN1Node node, string name, object value) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
if (node.Obj == null) node.Obj = new List<object>(); |
|||
((List<object>)node.Obj).Add(value); |
|||
} |
|||
else |
|||
{ |
|||
if (node.Obj == null) node.Obj = new Dictionary<string, object>(); |
|||
|
|||
var dict = (Dictionary<string, object>)node.Obj; |
|||
if (!dict.ContainsKey(name)) |
|||
{ |
|||
dict[name] = value; |
|||
} |
|||
else |
|||
{ |
|||
var cur = dict[name]; |
|||
if (cur is List<object> list) |
|||
{ |
|||
list.Add(value); |
|||
} |
|||
else |
|||
{ |
|||
dict[name] = new List<object> { cur, value }; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 深度访问ASN1对象
|
|||
/// </summary>
|
|||
/// <param name="asn1">ASN1对象</param>
|
|||
/// <param name="path">访问路径</param>
|
|||
/// <returns>访问到的值</returns>
|
|||
public static object Dig(Dictionary<string, object> 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<string, object> dict) |
|||
{ |
|||
var key = arg.ToString(); |
|||
if (!dict.ContainsKey(key)) |
|||
return null; |
|||
current = dict[key]; |
|||
} |
|||
else if (current is List<object> 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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 映射ASN1数组
|
|||
/// </summary>
|
|||
/// <typeparam name="T">返回类型</typeparam>
|
|||
/// <param name="asn1">ASN1对象</param>
|
|||
/// <param name="callback">映射函数</param>
|
|||
/// <returns>映射结果</returns>
|
|||
public static List<T> Map<T>(object asn1, Func<object, T> callback) |
|||
{ |
|||
// 参数验证
|
|||
if (callback == null) |
|||
throw new ArgumentNullException(nameof(callback), "回调函数不能为空"); |
|||
|
|||
// 如果asn1为null,返回空列表
|
|||
if (asn1 == null) |
|||
return new List<T>(); |
|||
|
|||
try |
|||
{ |
|||
if (asn1 is List<object> list) |
|||
{ |
|||
// 处理列表类型,过滤掉null值并应用映射
|
|||
return list.Where(item => item != null) |
|||
.Select(callback) |
|||
.ToList<T>(); |
|||
} |
|||
else |
|||
{ |
|||
// 处理单个对象
|
|||
return new List<T> { callback(asn1) }; |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
// 记录异常信息(在实际应用中可以使用日志框架)
|
|||
throw new InvalidOperationException($"映射ASN1数据时发生错误: {ex.Message}", ex); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 映射ASN1数组(带索引)
|
|||
/// </summary>
|
|||
/// <typeparam name="T">返回类型</typeparam>
|
|||
/// <param name="asn1">ASN1对象</param>
|
|||
/// <param name="callback">带索引的映射函数</param>
|
|||
/// <returns>映射结果</returns>
|
|||
public static List<T> Map<T>(object asn1, Func<object, int, T> callback) |
|||
{ |
|||
// 参数验证
|
|||
if (callback == null) |
|||
throw new ArgumentNullException(nameof(callback), "回调函数不能为空"); |
|||
|
|||
// 如果asn1为null,返回空列表
|
|||
if (asn1 == null) |
|||
return new List<T>(); |
|||
|
|||
try |
|||
{ |
|||
if (asn1 is List<object> list) |
|||
{ |
|||
// 处理列表类型,过滤掉null值并应用带索引的映射
|
|||
var result = new List<T>(); |
|||
int index = 0; |
|||
foreach (var item in list) |
|||
{ |
|||
if (item != null) |
|||
{ |
|||
result.Add(callback(item, index)); |
|||
} |
|||
index++; |
|||
} |
|||
return result; |
|||
} |
|||
else |
|||
{ |
|||
// 处理单个对象
|
|||
return new List<T> { callback(asn1, 0) }; |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
// 记录异常信息(在实际应用中可以使用日志框架)
|
|||
throw new InvalidOperationException($"映射ASN1数据时发生错误: {ex.Message}", ex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ASN1节点
|
|||
/// </summary>
|
|||
public class ASN1Node |
|||
{ |
|||
public string Name { get; set; } |
|||
public object Obj { get; set; } |
|||
} |
|||
} |
@ -0,0 +1,441 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LTEMvcApp.Models |
|||
{ |
|||
/// <summary>
|
|||
/// LTE能力解析器
|
|||
/// </summary>
|
|||
public class LTECapabilities |
|||
{ |
|||
public string UEId { get; set; } |
|||
public List<BandInfo> Bands { get; set; } = new List<BandInfo>(); |
|||
public int? Category { get; set; } |
|||
public int? CategoryDL { get; set; } |
|||
public int? CategoryUL { get; set; } |
|||
public List<Dictionary<string, object>> ASN1Data { get; set; } = new List<Dictionary<string, object>>(); |
|||
public Dictionary<string, object> BandCombinations { get; set; } = new Dictionary<string, object>(); |
|||
public List<List<BandCombinationInfo>> CA { get; set; } |
|||
public List<BandInfo> NRBands { get; set; } |
|||
public string VoNR { get; set; } |
|||
public List<List<BandCombinationInfo>> MRDC { get; set; } |
|||
|
|||
private List<string[]> _pendingData = new List<string[]>(); |
|||
private int _count = 0; |
|||
|
|||
public static readonly Dictionary<string, int> UE_CAPS_MIMO = new Dictionary<string, int> |
|||
{ |
|||
["twoLayers"] = 2, |
|||
["fourLayers"] = 4, |
|||
["eightLayers"] = 8 |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// 获取类别信息
|
|||
/// </summary>
|
|||
public string GetCategory() |
|||
{ |
|||
var cat = new List<string>(); |
|||
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 "?"; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加待解析数据
|
|||
/// </summary>
|
|||
public void Add(string[] data) |
|||
{ |
|||
if (data != null && data.Length > 0) |
|||
{ |
|||
_pendingData.Add(data); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析所有待处理数据
|
|||
/// </summary>
|
|||
public LTECapabilities Parse() |
|||
{ |
|||
while (_pendingData.Count > 0) |
|||
{ |
|||
_count++; |
|||
ParseASN1(ASN1Parser.FromGSER(_pendingData[0])); |
|||
_pendingData.RemoveAt(0); |
|||
} |
|||
return _count > 0 ? this : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析ASN1数据
|
|||
/// </summary>
|
|||
private void ParseASN1(Dictionary<string, object> asn1) |
|||
{ |
|||
if (asn1 == null) return; |
|||
|
|||
ASN1Data.Add(asn1); |
|||
|
|||
var ratList = ASN1Parser.Dig(asn1, "ueCapabilityInformation", "ueCapabilityInformation-r8", "ue-CapabilityRAT-ContainerList") as List<object> ?? new List<object>(); |
|||
var ratList1 = ASN1Parser.Dig(asn1, "ueCapabilityInformation", "ueCapabilityInformation", "ue-CapabilityRAT-ContainerList") as List<object>; |
|||
|
|||
if (ratList1 != null) |
|||
{ |
|||
ratList.AddRange(ratList1); |
|||
} |
|||
|
|||
foreach (var rat in ratList) |
|||
{ |
|||
if (rat is Dictionary<string, object> 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<string, object>); |
|||
break; |
|||
case "nr": |
|||
ParseRatNR(container as Dictionary<string, object>); |
|||
break; |
|||
case "eutra-nr": |
|||
ParseRatEUTRA_NR(container as Dictionary<string, object>); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (Category == null) |
|||
{ |
|||
ParseR13(asn1); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析R13版本
|
|||
/// </summary>
|
|||
private void ParseR13(Dictionary<string, object> asn1) |
|||
{ |
|||
var root = ASN1Parser.Dig(asn1, "ueCapabilityInformation-r13", "ueCapabilityInformation-r13", "ue-Capability-Container-r13") as Dictionary<string, object>; |
|||
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<object>; |
|||
if (bands != null) |
|||
{ |
|||
var mappedBands = ASN1Parser.Map<BandInfo>(bands, b => |
|||
{ |
|||
if (b is Dictionary<string, object> bandDict) |
|||
{ |
|||
return new BandInfo { Band = Convert.ToInt32(bandDict["band-r13"]) }; |
|||
} |
|||
return new BandInfo(); |
|||
}); |
|||
|
|||
if (mappedBands != null && mappedBands.Count > 0) |
|||
{ |
|||
//Bands = mappedBands.Cast<BandInfo>().ToList();
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析EUTRA RAT
|
|||
/// </summary>
|
|||
private void ParseRatEUTRA(Dictionary<string, object> 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<int>(); |
|||
var bands = ASN1Parser.Dig(asn1, "rf-Parameters", "supportedBandListEUTRA") as List<object>; |
|||
if (bands != null) |
|||
{ |
|||
var mappedBands = ASN1Parser.Map<BandInfo>(bands, (b, i) => |
|||
{ |
|||
if (b is Dictionary<string, object> 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<BandInfo>().ToList();
|
|||
var ext = ASN1Parser.Dig(asn1, 2, "nonCriticalExtension", "lateNonCriticalExtension", 3, "nonCriticalExtension", "rf-Parameters-v9e0", "supportedBandListEUTRA-v9e0") as List<object>; |
|||
if (ext != null) |
|||
{ |
|||
foreach (var b in extBands) |
|||
{ |
|||
if (b < ext.Count && ext[b] is Dictionary<string, object> extBand) |
|||
{ |
|||
Bands[b].Band = Convert.ToInt32(extBand["bandEUTRA-v9e0"]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// CA
|
|||
var ca = ASN1Parser.Dig(asn1, 2, "nonCriticalExtension") as Dictionary<string, object>; |
|||
if (ca != null) |
|||
{ |
|||
try |
|||
{ |
|||
extBands.Clear(); |
|||
var ca1 = ca.GetValueOrDefault("nonCriticalExtension") as Dictionary<string, object>; |
|||
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<string, object>; |
|||
if (rfParams != null) |
|||
{ |
|||
var supportedBandComb = rfParams.GetValueOrDefault("supportedBandCombination-r10") as List<object>; |
|||
if (supportedBandComb != null) |
|||
{ |
|||
CA = ASN1Parser.Map<List<BandCombinationInfo>>(supportedBandComb, (c0, i0) => |
|||
{ |
|||
if (c0 is List<object> c0List) |
|||
{ |
|||
return ASN1Parser.Map<BandCombinationInfo>(c0List, (c1, i1) => |
|||
{ |
|||
if (c1 is Dictionary<string, object> c1Dict) |
|||
{ |
|||
var band = Convert.ToInt32(c1Dict["bandEUTRA-r10"]); |
|||
if (band == 64) |
|||
{ |
|||
//extBands.Add(new[] { i0, i1 });
|
|||
} |
|||
|
|||
var dl = c1Dict.GetValueOrDefault("bandParametersDL-r10") as List<object>; |
|||
var ul = c1Dict.GetValueOrDefault("bandParametersUL-r10") as List<object>; |
|||
|
|||
var result = new BandCombinationInfo |
|||
{ |
|||
Band = band, |
|||
MIMO = mimo |
|||
}; |
|||
|
|||
if (dl != null && dl.Count > 0 && dl[0] is Dictionary<string, object> 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<string, object> ulDict) |
|||
{ |
|||
result.UL = ulDict.GetValueOrDefault("ca-BandwidthClassUL-r10")?.ToString(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
return new BandCombinationInfo(); |
|||
}); |
|||
} |
|||
return new List<BandCombinationInfo>(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (Exception) { } |
|||
} |
|||
|
|||
if (Bands != null && Bands.Count > 0) |
|||
{ |
|||
//Bands = Bands.OrderBy(b => b.Band).ToList();
|
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析NR RAT
|
|||
/// </summary>
|
|||
private void ParseRatNR(Dictionary<string, object> asn1) |
|||
{ |
|||
if (asn1 == null) return; |
|||
|
|||
// Bands
|
|||
var bands = ASN1Parser.Dig(asn1, "rf-Parameters", "supportedBandListNR") as List<object>; |
|||
if (bands != null) |
|||
{ |
|||
var mappedBands = ASN1Parser.Map<BandInfo>(bands, b => |
|||
{ |
|||
if (b is Dictionary<string, object> 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<string, object>; |
|||
if (v1540 != null) |
|||
{ |
|||
var vonr = new List<string>(); |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 解析EUTRA-NR RAT
|
|||
/// </summary>
|
|||
private void ParseRatEUTRA_NR(Dictionary<string, object> asn1) |
|||
{ |
|||
if (asn1 == null) return; |
|||
|
|||
var bands = ASN1Parser.Dig(asn1, "rf-ParametersMRDC", "supportedBandCombinationList") as List<object>; |
|||
if (bands != null) |
|||
{ |
|||
MRDC = ASN1Parser.Map<List<BandCombinationInfo>>(bands, band => |
|||
{ |
|||
if (band is Dictionary<string, object> bandDict) |
|||
{ |
|||
var bl = bandDict.GetValueOrDefault("bandList") as Dictionary<string, object>; |
|||
if (bl != null) |
|||
{ |
|||
var result = new List<BandCombinationInfo>(); |
|||
|
|||
var nr = bl.GetValueOrDefault("nr") as List<object>; |
|||
if (nr != null) |
|||
{ |
|||
result.AddRange(ASN1Parser.Map<BandCombinationInfo>(nr, b => |
|||
{ |
|||
if (b is Dictionary<string, object> 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<object>; |
|||
if (eutra != null) |
|||
{ |
|||
result.AddRange(ASN1Parser.Map<BandCombinationInfo>(eutra, b => |
|||
{ |
|||
if (b is Dictionary<string, object> 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<BandCombinationInfo>(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 设置频段组合
|
|||
/// </summary>
|
|||
public void SetBandComb(Dictionary<string, object> json, string ratType) |
|||
{ |
|||
BandCombinations[ratType] = json; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 频段信息
|
|||
/// </summary>
|
|||
public class BandInfo |
|||
{ |
|||
public int Band { get; set; } |
|||
public bool DL256QAM { get; set; } |
|||
public string BandType { get; set; } = "LTE"; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 频段组合信息
|
|||
/// </summary>
|
|||
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"; |
|||
} |
|||
} |
Loading…
Reference in new issue