Compare commits
4 Commits
master
...
feature/pr
Author | SHA1 | Date |
---|---|---|
|
571232a1f2 | 2 weeks ago |
|
1ac911297b | 1 month ago |
|
50844d7ca1 | 1 month 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