using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WebSocket4Net; using LTEMvcApp.Models; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; namespace LTEMvcApp.Services { /// /// LTE客户端WebSocket实现 - 对应JavaScript中的lte.client.server /// public class LTEClientWebSocket : IDisposable { #region 私有字段 private WebSocket? _webSocket; private readonly LTEClient _client; private readonly ClientConfig _config; private readonly ConcurrentDictionary _messageHandlers; private readonly ConcurrentDictionary _messageHandlersByName; private readonly ConcurrentQueue _messageFifo; private readonly CancellationTokenSource _cancellationTokenSource; private Timer? _reconnectTimer; private Timer? _statsTimer; private Timer? _messageDeferTimer; private Timer? _readyTimer; private int _messageId; private int _logGetId; private bool _disposed; private LogParserService logParser = new LogParserService(); private readonly ILogger _logger; private readonly List _sentMessages = new List(); private readonly object _sentLock = new object(); private readonly List _receivedMessages = new List(); private readonly object _receivedLock = new object(); #endregion #region 事件 /// /// 连接打开事件 /// public event EventHandler? ConnectionOpened; /// /// 连接关闭事件 /// public event EventHandler? ConnectionClosed; /// /// 连接错误事件 /// public event EventHandler? ConnectionError; /// /// 消息接收事件 /// public event EventHandler? MessageReceived; /// /// 日志接收事件 /// public event EventHandler>? LogsReceived; /// /// 统计信息接收事件 /// public event EventHandler? StatsReceived; /// /// 状态改变事件 /// public event EventHandler? StateChanged; /// /// 客户端网格刷新事件 /// public event EventHandler? ClientGridRefreshed; #endregion #region 属性 /// /// 客户端 /// public LTEClient Client => _client; /// /// 配置 /// public ClientConfig Config => _config; /// /// 是否已连接 /// public bool IsConnected => _webSocket?.State == WebSocketState.Open; /// /// 当前状态 /// public ClientState State => _client.State; /// /// 是否暂停 /// public bool IsPaused => _config.Pause; /// /// 是否只读 /// public bool IsReadonly => _config.Readonly; public int SentMessagesCount { get { lock (_sentLock) { return _sentMessages.Count; } } } public int ReceivedMessagesCount { get { lock (_receivedLock) { return _receivedMessages.Count; } } } public List GetNewSentMessages(int startIndex) { lock (_sentLock) { var count = _sentMessages.Count - startIndex; if (count > 0) { return _sentMessages.GetRange(startIndex, count); } return new List(); } } public List GetNewReceivedMessages(int startIndex) { lock (_receivedLock) { var count = _receivedMessages.Count - startIndex; if (count > 0) { return _receivedMessages.GetRange(startIndex, count); } return new List(); } } #endregion #region 构造函数 /// /// 构造函数 /// /// 客户端配置 /// ILogger实例 public LTEClientWebSocket(ClientConfig config, ILogger logger) { _config = config; _client = new LTEClient(config); _messageHandlers = new ConcurrentDictionary(); _messageHandlersByName = new ConcurrentDictionary(); _messageFifo = new ConcurrentQueue(); _cancellationTokenSource = new CancellationTokenSource(); _messageId = 0; _logger = logger; _logger.LogInformation($"创建WebSocket客户端: {config.Name}"); } #endregion #region 公共方法 /// /// 启动WebSocket连接 /// public void Start() { if (_disposed) return; try { _logger.LogInformation($"[{_config.Name}] 尝试连接: {_config.Address}"); SetState(ClientState.Connecting); // 构建WebSocket URL var address = _config.Address ?? "192.168.13.12:9001"; var url = (_config.Ssl ? "wss://" : "ws://") + address; _webSocket = new WebSocket(url); _webSocket.EnableAutoSendPing = false; // 绑定事件处理器 _webSocket.Opened += OnSocketOpened; _webSocket.Closed += OnSocketClosed; _webSocket.MessageReceived += OnSocketMessage0; _webSocket.Error += OnSocketError; _webSocket.Open(); } catch (Exception ex) { _logger.LogError(ex, $"[{_config.Name}] 连接异常: {ex.Message}"); ConnectionError?.Invoke(this, $"无法连接到 {_config.Address}: {ex.Message}"); SetState(ClientState.Error); } } /// /// 停止WebSocket连接 /// public void Stop() { SetState(ClientState.Stop); StopTimers(); } /// /// 重置日志 /// public void ResetLogs() { if (State == ClientState.Connected) { SendMessage(new JObject { ["message"] = "log_reset" }, response => { _client.ResetLogs(); }); } else { _client.ResetLogs(); } } /// /// 切换播放/暂停状态 /// public void PlayPause() { if (_config.Pause) { _config.Pause = false; if (State == ClientState.Connected) { LogGet(); } } else { _config.Pause = true; } } /// /// 设置日志配置 /// /// 日志配置 /// 是否保存 public void SetLogsConfig(Dictionary config, bool save = false) { // 重新格式化配置 var logs = new JObject(); foreach (var kvp in config) { switch (kvp.Key) { case "layers": var layers = new JObject(); var layersDict = kvp.Value as Dictionary; if (layersDict != null) { foreach (var layer in layersDict) { var layerConfig = layer.Value as Dictionary; if (layerConfig != null) { layers[layer.Key] = new JObject { ["level"] = layerConfig.GetValueOrDefault("level", "warn").ToString(), ["max_size"] = Convert.ToInt32(layerConfig.GetValueOrDefault("max_size", 0)), ["payload"] = Convert.ToBoolean(layerConfig.GetValueOrDefault("payload", false)) }; } } } logs["layers"] = layers; break; default: if (IsLogParameter(kvp.Key)) { logs[kvp.Key] = JToken.FromObject(kvp.Value); } break; } } SendMessage(new JObject { ["message"] = "config_set", ["logs"] = logs }, response => { if (save) { // 保存配置 foreach (var kvp in config) { if (_config.Logs.ContainsKey(kvp.Key)) { _config.Logs[kvp.Key] = kvp.Value; } else { _config.Logs.Add(kvp.Key, kvp.Value); } } } LogGet(new Dictionary { ["timeout"] = 0 }); }, true); } /// /// 设置消息处理器 /// /// 消息名称 /// 处理器 public void SetMessageHandler(string[] names, MessageHandler handler) { SendMessage(new JObject { ["message"] = "register", ["register"] = string.Join(",", names) }); foreach (var name in names) { _messageHandlersByName[name] = handler; } } /// /// 取消消息处理器 /// /// 消息名称 public void UnsetMessageHandler(string[] names) { SendMessage(new JObject { ["message"] = "register", ["unregister"] = string.Join(",", names) }); foreach (var name in names) { _messageHandlersByName.TryRemove(name, out _); } } /// /// 发送消息 /// /// 消息 /// 回调 /// 错误处理器 /// 消息ID public int SendMessage(JObject message, Action? callback = null, bool errorHandler = false) { if (_webSocket?.State != WebSocketState.Open) return -1; if (message == null) return -1; var id = ++_messageId; message["message_id"] = id; if (callback != null) { _messageHandlers[id] = new MessageHandler { Callback = callback, ErrorHandler = errorHandler }; } if (_messageDeferTimer != null) { _messageDeferTimer.Dispose(); _messageDeferTimer = null; } _messageFifo.Enqueue(message); // 记录发送的消息 lock (_sentLock) { _sentMessages.Add(message.ToString(Formatting.Indented)); } if (_messageFifo.Count < 100) // 批处理大小 { _messageDeferTimer = new Timer(_ => SendMessageNow(), null, 1, Timeout.Infinite); } else { SendMessageNow(); } return id; } /// /// 获取日志 /// /// 参数 public void LogGet(Dictionary? parameters = null) { var config = _config; if (config.Pause) return; var layers = new JObject(); if (config.Logs.ContainsKey("layers")) { // 处理layers配置,确保键是字符串类型 if (config.Logs["layers"] is JObject layersJObject) { foreach (var layer in layersJObject) { var layerName = layer.Key; // 这里layer.Key已经是字符串 var layerConfig = layer.Value as JObject; if (layerConfig != null && layerConfig.ContainsKey("filter")) { layers[layerName] = layerConfig["filter"].ToString(); } } } else if (config.Logs["layers"] is Dictionary layersDict) { foreach (var layer in layersDict) { var layerConfig = layer.Value as Dictionary; if (layerConfig != null && layerConfig.ContainsKey("filter")) { layers[layer.Key] = layerConfig["filter"].ToString(); } } } } var message = new JObject { ["timeout"] = 1, ["min"] = 64, ["max"] = 2048, ["layers"] = layers, ["message"] = "log_get", ["headers"] = _client.LogCount == 0 }; if (parameters != null) { foreach (var param in parameters) { message[param.Key] = JToken.FromObject(param.Value); } } _logGetId = SendMessage(message, LogGetParse); } #endregion #region 私有方法 /// /// WebSocket连接打开事件 /// private void OnSocketOpened(object? sender, EventArgs e) { _logger.LogInformation($"[{_config.Name}] WebSocket连接已打开"); StopTimers(); _readyTimer = new Timer(_ => OnSocketReady(), null, 2500, Timeout.Infinite); ConnectionOpened?.Invoke(this, EventArgs.Empty); } /// /// WebSocket连接关闭事件 /// private void OnSocketClosed(object? sender, EventArgs e) { _logger.LogWarning($"[{_config.Name}] WebSocket连接已关闭"); ConnectionClosed?.Invoke(this, EventArgs.Empty); StopTimers(); CloseComponents(); if (State == ClientState.Connected) { // 处理监控窗口停止 } if (_config.Enabled) { Console.WriteLine("启动重连定时器"); if (State != ClientState.Stop) { SetState(ClientState.Error); } _reconnectTimer = new Timer(_ => Start(), null, _config.ReconnectDelay, Timeout.Infinite); } } /// /// WebSocket错误事件 /// private void OnSocketError(object? sender, SuperSocket.ClientEngine.ErrorEventArgs e) { _logger.LogError(e.Exception, $"[{_config.Name}] WebSocket错误: {e.Exception.Message}"); SetState(ClientState.Error); ConnectionError?.Invoke(this, e.Exception.Message); } /// /// 初始消息处理(对应_onSocketMessage0) /// private void OnSocketMessage0(object? sender, MessageReceivedEventArgs e) { _logger.LogDebug($"[{_config.Name}] 收到初始消息: {e.Message}"); StopTimers(); // 记录接收的消息 lock (_receivedLock) { _receivedMessages.Add(JToken.Parse(e.Message).ToString(Formatting.Indented)); } try { var data = e.Message; var msg = JObject.Parse(data); switch (msg["message"]?.ToString()) { case "authenticate": if (msg["ready"]?.Value() == true) { OnSocketReady(); } else if (msg["error"] == null && !string.IsNullOrEmpty(_config.Password)) { // 重新认证 Authenticate(_config.Password, msg["challenge"]?.ToString() ?? ""); } else { // 提示输入密码 PromptPassword(msg); } break; case "ready": OnSocketReady(); break; default: break; } } catch (Exception ex) { ConnectionError?.Invoke(this, $"JSON解析错误: {ex.Message}"); } } /// /// WebSocket准备就绪(对应_onSocketReady) /// private void OnSocketReady() { if (_webSocket == null) return; // 切换到正常的消息处理函数 _webSocket.MessageReceived -= OnSocketMessage0; _webSocket.MessageReceived += OnSocketMessage; _messageFifo.Clear(); // 检查当前配置 var firstCon = !_config.Logs.ContainsKey("layers"); // 获取配置 SendMessage(new JObject { ["message"] = "config_get" }, config => { Console.WriteLine("配置已接收"); _client.ResetLogs(); // 设置基本信息 _client.Version = config["version"]?.ToString(); _client.Name = config["name"]?.ToString() ?? _config.Name; _client.Model = config["type"]?.ToString(); if (config["profiling"]?.Value() == true) { // 设置性能分析可用标志 } var ro = _config.Readonly; if (ro || firstCon) { _config.Logs = config["logs"]?.ToObject>() ?? new Dictionary(); } else { var serverLogs = config["logs"]?.ToObject>() ?? new Dictionary(); foreach (var kvp in serverLogs) { if (!_config.Logs.ContainsKey(kvp.Key)) { _config.Logs[kvp.Key] = kvp.Value; } } } // 清理和设置层配置 if (_config.Logs.ContainsKey("layers")) { var layers = _config.Logs["layers"] as Dictionary; var configLayers = config["logs"]?["layers"]?.ToObject>(); if (layers != null && configLayers != null) { var keysToRemove = layers.Keys.Where(k => !configLayers.ContainsKey(k)).ToList(); foreach (var key in keysToRemove) { layers.Remove(key); } foreach (var layer in layers) { var layerConfig = layer.Value as Dictionary; if (layerConfig != null && !layerConfig.ContainsKey("filter")) { if (ro) { layerConfig["filter"] = layerConfig.GetValueOrDefault("level", "warn").ToString(); } else { layerConfig["filter"] = GetDefaultFilter(layer.Key); } } } } } // 添加小区信息 if (config["cells"] != null) { var cells = config["cells"]?.ToObject>(); if (cells != null) { foreach (var cell in cells) { // 添加小区配置 } } } SetState(ClientState.Connected); if (firstCon && !_config.SkipLogMenu) { // 显示配置窗口 Console.WriteLine("请配置日志"); } else if (ro) { LogGet(new Dictionary { ["timeout"] = 0 }); } else { SetLogsConfig(_config.Logs); } }); } /// /// 正常消息处理(对应_onSocketMessage) /// private void OnSocketMessage(object? sender, MessageReceivedEventArgs e) { _logger.LogDebug($"[{_config.Name}] 收到消息: {e.Message}"); // 记录接收的消息 lock (_receivedLock) { _receivedMessages.Add(JToken.Parse(e.Message).ToString(Formatting.Indented)); } try { var data = e.Message; JObject msg; if (data.StartsWith("[")) { // 处理二进制数据 var array = JArray.Parse(data); msg = array[0] as JObject ?? new JObject(); // 处理二进制数据部分 } else { msg = JObject.Parse(data); } MessageReceived?.Invoke(this, msg); // 检查消息处理器 var id = msg["message_id"]?.Value(); if (id.HasValue && _messageHandlers.TryGetValue(id.Value, out var handler)) { if (msg["notification"]?.Value() != true) { _messageHandlers.TryRemove(id.Value, out _); } if (msg["error"] != null) { if (!handler.ErrorHandler) { ConnectionError?.Invoke(this, msg["error"]?.ToString() ?? "未知错误"); } else { handler.Callback?.Invoke(msg); } return; } handler.Callback?.Invoke(msg); return; } // 检查按名称的消息处理器 var name = msg["message"]?.ToString(); if (!string.IsNullOrEmpty(name) && _messageHandlersByName.TryGetValue(name, out var nameHandler)) { nameHandler.Callback?.Invoke(msg); return; } // 处理特定消息类型 switch (name) { case "log_get": LogGetParse(msg); break; case "stats": StatsReceived?.Invoke(this, msg); break; default: Console.WriteLine($"未知消息: {name}"); break; } } catch (Exception ex) { ConnectionError?.Invoke(this, $"消息处理错误: {ex.Message}"); } } /// /// 日志获取解析 /// private void LogGetParse(JObject msg) { _logger.LogInformation($"[{_config.Name}] 解析日志消息: {msg}"); var count = _client.LogCount; if (msg["headers"] != null) { var headers = msg["headers"]?.ToObject(); if (headers != null) { _client.SetHeaders(headers); } } // 初始化模型猜测 _client.LogModelGuessInit(); var logs = msg["logs"]; if (logs != null) { Console.WriteLine($"接收到日志: {logs.Count()} 条"); if (logs is JArray logsArray) { // 模型猜测 _client.LogModelGuess(logsArray.ToObject>>() ?? new List>()); var logList = new List(); foreach (var logItem in logsArray) { if (logItem is JObject logObj) { //var logData = logObj.ToObject>() ?? new Dictionary(); // 创建LTELog对象 var log = JsonConvert.DeserializeObject(logObj.ToString()); //new LTELog(logData); // 处理消息和数据 if (log.Data is List dataList && dataList.Count > 0) { log.Message = dataList[0]; dataList.RemoveAt(0); } // 设置方向 log.Direction = _client.DirConvert(log); // 处理信息字段 if (log.Info != null) { log.Info = _client.StringToId(log.Info.ToString()); } // 处理PHY层的信号记录 if (log.Layer == "PHY" && log.Data is List data) { var signalRecord = new Dictionary(); for (int j = data.Count - 1; j >= 0; j--) { var line = data[j]; var match = Regex.Match(line, @"Link:\s([\w\d]+)@(\d+)"); if (match.Success) { var linkName = match.Groups[1].Value; var offset = uint.Parse(match.Groups[2].Value); signalRecord[linkName] = new { offset = offset }; data.RemoveAt(j); } } if (signalRecord.Count > 0) { //log.SignalRecord = signalRecord; _client.HasSignalRecord = true; } } log.Client = _client; logList.Add(log); } } logParser.ParseLogList(_client, logList, true); //_client.ParseLogList(logList, true); LogsReceived?.Invoke(this, logList); } } if (count == 0 && _client.LogCount > 0) { // 刷新客户端网格 ClientGridRefreshed?.Invoke(this, EventArgs.Empty); } // 更新日志获取 - 只有在最后一个请求时才更新 if (msg["message_id"]?.Value() == _logGetId) { LogGet(); } } /// /// 发送消息 /// private void SendMessageNow() { if (_webSocket?.State != WebSocketState.Open) return; var messages = new List(); while (_messageFifo.TryDequeue(out var message)) { messages.Add(message); } if (messages.Count == 1) { var json = JsonConvert.SerializeObject(messages[0]); _webSocket.Send(json); // 记录发送的消息 lock (_sentLock) { _sentMessages.Add(JToken.Parse(json).ToString(Formatting.Indented)); } } else if (messages.Count > 1) { var json = JsonConvert.SerializeObject(messages); _webSocket.Send(json); // 记录发送的消息 lock (_sentLock) { _sentMessages.Add(JToken.Parse(json).ToString(Formatting.Indented)); } } _messageDeferTimer?.Dispose(); _messageDeferTimer = null; } /// /// 停止定时器 /// private void StopTimers() { _reconnectTimer?.Dispose(); _reconnectTimer = null; _statsTimer?.Dispose(); _statsTimer = null; _messageDeferTimer?.Dispose(); _messageDeferTimer = null; _readyTimer?.Dispose(); _readyTimer = null; } /// /// 关闭组件 /// private void CloseComponents() { // 关闭所有组件 } /// /// 设置状态 /// private void SetState(ClientState state) { if (_client.State != state) { _client.State = state; StateChanged?.Invoke(this, state); } } /// /// 获取默认过滤器 /// private string GetDefaultFilter(string layer) { return layer switch { "NAS" or "RRC" => "debug", "EVENT" or "ALARM" or "MON" or "PROD" => "info", _ => "warn" }; } /// /// 检查是否为日志参数 /// private bool IsLogParameter(string parameter) { var logParams = new[] { "signal", "cch", "bcch", "mib", "rep", "csi", "dci_size", "cell_meas" }; return logParams.Contains(parameter); } /// /// 认证 /// private void Authenticate(string password, string challenge) { // 实现认证逻辑 var authMessage = new JObject { ["message"] = "authenticate", ["password"] = password }; SendMessage(authMessage); } /// /// 提示输入密码 /// private void PromptPassword(JObject msg) { // 实现密码提示逻辑 Console.WriteLine("需要认证,请输入密码"); } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; Stop(); StopTimers(); _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); _webSocket?.Dispose(); _logger.LogInformation($"[{_config.Name}] 释放WebSocket客户端"); } #endregion } /// /// 消息处理器 /// public class MessageHandler { public Action? Callback { get; set; } public bool ErrorHandler { get; set; } } }