using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.IotSystems.Ammeters; using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol3761; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using TouchSocket.Core; using TouchSocket.Sockets; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; namespace JiShe.CollectBus.Plugins { public partial class TcpMonitor : PluginBase, ITransientDependency, ITcpReceivedPlugin, ITcpConnectingPlugin, ITcpConnectedPlugin, ITcpClosedPlugin, ITcpClosingPlugin { private readonly IProducerService _producerService; private readonly ILogger _logger; private readonly IDistributedCache _ammeterInfoCache; private readonly IServiceProvider _serviceProvider; private readonly IProtocolService _protocolService; private readonly ServerApplicationOptions _serverApplicationOptions; private readonly IFreeRedisProvider _freeRedisProvider; /// /// /// /// /// /// /// /// /// /// public TcpMonitor(IProducerService producerService, ILogger logger, IDistributedCache ammeterInfoCache, IServiceProvider serviceProvider, IOptions serverApplicationOptions, IProtocolService protocolService, IFreeRedisProvider freeRedisProvider) { _producerService = producerService; _logger = logger; _ammeterInfoCache = ammeterInfoCache; _serviceProvider= serviceProvider; _protocolService = protocolService; _serverApplicationOptions = serverApplicationOptions.Value; _freeRedisProvider = freeRedisProvider; } public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e) { if (string.IsNullOrWhiteSpace(_serverApplicationOptions.DefaultProtocolPlugin)) { _logger.LogError($"请配置默认的端到云协议插件!"); } var messageHexString = Convert.ToHexString(e.ByteBlock.Span); var protocolPlugin = await _protocolService.GetProtocolServiceAsync(_serverApplicationOptions.DefaultProtocolPlugin); if (protocolPlugin == null) { _logger.LogError($"默认的端到云协议插件{_serverApplicationOptions.DefaultProtocolPlugin}不存在!"); } var tcpSessionClient = (ITcpSessionClient)client; TB3761? tB3761 = await protocolPlugin!.AnalyzeAsync(tcpSessionClient, messageHexString); if (tB3761 == null) { // TODO: 暂时不处理,后续再处理 _logger.LogError($"指令初步解析失败,指令内容:{messageHexString}"); } //登录和心跳的时候,需要过滤TCP连接 if (tB3761.DT?.Fn == (int)FN.登录 || tB3761.DT?.Fn == (int)FN.心跳) { string focusAddress = tB3761.A.Code; if (string.IsNullOrWhiteSpace(focusAddress))//集中器地址为空,主动关闭连接 { await tcpSessionClient.CloseAsync(); _logger.LogError($"指令解析失败,没有找到集中器地址,指令内容:{messageHexString}"); return; } //查找Redis缓存的集中器信息 var focusCacheInfos = _freeRedisProvider.Instance.HGet(RedisConst.CacheAllFocusInfoHashKey, focusAddress); if (focusCacheInfos == null) { await tcpSessionClient.CloseAsync(); _logger.LogError($"TCP连接关闭,没有找到集中器地址{focusAddress}的缓存信息"); return; } //检查集中器的信息的服务器标记是否为空,如果不为空,需要检查是否与当前服务器的标记一致 if (!string.IsNullOrWhiteSpace(focusCacheInfos.ServerTagName) && focusCacheInfos.ServerTagName != _serverApplicationOptions.ServerTagName) { await tcpSessionClient.CloseAsync(); _logger.LogError($"TCP连接关闭,集中器地址{focusAddress}的服务器标记为:{focusCacheInfos.ServerTagName},当前服务器的标记为:{_serverApplicationOptions.ServerTagName},不一致拒绝连接。"); return; } int currentTCPConnectionCount = tcpSessionClient.Service.Count; int configTCPConnectionCount = _serverApplicationOptions.TCPConnectionCount;//配置TCP连接数量 _logger.LogWarning($"当前配置连接数量为:{configTCPConnectionCount},当前连接数量为:{currentTCPConnectionCount}"); if (currentTCPConnectionCount >= configTCPConnectionCount) { _logger.LogError($"当前连接数量为:{currentTCPConnectionCount},大于配置连接数量{configTCPConnectionCount},将拒绝集中{focusAddress}连接"); await tcpSessionClient.CloseAsync(); return; } //缓存更新状态 focusCacheInfos.LastSurvivalTime = DateTime.Now; focusCacheInfos.ServerTagName = _serverApplicationOptions.ServerTagName; focusCacheInfos.OnLineStatus = true; _freeRedisProvider.Instance.HSet(RedisConst.CacheAllFocusInfoHashKey, focusAddress, focusCacheInfos); //连接成功以后,通知信息到后台 Dictionary channelMessage = new Dictionary(); channelMessage.Add(ServiceCommunicationTypeEnum.FocusStatusReceived, focusCacheInfos.Serialize()); _= _producerService.ProduceAsync(KafkaTopicConsts.ServiceCommunicationChannelTopic, channelMessage); } //todo 后续可以考虑Redis的bitmap做日活签到。 // TODO:要注意保存树模型的时时标字段,如果没有时标,则取接受时间做兼容 await e.InvokeNext(); } public async Task OnTcpConnecting(ITcpSession client, ConnectingEventArgs e) { var tcpSessionClient = (ITcpSessionClient)client; _logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}正在连接中..."); await e.InvokeNext(); } public async Task OnTcpConnected(ITcpSession client, ConnectedEventArgs e) { var tcpSessionClient = (ITcpSessionClient)client; _logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已连接"); await e.InvokeNext(); } public async Task OnTcpClosed(ITcpSession client, ClosedEventArgs e) { var tcpSessionClient = (ITcpSessionClient)client; _logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已关闭连接"); //查找Redis缓存的集中器信息 var focusCacheInfos = _freeRedisProvider.Instance.HGet(RedisConst.CacheAllFocusInfoHashKey, tcpSessionClient.Id); if (focusCacheInfos != null) { //缓存更新状态 focusCacheInfos.LastSurvivalTime = DateTime.Now; focusCacheInfos.ServerTagName = _serverApplicationOptions.ServerTagName; focusCacheInfos.OnLineStatus = false; _freeRedisProvider.Instance.HSet(RedisConst.CacheAllFocusInfoHashKey, tcpSessionClient.Id, focusCacheInfos); } await e.InvokeNext(); } public Task OnTcpClosing(ITcpSession client, ClosingEventArgs e) { var tcpSessionClient = (ITcpSessionClient)client; _logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}终端主动关闭"); return Task.CompletedTask; } } }