You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
653 lines
22 KiB
653 lines
22 KiB
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using LTEMvcApp.Models;
|
|
using Newtonsoft.Json.Linq;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
|
|
namespace LTEMvcApp.Services
|
|
{
|
|
/// <summary>
|
|
/// WebSocket管理器服务 - 管理多个LTE客户端连接
|
|
/// </summary>
|
|
public class WebSocketManagerService
|
|
{
|
|
#region 私有字段
|
|
|
|
private readonly ConcurrentDictionary<string, LTEClientWebSocket> _clients;
|
|
private readonly ConcurrentDictionary<string, ClientConfig> _configs;
|
|
private readonly LogParserService _logParser;
|
|
private readonly ILogger<WebSocketManagerService> _logger;
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private ClientConfig _testClientConfig;
|
|
private const int LogCacheSize = 10000; // 服务器最多缓存10000条最新日志
|
|
private readonly ConcurrentQueue<LTELog> _logCache = new ConcurrentQueue<LTELog>();
|
|
private readonly string _configFilePath = "test_client_config.json";
|
|
|
|
#endregion
|
|
|
|
#region 事件
|
|
|
|
/// <summary>
|
|
/// 客户端连接事件
|
|
/// </summary>
|
|
public event EventHandler<LTEClientWebSocket>? ClientConnected;
|
|
|
|
/// <summary>
|
|
/// 客户端断开事件
|
|
/// </summary>
|
|
public event EventHandler<LTEClientWebSocket>? ClientDisconnected;
|
|
|
|
/// <summary>
|
|
/// 日志接收事件
|
|
/// </summary>
|
|
public event EventHandler<(string clientName, List<LTELog> logs)>? LogsReceived;
|
|
|
|
/// <summary>
|
|
/// 状态变化事件
|
|
/// </summary>
|
|
public event EventHandler<(string clientName, ClientState state)>? StateChanged;
|
|
|
|
#endregion
|
|
|
|
#region 构造函数
|
|
|
|
/// <summary>
|
|
/// 构造函数
|
|
/// </summary>
|
|
public WebSocketManagerService(ILogger<WebSocketManagerService> logger, LogParserService logParser, IServiceProvider serviceProvider)
|
|
{
|
|
_clients = new ConcurrentDictionary<string, LTEClientWebSocket>();
|
|
_configs = new ConcurrentDictionary<string, ClientConfig>();
|
|
_logParser = logParser;
|
|
_logger = logger;
|
|
_serviceProvider = serviceProvider;
|
|
|
|
LoadTestClientConfig();
|
|
|
|
_logger.LogInformation("WebSocketManagerService 初始化");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载测试客户端配置
|
|
/// </summary>
|
|
private void LoadTestClientConfig()
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(_configFilePath))
|
|
{
|
|
var json = File.ReadAllText(_configFilePath);
|
|
_testClientConfig = JsonSerializer.Deserialize<ClientConfig>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
|
|
_logger.LogInformation("成功从 {FilePath} 加载测试客户端配置。", _configFilePath);
|
|
}
|
|
else
|
|
{
|
|
_logger.LogWarning("配置文件 {FilePath} 未找到,将创建并使用默认配置。", _configFilePath);
|
|
_testClientConfig = GetDefaultTestConfig();
|
|
SaveTestClientConfig();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "加载或创建测试客户端配置文件时出错。将使用默认配置。");
|
|
_testClientConfig = GetDefaultTestConfig();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存测试客户端配置到文件
|
|
/// </summary>
|
|
private void SaveTestClientConfig()
|
|
{
|
|
try
|
|
{
|
|
var options = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
|
var json = JsonSerializer.Serialize(_testClientConfig, options);
|
|
File.WriteAllText(_configFilePath, json);
|
|
_logger.LogInformation("测试客户端配置已成功保存到 {FilePath}。", _configFilePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "保存测试客户端配置文件失败。");
|
|
}
|
|
}
|
|
|
|
private ClientConfig GetDefaultTestConfig()
|
|
{
|
|
return new ClientConfig
|
|
{
|
|
Name = "TestClient",
|
|
Enabled = true,
|
|
Address = "192.168.13.12:9001",
|
|
Ssl = false,
|
|
ReconnectDelay = 15000,
|
|
Password = "test123",
|
|
Logs = new Dictionary<string, object>
|
|
{
|
|
["layers"] = new Dictionary<string, object>
|
|
{
|
|
["PHY"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = true },
|
|
["MAC"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = true },
|
|
["RLC"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["PDCP"] = new LogLayerConfig { Level = "warn", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["RRC"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = true },
|
|
["NAS"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = true },
|
|
["S1AP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["NGAP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["GTPU"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["X2AP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["XnAP"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
|
|
["M2AP"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false }
|
|
},
|
|
["signal"] = true,
|
|
["cch"] = true
|
|
}
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 公共方法
|
|
|
|
/// <summary>
|
|
/// 添加客户端配置
|
|
/// </summary>
|
|
/// <param name="config">客户端配置</param>
|
|
/// <returns>是否成功添加</returns>
|
|
public bool AddClientConfig(ClientConfig config)
|
|
{
|
|
if (string.IsNullOrEmpty(config.Name))
|
|
{
|
|
_logger.LogWarning("尝试添加空名称客户端配置");
|
|
return false;
|
|
}
|
|
_logger.LogInformation($"添加客户端配置: {config.Name}");
|
|
_configs[config.Name] = config;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 移除客户端配置
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>是否成功移除</returns>
|
|
public bool RemoveClientConfig(string clientName)
|
|
{
|
|
_logger.LogInformation($"移除客户端配置: {clientName}");
|
|
if (_configs.TryRemove(clientName, out _))
|
|
{
|
|
// 如果客户端正在运行,停止它
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
client.Stop();
|
|
_clients.TryRemove(clientName, out _);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动客户端
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>是否成功启动</returns>
|
|
public bool StartClient(string clientName)
|
|
{
|
|
_logger.LogInformation($"启动客户端: {clientName}");
|
|
|
|
ClientConfig config;
|
|
if (clientName == _testClientConfig.Name)
|
|
{
|
|
// 使用测试客户端配置
|
|
config = _testClientConfig;
|
|
_logger.LogInformation($"使用测试客户端配置: {config.Name}");
|
|
}
|
|
else if (!_configs.TryGetValue(clientName, out config))
|
|
{
|
|
_logger.LogWarning($"客户端配置不存在: {clientName}");
|
|
return false;
|
|
}
|
|
|
|
// 如果客户端已存在,先停止
|
|
if (_clients.TryGetValue(clientName, out var existingClient))
|
|
{
|
|
existingClient.Stop();
|
|
_clients.TryRemove(clientName, out _);
|
|
}
|
|
|
|
// 通过依赖注入获取ILogger<LTEClientWebSocket>
|
|
var logger = _serviceProvider.GetService(typeof(ILogger<LTEClientWebSocket>)) as ILogger<LTEClientWebSocket>;
|
|
var client = new LTEClientWebSocket(config, logger!);
|
|
|
|
// 订阅事件
|
|
client.ConnectionOpened += (sender, e) => OnClientConnected(client);
|
|
client.ConnectionClosed += (sender, e) => OnClientDisconnected(client);
|
|
client.LogsReceived += (sender, logs) => OnLogsReceived(clientName, logs);
|
|
client.StateChanged += (sender, state) => OnStateChanged(clientName, state);
|
|
|
|
// 启动客户端
|
|
client.Start();
|
|
|
|
// 添加到客户端列表
|
|
_clients[clientName] = client;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 停止客户端
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>是否成功停止</returns>
|
|
public bool StopClient(string clientName)
|
|
{
|
|
_logger.LogInformation($"停止客户端: {clientName}");
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
client.Stop();
|
|
_clients.TryRemove(clientName, out _);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户端状态
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>客户端状态</returns>
|
|
public ClientState? GetClientState(string clientName)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
return client.State;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有客户端状态
|
|
/// </summary>
|
|
/// <returns>客户端状态字典</returns>
|
|
public Dictionary<string, ClientState> GetAllClientStates()
|
|
{
|
|
return _clients.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.State);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户端配置
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>客户端配置</returns>
|
|
public ClientConfig? GetClientConfig(string clientName)
|
|
{
|
|
_configs.TryGetValue(clientName, out var config);
|
|
return config;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有客户端配置
|
|
/// </summary>
|
|
/// <returns>客户端配置列表</returns>
|
|
public List<ClientConfig> GetAllClientConfigs()
|
|
{
|
|
return _configs.Values.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户端日志
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>客户端日志列表</returns>
|
|
public List<LTELog>? GetClientLogs(string clientName)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
return client.Client.Logs;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置客户端日志
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>是否成功重置</returns>
|
|
public bool ResetClientLogs(string clientName)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
client.ResetLogs();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置客户端日志配置
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <param name="logsConfig">日志配置</param>
|
|
/// <param name="save">是否保存</param>
|
|
/// <returns>是否成功设置</returns>
|
|
public bool SetClientLogsConfig(string clientName, Dictionary<string, object> logsConfig, bool save = false)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
client.SetLogsConfig(logsConfig, save);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 播放/暂停客户端
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>是否成功操作</returns>
|
|
public bool PlayPauseClient(string clientName)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
client.PlayPause();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 发送消息到客户端
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <param name="message">消息</param>
|
|
/// <param name="callback">回调</param>
|
|
/// <returns>消息ID</returns>
|
|
public int SendMessageToClient(string clientName, JObject message, Action<JObject>? callback = null)
|
|
{
|
|
if (_clients.TryGetValue(clientName, out var client))
|
|
{
|
|
return client.SendMessage(message, callback);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取连接统计信息
|
|
/// </summary>
|
|
/// <returns>连接统计信息</returns>
|
|
public ConnectionStatistics GetConnectionStatistics()
|
|
{
|
|
var stats = new ConnectionStatistics
|
|
{
|
|
TotalClients = _clients.Count,
|
|
ConnectedClients = _clients.Values.Count(c => c.IsConnected),
|
|
DisconnectedClients = _clients.Values.Count(c => !c.IsConnected),
|
|
TotalLogs = _clients.Values.Sum(c => c.Client.LogCount),
|
|
ClientStates = GetAllClientStates()
|
|
};
|
|
|
|
return stats;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 停止所有客户端
|
|
/// </summary>
|
|
public void StopAllClients()
|
|
{
|
|
foreach (var client in _clients.Values)
|
|
{
|
|
client.Stop();
|
|
}
|
|
_clients.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动所有已配置的客户端
|
|
/// </summary>
|
|
public void StartAllConfiguredClients()
|
|
{
|
|
foreach (var config in _configs.Values)
|
|
{
|
|
if (config.Enabled)
|
|
{
|
|
StartClient(config.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取客户端实例
|
|
/// </summary>
|
|
/// <param name="clientName">客户端名称</param>
|
|
/// <returns>客户端实例</returns>
|
|
public LTEClientWebSocket? GetClientInstance(string clientName)
|
|
{
|
|
_clients.TryGetValue(clientName, out var client);
|
|
return client;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取测试客户端配置
|
|
/// </summary>
|
|
/// <returns>测试客户端配置</returns>
|
|
public ClientConfig GetTestClientConfig()
|
|
{
|
|
return _testClientConfig;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置测试客户端配置
|
|
/// </summary>
|
|
/// <param name="config">测试客户端配置</param>
|
|
/// <returns>是否成功设置</returns>
|
|
public bool SetTestClientConfig(ClientConfig config)
|
|
{
|
|
if (string.IsNullOrEmpty(config.Name))
|
|
{
|
|
_logger.LogWarning("尝试设置空名称的测试客户端配置");
|
|
return false;
|
|
}
|
|
|
|
_logger.LogInformation($"更新测试客户端配置: {config.Name}");
|
|
_testClientConfig = config;
|
|
SaveTestClientConfig();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 启动测试客户端
|
|
/// </summary>
|
|
/// <returns>是否成功启动</returns>
|
|
public bool StartTestClient()
|
|
{
|
|
_logger.LogInformation("启动测试客户端");
|
|
return StartClient(_testClientConfig.Name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 停止测试客户端
|
|
/// </summary>
|
|
/// <returns>是否成功停止</returns>
|
|
public bool StopTestClient()
|
|
{
|
|
_logger.LogInformation("停止测试客户端");
|
|
return StopClient(_testClientConfig.Name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取测试客户端实例
|
|
/// </summary>
|
|
/// <returns>测试客户端的WebSocket实例</returns>
|
|
public LTEClientWebSocket? GetTestClient()
|
|
{
|
|
return GetClientInstance(_testClientConfig.Name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前缓存的日志
|
|
/// </summary>
|
|
public IEnumerable<LTELog> GetLogCache() => _logCache;
|
|
|
|
/// <summary>
|
|
/// 获取当前缓存的日志总数
|
|
/// </summary>
|
|
public int GetLogCacheCount() => _logCache.Count;
|
|
|
|
/// <summary>
|
|
/// 清空日志缓存
|
|
/// </summary>
|
|
public void ClearLogCache()
|
|
{
|
|
_logger.LogInformation("清空日志缓存");
|
|
while (_logCache.TryDequeue(out _))
|
|
{
|
|
// 清空所有日志
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置日志缓存(清空并重新初始化)
|
|
/// </summary>
|
|
public void ResetLogCache()
|
|
{
|
|
_logger.LogInformation("重置日志缓存");
|
|
ClearLogCache();
|
|
// 可以在这里添加其他重置逻辑
|
|
}
|
|
|
|
/// <summary>
|
|
/// 手动添加日志到缓存
|
|
/// </summary>
|
|
/// <param name="log">日志对象</param>
|
|
public void AddLogToCache(LTELog log)
|
|
{
|
|
if (log != null)
|
|
{
|
|
_logCache.Enqueue(log);
|
|
_logger.LogInformation("手动添加日志到缓存: {Layer} - {Message}", log.Layer, log.Message);
|
|
|
|
// 维持缓存大小
|
|
while (_logCache.Count > LogCacheSize)
|
|
{
|
|
_logCache.TryDequeue(out _);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 手动添加多个日志到缓存
|
|
/// </summary>
|
|
/// <param name="logs">日志列表</param>
|
|
public void AddLogsToCache(List<LTELog> logs)
|
|
{
|
|
if (logs != null && logs.Any())
|
|
{
|
|
foreach (var log in logs)
|
|
{
|
|
_logCache.Enqueue(log);
|
|
}
|
|
|
|
_logger.LogInformation("手动添加 {Count} 条日志到缓存", logs.Count);
|
|
|
|
// 维持缓存大小
|
|
while (_logCache.Count > LogCacheSize)
|
|
{
|
|
_logCache.TryDequeue(out _);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 私有方法
|
|
|
|
/// <summary>
|
|
/// 客户端连接事件处理
|
|
/// </summary>
|
|
private void OnClientConnected(LTEClientWebSocket client)
|
|
{
|
|
_logger.LogInformation($"客户端已连接: {client.Client.Config.Name}");
|
|
ClientConnected?.Invoke(this, client);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 客户端断开事件处理
|
|
/// </summary>
|
|
private void OnClientDisconnected(LTEClientWebSocket client)
|
|
{
|
|
_logger.LogWarning($"客户端已断开: {client.Client.Config.Name}");
|
|
ClientDisconnected?.Invoke(this, client);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 日志接收事件处理
|
|
/// </summary>
|
|
private void OnLogsReceived(string clientName, List<LTELog> logs)
|
|
{
|
|
_logger.LogInformation($"客户端 {clientName} 收到日志: {logs.Count} 条");
|
|
|
|
// 将新日志存入中央缓存
|
|
foreach (var log in logs)
|
|
{
|
|
_logCache.Enqueue(log);
|
|
}
|
|
|
|
// 维持缓存大小
|
|
while (_logCache.Count > LogCacheSize)
|
|
{
|
|
_logCache.TryDequeue(out _);
|
|
}
|
|
|
|
LogsReceived?.Invoke(this, (clientName, logs));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 状态变化事件处理
|
|
/// </summary>
|
|
private void OnStateChanged(string clientName, ClientState state)
|
|
{
|
|
_logger.LogInformation($"客户端 {clientName} 状态变更: {state}");
|
|
StateChanged?.Invoke(this, (clientName, state));
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// 连接统计信息
|
|
/// </summary>
|
|
public class ConnectionStatistics
|
|
{
|
|
/// <summary>
|
|
/// 总客户端数
|
|
/// </summary>
|
|
public int TotalClients { get; set; }
|
|
|
|
/// <summary>
|
|
/// 已连接客户端数
|
|
/// </summary>
|
|
public int ConnectedClients { get; set; }
|
|
|
|
/// <summary>
|
|
/// 未连接客户端数
|
|
/// </summary>
|
|
public int DisconnectedClients { get; set; }
|
|
|
|
/// <summary>
|
|
/// 总日志数
|
|
/// </summary>
|
|
public int TotalLogs { get; set; }
|
|
|
|
/// <summary>
|
|
/// 客户端状态字典
|
|
/// </summary>
|
|
public Dictionary<string, ClientState> ClientStates { get; set; } = new();
|
|
}
|
|
}
|