Compare commits
4 Commits
master
...
feature/pr
Author | SHA1 | Date |
---|---|---|
|
571232a1f2 | 2 weeks ago |
|
1ac911297b | 1 month ago |
|
50844d7ca1 | 2 months ago |
|
89eda9ec4e | 2 months ago |
35 changed files with 3228 additions and 6 deletions
@ -0,0 +1 @@ |
|||||
|
dotnet publish -r linux-x64 -c Debug -p:PublishSingeFile=true --self-contained -o D:\HistoryCode\CoreAgent\src\output |
@ -0,0 +1,48 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Helpers |
||||
|
{ |
||||
|
public static class JsonHelper |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 对象序列化
|
||||
|
/// </summary>
|
||||
|
/// <param name="obj">对象</param>
|
||||
|
/// <param name="isUseTextJson">是否使用textjson</param>
|
||||
|
/// <returns>返回json字符串</returns>
|
||||
|
public static string ObjToJson(this object obj, bool isUseTextJson = false) |
||||
|
{ |
||||
|
if (isUseTextJson) |
||||
|
{ |
||||
|
return System.Text.Json.JsonSerializer.Serialize(obj); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Newtonsoft.Json.JsonConvert.SerializeObject(obj); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// json反序列化obj
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">反序列类型</typeparam>
|
||||
|
/// <param name="strJson">json</param>
|
||||
|
/// <param name="isUseTextJson">是否使用textjson</param>
|
||||
|
/// <returns>返回对象</returns>
|
||||
|
public static T JsonToObj<T>(this string strJson, bool isUseTextJson = false) |
||||
|
{ |
||||
|
if (isUseTextJson) |
||||
|
{ |
||||
|
return System.Text.Json.JsonSerializer.Deserialize<T>(strJson); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(strJson); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.CustomWSClient |
||||
|
{ |
||||
|
public interface ICustomMessageHandler |
||||
|
{ |
||||
|
void HandleMessage(string MsgData, IObserverCustomWebSocketClient observer); |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.CustomWSClient |
||||
|
{ |
||||
|
public interface IObserverCustomWebSocketClient |
||||
|
{ |
||||
|
public void SendMessage(string message); |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.ProtocolLogHandlers |
||||
|
{ |
||||
|
public interface IProtocolLogsProviderObserver |
||||
|
{ |
||||
|
void OnData(string msg); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.ProtocolLogHandlers |
||||
|
{ |
||||
|
public interface IProtocollHandleLogs |
||||
|
{ |
||||
|
public void RunStart(); |
||||
|
|
||||
|
public void RunStop(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using System; |
||||
|
using System.Text.Json.Serialization; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 基础网络日志实体类
|
||||
|
/// 提供网络日志的通用属性和配置
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">日志层类型(如ImsLayerLog或RanLayerLog)</typeparam>
|
||||
|
public class BaseNetworkLog<T> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 超时时间(毫秒)
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("timeout")] |
||||
|
public int Timeout { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 最小日志数量
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("min")] |
||||
|
public int MinLogCount { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 最大日志数量
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("max")] |
||||
|
public int MaxLogCount { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志层配置
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("layers")] |
||||
|
public T LayerConfig { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志消息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("message")] |
||||
|
public string Message { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 是否包含消息头
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("headers")] |
||||
|
public bool IncludeHeaders { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("message_id")] |
||||
|
public int MessageId { get; set; } |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志方向类型枚举
|
||||
|
/// 定义了协议日志的传输方向
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public enum DirectionLogsType |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 未知方向
|
||||
|
/// </summary>
|
||||
|
Unknown = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 上行方向(UE到网络)
|
||||
|
/// </summary>
|
||||
|
Uplink = 1, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 下行方向(网络到UE)
|
||||
|
/// </summary>
|
||||
|
Downlink = 2, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 双向
|
||||
|
/// </summary>
|
||||
|
Bidirectional = 3, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 内部处理
|
||||
|
/// </summary>
|
||||
|
Internal = 4 |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
using System; |
||||
|
using System.Text.Json.Serialization; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IMS层日志实体类
|
||||
|
/// 该实体用于记录IMS(IP多媒体子系统)相关的各层日志信息
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public class ImsLayerLog |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// CX协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string CX { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IMS协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string IMS { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IPSEC协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string IPSEC { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MEDIA协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string MEDIA { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MMS协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string MMS { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RX协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string RX { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SIP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string SIP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化IMS层日志级别
|
||||
|
/// </summary>
|
||||
|
public void InitializeLogLevels() |
||||
|
{ |
||||
|
CX = LogLevel.Warn.ToString().ToLower(); |
||||
|
IMS = LogLevel.Warn.ToString().ToLower(); |
||||
|
IPSEC = LogLevel.Warn.ToString().ToLower(); |
||||
|
MEDIA = LogLevel.Warn.ToString().ToLower(); |
||||
|
MMS = LogLevel.Warn.ToString().ToLower(); |
||||
|
RX = LogLevel.Warn.ToString().ToLower(); |
||||
|
SIP = LogLevel.Debug.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 更新指定层的日志级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="layerName">层名称</param>
|
||||
|
/// <param name="logLevel">日志级别</param>
|
||||
|
/// <returns>是否更新成功</returns>
|
||||
|
public bool UpdateLogLevel(string layerName, LogLevel logLevel) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(layerName)) |
||||
|
return false; |
||||
|
|
||||
|
var property = GetType().GetProperty(layerName); |
||||
|
if (property == null) |
||||
|
return false; |
||||
|
|
||||
|
property.SetValue(this, logLevel.ToString().ToLower()); |
||||
|
return true; |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志级别枚举
|
||||
|
/// </summary>
|
||||
|
public enum LogLevel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 调试级别
|
||||
|
/// </summary>
|
||||
|
Debug, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 信息级别
|
||||
|
/// </summary>
|
||||
|
Info, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 警告级别
|
||||
|
/// </summary>
|
||||
|
Warn, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误级别
|
||||
|
/// </summary>
|
||||
|
Error |
||||
|
} |
@ -0,0 +1,200 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 移动用户身份信息
|
||||
|
/// 用于存储移动网络用户的基本身份和位置信息
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public class MobileUserIdentity |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 公共陆地移动网络标识
|
||||
|
/// Public Land Mobile Network Identifier
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("plmn")] |
||||
|
public string? Plmn { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 旧的临时移动用户标识
|
||||
|
/// Old Temporary Mobile Subscriber Identity
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("oldTmsi")] |
||||
|
public string? OldTmsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 临时移动用户标识
|
||||
|
/// Temporary Mobile Subscriber Identity
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("tmsi")] |
||||
|
public string? Tmsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 国际移动用户标识
|
||||
|
/// International Mobile Subscriber Identity
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("imsi")] |
||||
|
public string? Imsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 小区ID
|
||||
|
/// Cell Identifier
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("cellId")] |
||||
|
public int? CellId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 用户设备ID
|
||||
|
/// User Equipment Identifier
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("ueId")] |
||||
|
public int? UeId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化移动用户身份信息的新实例
|
||||
|
/// </summary>
|
||||
|
public MobileUserIdentity() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化移动用户身份信息的新实例
|
||||
|
/// </summary>
|
||||
|
/// <param name="plmn">公共陆地移动网络标识</param>
|
||||
|
/// <param name="oldTmsi">旧的临时移动用户标识</param>
|
||||
|
/// <param name="tmsi">临时移动用户标识</param>
|
||||
|
/// <param name="imsi">国际移动用户标识</param>
|
||||
|
/// <param name="cellId">小区ID</param>
|
||||
|
/// <param name="ueId">用户设备ID</param>
|
||||
|
public MobileUserIdentity( |
||||
|
string? plmn = null, |
||||
|
string? oldTmsi = null, |
||||
|
string? tmsi = null, |
||||
|
string? imsi = null, |
||||
|
int? cellId = null, |
||||
|
int? ueId = null) |
||||
|
{ |
||||
|
Plmn = plmn; |
||||
|
OldTmsi = oldTmsi; |
||||
|
Tmsi = tmsi; |
||||
|
Imsi = imsi; |
||||
|
CellId = cellId; |
||||
|
UeId = ueId; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查是否包含有效的用户身份信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>是否包含有效信息</returns>
|
||||
|
public bool HasValidIdentity() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(Imsi) || |
||||
|
!string.IsNullOrWhiteSpace(Tmsi) || |
||||
|
UeId.HasValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查是否包含位置信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>是否包含位置信息</returns>
|
||||
|
public bool HasLocationInfo() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(Plmn) || CellId.HasValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取用户身份摘要信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>摘要信息</returns>
|
||||
|
public string GetIdentitySummary() |
||||
|
{ |
||||
|
var parts = new List<string>(); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Imsi)) |
||||
|
parts.Add($"IMSI:{Imsi}"); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Tmsi)) |
||||
|
parts.Add($"TMSI:{Tmsi}"); |
||||
|
|
||||
|
if (UeId.HasValue) |
||||
|
parts.Add($"UE:{UeId}"); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Plmn)) |
||||
|
parts.Add($"PLMN:{Plmn}"); |
||||
|
|
||||
|
if (CellId.HasValue) |
||||
|
parts.Add($"Cell:{CellId}"); |
||||
|
|
||||
|
return parts.Count > 0 ? string.Join(" | ", parts) : "No Identity Info"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查TMSI是否发生变化
|
||||
|
/// </summary>
|
||||
|
/// <returns>TMSI是否发生变化</returns>
|
||||
|
public bool HasTmsiChanged() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(OldTmsi) && |
||||
|
!string.IsNullOrWhiteSpace(Tmsi) && |
||||
|
!OldTmsi.Equals(Tmsi, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建身份信息的副本
|
||||
|
/// </summary>
|
||||
|
/// <returns>新的移动用户身份信息</returns>
|
||||
|
public MobileUserIdentity Copy() |
||||
|
{ |
||||
|
return new MobileUserIdentity |
||||
|
{ |
||||
|
Plmn = Plmn, |
||||
|
OldTmsi = OldTmsi, |
||||
|
Tmsi = Tmsi, |
||||
|
Imsi = Imsi, |
||||
|
CellId = CellId, |
||||
|
UeId = UeId |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 更新TMSI信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="newTmsi">新的TMSI</param>
|
||||
|
public void UpdateTmsi(string? newTmsi) |
||||
|
{ |
||||
|
if (!string.IsNullOrWhiteSpace(Tmsi)) |
||||
|
{ |
||||
|
OldTmsi = Tmsi; |
||||
|
} |
||||
|
Tmsi = newTmsi; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 转换为JSON字符串
|
||||
|
/// </summary>
|
||||
|
/// <returns>JSON字符串</returns>
|
||||
|
public string ToJson() |
||||
|
{ |
||||
|
return JsonConvert.SerializeObject(this, Formatting.Indented); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从JSON字符串创建移动用户身份信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="json">JSON字符串</param>
|
||||
|
/// <returns>移动用户身份信息</returns>
|
||||
|
public static MobileUserIdentity FromJson(string json) |
||||
|
{ |
||||
|
return JsonConvert.DeserializeObject<MobileUserIdentity>(json) ?? new MobileUserIdentity(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 重写ToString方法
|
||||
|
/// </summary>
|
||||
|
/// <returns>字符串表示</returns>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return GetIdentitySummary(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
using System; |
||||
|
using System.Text.Json.Serialization; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络层日志集合
|
||||
|
/// 用于统一管理不同网络层的日志配置
|
||||
|
/// </summary>
|
||||
|
public class NetworkLayerLogs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// IMS层日志配置
|
||||
|
/// </summary>
|
||||
|
public ImsLayerLog ImsLog { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RAN层日志配置
|
||||
|
/// </summary>
|
||||
|
public RanLayerLog RanLog { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化所有网络层的日志级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="isNonStandaloneMode">是否为非独立组网模式(NSA模式)</param>
|
||||
|
public void InitializeAllLogLevels(bool isNonStandaloneMode = false) |
||||
|
{ |
||||
|
ImsLog = new ImsLayerLog(); |
||||
|
RanLog = new RanLayerLog(); |
||||
|
|
||||
|
ImsLog.InitializeLogLevels(); |
||||
|
RanLog.InitializeLogLevels(isNonStandaloneMode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 更新指定网络层和指定层的日志级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="networkType">网络类型("IMS" 或 "RAN")</param>
|
||||
|
/// <param name="layerName">层名称</param>
|
||||
|
/// <param name="logLevel">日志级别</param>
|
||||
|
/// <returns>是否更新成功</returns>
|
||||
|
public bool UpdateLogLevel(string networkType, string layerName, LogLevel logLevel) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(networkType) || string.IsNullOrEmpty(layerName)) |
||||
|
return false; |
||||
|
|
||||
|
return networkType.ToUpper() switch |
||||
|
{ |
||||
|
"IMS" => ImsLog?.UpdateLogLevel(layerName, logLevel) ?? false, |
||||
|
"RAN" => RanLog?.UpdateLogLevel(layerName, logLevel) ?? false, |
||||
|
_ => false |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,242 @@ |
|||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议层类型枚举
|
||||
|
/// 定义了系统中支持的各种协议层类型
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public enum ProtocolLayerType |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 无协议层类型
|
||||
|
/// </summary>
|
||||
|
NONE = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// GTP-U协议层
|
||||
|
/// GPRS Tunnelling Protocol User Plane
|
||||
|
/// </summary>
|
||||
|
GTPU = 1, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// LPPa协议层
|
||||
|
/// LTE Positioning Protocol Annex
|
||||
|
/// </summary>
|
||||
|
LPPa = 2, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// M2AP协议层
|
||||
|
/// M2 Application Protocol
|
||||
|
/// </summary>
|
||||
|
M2AP = 3, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MAC协议层
|
||||
|
/// Medium Access Control
|
||||
|
/// </summary>
|
||||
|
MAC = 4, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NAS协议层
|
||||
|
/// Non-Access Stratum
|
||||
|
/// </summary>
|
||||
|
NAS = 5, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NGAP协议层
|
||||
|
/// Next Generation Application Protocol
|
||||
|
/// </summary>
|
||||
|
NGAP = 6, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NRPPa协议层
|
||||
|
/// NR Positioning Protocol Annex
|
||||
|
/// </summary>
|
||||
|
NRPPa = 7, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PDCP协议层
|
||||
|
/// Packet Data Convergence Protocol
|
||||
|
/// </summary>
|
||||
|
PDCP = 8, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PROD协议层
|
||||
|
/// Production Protocol
|
||||
|
/// </summary>
|
||||
|
PROD = 9, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PHY协议层
|
||||
|
/// Physical Layer
|
||||
|
/// </summary>
|
||||
|
PHY = 10, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RLC协议层
|
||||
|
/// Radio Link Control
|
||||
|
/// </summary>
|
||||
|
RLC = 11, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RRC协议层
|
||||
|
/// Radio Resource Control
|
||||
|
/// </summary>
|
||||
|
RRC = 12, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// S1AP协议层
|
||||
|
/// S1 Application Protocol
|
||||
|
/// </summary>
|
||||
|
S1AP = 13, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TRX协议层
|
||||
|
/// Transceiver Protocol
|
||||
|
/// </summary>
|
||||
|
TRX = 14, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X2AP协议层
|
||||
|
/// X2 Application Protocol
|
||||
|
/// </summary>
|
||||
|
X2AP = 15, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// XnAP协议层
|
||||
|
/// Xn Application Protocol
|
||||
|
/// </summary>
|
||||
|
XnAP = 16, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IP协议层
|
||||
|
/// Internet Protocol
|
||||
|
/// </summary>
|
||||
|
IP = 17, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IMS协议层
|
||||
|
/// IP Multimedia Subsystem
|
||||
|
/// </summary>
|
||||
|
IMS = 18, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CX协议层
|
||||
|
/// Diameter CX Interface
|
||||
|
/// </summary>
|
||||
|
CX = 19, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RX协议层
|
||||
|
/// Diameter RX Interface
|
||||
|
/// </summary>
|
||||
|
RX = 20, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// S6协议层
|
||||
|
/// Diameter S6 Interface
|
||||
|
/// </summary>
|
||||
|
S6 = 21, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// S13协议层
|
||||
|
/// Diameter S13 Interface
|
||||
|
/// </summary>
|
||||
|
S13 = 22, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SGsAP协议层
|
||||
|
/// SGs Application Protocol
|
||||
|
/// </summary>
|
||||
|
SGsAP = 23, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SBcAP协议层
|
||||
|
/// SBc Application Protocol
|
||||
|
/// </summary>
|
||||
|
SBcAP = 24, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// LCSAP协议层
|
||||
|
/// LCS Application Protocol
|
||||
|
/// </summary>
|
||||
|
LCSAP = 25, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// N12协议层
|
||||
|
/// Diameter N12 Interface
|
||||
|
/// </summary>
|
||||
|
N12 = 26, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// N8协议层
|
||||
|
/// Diameter N8 Interface
|
||||
|
/// </summary>
|
||||
|
N8 = 27, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// N17协议层
|
||||
|
/// Diameter N17 Interface
|
||||
|
/// </summary>
|
||||
|
N17 = 28, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// N50协议层
|
||||
|
/// Diameter N50 Interface
|
||||
|
/// </summary>
|
||||
|
N50 = 29, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// N13协议层
|
||||
|
/// Diameter N13 Interface
|
||||
|
/// </summary>
|
||||
|
N13 = 30, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NL1协议层
|
||||
|
/// Network Layer 1
|
||||
|
/// </summary>
|
||||
|
NL1 = 31, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// HTTP2协议层
|
||||
|
/// Hypertext Transfer Protocol 2
|
||||
|
/// </summary>
|
||||
|
HTTP2 = 32, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// EPDG协议层
|
||||
|
/// Evolved Packet Data Gateway
|
||||
|
/// </summary>
|
||||
|
EPDG = 33, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IKEV2协议层
|
||||
|
/// Internet Key Exchange Version 2
|
||||
|
/// </summary>
|
||||
|
IKEV2 = 34, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IPSEC协议层
|
||||
|
/// Internet Protocol Security
|
||||
|
/// </summary>
|
||||
|
IPSEC = 35, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MEDIA协议层
|
||||
|
/// Media Protocol
|
||||
|
/// </summary>
|
||||
|
MEDIA = 36, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MMS协议层
|
||||
|
/// Multimedia Messaging Service
|
||||
|
/// </summary>
|
||||
|
MMS = 37, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SIP协议层
|
||||
|
/// Session Initiation Protocol
|
||||
|
/// </summary>
|
||||
|
SIP = 38 |
||||
|
} |
@ -0,0 +1,258 @@ |
|||||
|
using System.ComponentModel; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ProtocolLayerType 枚举扩展方法
|
||||
|
/// 提供协议层类型的实用功能
|
||||
|
/// </summary>
|
||||
|
public static class ProtocolLayerTypeExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取协议层类型的描述信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>描述信息</returns>
|
||||
|
public static string GetDescription(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
var field = protocolLayerType.GetType().GetField(protocolLayerType.ToString()); |
||||
|
var attribute = field?.GetCustomAttribute<DescriptionAttribute>(); |
||||
|
return attribute?.Description ?? protocolLayerType.ToString(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取协议层类型的显示名称
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>显示名称</returns>
|
||||
|
public static string GetDisplayName(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
return protocolLayerType switch |
||||
|
{ |
||||
|
ProtocolLayerType.NONE => "无", |
||||
|
ProtocolLayerType.GTPU => "GTP-U", |
||||
|
ProtocolLayerType.LPPa => "LPPa", |
||||
|
ProtocolLayerType.M2AP => "M2AP", |
||||
|
ProtocolLayerType.MAC => "MAC", |
||||
|
ProtocolLayerType.NAS => "NAS", |
||||
|
ProtocolLayerType.NGAP => "NGAP", |
||||
|
ProtocolLayerType.NRPPa => "NRPPa", |
||||
|
ProtocolLayerType.PDCP => "PDCP", |
||||
|
ProtocolLayerType.PROD => "PROD", |
||||
|
ProtocolLayerType.PHY => "PHY", |
||||
|
ProtocolLayerType.RLC => "RLC", |
||||
|
ProtocolLayerType.RRC => "RRC", |
||||
|
ProtocolLayerType.S1AP => "S1AP", |
||||
|
ProtocolLayerType.TRX => "TRX", |
||||
|
ProtocolLayerType.X2AP => "X2AP", |
||||
|
ProtocolLayerType.XnAP => "XnAP", |
||||
|
ProtocolLayerType.IP => "IP", |
||||
|
ProtocolLayerType.IMS => "IMS", |
||||
|
ProtocolLayerType.CX => "CX", |
||||
|
ProtocolLayerType.RX => "RX", |
||||
|
ProtocolLayerType.S6 => "S6", |
||||
|
ProtocolLayerType.S13 => "S13", |
||||
|
ProtocolLayerType.SGsAP => "SGsAP", |
||||
|
ProtocolLayerType.SBcAP => "SBcAP", |
||||
|
ProtocolLayerType.LCSAP => "LCSAP", |
||||
|
ProtocolLayerType.N12 => "N12", |
||||
|
ProtocolLayerType.N8 => "N8", |
||||
|
ProtocolLayerType.N17 => "N17", |
||||
|
ProtocolLayerType.N50 => "N50", |
||||
|
ProtocolLayerType.N13 => "N13", |
||||
|
ProtocolLayerType.NL1 => "NL1", |
||||
|
ProtocolLayerType.HTTP2 => "HTTP/2", |
||||
|
ProtocolLayerType.EPDG => "EPDG", |
||||
|
ProtocolLayerType.IKEV2 => "IKEv2", |
||||
|
ProtocolLayerType.IPSEC => "IPSec", |
||||
|
ProtocolLayerType.MEDIA => "MEDIA", |
||||
|
ProtocolLayerType.MMS => "MMS", |
||||
|
ProtocolLayerType.SIP => "SIP", |
||||
|
_ => protocolLayerType.ToString() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 判断是否为RAN相关协议层
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>是否为RAN协议层</returns>
|
||||
|
public static bool IsRanProtocol(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
return protocolLayerType switch |
||||
|
{ |
||||
|
ProtocolLayerType.GTPU or |
||||
|
ProtocolLayerType.LPPa or |
||||
|
ProtocolLayerType.M2AP or |
||||
|
ProtocolLayerType.MAC or |
||||
|
ProtocolLayerType.NAS or |
||||
|
ProtocolLayerType.NGAP or |
||||
|
ProtocolLayerType.NRPPa or |
||||
|
ProtocolLayerType.PDCP or |
||||
|
ProtocolLayerType.PHY or |
||||
|
ProtocolLayerType.RLC or |
||||
|
ProtocolLayerType.RRC or |
||||
|
ProtocolLayerType.S1AP or |
||||
|
ProtocolLayerType.TRX or |
||||
|
ProtocolLayerType.X2AP or |
||||
|
ProtocolLayerType.XnAP => true, |
||||
|
_ => false |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 判断是否为IMS相关协议层
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>是否为IMS协议层</returns>
|
||||
|
public static bool IsImsProtocol(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
return protocolLayerType switch |
||||
|
{ |
||||
|
ProtocolLayerType.IMS or |
||||
|
ProtocolLayerType.CX or |
||||
|
ProtocolLayerType.RX or |
||||
|
ProtocolLayerType.S6 or |
||||
|
ProtocolLayerType.S13 or |
||||
|
ProtocolLayerType.SGsAP or |
||||
|
ProtocolLayerType.SBcAP or |
||||
|
ProtocolLayerType.LCSAP or |
||||
|
ProtocolLayerType.N12 or |
||||
|
ProtocolLayerType.N8 or |
||||
|
ProtocolLayerType.N17 or |
||||
|
ProtocolLayerType.N50 or |
||||
|
ProtocolLayerType.N13 or |
||||
|
ProtocolLayerType.HTTP2 or |
||||
|
ProtocolLayerType.EPDG or |
||||
|
ProtocolLayerType.IKEV2 or |
||||
|
ProtocolLayerType.IPSEC or |
||||
|
ProtocolLayerType.MEDIA or |
||||
|
ProtocolLayerType.MMS or |
||||
|
ProtocolLayerType.SIP => true, |
||||
|
_ => false |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 判断是否为Diameter协议层
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>是否为Diameter协议层</returns>
|
||||
|
public static bool IsDiameterProtocol(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
return protocolLayerType switch |
||||
|
{ |
||||
|
ProtocolLayerType.CX or |
||||
|
ProtocolLayerType.RX or |
||||
|
ProtocolLayerType.S6 or |
||||
|
ProtocolLayerType.S13 or |
||||
|
ProtocolLayerType.N12 or |
||||
|
ProtocolLayerType.N8 or |
||||
|
ProtocolLayerType.N17 or |
||||
|
ProtocolLayerType.N50 or |
||||
|
ProtocolLayerType.N13 => true, |
||||
|
_ => false |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取协议层类型的分类
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLayerType">协议层类型</param>
|
||||
|
/// <returns>协议层分类</returns>
|
||||
|
public static string GetCategory(this ProtocolLayerType protocolLayerType) |
||||
|
{ |
||||
|
return protocolLayerType switch |
||||
|
{ |
||||
|
ProtocolLayerType.NONE => "None", |
||||
|
ProtocolLayerType.GTPU or |
||||
|
ProtocolLayerType.LPPa or |
||||
|
ProtocolLayerType.M2AP or |
||||
|
ProtocolLayerType.MAC or |
||||
|
ProtocolLayerType.NAS or |
||||
|
ProtocolLayerType.NGAP or |
||||
|
ProtocolLayerType.NRPPa or |
||||
|
ProtocolLayerType.PDCP or |
||||
|
ProtocolLayerType.PHY or |
||||
|
ProtocolLayerType.RLC or |
||||
|
ProtocolLayerType.RRC or |
||||
|
ProtocolLayerType.S1AP or |
||||
|
ProtocolLayerType.TRX or |
||||
|
ProtocolLayerType.X2AP or |
||||
|
ProtocolLayerType.XnAP => "RAN", |
||||
|
ProtocolLayerType.IMS or |
||||
|
ProtocolLayerType.CX or |
||||
|
ProtocolLayerType.RX or |
||||
|
ProtocolLayerType.S6 or |
||||
|
ProtocolLayerType.S13 or |
||||
|
ProtocolLayerType.SGsAP or |
||||
|
ProtocolLayerType.SBcAP or |
||||
|
ProtocolLayerType.LCSAP or |
||||
|
ProtocolLayerType.N12 or |
||||
|
ProtocolLayerType.N8 or |
||||
|
ProtocolLayerType.N17 or |
||||
|
ProtocolLayerType.N50 or |
||||
|
ProtocolLayerType.N13 or |
||||
|
ProtocolLayerType.HTTP2 or |
||||
|
ProtocolLayerType.EPDG or |
||||
|
ProtocolLayerType.IKEV2 or |
||||
|
ProtocolLayerType.IPSEC or |
||||
|
ProtocolLayerType.MEDIA or |
||||
|
ProtocolLayerType.MMS or |
||||
|
ProtocolLayerType.SIP => "IMS", |
||||
|
ProtocolLayerType.IP => "Network", |
||||
|
ProtocolLayerType.NL1 => "Network", |
||||
|
ProtocolLayerType.PROD => "Production", |
||||
|
_ => "Other" |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从字符串转换为ProtocolLayerType枚举
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">字符串值</param>
|
||||
|
/// <returns>ProtocolLayerType枚举值,如果转换失败返回NONE</returns>
|
||||
|
public static ProtocolLayerType FromString(string value) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(value)) |
||||
|
return ProtocolLayerType.NONE; |
||||
|
|
||||
|
return Enum.TryParse<ProtocolLayerType>(value, true, out var result) |
||||
|
? result |
||||
|
: ProtocolLayerType.NONE; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取所有RAN协议层类型
|
||||
|
/// </summary>
|
||||
|
/// <returns>RAN协议层类型数组</returns>
|
||||
|
public static ProtocolLayerType[] GetRanProtocols() |
||||
|
{ |
||||
|
return Enum.GetValues<ProtocolLayerType>() |
||||
|
.Where(p => p.IsRanProtocol()) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取所有IMS协议层类型
|
||||
|
/// </summary>
|
||||
|
/// <returns>IMS协议层类型数组</returns>
|
||||
|
public static ProtocolLayerType[] GetImsProtocols() |
||||
|
{ |
||||
|
return Enum.GetValues<ProtocolLayerType>() |
||||
|
.Where(p => p.IsImsProtocol()) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取所有Diameter协议层类型
|
||||
|
/// </summary>
|
||||
|
/// <returns>Diameter协议层类型数组</returns>
|
||||
|
public static ProtocolLayerType[] GetDiameterProtocols() |
||||
|
{ |
||||
|
return Enum.GetValues<ProtocolLayerType>() |
||||
|
.Where(p => p.IsDiameterProtocol()) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,97 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议日志实体类
|
||||
|
/// </summary>
|
||||
|
public record class ProtocolLog |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 消息ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("message_id")] |
||||
|
public int? MessageId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息头信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("headers")] |
||||
|
public string[]? Headers { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息内容
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("message")] |
||||
|
public string Message { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息类型
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("type")] |
||||
|
public string? Type { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息名称
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("name")] |
||||
|
public string? Name { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议版本
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("version")] |
||||
|
public string? Version { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间戳
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("time")] |
||||
|
public double? Time { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// UTC时间戳
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("utc")] |
||||
|
public double? Utc { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志明细
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("logs")] |
||||
|
public JToken? Logs { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化协议日志实体类的新实例
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">消息内容</param>
|
||||
|
/// <param name="type">消息类型</param>
|
||||
|
/// <param name="version">协议版本</param>
|
||||
|
/// <param name="time">时间戳</param>
|
||||
|
/// <param name="utc">UTC时间戳</param>
|
||||
|
/// <param name="logs">日志明细</param>
|
||||
|
/// <param name="messageId">消息ID</param>
|
||||
|
/// <param name="headers">消息头信息</param>
|
||||
|
public ProtocolLog( |
||||
|
string message, |
||||
|
string? type, |
||||
|
string? version, |
||||
|
double? time, |
||||
|
double? utc, |
||||
|
JToken? logs, |
||||
|
int? messageId, |
||||
|
string[]? headers) |
||||
|
{ |
||||
|
Message = message; |
||||
|
Type = type; |
||||
|
Version = version; |
||||
|
Name = type; |
||||
|
Utc = utc; |
||||
|
Time = time; |
||||
|
Logs = logs; |
||||
|
MessageId = messageId ?? 0; |
||||
|
Headers = headers; |
||||
|
} |
||||
|
} |
@ -0,0 +1,76 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议日志明细
|
||||
|
/// </summary>
|
||||
|
public class ProtocolLogDetail |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 源信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("src")] |
||||
|
public string Src { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 索引
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("idx")] |
||||
|
public int Idx { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志级别
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("level")] |
||||
|
public int Level { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 方向
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("dir")] |
||||
|
public string Dir { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间戳
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("timestamp")] |
||||
|
public long Timestamp { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 小区信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("cell")] |
||||
|
public int? Cell { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 数据列表
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("data")] |
||||
|
public List<string> Data { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 层信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("layer")] |
||||
|
public string Layer { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// UE标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("ue_id")] |
||||
|
public int? UeId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 帧信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("frame")] |
||||
|
public int? Frame { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时隙信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("slot")] |
||||
|
public int? Slot { get; set; } |
||||
|
} |
@ -0,0 +1,338 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议日志解析结果
|
||||
|
/// 用于存储ProtocolLog解析后的分析结果,用于数据传输和处理
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public class ProtocolLogParsedResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 唯一标识符
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("id")] |
||||
|
public string Id { get; set; } = Guid.NewGuid().ToString(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志索引
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("index")] |
||||
|
public int? Index { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("messageId")] |
||||
|
public int? MessageId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议层类型
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("protocolLayer")] |
||||
|
public ProtocolLayerType ProtocolLayer { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议类型描述
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("protocolType")] |
||||
|
public string ProtocolType { get; set; } = string.Empty; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 用户设备ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("ueId")] |
||||
|
public int? UeId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 公共陆地移动网络标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("plmn")] |
||||
|
public string? Plmn { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 临时移动用户标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("tmsi")] |
||||
|
public string? Tmsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 国际移动设备标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("imei")] |
||||
|
public string? Imei { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 国际移动用户标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("imsi")] |
||||
|
public string? Imsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 小区ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("cellId")] |
||||
|
public int? CellId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 小区信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("cell")] |
||||
|
public string? Cell { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志信息
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("info")] |
||||
|
public string? Info { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息内容
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("message")] |
||||
|
public string? Message { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息数据数组
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("messageData")] |
||||
|
public string[]? MessageData { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间间隔
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("time")] |
||||
|
public TimeSpan Time { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 时间戳
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("timestamp")] |
||||
|
public long Timestamp { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志方向
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("direction")] |
||||
|
public DirectionLogsType Direction { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议层分类
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("category")] |
||||
|
public string Category => ProtocolLayer.GetCategory(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 协议层显示名称
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("displayName")] |
||||
|
public string DisplayName => ProtocolLayer.GetDisplayName(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化协议日志解析结果的新实例
|
||||
|
/// </summary>
|
||||
|
public ProtocolLogParsedResult() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化协议日志解析结果的新实例
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageId">消息ID</param>
|
||||
|
/// <param name="index">日志索引</param>
|
||||
|
/// <param name="protocolLayer">协议层类型</param>
|
||||
|
/// <param name="ueId">用户设备ID</param>
|
||||
|
/// <param name="plmn">公共陆地移动网络标识</param>
|
||||
|
/// <param name="tmsi">临时移动用户标识</param>
|
||||
|
/// <param name="imei">国际移动设备标识</param>
|
||||
|
/// <param name="imsi">国际移动用户标识</param>
|
||||
|
/// <param name="cellId">小区ID</param>
|
||||
|
/// <param name="cell">小区信息</param>
|
||||
|
/// <param name="info">日志信息</param>
|
||||
|
/// <param name="message">消息内容</param>
|
||||
|
/// <param name="messageData">消息数据数组</param>
|
||||
|
/// <param name="direction">日志方向</param>
|
||||
|
public ProtocolLogParsedResult( |
||||
|
int? messageId = null, |
||||
|
int? index = null, |
||||
|
ProtocolLayerType protocolLayer = ProtocolLayerType.NONE, |
||||
|
int? ueId = null, |
||||
|
string? plmn = null, |
||||
|
string? tmsi = null, |
||||
|
string? imei = null, |
||||
|
string? imsi = null, |
||||
|
int? cellId = null, |
||||
|
string? cell = null, |
||||
|
string? info = null, |
||||
|
string? message = null, |
||||
|
string[]? messageData = null, |
||||
|
DirectionLogsType direction = DirectionLogsType.Unknown) |
||||
|
{ |
||||
|
MessageId = messageId; |
||||
|
Index = index; |
||||
|
ProtocolLayer = protocolLayer; |
||||
|
UeId = ueId; |
||||
|
Plmn = plmn; |
||||
|
Tmsi = tmsi; |
||||
|
Imei = imei; |
||||
|
Imsi = imsi; |
||||
|
CellId = cellId; |
||||
|
Cell = cell; |
||||
|
Info = info; |
||||
|
Message = message; |
||||
|
MessageData = messageData; |
||||
|
Direction = direction; |
||||
|
ProtocolType = protocolLayer.GetDisplayName(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从ProtocolLog创建ProtocolLogParsedResult
|
||||
|
/// </summary>
|
||||
|
/// <param name="protocolLog">协议日志</param>
|
||||
|
/// <param name="index">日志索引</param>
|
||||
|
/// <returns>协议日志解析结果</returns>
|
||||
|
public static ProtocolLogParsedResult FromProtocolLog(ProtocolLog protocolLog, int? index = null) |
||||
|
{ |
||||
|
// 处理时间戳转换
|
||||
|
long timestamp; |
||||
|
if (protocolLog.Time.HasValue) |
||||
|
{ |
||||
|
// 如果 Time 是秒为单位,转换为毫秒
|
||||
|
timestamp = (long)(protocolLog.Time.Value * 1000); |
||||
|
} |
||||
|
else if (protocolLog.Utc.HasValue) |
||||
|
{ |
||||
|
// 如果 Utc 是秒为单位,转换为毫秒
|
||||
|
timestamp = (long)(protocolLog.Utc.Value * 1000); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// 默认使用当前时间
|
||||
|
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); |
||||
|
} |
||||
|
|
||||
|
return new ProtocolLogParsedResult |
||||
|
{ |
||||
|
MessageId = protocolLog.MessageId, |
||||
|
Index = index, |
||||
|
ProtocolLayer = ProtocolLayerType.NONE, // 需要根据实际内容解析
|
||||
|
Message = protocolLog.Message, |
||||
|
MessageData = protocolLog.Headers, |
||||
|
Timestamp = timestamp, |
||||
|
Time = TimeSpan.FromMilliseconds(protocolLog.Time ?? 0), |
||||
|
Direction = DirectionLogsType.Unknown, |
||||
|
ProtocolType = protocolLog.Type ?? string.Empty |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从ProtocolLogDetail创建ProtocolLogParsedResult
|
||||
|
/// </summary>
|
||||
|
/// <param name="detail">协议日志明细</param>
|
||||
|
/// <param name="index">日志索引</param>
|
||||
|
/// <returns>协议日志解析结果</returns>
|
||||
|
public static ProtocolLogParsedResult FromProtocolLogDetail(ProtocolLogDetail detail, int? index = null) |
||||
|
{ |
||||
|
return new ProtocolLogParsedResult |
||||
|
{ |
||||
|
Index = index ?? detail.Idx, |
||||
|
ProtocolLayer = ProtocolLayerTypeExtensions.FromString(detail.Layer), |
||||
|
UeId = detail.UeId, |
||||
|
CellId = detail.Cell, |
||||
|
Info = detail.Src, |
||||
|
MessageData = detail.Data?.ToArray(), |
||||
|
Timestamp = detail.Timestamp, |
||||
|
Time = TimeSpan.FromMilliseconds(detail.Timestamp), |
||||
|
Direction = ParseDirection(detail.Dir), |
||||
|
ProtocolType = detail.Layer |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 解析方向字符串为DirectionLogsType
|
||||
|
/// </summary>
|
||||
|
/// <param name="direction">方向字符串</param>
|
||||
|
/// <returns>方向类型</returns>
|
||||
|
private static DirectionLogsType ParseDirection(string? direction) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(direction)) |
||||
|
return DirectionLogsType.Unknown; |
||||
|
|
||||
|
return direction.ToLower() switch |
||||
|
{ |
||||
|
"up" or "uplink" or "ul" => DirectionLogsType.Uplink, |
||||
|
"down" or "downlink" or "dl" => DirectionLogsType.Downlink, |
||||
|
"bidirectional" or "bi" => DirectionLogsType.Bidirectional, |
||||
|
"internal" or "int" => DirectionLogsType.Internal, |
||||
|
_ => DirectionLogsType.Unknown |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查解析结果是否包含有效数据
|
||||
|
/// </summary>
|
||||
|
/// <returns>是否包含有效数据</returns>
|
||||
|
public bool HasValidData() |
||||
|
{ |
||||
|
return !string.IsNullOrEmpty(Id) && |
||||
|
(ProtocolLayer != ProtocolLayerType.NONE || !string.IsNullOrEmpty(ProtocolType)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取解析结果摘要信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>摘要信息</returns>
|
||||
|
public string GetSummary() |
||||
|
{ |
||||
|
return $"ProtocolLogParsed[{Id}] - {ProtocolLayer.GetDisplayName()} - {Direction} - UE:{UeId} - Cell:{CellId}"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建解析结果的副本
|
||||
|
/// </summary>
|
||||
|
/// <returns>新的协议日志解析结果</returns>
|
||||
|
public ProtocolLogParsedResult Copy() |
||||
|
{ |
||||
|
return new ProtocolLogParsedResult |
||||
|
{ |
||||
|
Id = Guid.NewGuid().ToString(), // 生成新的ID
|
||||
|
Index = Index, |
||||
|
MessageId = MessageId, |
||||
|
ProtocolLayer = ProtocolLayer, |
||||
|
ProtocolType = ProtocolType, |
||||
|
UeId = UeId, |
||||
|
Plmn = Plmn, |
||||
|
Tmsi = Tmsi, |
||||
|
Imei = Imei, |
||||
|
Imsi = Imsi, |
||||
|
CellId = CellId, |
||||
|
Cell = Cell, |
||||
|
Info = Info, |
||||
|
Message = Message, |
||||
|
MessageData = MessageData?.Clone() as string[], |
||||
|
Time = Time, |
||||
|
Timestamp = Timestamp, |
||||
|
Direction = Direction |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 转换为JSON字符串
|
||||
|
/// </summary>
|
||||
|
/// <returns>JSON字符串</returns>
|
||||
|
public string ToJson() |
||||
|
{ |
||||
|
return JsonConvert.SerializeObject(this, Formatting.Indented); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从JSON字符串创建解析结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="json">JSON字符串</param>
|
||||
|
/// <returns>协议日志解析结果</returns>
|
||||
|
public static ProtocolLogParsedResult FromJson(string json) |
||||
|
{ |
||||
|
return JsonConvert.DeserializeObject<ProtocolLogParsedResult>(json) ?? new ProtocolLogParsedResult(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,136 @@ |
|||||
|
using System; |
||||
|
using System.Text.Json.Serialization; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RAN层日志实体类
|
||||
|
/// 该实体用于记录RAN(无线接入网)相关的各层日志信息
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public class RanLayerLog |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// GTP-U协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string GTPU { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// LPPa协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string LPPa { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// M2AP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string M2AP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MAC协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string MAC { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NAS协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string NAS { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NGAP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string NGAP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NRPPa协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string NRPPa { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PDCP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string PDCP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PHY协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string PHY { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RLC协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string RLC { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// RRC协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string RRC { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// S1AP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string S1AP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// TRX协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string TRX { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X2AP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string X2AP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// XnAP协议层日志级别
|
||||
|
/// </summary>
|
||||
|
public string XnAP { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PROD协议层日志级别
|
||||
|
/// </summary>
|
||||
|
[JsonIgnore] |
||||
|
public string PROD { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化RAN层日志级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="isNonStandaloneMode">是否为非独立组网模式(NSA模式)</param>
|
||||
|
public void InitializeLogLevels(bool isNonStandaloneMode = false) |
||||
|
{ |
||||
|
GTPU = LogLevel.Warn.ToString().ToLower(); |
||||
|
LPPa = LogLevel.Warn.ToString().ToLower(); |
||||
|
M2AP = LogLevel.Warn.ToString().ToLower(); |
||||
|
MAC = LogLevel.Warn.ToString().ToLower(); |
||||
|
NAS = LogLevel.Warn.ToString().ToLower(); |
||||
|
NGAP = LogLevel.Warn.ToString().ToLower(); |
||||
|
NRPPa = LogLevel.Warn.ToString().ToLower(); |
||||
|
PDCP = LogLevel.Warn.ToString().ToLower(); |
||||
|
PHY = LogLevel.Warn.ToString().ToLower(); |
||||
|
RLC = LogLevel.Warn.ToString().ToLower(); |
||||
|
RRC = LogLevel.Warn.ToString().ToLower(); |
||||
|
S1AP = LogLevel.Warn.ToString().ToLower(); |
||||
|
TRX = LogLevel.Warn.ToString().ToLower(); |
||||
|
X2AP = LogLevel.Warn.ToString().ToLower(); |
||||
|
XnAP = LogLevel.Warn.ToString().ToLower(); |
||||
|
PROD = LogLevel.Warn.ToString().ToLower(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 更新指定层的日志级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="layerName">层名称</param>
|
||||
|
/// <param name="logLevel">日志级别</param>
|
||||
|
/// <returns>是否更新成功</returns>
|
||||
|
public bool UpdateLogLevel(string layerName, LogLevel logLevel) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(layerName)) |
||||
|
return false; |
||||
|
|
||||
|
var property = GetType().GetProperty(layerName); |
||||
|
if (property == null) |
||||
|
return false; |
||||
|
|
||||
|
property.SetValue(this, logLevel.ToString().ToLower()); |
||||
|
return true; |
||||
|
} |
||||
|
} |
@ -0,0 +1,219 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Protocol; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 用户网络身份信息
|
||||
|
/// 用于存储移动网络用户的基本身份和位置信息
|
||||
|
/// 遵循DDD(领域驱动设计)原则,作为领域模型的一部分
|
||||
|
/// </summary>
|
||||
|
public class UserNetworkIdentity |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 公共陆地移动网络标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("plmn")] |
||||
|
public string? Plmn { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 旧的临时移动用户标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("oldTmsi")] |
||||
|
public string? OldTmsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 临时移动用户标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("tmsi")] |
||||
|
public string? Tmsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 国际移动用户标识
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("imsi")] |
||||
|
public string? Imsi { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 小区ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("cellId")] |
||||
|
public int? CellId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 用户设备ID
|
||||
|
/// </summary>
|
||||
|
[JsonProperty("ueId")] |
||||
|
public int? UeId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化用户网络身份信息的新实例
|
||||
|
/// </summary>
|
||||
|
public UserNetworkIdentity() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化用户网络身份信息的新实例
|
||||
|
/// </summary>
|
||||
|
/// <param name="plmn">公共陆地移动网络标识</param>
|
||||
|
/// <param name="oldTmsi">旧的临时移动用户标识</param>
|
||||
|
/// <param name="tmsi">临时移动用户标识</param>
|
||||
|
/// <param name="imsi">国际移动用户标识</param>
|
||||
|
/// <param name="cellId">小区ID</param>
|
||||
|
/// <param name="ueId">用户设备ID</param>
|
||||
|
public UserNetworkIdentity( |
||||
|
string? plmn = null, |
||||
|
string? oldTmsi = null, |
||||
|
string? tmsi = null, |
||||
|
string? imsi = null, |
||||
|
int? cellId = null, |
||||
|
int? ueId = null) |
||||
|
{ |
||||
|
Plmn = plmn; |
||||
|
OldTmsi = oldTmsi; |
||||
|
Tmsi = tmsi; |
||||
|
Imsi = imsi; |
||||
|
CellId = cellId; |
||||
|
UeId = ueId; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查是否包含有效的用户身份信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>是否包含有效信息</returns>
|
||||
|
public bool HasValidIdentity() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(Imsi) || |
||||
|
!string.IsNullOrWhiteSpace(Tmsi) || |
||||
|
UeId.HasValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查是否包含位置信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>是否包含位置信息</returns>
|
||||
|
public bool HasLocationInfo() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(Plmn) || CellId.HasValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取用户身份摘要信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>摘要信息</returns>
|
||||
|
public string GetIdentitySummary() |
||||
|
{ |
||||
|
var parts = new List<string>(); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Imsi)) |
||||
|
parts.Add($"IMSI:{Imsi}"); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Tmsi)) |
||||
|
parts.Add($"TMSI:{Tmsi}"); |
||||
|
|
||||
|
if (UeId.HasValue) |
||||
|
parts.Add($"UE:{UeId}"); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(Plmn)) |
||||
|
parts.Add($"PLMN:{Plmn}"); |
||||
|
|
||||
|
if (CellId.HasValue) |
||||
|
parts.Add($"Cell:{CellId}"); |
||||
|
|
||||
|
return parts.Count > 0 ? string.Join(" | ", parts) : "No Identity Info"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 检查TMSI是否发生变化
|
||||
|
/// </summary>
|
||||
|
/// <returns>TMSI是否发生变化</returns>
|
||||
|
public bool HasTmsiChanged() |
||||
|
{ |
||||
|
return !string.IsNullOrWhiteSpace(OldTmsi) && |
||||
|
!string.IsNullOrWhiteSpace(Tmsi) && |
||||
|
!OldTmsi.Equals(Tmsi, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取TMSI变化信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>TMSI变化信息</returns>
|
||||
|
public string GetTmsiChangeInfo() |
||||
|
{ |
||||
|
if (HasTmsiChanged()) |
||||
|
{ |
||||
|
return $"TMSI Changed: {OldTmsi} -> {Tmsi}"; |
||||
|
} |
||||
|
return "TMSI Unchanged"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建用户网络身份信息的副本
|
||||
|
/// </summary>
|
||||
|
/// <returns>新的用户网络身份信息</returns>
|
||||
|
public UserNetworkIdentity Copy() |
||||
|
{ |
||||
|
return new UserNetworkIdentity |
||||
|
{ |
||||
|
Plmn = Plmn, |
||||
|
OldTmsi = OldTmsi, |
||||
|
Tmsi = Tmsi, |
||||
|
Imsi = Imsi, |
||||
|
CellId = CellId, |
||||
|
UeId = UeId |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 更新TMSI信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="newTmsi">新的TMSI</param>
|
||||
|
public void UpdateTmsi(string? newTmsi) |
||||
|
{ |
||||
|
if (!string.IsNullOrWhiteSpace(Tmsi)) |
||||
|
{ |
||||
|
OldTmsi = Tmsi; |
||||
|
} |
||||
|
Tmsi = newTmsi; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 转换为JSON字符串
|
||||
|
/// </summary>
|
||||
|
/// <returns>JSON字符串</returns>
|
||||
|
public string ToJson() |
||||
|
{ |
||||
|
return JsonConvert.SerializeObject(this, Formatting.Indented); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从JSON字符串创建用户网络身份信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="json">JSON字符串</param>
|
||||
|
/// <returns>用户网络身份信息</returns>
|
||||
|
public static UserNetworkIdentity FromJson(string json) |
||||
|
{ |
||||
|
return JsonConvert.DeserializeObject<UserNetworkIdentity>(json) ?? new UserNetworkIdentity(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 合并另一个用户网络身份信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="other">另一个用户网络身份信息</param>
|
||||
|
/// <returns>合并后的用户网络身份信息</returns>
|
||||
|
public UserNetworkIdentity Merge(UserNetworkIdentity other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
return this; |
||||
|
|
||||
|
return new UserNetworkIdentity |
||||
|
{ |
||||
|
Plmn = Plmn ?? other.Plmn, |
||||
|
OldTmsi = OldTmsi ?? other.OldTmsi, |
||||
|
Tmsi = Tmsi ?? other.Tmsi, |
||||
|
Imsi = Imsi ?? other.Imsi, |
||||
|
CellId = CellId ?? other.CellId, |
||||
|
UeId = UeId ?? other.UeId |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,172 @@ |
|||||
|
using CoreAgent.Domain.Helpers; |
||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using WebSocket4Net; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.CustomWSClient |
||||
|
{ |
||||
|
public class CustomWebSocketClient : IDisposable |
||||
|
{ |
||||
|
private readonly ILogger logger; |
||||
|
private readonly string serverUrl; |
||||
|
private WebSocket? webSocket; |
||||
|
protected readonly ICustomMessageHandler messageHandler; |
||||
|
private bool _disposed; |
||||
|
|
||||
|
protected CustomWebSocketClient(ILogger logger, string serverUrl, ICustomMessageHandler messageHandler) |
||||
|
{ |
||||
|
this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
||||
|
this.serverUrl = $"ws://{serverUrl}"; |
||||
|
this.messageHandler = messageHandler ?? throw new ArgumentNullException(nameof(messageHandler)); |
||||
|
} |
||||
|
|
||||
|
public void Start() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
logger.LogInformation("正在启动WebSocket客户端,连接到服务器: {ServerUrl}", serverUrl); |
||||
|
webSocket = new WebSocket4Net.WebSocket(serverUrl); |
||||
|
ConfigureWebSocketHandlers(); |
||||
|
webSocket.EnableAutoSendPing = false; |
||||
|
webSocket.Open(); |
||||
|
logger.LogInformation("WebSocket客户端启动成功"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "WebSocket客户端启动失败: {Message}", ex.Message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ConfigureWebSocketHandlers() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
webSocket!.Opened += (s, e) => HandleOpen(e); |
||||
|
webSocket.Closed += (s, e) => HandleClosed(e); |
||||
|
webSocket.Error += (s, e) => HandleError(e); |
||||
|
webSocket.MessageReceived += (s, e) => HandleMessage(e); |
||||
|
webSocket.DataReceived += (s, e) => HandleDataReceivedMessage(e); |
||||
|
logger.LogDebug("WebSocket事件处理器配置完成"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "配置WebSocket事件处理器时发生错误: {Message}", ex.Message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleOpen(EventArgs @event) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
logger.LogInformation("WebSocket已连接到服务器: {ServerUrl}", serverUrl); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理WebSocket打开事件时发生错误: {Message}", ex.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleClosed(EventArgs @event) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
logger.LogInformation("WebSocket连接已关闭,状态: {State}", webSocket?.State); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理WebSocket关闭事件时发生错误: {Message}", ex.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleError(SuperSocket.ClientEngine.ErrorEventArgs ex) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
logger.LogError(ex.Exception, "WebSocket发生错误: {Message}", ex.Exception.Message); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
logger.LogError(e, "处理WebSocket错误事件时发生异常: {Message}", e.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleMessage(MessageReceivedEventArgs e) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (webSocket is { State: WebSocketState.Connecting or WebSocketState.Open }) |
||||
|
{ |
||||
|
logger.LogDebug("收到WebSocket消息: {Message}", e.Message); |
||||
|
messageHandler.HandleMessage(e.Message, new ObserverCustomWebSocketClient(webSocket, logger)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
logger.LogWarning("收到消息时WebSocket状态异常: {State}, 消息内容: {Message}", webSocket!.State, e.Message); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理WebSocket消息时发生错误: {Message}, 原始消息: {OriginalMessage}", ex.Message, e.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleDataReceivedMessage(DataReceivedEventArgs data) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
string message = Encoding.UTF8.GetString(data.Data); |
||||
|
logger.LogDebug("收到WebSocket二进制数据: {Message}", message); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理WebSocket二进制数据时发生错误: {Message}, 数据: {Data}", ex.Message, data.ObjToJson()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Stop() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
logger.LogInformation("正在停止WebSocket客户端"); |
||||
|
webSocket?.Close(); |
||||
|
webSocket?.Dispose(); |
||||
|
logger.LogInformation("WebSocket客户端已停止"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "停止WebSocket客户端时发生错误: {Message}", ex.Message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Dispose(true); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
protected virtual void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (!_disposed) |
||||
|
{ |
||||
|
if (disposing) |
||||
|
{ |
||||
|
Stop(); |
||||
|
} |
||||
|
_disposed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~CustomWebSocketClient() |
||||
|
{ |
||||
|
Dispose(false); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,143 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Net.WebSockets; |
||||
|
using System.Text; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using WebSocket4Net; |
||||
|
using WebSocket = WebSocket4Net.WebSocket; |
||||
|
using WebSocketState = WebSocket4Net.WebSocketState; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.CustomWSClient |
||||
|
{ |
||||
|
public class ObserverCustomWebSocketClient : IObserverCustomWebSocketClient, IDisposable |
||||
|
{ |
||||
|
private readonly WebSocket _client; |
||||
|
private readonly ILogger _logger; |
||||
|
private readonly BlockingCollection<string> _sendQueue; |
||||
|
private readonly CancellationTokenSource _cancellationTokenSource; |
||||
|
private readonly Task _sendTask; |
||||
|
private bool _disposed; |
||||
|
private const int SEND_INTERVAL_MS = 100; // 发送间隔,可以根据需要调整
|
||||
|
|
||||
|
public ObserverCustomWebSocketClient(WebSocket client, ILogger logger) |
||||
|
{ |
||||
|
_client = client ?? throw new ArgumentNullException(nameof(client)); |
||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
||||
|
|
||||
|
_sendQueue = new BlockingCollection<string>(); |
||||
|
_cancellationTokenSource = new CancellationTokenSource(); |
||||
|
_sendTask = Task.Run(ProcessSendQueue); |
||||
|
|
||||
|
_logger.LogInformation("WebSocket消息发送队列已启动"); |
||||
|
} |
||||
|
|
||||
|
public void SendMessage(string message) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(message)) |
||||
|
{ |
||||
|
_logger.LogWarning("尝试发送空消息"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("将消息加入发送队列: {Message}", message); |
||||
|
_sendQueue.Add(message); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "将消息加入发送队列时发生错误: {Message}", message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task ProcessSendQueue() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogInformation("开始处理WebSocket消息发送队列"); |
||||
|
foreach (var message in _sendQueue.GetConsumingEnumerable(_cancellationTokenSource.Token)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await SendMessageInternalAsync(message); |
||||
|
await Task.Delay(SEND_INTERVAL_MS); // 添加发送间隔
|
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "发送WebSocket消息时发生错误: {Message}", message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (OperationCanceledException) |
||||
|
{ |
||||
|
_logger.LogInformation("WebSocket消息发送队列处理已取消"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "WebSocket消息发送队列处理过程中发生错误"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task SendMessageInternalAsync(string message) |
||||
|
{ |
||||
|
if (_client.State != WebSocketState.Open) |
||||
|
{ |
||||
|
_logger.LogWarning("WebSocket未处于打开状态,无法发送消息。当前状态: {State}, 消息内容: {Message}", |
||||
|
_client.State, message); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("正在发送WebSocket消息: {Message}", message); |
||||
|
await Task.Run(() => _client.Send(message)); |
||||
|
_logger.LogDebug("WebSocket消息发送成功"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "发送WebSocket消息时发生错误: {Message}", message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Dispose(true); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
protected virtual void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (!_disposed) |
||||
|
{ |
||||
|
if (disposing) |
||||
|
{ |
||||
|
_cancellationTokenSource.Cancel(); |
||||
|
_sendQueue.CompleteAdding(); |
||||
|
try |
||||
|
{ |
||||
|
_sendTask.Wait(TimeSpan.FromSeconds(5)); |
||||
|
} |
||||
|
catch (AggregateException ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "等待消息发送队列处理完成时发生错误"); |
||||
|
} |
||||
|
_sendQueue.Dispose(); |
||||
|
_cancellationTokenSource.Dispose(); |
||||
|
} |
||||
|
_disposed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~ObserverCustomWebSocketClient() |
||||
|
{ |
||||
|
Dispose(false); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,256 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using CoreAgent.Domain.Interfaces.Network; |
||||
|
using CoreAgent.Domain.Models.Protocol; |
||||
|
using CoreAgent.Infrastructure.Contexts; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// IMS协议消息处理器
|
||||
|
/// 负责处理IMS相关的WebSocket消息,包括用户更新、短信、邀请等功能
|
||||
|
/// </summary>
|
||||
|
public class IMSLogMessageHandler : ICustomMessageHandler, IDisposable |
||||
|
{ |
||||
|
private readonly ILogger _logger; |
||||
|
private int _messageId = 0; |
||||
|
private string _currentMessageId = string.Empty; |
||||
|
private readonly Action<string> _messageCallback; |
||||
|
private readonly ICellularNetworkContext _context; |
||||
|
private readonly BlockingCollection<(string MessageData, IObserverCustomWebSocketClient Observer)> _messageQueue; |
||||
|
private readonly CancellationTokenSource _cancellationTokenSource; |
||||
|
private readonly Task _processTask; |
||||
|
private bool _disposed; |
||||
|
|
||||
|
public IMSLogMessageHandler(ILogger logger, ICellularNetworkContext context, Action<string> action) |
||||
|
{ |
||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
||||
|
_messageCallback = action ?? throw new ArgumentNullException(nameof(action)); |
||||
|
_context = context ?? throw new ArgumentNullException(nameof(context)); |
||||
|
|
||||
|
_messageQueue = new BlockingCollection<(string, IObserverCustomWebSocketClient)>(); |
||||
|
_cancellationTokenSource = new CancellationTokenSource(); |
||||
|
_processTask = Task.Run(ProcessMessageQueue); |
||||
|
|
||||
|
_logger.LogInformation("IMS协议消息处理器初始化完成,消息队列已启动"); |
||||
|
} |
||||
|
|
||||
|
public void HandleMessage(string messageData, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("将消息加入处理队列: {MessageData}", messageData); |
||||
|
_messageQueue.Add((messageData, observer)); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "将消息加入队列时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task ProcessMessageQueue() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogInformation("开始处理IMS消息队列"); |
||||
|
foreach (var (messageData, observer) in _messageQueue.GetConsumingEnumerable(_cancellationTokenSource.Token)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await ProcessMessageAsync(messageData, observer); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "处理队列中的消息时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (OperationCanceledException) |
||||
|
{ |
||||
|
_logger.LogInformation("IMS消息队列处理已取消"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "IMS消息队列处理过程中发生错误"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task ProcessMessageAsync(string messageData, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("开始处理IMS协议消息: {MessageData}", messageData); |
||||
|
var data = JObject.Parse(messageData); |
||||
|
string messageType = data["message"]!.ToString(); |
||||
|
_logger.LogInformation("收到IMS协议消息类型: {MessageType}", messageData); |
||||
|
await HandleMessageByTypeAsync(messageType, data, observer); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "处理IMS协议消息时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task HandleMessageByTypeAsync(string messageType, JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
_currentMessageId = _messageId.ToString(); |
||||
|
|
||||
|
switch (messageType) |
||||
|
{ |
||||
|
case "ready": |
||||
|
await HandleReadyMessageAsync(observer); |
||||
|
break; |
||||
|
case "config_get": |
||||
|
await HandleConfigGetMessageAsync(data, observer); |
||||
|
break; |
||||
|
case "config_set": |
||||
|
await HandleConfigSetMessageAsync(observer); |
||||
|
break; |
||||
|
case "log_get": |
||||
|
await HandleLogGetMessageAsync(data, observer); |
||||
|
break; |
||||
|
case "stats": |
||||
|
await HandleStatsMessageAsync(observer); |
||||
|
break; |
||||
|
default: |
||||
|
_logger.LogWarning("收到未知的IMS协议消息类型: {MessageType}", messageType); |
||||
|
await Task.Run(() => observer.SendMessage(LayerLogslevelSetting(false))); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task HandleReadyMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string readyResponse = CreateMessage("config_get"); |
||||
|
_logger.LogInformation("发送ready响应: {Response}", readyResponse); |
||||
|
await Task.Run(() => observer.SendMessage(readyResponse)); |
||||
|
} |
||||
|
|
||||
|
private async Task HandleConfigGetMessageAsync(JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
if (_currentMessageId == data["message_id"]!.ToString()) |
||||
|
{ |
||||
|
_logger.LogInformation("处理config_get请求"); |
||||
|
var responseArray = new JArray |
||||
|
{ |
||||
|
CreateRegisterMessage("users_update"), |
||||
|
CreateRegisterMessage("sms"), |
||||
|
CreateRegisterMessage("invite"), |
||||
|
CreateStatsMessage(), |
||||
|
SettingBaseLayerLogslevel(JObject.Parse(data["logs"].ToString())) |
||||
|
}; |
||||
|
_logger.LogInformation("发送config_get响应: {Response}", responseArray.ToString()); |
||||
|
await Task.Run(() => observer.SendMessage(responseArray.ToString())); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_logger.LogWarning("config_get消息ID不匹配: 收到={ReceivedId}, 期望={ExpectedId}", |
||||
|
data["message_id"]!.ToString(), _currentMessageId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task HandleConfigSetMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string configResponse = LayerLogslevelSetting(true); |
||||
|
_logger.LogInformation("发送config_set响应: {Response}", configResponse); |
||||
|
await Task.Run(() => observer.SendMessage(configResponse)); |
||||
|
//_currentMessageId = _messageId.ToString();
|
||||
|
} |
||||
|
|
||||
|
private async Task HandleLogGetMessageAsync(JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
if (JArray.FromObject(data["logs"]).Count > 0) |
||||
|
{ |
||||
|
string logResponse = LayerLogslevelSetting(false); |
||||
|
_logger.LogInformation("发送log_get响应: {Response}", logResponse); |
||||
|
await Task.Run(() => observer.SendMessage(logResponse)); |
||||
|
//_currentMessageId = _messageId.ToString();
|
||||
|
await Task.Run(() => _messageCallback.Invoke(data.ToString())); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task HandleStatsMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string statsResponse = CreateStatsMessage(); |
||||
|
_logger.LogInformation("发送stats响应: {Response}", statsResponse); |
||||
|
await Task.Run(() => observer.SendMessage(statsResponse)); |
||||
|
} |
||||
|
|
||||
|
private string CreateMessage(string message) |
||||
|
{ |
||||
|
return JObject.FromObject(new { message, message_id = Interlocked.Increment(ref _messageId) }).ToString(); |
||||
|
} |
||||
|
|
||||
|
private JObject CreateRegisterMessage(string register) |
||||
|
{ |
||||
|
return JObject.FromObject(new { message = "register", message_id = Interlocked.Increment(ref _messageId), register }); |
||||
|
} |
||||
|
|
||||
|
private string CreateStatsMessage() |
||||
|
{ |
||||
|
return CreateMessage("stats"); |
||||
|
} |
||||
|
|
||||
|
private JObject SettingBaseLayerLogslevel(JObject keyValues, bool isCloseSystemInfo = false) |
||||
|
{ |
||||
|
keyValues.Remove("rotate"); |
||||
|
keyValues.Remove("path"); |
||||
|
keyValues.Remove("count"); |
||||
|
keyValues["bcch"] = isCloseSystemInfo; |
||||
|
|
||||
|
return JObject.FromObject(new |
||||
|
{ |
||||
|
message = "config_set", |
||||
|
logs = keyValues, |
||||
|
message_id = Interlocked.Increment(ref _messageId) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private string LayerLogslevelSetting(bool isHead = false) |
||||
|
{ |
||||
|
BaseNetworkLog<ImsLayerLog> logtype = new BaseNetworkLog<ImsLayerLog> |
||||
|
{ |
||||
|
Timeout = isHead ? 0 : 1, |
||||
|
MinLogCount = 64, |
||||
|
MaxLogCount = 2048, |
||||
|
LayerConfig = _context.NetworkLogs.ImsLog, |
||||
|
Message = "log_get", |
||||
|
IncludeHeaders = isHead, |
||||
|
MessageId = Interlocked.Increment(ref _messageId) |
||||
|
}; |
||||
|
return JObject.FromObject(logtype).ToString(); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Dispose(true); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
protected virtual void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (!_disposed) |
||||
|
{ |
||||
|
if (disposing) |
||||
|
{ |
||||
|
_cancellationTokenSource.Cancel(); |
||||
|
_messageQueue.CompleteAdding(); |
||||
|
_processTask.Wait(); |
||||
|
_cancellationTokenSource.Dispose(); |
||||
|
_messageQueue.Dispose(); |
||||
|
} |
||||
|
_disposed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~IMSLogMessageHandler() |
||||
|
{ |
||||
|
Dispose(false); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,147 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.Network; |
||||
|
using CoreAgent.Domain.Interfaces.ProtocolLogHandlers; |
||||
|
using CoreAgent.Domain.Models.Network; |
||||
|
using CoreAgent.Infrastructure.Contexts; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 协议日志提供者观察者
|
||||
|
/// 负责接收、缓存和转发协议日志数据
|
||||
|
/// 使用生产者-消费者模式处理日志数据流
|
||||
|
/// </summary>
|
||||
|
public class ProtocolLogsProviderObserver : IProtocolLogsProviderObserver |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 用于存储待处理的协议日志消息的阻塞队列
|
||||
|
/// </summary>
|
||||
|
private readonly static BlockingCollection<string> BlockingQueue = new BlockingCollection<string>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志记录器
|
||||
|
/// </summary>
|
||||
|
private readonly ILogger logger; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络上下文,用于检查网络状态
|
||||
|
/// </summary>
|
||||
|
private readonly CellularNetworkContext networkContext; |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化协议日志提供者观察者
|
||||
|
/// </summary>
|
||||
|
/// <param name="networkContext">网络上下文</param>
|
||||
|
/// <param name="logger">日志记录器</param>
|
||||
|
public ProtocolLogsProviderObserver(CellularNetworkContext networkContext, ILogger logger) |
||||
|
{ |
||||
|
this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
||||
|
this.networkContext = networkContext ?? throw new ArgumentNullException(nameof(networkContext)); |
||||
|
|
||||
|
logger.LogInformation("ProtocolLogsProviderObserver 初始化完成"); |
||||
|
_ = Task.Run(Consumer); |
||||
|
logger.LogInformation("消费者任务已启动"); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 接收并处理新的协议日志数据
|
||||
|
/// </summary>
|
||||
|
/// <param name="msg">协议日志消息</param>
|
||||
|
public void OnData(string msg) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(msg)) |
||||
|
{ |
||||
|
logger.LogWarning("收到空的协议日志消息"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
logger.LogDebug("收到新的协议日志消息: {Message}", msg); |
||||
|
Producer(msg); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理协议日志消息时发生错误: {Message}", msg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 生产者方法:将消息添加到阻塞队列中
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">要添加的消息</param>
|
||||
|
private void Producer(string message) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var networkState = networkContext.GetNetworkState(); |
||||
|
if (networkState.CurrentStatus == NetworkStatus.Connected) |
||||
|
{ |
||||
|
logger.LogDebug("网络已连接,将消息添加到队列: {Message}", message); |
||||
|
BlockingQueue.Add(message); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
logger.LogWarning("网络未连接,丢弃消息: {Message}", message); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "添加消息到队列时发生错误: {Message}", message); |
||||
|
throw; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消费者方法:从队列中获取并处理消息
|
||||
|
/// </summary>
|
||||
|
public async Task Consumer() |
||||
|
{ |
||||
|
logger.LogInformation("消费者任务开始运行"); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
while (networkContext.GetNetworkState().CurrentStatus == NetworkStatus.Connected) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (BlockingQueue.TryTake(out var message, TimeSpan.FromMilliseconds(1000))) |
||||
|
{ |
||||
|
logger.LogDebug("从队列中获取到消息: {Message}", message); |
||||
|
// TODO: 实现消息发送逻辑
|
||||
|
// await client.SendAsync(message, true);
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
await Task.Delay(1000); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "处理队列消息时发生错误"); |
||||
|
await Task.Delay(1000); // 发生错误时等待一段时间再继续
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (OperationCanceledException) |
||||
|
{ |
||||
|
logger.LogWarning("消费者任务被取消"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError(ex, "消费者任务发生未处理的错误"); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
logger.LogInformation("消费者任务已结束"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using CoreAgent.Domain.Interfaces.ProtocolLogHandlers; |
||||
|
using CoreAgent.Infrastructure.Services.CustomWSClient; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
public class ProtocolMSHandleLogs : CustomWebSocketClient, IProtocollHandleLogs |
||||
|
{ |
||||
|
protected ProtocolMSHandleLogs(ILogger logger, string serverUrl, ICustomMessageHandler messageHandler) : base(logger, serverUrl, messageHandler) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void RunStart() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void RunStop() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
internal class ProtocolMSParesHandleLogs |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
using CoreAgent.Domain.Models.Protocol; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
public abstract class ProtocolParesHandleLogs |
||||
|
{ |
||||
|
#region Regex
|
||||
|
public readonly Regex _regExpPhy = new Regex(@"^([a-f0-9\-]+)\s+([a-f0-9\-]+)\s+([\d\.\-]+) (\w+): (.+)"); |
||||
|
public readonly Regex _regExpInfo1 = new Regex(@"^([\w\-]+): (.+)"); |
||||
|
public readonly Regex _regExpInfo2 = new Regex(@"^([\w]+) (.+)"); |
||||
|
public readonly Regex _regExpParams1 = new Regex(@"\s+"); |
||||
|
public readonly Regex _regExpParams2 = new Regex(@"=|:"); |
||||
|
public readonly Regex _regExpIP = new Regex(@"^(len=\d+)\s+(\S+)\s+(.*)"); |
||||
|
public readonly Regex _regExpIPsec = new Regex(@"^len=(\d+)\s+(.*)"); |
||||
|
public readonly Regex _regExpIKEV2 = new Regex(@"^(\S+)\s+(.*)"); |
||||
|
public readonly Regex _regExpSDULen = new Regex(@"SDU_len=(\d+)"); |
||||
|
public readonly Regex _regExpSIP = new Regex(@"^([:\.\[\]\da-f]+)\s+(\S+) (.+)"); |
||||
|
public readonly Regex _regExpMediaReq = new Regex(@"^(\S+) (.+)"); |
||||
|
public readonly Regex _regExpSignalRecord = new Regex(@"Link:\s([\w\d]+)@(\d+)"); |
||||
|
public readonly Regex _regExpCellID = new Regex(@"^([a-f0-9\-]+) (.+)"); |
||||
|
public readonly Regex _regExpRRC_UE_ID = new Regex(@"Changing UE_ID to 0x(\d+)"); |
||||
|
public readonly Regex _regExpRRC_TMSI = new Regex(@"(5G|m)-TMSI '([\dA-F]+)'H"); |
||||
|
public readonly Regex _regExpRRC_NEW_ID = new Regex(@"newUE-Identity (['\dA-FH]+)"); |
||||
|
public readonly Regex _regExpRRC_CRNTI = new Regex(@"c-RNTI '([\dA-F]+)'H"); |
||||
|
public readonly Regex _regExpNAS_TMSI = new Regex(@"m-TMSI = 0x([\da-f]+)"); |
||||
|
public readonly Regex _regExpNAS_5GTMSI = new Regex(@"5G-TMSI = 0x([\da-f]+)"); |
||||
|
public readonly Regex _regExpRRC_BC = new Regex(@"(EUTRA|MRDC|NR|NRDC) band combinations"); |
||||
|
public readonly Regex _regExpPDCCH = new Regex(@"^\s*(.+)=(\d+)$"); |
||||
|
public readonly Regex _regExpS1NGAP = new Regex(@"^([\da-f\-]+)\s+([\da-f\-]+) (([^\s]+) .+)"); |
||||
|
public readonly Regex _regExpECPRI = new Regex(@"len=(\d+)"); |
||||
|
public readonly Regex _regExpHexDump = new Regex(@"^[\da-f]+:(\s+[\da-f]{2}){1,16}\s+.{1,16}$"); |
||||
|
#endregion
|
||||
|
public abstract Task GetTryParesLogDataHandle(ProtocolLog model); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using CoreAgent.Domain.Interfaces.ProtocolLogHandlers; |
||||
|
using CoreAgent.Infrastructure.Services.CustomWSClient; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
public class ProtocolRanHandleLogs : CustomWebSocketClient, IProtocollHandleLogs |
||||
|
{ |
||||
|
protected ProtocolRanHandleLogs(ILogger logger, string serverUrl, ICustomMessageHandler messageHandler) : base(logger, serverUrl, messageHandler) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void RunStart() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void RunStop() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
using CoreAgent.Domain.Helpers; |
||||
|
using CoreAgent.Domain.Interfaces.ProtocolLogHandlers; |
||||
|
using CoreAgent.Domain.Models.Protocol; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
|
||||
|
public class ProtocolLogsParesRanHandle : ProtocolParesHandleLogs |
||||
|
{ |
||||
|
private readonly ILogger logger; |
||||
|
private readonly IProtocolLogsProviderObserver observer; |
||||
|
public ProtocolLogsParesRanHandle(IProtocolLogsProviderObserver observer, ILogger logger) |
||||
|
{ |
||||
|
this.observer = observer; |
||||
|
this.logger = logger; |
||||
|
} |
||||
|
|
||||
|
public override async Task GetTryParesLogDataHandle(ProtocolLog model) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (model is { Logs: null }) |
||||
|
{ |
||||
|
logger.LogError($"logs is null =====>GetTryParesLogDataHandle Data {model?.ObjToJson()}"); |
||||
|
return; |
||||
|
} |
||||
|
var ParseJsonData = model.Logs.ToString().JsonToObj<ProtocolLogDetail[]>(); |
||||
|
var parseResultData = LogParsedResultHandle(ParseJsonData, (model.MessageId ?? 0)); |
||||
|
//if (parseResultData.Any())
|
||||
|
//{
|
||||
|
// var MsgData = new { data = parseResultData, MessageType = CSCIMessageType.ProtocolLogsRAN }.ObjToJson();
|
||||
|
// logger.LogInformation($"OnData:{MsgData}");
|
||||
|
// observer.OnData(MsgData);
|
||||
|
//}
|
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
logger.LogError($"GetTryParesLogDataHandle Data {model?.ObjToJson()}"); |
||||
|
logger.LogError(ex.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ProtocolLogParsedResult[] LogParsedResultHandle(ProtocolLogDetail[] logDetails, int MessageId) |
||||
|
{ |
||||
|
List<ProtocolLogParsedResult> parsedResults = new List<ProtocolLogParsedResult>(); |
||||
|
|
||||
|
return parsedResults.ToArray(); |
||||
|
} |
||||
|
|
||||
|
public void LogListParse(ProtocolLogDetail[] list) |
||||
|
{ |
||||
|
int length = list.Length; |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
var log= list[i]; |
||||
|
switch (log.Layer) |
||||
|
{ |
||||
|
case "PHY": |
||||
|
break; |
||||
|
case "MAC": |
||||
|
break; |
||||
|
case "RRC": |
||||
|
break; |
||||
|
case "NAS": |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,362 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.CustomWSClient; |
||||
|
using CoreAgent.Domain.Interfaces.Network; |
||||
|
using CoreAgent.Domain.Models.Protocol; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Services.ProtocolLogHandlers |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// RAN协议WebSocket消息处理器
|
||||
|
/// 负责处理无线接入网(RAN)相关的WebSocket消息,包括配置获取、日志获取等功能
|
||||
|
/// </summary>
|
||||
|
public class RanLogMessageHandler : ICustomMessageHandler, IDisposable |
||||
|
{ |
||||
|
private readonly ILogger _logger; |
||||
|
private int _messageId = 0; |
||||
|
private string _currentMessageId = string.Empty; |
||||
|
private readonly Action<string> _messageCallback; |
||||
|
private readonly ICellularNetworkContext _networkContext; |
||||
|
private readonly BlockingCollection<(string MessageData, IObserverCustomWebSocketClient Observer)> _messageQueue; |
||||
|
private readonly CancellationTokenSource _cancellationTokenSource; |
||||
|
private readonly Task _processTask; |
||||
|
private bool _disposed; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 初始化RAN协议消息处理器
|
||||
|
/// </summary>
|
||||
|
/// <param name="logger">日志记录器</param>
|
||||
|
/// <param name="context">蜂窝网络上下文</param>
|
||||
|
/// <param name="action">消息回调处理函数</param>
|
||||
|
public RanLogMessageHandler(ILogger logger, ICellularNetworkContext context, Action<string> action) |
||||
|
{ |
||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
||||
|
_messageCallback = action ?? throw new ArgumentNullException(nameof(action)); |
||||
|
_networkContext = context ?? throw new ArgumentNullException(nameof(context)); |
||||
|
|
||||
|
_messageQueue = new BlockingCollection<(string, IObserverCustomWebSocketClient)>(); |
||||
|
_cancellationTokenSource = new CancellationTokenSource(); |
||||
|
_processTask = Task.Run(ProcessMessageQueue); |
||||
|
|
||||
|
_logger.LogInformation("RAN协议消息处理器初始化完成,消息队列已启动"); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理接收到的WebSocket消息
|
||||
|
/// 将消息加入处理队列,由队列处理器异步处理
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageData">接收到的消息数据</param>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
public void HandleMessage(string messageData, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("将消息加入处理队列: {MessageData}", messageData); |
||||
|
_messageQueue.Add((messageData, observer)); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "将消息加入队列时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理消息队列中的消息
|
||||
|
/// 持续从队列中获取消息并处理,直到队列关闭或取消
|
||||
|
/// </summary>
|
||||
|
private async Task ProcessMessageQueue() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogInformation("开始处理消息队列"); |
||||
|
foreach (var (messageData, observer) in _messageQueue.GetConsumingEnumerable(_cancellationTokenSource.Token)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await ProcessMessageAsync(messageData, observer); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "处理队列中的消息时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (OperationCanceledException) |
||||
|
{ |
||||
|
_logger.LogInformation("消息队列处理已取消"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "消息队列处理过程中发生错误"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理单条消息
|
||||
|
/// 解析消息类型并分发到对应的处理方法
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageData">消息数据</param>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task ProcessMessageAsync(string messageData, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("开始处理RAN协议消息: {MessageData}", messageData); |
||||
|
var jsonData = JObject.Parse(messageData); |
||||
|
string messageType = jsonData["message"]!.ToString(); |
||||
|
_logger.LogInformation("收到RAN协议消息类型: {MessageType}", messageType); |
||||
|
await ProcessMessageByTypeAsync(messageType, jsonData, observer); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "处理RAN协议消息时发生错误: {MessageData}", messageData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 根据消息类型分发到对应的处理方法
|
||||
|
/// </summary>
|
||||
|
/// <param name="messageType">消息类型</param>
|
||||
|
/// <param name="data">消息数据</param>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task ProcessMessageByTypeAsync(string messageType, JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
_logger.LogDebug("开始处理RAN协议消息类型: {MessageType}", messageType); |
||||
|
switch (messageType) |
||||
|
{ |
||||
|
case "ready": |
||||
|
await HandleReadyMessageAsync(observer); |
||||
|
break; |
||||
|
case "config_get": |
||||
|
await HandleConfigGetMessageAsync(data, observer); |
||||
|
break; |
||||
|
case "config_set": |
||||
|
await HandleConfigSetMessageAsync(observer); |
||||
|
break; |
||||
|
case "log_get": |
||||
|
await HandleLogGetMessageAsync(data, observer); |
||||
|
break; |
||||
|
case "stats": |
||||
|
await HandleStatsMessageAsync(observer); |
||||
|
break; |
||||
|
default: |
||||
|
_logger.LogWarning("收到未知的RAN协议消息类型: {MessageType}", messageType); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理ready消息
|
||||
|
/// 发送config_get请求,准备获取配置信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task HandleReadyMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string readyResponse = JObject.FromObject(new { message = "config_get", message_id = Interlocked.Increment(ref _messageId) }).ToString(); |
||||
|
_logger.LogInformation("发送ready响应: {Response}", readyResponse); |
||||
|
await Task.Run(() => observer.SendMessage(readyResponse)); |
||||
|
_currentMessageId = _messageId.ToString(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理config_get消息
|
||||
|
/// 发送统计信息和基础层日志配置
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">消息数据</param>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task HandleConfigGetMessageAsync(JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
if (_currentMessageId == data["message_id"]!.ToString()) |
||||
|
{ |
||||
|
_logger.LogInformation("处理config_get请求"); |
||||
|
var responseArray = new JArray(); |
||||
|
|
||||
|
var statsConfig = new { message = "stats", message_id = Interlocked.Increment(ref _messageId), rf = false, samples = false }; |
||||
|
responseArray.Add(JObject.FromObject(statsConfig)); |
||||
|
|
||||
|
string baseLayerConfig = ConfigureBaseLayerLogs(data); |
||||
|
responseArray.Add(JObject.Parse(baseLayerConfig)); |
||||
|
|
||||
|
_logger.LogInformation("发送config_get响应: {Response}", responseArray.ToString()); |
||||
|
await Task.Run(() => observer.SendMessage(responseArray.ToString())); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_logger.LogWarning("config_get消息ID不匹配: 收到={ReceivedId}, 期望={ExpectedId}", |
||||
|
data["message_id"]!.ToString(), _currentMessageId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理config_set消息
|
||||
|
/// 发送层日志配置
|
||||
|
/// </summary>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task HandleConfigSetMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string configResponse = ConfigureLayerLogs(true); |
||||
|
await Task.Run(() => observer.SendMessage(configResponse)); |
||||
|
_currentMessageId = _messageId.ToString(); |
||||
|
_logger.LogInformation("发送config_set响应: {Response}", configResponse); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理log_get消息
|
||||
|
/// 发送日志配置并触发回调
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">消息数据</param>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task HandleLogGetMessageAsync(JObject data, IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
if (_currentMessageId == data["message_id"]!.ToString()) |
||||
|
{ |
||||
|
string logResponse = ConfigureLayerLogs(false); |
||||
|
await Task.Run(() => observer.SendMessage(logResponse)); |
||||
|
_currentMessageId = _messageId.ToString(); |
||||
|
_logger.LogInformation("发送log_get响应: {Response}", logResponse); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_logger.LogWarning("log_get消息ID不匹配: 收到={ReceivedId}, 期望={ExpectedId}", |
||||
|
data["message_id"]!.ToString(), _currentMessageId); |
||||
|
} |
||||
|
await Task.Run(() => _messageCallback.Invoke(data.ToString())); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 处理stats消息
|
||||
|
/// 发送统计信息请求
|
||||
|
/// </summary>
|
||||
|
/// <param name="observer">WebSocket客户端观察者</param>
|
||||
|
private async Task HandleStatsMessageAsync(IObserverCustomWebSocketClient observer) |
||||
|
{ |
||||
|
string statsResponse = JObject.FromObject(new |
||||
|
{ |
||||
|
message = "stats", |
||||
|
message_id = Interlocked.Increment(ref _messageId), |
||||
|
rf = false, |
||||
|
samples = false |
||||
|
}).ToString(); |
||||
|
await Task.Run(() => observer.SendMessage(statsResponse)); |
||||
|
_logger.LogInformation("发送stats响应: {Response}", statsResponse); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 配置基础层日志
|
||||
|
/// 设置各种日志级别的开关状态
|
||||
|
/// </summary>
|
||||
|
/// <param name="keyValues">配置键值对</param>
|
||||
|
/// <param name="isCloseSystemInfo">是否关闭系统信息</param>
|
||||
|
/// <returns>配置后的JSON字符串</returns>
|
||||
|
private string ConfigureBaseLayerLogs(JObject keyValues, bool isCloseSystemInfo = false) |
||||
|
{ |
||||
|
_logger.LogDebug("开始配置基础层日志: {KeyValues}", keyValues.ToString()); |
||||
|
|
||||
|
// 移除不需要的配置项
|
||||
|
if (keyValues.Remove("rotate") && keyValues.Remove("path") && keyValues.Remove("count")) |
||||
|
{ |
||||
|
_logger.LogDebug("已移除rotate、path和count配置项"); |
||||
|
} |
||||
|
|
||||
|
// 设置系统信息相关配置
|
||||
|
keyValues["bcch"] = isCloseSystemInfo; |
||||
|
keyValues["cch"] = isCloseSystemInfo; |
||||
|
keyValues["cell_meas"] = isCloseSystemInfo; |
||||
|
keyValues["csi"] = isCloseSystemInfo; |
||||
|
|
||||
|
// 设置其他配置项
|
||||
|
keyValues["dci_size"] = false; |
||||
|
keyValues["mib"] = false; |
||||
|
keyValues["rep"] = false; |
||||
|
keyValues["signal"] = false; |
||||
|
|
||||
|
var configMessage = new |
||||
|
{ |
||||
|
message = "config_set", |
||||
|
logs = keyValues, |
||||
|
message_id = Interlocked.Increment(ref _messageId), |
||||
|
}; |
||||
|
|
||||
|
string response = JObject.FromObject(configMessage).ToString(); |
||||
|
_logger.LogInformation("基础层日志配置完成: {Response}", response); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 配置层日志
|
||||
|
/// 设置日志超时、计数等参数
|
||||
|
/// </summary>
|
||||
|
/// <param name="includeHeaders">是否包含头部信息</param>
|
||||
|
/// <returns>配置后的JSON字符串</returns>
|
||||
|
private string ConfigureLayerLogs(bool includeHeaders = false) |
||||
|
{ |
||||
|
_logger.LogDebug("开始配置层日志: IncludeHeaders={IncludeHeaders}", includeHeaders); |
||||
|
|
||||
|
var logConfig = new BaseNetworkLog<RanLayerLog> |
||||
|
{ |
||||
|
Timeout = includeHeaders ? 0 : 1, |
||||
|
MinLogCount = 64, |
||||
|
MaxLogCount = 2048, |
||||
|
LayerConfig = _networkContext.NetworkLogs.RanLog, |
||||
|
Message = "log_get", |
||||
|
IncludeHeaders = includeHeaders, |
||||
|
MessageId = Interlocked.Increment(ref _messageId), |
||||
|
}; |
||||
|
|
||||
|
string response = JObject.FromObject(logConfig).ToString(); |
||||
|
_logger.LogInformation("层日志配置完成: {Response}", response); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 释放资源
|
||||
|
/// </summary>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Dispose(true); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 释放资源的具体实现
|
||||
|
/// </summary>
|
||||
|
/// <param name="disposing">是否正在释放托管资源</param>
|
||||
|
protected virtual void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (!_disposed) |
||||
|
{ |
||||
|
if (disposing) |
||||
|
{ |
||||
|
_cancellationTokenSource.Cancel(); |
||||
|
_messageQueue.CompleteAdding(); |
||||
|
try |
||||
|
{ |
||||
|
_processTask.Wait(TimeSpan.FromSeconds(5)); |
||||
|
} |
||||
|
catch (AggregateException ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "等待消息队列处理完成时发生错误"); |
||||
|
} |
||||
|
_messageQueue.Dispose(); |
||||
|
_cancellationTokenSource.Dispose(); |
||||
|
} |
||||
|
_disposed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 析构函数
|
||||
|
/// </summary>
|
||||
|
~RanLogMessageHandler() |
||||
|
{ |
||||
|
Dispose(false); |
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue