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