2024-12-19 16:07:07 +08:00
|
|
|
|
using System;
|
2025-03-27 08:38:19 +08:00
|
|
|
|
using System.Runtime.CompilerServices;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2025-03-27 08:38:19 +08:00
|
|
|
|
using DeviceDetectorNET.Parser.Device;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
using DotNetCore.CAP;
|
|
|
|
|
|
using JiShe.CollectBus.Ammeters;
|
|
|
|
|
|
using JiShe.CollectBus.Common.Enums;
|
|
|
|
|
|
using JiShe.CollectBus.Common.Extensions;
|
|
|
|
|
|
using JiShe.CollectBus.Enums;
|
|
|
|
|
|
using JiShe.CollectBus.Interceptors;
|
2025-03-14 14:28:04 +08:00
|
|
|
|
using JiShe.CollectBus.IotSystems.Devices;
|
|
|
|
|
|
using JiShe.CollectBus.IotSystems.MessageReceiveds;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
using JiShe.CollectBus.Protocol.Contracts;
|
|
|
|
|
|
using MassTransit;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using TouchSocket.Core;
|
|
|
|
|
|
using TouchSocket.Sockets;
|
|
|
|
|
|
using Volo.Abp.Caching;
|
|
|
|
|
|
using Volo.Abp.DependencyInjection;
|
|
|
|
|
|
using Volo.Abp.Domain.Entities;
|
|
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
2025-04-08 23:15:46 +08:00
|
|
|
|
using static FreeSql.Internal.GlobalFilter;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
|
|
|
|
|
|
namespace JiShe.CollectBus.Plugins
|
|
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
public partial class TcpMonitor : PluginBase, ITransientDependency, ITcpReceivedPlugin, ITcpConnectingPlugin, ITcpConnectedPlugin, ITcpClosedPlugin
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-04-07 21:50:50 +08:00
|
|
|
|
private readonly ICapPublisher _producerBus;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
private readonly ILogger<TcpMonitor> _logger;
|
|
|
|
|
|
private readonly IRepository<Device, Guid> _deviceRepository;
|
|
|
|
|
|
private readonly IDistributedCache<AmmeterInfo> _ammeterInfoCache;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
2025-04-01 22:50:34 +08:00
|
|
|
|
/// <param name="producerBus"></param>
|
2024-12-19 16:07:07 +08:00
|
|
|
|
/// <param name="logger"></param>
|
|
|
|
|
|
/// <param name="deviceRepository"></param>
|
|
|
|
|
|
/// <param name="ammeterInfoCache"></param>
|
2025-04-07 21:50:50 +08:00
|
|
|
|
public TcpMonitor(ICapPublisher producerBus,
|
2025-03-27 08:38:19 +08:00
|
|
|
|
ILogger<TcpMonitor> logger,
|
|
|
|
|
|
IRepository<Device, Guid> deviceRepository,
|
2024-12-19 16:07:07 +08:00
|
|
|
|
IDistributedCache<AmmeterInfo> ammeterInfoCache)
|
|
|
|
|
|
{
|
2025-04-01 22:50:34 +08:00
|
|
|
|
_producerBus = producerBus;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
_logger = logger;
|
|
|
|
|
|
_deviceRepository = deviceRepository;
|
|
|
|
|
|
_ammeterInfoCache = ammeterInfoCache;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-27 08:38:19 +08:00
|
|
|
|
public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
var messageHexString = Convert.ToHexString(e.ByteBlock.Span);
|
|
|
|
|
|
var hexStringList = messageHexString.StringToPairs();
|
|
|
|
|
|
var aFn = (int?)hexStringList.GetAnalyzeValue(CommandChunkEnum.AFN);
|
|
|
|
|
|
var fn = (int?)hexStringList.GetAnalyzeValue(CommandChunkEnum.FN);
|
|
|
|
|
|
var aTuple = (Tuple<string, int>)hexStringList.GetAnalyzeValue(CommandChunkEnum.A);
|
2025-03-12 09:58:37 +08:00
|
|
|
|
if (aFn.HasValue && fn.HasValue && aTuple != null && !string.IsNullOrWhiteSpace(aTuple.Item1))
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
var tcpSessionClient = (ITcpSessionClient)client;
|
|
|
|
|
|
|
2024-12-19 16:07:07 +08:00
|
|
|
|
if ((AFN)aFn == AFN.链路接口检测)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (fn)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 1:
|
2025-03-27 08:38:19 +08:00
|
|
|
|
await OnTcpLoginReceived(tcpSessionClient, messageHexString, aTuple.Item1);
|
2024-12-19 16:07:07 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case 3:
|
2025-03-27 08:38:19 +08:00
|
|
|
|
//心跳帧有两种情况:
|
|
|
|
|
|
//1. 集中器先有登录帧,再有心跳帧
|
|
|
|
|
|
//2. 集中器没有登录帧,只有心跳帧
|
|
|
|
|
|
await OnTcpHeartbeatReceived(tcpSessionClient, messageHexString, aTuple.Item1);
|
2024-12-19 16:07:07 +08:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
_logger.LogError($"指令初步解析失败,指令内容:{messageHexString}");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-08 23:15:46 +08:00
|
|
|
|
await OnTcpNormalReceived(tcpSessionClient, messageHexString, aTuple.Item1,aFn.ToString()!.PadLeft(2,'0'));
|
2024-12-19 16:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"指令初步解析失败,指令内容:{messageHexString}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await e.InvokeNext();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-27 08:38:19 +08:00
|
|
|
|
//[GeneratorPlugin(typeof(ITcpConnectingPlugin))]
|
|
|
|
|
|
public async Task OnTcpConnecting(ITcpSession client, ConnectingEventArgs e)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
var tcpSessionClient = (ITcpSessionClient)client;
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}正在连接中...");
|
2024-12-19 16:07:07 +08:00
|
|
|
|
await e.InvokeNext();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-27 08:38:19 +08:00
|
|
|
|
//[GeneratorPlugin(typeof(ITcpConnectedPlugin))]
|
|
|
|
|
|
public async Task OnTcpConnected(ITcpSession client, ConnectedEventArgs e)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
var tcpSessionClient = (ITcpSessionClient)client;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已连接");
|
2024-12-19 16:07:07 +08:00
|
|
|
|
await e.InvokeNext();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-27 08:38:19 +08:00
|
|
|
|
//[GeneratorPlugin(typeof(ITcpClosedPlugin))]//ITcpSessionClient
|
|
|
|
|
|
public async Task OnTcpClosed(ITcpSession client, ClosedEventArgs e)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
|
|
|
|
|
|
var tcpSessionClient = (ITcpSessionClient)client;
|
|
|
|
|
|
var entity = await _deviceRepository.FindAsync(a => a.ClientId == tcpSessionClient.Id);
|
2024-12-19 16:07:07 +08:00
|
|
|
|
if (entity != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
entity.UpdateByOnClosed();
|
|
|
|
|
|
await _deviceRepository.UpdateAsync(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
_logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已关闭连接,但采集程序检索失败");
|
2024-12-19 16:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await e.InvokeNext();
|
|
|
|
|
|
}
|
2025-03-12 09:58:37 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 登录帧处理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="client"></param>
|
|
|
|
|
|
/// <param name="messageHexString"></param>
|
|
|
|
|
|
/// <param name="deviceNo">集中器编号</param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-03-12 17:10:00 +08:00
|
|
|
|
private async Task OnTcpLoginReceived(ITcpSessionClient client, string messageHexString, string deviceNo)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
string oldClientId = $"{client.Id}";
|
|
|
|
|
|
|
|
|
|
|
|
await client.ResetIdAsync(deviceNo);
|
|
|
|
|
|
|
|
|
|
|
|
var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo);
|
|
|
|
|
|
if (entity == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _deviceRepository.InsertAsync(new Device(deviceNo, oldClientId, DateTime.Now, DateTime.Now, DeviceStatus.Online));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
entity.UpdateByLoginAndHeartbeat(oldClientId);
|
|
|
|
|
|
await _deviceRepository.UpdateAsync(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-19 16:07:07 +08:00
|
|
|
|
var messageReceivedLoginEvent = new MessageReceivedLogin
|
|
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
ClientId = deviceNo,
|
2024-12-19 16:07:07 +08:00
|
|
|
|
ClientIp = client.IP,
|
|
|
|
|
|
ClientPort = client.Port,
|
|
|
|
|
|
MessageHexString = messageHexString,
|
|
|
|
|
|
DeviceNo = deviceNo,
|
|
|
|
|
|
MessageId = NewId.NextGuid().ToString()
|
|
|
|
|
|
};
|
2025-04-08 23:15:46 +08:00
|
|
|
|
await _producerBus.PublishAsync(ProtocolConst.SubscriberLoginReceivedEventName, messageReceivedLoginEvent);
|
2025-04-01 22:50:34 +08:00
|
|
|
|
|
2025-04-07 21:50:50 +08:00
|
|
|
|
//await _producerBus.Publish( messageReceivedLoginEvent);
|
2025-03-27 08:38:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task OnTcpHeartbeatReceived(ITcpSessionClient client, string messageHexString, string deviceNo)
|
|
|
|
|
|
{
|
|
|
|
|
|
string clientId = deviceNo;
|
|
|
|
|
|
string oldClientId = $"{client.Id}";
|
|
|
|
|
|
|
2024-12-19 16:07:07 +08:00
|
|
|
|
var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo);
|
2025-03-27 08:38:19 +08:00
|
|
|
|
if (entity == null) //没有登录帧的设备,只有心跳帧
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
await client.ResetIdAsync(clientId);
|
|
|
|
|
|
await _deviceRepository.InsertAsync(new Device(deviceNo, oldClientId, DateTime.Now, DateTime.Now, DeviceStatus.Online));
|
2024-12-19 16:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
if (clientId != oldClientId)
|
|
|
|
|
|
{
|
|
|
|
|
|
entity.UpdateByLoginAndHeartbeat(oldClientId);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
entity.UpdateByLoginAndHeartbeat();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-19 16:07:07 +08:00
|
|
|
|
await _deviceRepository.UpdateAsync(entity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var messageReceivedHeartbeatEvent = new MessageReceivedHeartbeat
|
|
|
|
|
|
{
|
2025-03-27 08:38:19 +08:00
|
|
|
|
ClientId = clientId,
|
2024-12-19 16:07:07 +08:00
|
|
|
|
ClientIp = client.IP,
|
|
|
|
|
|
ClientPort = client.Port,
|
|
|
|
|
|
MessageHexString = messageHexString,
|
|
|
|
|
|
DeviceNo = deviceNo,
|
|
|
|
|
|
MessageId = NewId.NextGuid().ToString()
|
|
|
|
|
|
};
|
2025-04-08 23:15:46 +08:00
|
|
|
|
await _producerBus.PublishAsync(ProtocolConst.SubscriberHeartbeatReceivedEventName, messageReceivedHeartbeatEvent);
|
2025-04-07 21:50:50 +08:00
|
|
|
|
//await _producerBus.Publish(messageReceivedHeartbeatEvent);
|
2024-12-19 16:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-08 23:15:46 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 正常帧处理,将不同的AFN进行分发
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="client"></param>
|
|
|
|
|
|
/// <param name="messageHexString"></param>
|
|
|
|
|
|
/// <param name="deviceNo"></param>
|
|
|
|
|
|
/// <param name="aFn"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo,string aFn)
|
2024-12-19 16:07:07 +08:00
|
|
|
|
{
|
2025-04-07 21:50:50 +08:00
|
|
|
|
//await _producerBus.Publish(new MessageReceived
|
2025-04-01 22:50:34 +08:00
|
|
|
|
//{
|
|
|
|
|
|
// ClientId = client.Id,
|
|
|
|
|
|
// ClientIp = client.IP,
|
|
|
|
|
|
// ClientPort = client.Port,
|
|
|
|
|
|
// MessageHexString = messageHexString,
|
|
|
|
|
|
// DeviceNo = deviceNo,
|
|
|
|
|
|
// MessageId = NewId.NextGuid().ToString()
|
|
|
|
|
|
//});
|
2025-04-07 21:50:50 +08:00
|
|
|
|
|
2025-04-08 23:15:46 +08:00
|
|
|
|
|
2025-04-10 17:06:53 +08:00
|
|
|
|
//string topicName = string.Format(ProtocolConst.AFNTopicNameFormat, aFn);
|
2025-04-08 23:15:46 +08:00
|
|
|
|
|
2025-04-10 17:06:53 +08:00
|
|
|
|
await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived
|
2025-04-07 21:50:50 +08:00
|
|
|
|
{
|
|
|
|
|
|
ClientId = client.Id,
|
|
|
|
|
|
ClientIp = client.IP,
|
|
|
|
|
|
ClientPort = client.Port,
|
|
|
|
|
|
MessageHexString = messageHexString,
|
|
|
|
|
|
DeviceNo = deviceNo,
|
|
|
|
|
|
MessageId = NewId.NextGuid().ToString()
|
|
|
|
|
|
});
|
2024-12-19 16:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|