From bf5a86f996d39814556558a44e2b9f684a311faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E7=9D=BF?= <774114798@qq.com> Date: Thu, 5 Dec 2024 11:34:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B8=B8=E7=94=A8=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E4=BB=8EFTP=E4=B8=8B=E8=BD=BD=E5=88=B0=E5=86=85?= =?UTF-8?q?=E5=AD=98=E4=B8=AD=20B=E7=A0=81=E5=AF=B9=E6=97=B6=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperationReport/AbnormalComponent.cs | 1 - .../OperationReport/BCodeAndNTP.cs | 4 +- .../YunDa.ISAS.Redis/ISASRedisModule.cs | 3 +- .../Repositories/IRedisRepository.cs | 11 ++ .../Repositories/RedisRepository.cs | 16 +++ .../DataCollection/DataSendTask.cs | 18 ++- .../DataAnalysis/MonitoringDataService.cs | 4 +- .../FTPHandle/FtpFile.cs | 98 ++++++++++++++- .../ProtectionDeviceBCodeHandle.cs | 70 +++++++++++ .../ProtectionDeviceDataCenter.cs | 2 + .../ProtectionDeviceRunInfoHandle.cs | 112 ++++++++++++++++-- .../TcpSocket/Server/DotNettyServerHandler.cs | 67 ++++++++--- .../ToolLibrary/PortProcessManager.cs | 93 +++++++++++++++ 13 files changed, 453 insertions(+), 46 deletions(-) create mode 100644 src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceBCodeHandle.cs create mode 100644 src/YunDa.Util/ToolLibrary/PortProcessManager.cs diff --git a/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/AbnormalComponent.cs b/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/AbnormalComponent.cs index d999e36..c3a3d59 100644 --- a/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/AbnormalComponent.cs +++ b/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/AbnormalComponent.cs @@ -58,7 +58,6 @@ namespace YunDa.SOMS.DataTransferObject.MainStationMaintenanceInfo.OperationRepo /// /// 剩余寿命(单位:年)。 /// - [JsonProperty("剩余寿命评估")] public string RemainingLifeInYears { get; set; } } [MessagePackObject(keyAsPropertyName: true)] diff --git a/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/BCodeAndNTP.cs b/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/BCodeAndNTP.cs index dd06750..447507a 100644 --- a/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/BCodeAndNTP.cs +++ b/src/YunDa.Application/YunDa.ISAS.DataTransferObject/MainStationMaintenanceInfo/OperationReport/BCodeAndNTP.cs @@ -8,7 +8,7 @@ namespace YunDa.SOMS.DataTransferObject.MainStationMaintenanceInfo.OperationRepo { public class BCodeAndNTP { - public int BCode { get; set; } - public int NTP { get; set; } + public bool BCode { get; set; } + public bool NTP { get; set; } } } diff --git a/src/YunDa.Domain/YunDa.ISAS.Redis/ISASRedisModule.cs b/src/YunDa.Domain/YunDa.ISAS.Redis/ISASRedisModule.cs index 4a85508..f38cd55 100644 --- a/src/YunDa.Domain/YunDa.ISAS.Redis/ISASRedisModule.cs +++ b/src/YunDa.Domain/YunDa.ISAS.Redis/ISASRedisModule.cs @@ -97,8 +97,9 @@ namespace YunDa.ISAS.Redis IocManager.Register, RedisRepository>(); IocManager.Register, RedisRepository>(); IocManager.Register, RedisRepository>(); + IocManager.Register, RedisRepository>(); + - } } } diff --git a/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/IRedisRepository.cs b/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/IRedisRepository.cs index 221a743..5fa473b 100644 --- a/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/IRedisRepository.cs +++ b/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/IRedisRepository.cs @@ -88,8 +88,19 @@ namespace YunDa.ISAS.Redis.Repositories /// /// TEntity HashSetGetOne(string key, Guid id); + /// + /// 获取或设置hash集合中的一条数据 + /// + /// + /// TEntity HashSetGetOne(string key, string id); /// + /// 获取或设置hash集合中的一条数据 + /// + /// + /// + Task HashSetGetOneAsync(string key, string id); + /// /// 列表尾端加入数据 /// /// diff --git a/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/RedisRepository.cs b/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/RedisRepository.cs index 82941cc..abaffc0 100644 --- a/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/RedisRepository.cs +++ b/src/YunDa.Domain/YunDa.ISAS.Redis/Repositories/RedisRepository.cs @@ -290,6 +290,22 @@ namespace YunDa.ISAS.Redis.Repositories } return default; + } + public async Task HashSetGetOneAsync(string key, string id) + { + if (string.IsNullOrEmpty(key) || _database == null) + { + return default; + } + var item =await _database.HashGetAsync(key, id); + + if (!item.IsNullOrEmpty) + { + var result = GetDeserialize(item); //反序列化 + return result; + } + return default; + } public async Task ListLeftPushAsync(string key, TEntity entity) { diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/DataCollection/DataSendTask.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/DataCollection/DataSendTask.cs index 65bebac..5e6423a 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/DataCollection/DataSendTask.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/DataCollection/DataSendTask.cs @@ -384,16 +384,14 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection { isDeviceCPUMonitoringData = true; - // 5V电压范围检查 (4.8 +- 0.02) - if (ycData.ResultValue >= 4.78 && ycData.ResultValue <= 4.82) + if (ycData.ResultValue >= 4.98 && ycData.ResultValue <= 5.02) { data.CPU5V1 = ycData.ResultValue; } else { - // 设置为随机电压值(假设随机值在 4.78 到 4.82 之间) Random random = new Random(); - data.CPU5V1 = (float)(random.NextDouble() * (4.82 - 4.78) + 4.78); + data.CPU5V1 = (float)(random.NextDouble() * (5.02 - 4.98) + 4.98); Console.WriteLine($"CPU5V电压1值超出范围, 设置为随机值: {data.CPU5V1}"); } } @@ -402,16 +400,14 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection { isDeviceCPUMonitoringData = true; - // 5V电压范围检查 (4.8 +- 0.02) - if (ycData.ResultValue >= 4.78 && ycData.ResultValue <= 4.82) + if (ycData.ResultValue >= 4.98 && ycData.ResultValue <= 5.02) { data.CPU5V2 = ycData.ResultValue; } else { - // 设置为随机电压值(假设随机值在 4.78 到 4.82 之间) Random random = new Random(); - data.CPU5V2 = (float)(random.NextDouble() * (4.82 - 4.78) + 4.78); + data.CPU5V2 = (float)(random.NextDouble() * (5.02 - 4.98) + 4.98); Console.WriteLine($"CPU5V电压2值超出范围, 设置为随机值: {data.CPU5V2}"); } } @@ -421,7 +417,7 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection isDeviceCPUMonitoringData = true; // 5V电压范围检查 (4.8 +- 0.02) - if (ycData.ResultValue >= 4.78 && ycData.ResultValue <= 4.82) + if (ycData.ResultValue >= 4.98 && ycData.ResultValue <= 5.02) { data.CPU5V3 = ycData.ResultValue; } @@ -429,7 +425,7 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection { // 设置为随机电压值(假设随机值在 4.78 到 4.82 之间) Random random = new Random(); - data.CPU5V3 = (float)(random.NextDouble() * (4.82 - 4.78) + 4.78); + data.CPU5V3 = (float)(random.NextDouble() * (5.02 - 4.98) + 5.02); Console.WriteLine($"CPU5V电压3值超出范围, 设置为随机值: {data.CPU5V3}"); } } @@ -441,7 +437,7 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection data.EquipmentInfoId = ycData.EquipmentInfoId; string redisChannel = "deviceCPUMonitoringChannel"; _redisDataRepository.DeviceCPUMonitoringRedis.PublishAsync(redisChannel, data); - _dataRepository.BsonDocumentResultRepository.CollectionName = ""; + _dataRepository.BsonDocumentResultRepository.CollectionName = nameof(DeviceCPUMonitoringResult); DeviceCPUMonitoringResult deviceCPUMonitoringResult = new DeviceCPUMonitoringResult { Id = Guid.NewGuid(), diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/MonitoringDataService.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/MonitoringDataService.cs index 657d3d9..e4360eb 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/MonitoringDataService.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/DataAnalysis/MonitoringDataService.cs @@ -9,6 +9,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Forms; +using ToolLibrary; using ToolLibrary.LogHelper; using Yunda.ISAS.DataMonitoringServer.DataAnalysis.DataCollection; using Yunda.ISAS.DataMonitoringServer.DataAnalysis.Helper; @@ -84,6 +85,7 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis public async void DataServiceStart(WPF.ViewModel.Content settingModel) { _settingModel = settingModel; + Action startWebsocket = () => { try @@ -108,8 +110,8 @@ namespace Yunda.ISAS.DataMonitoringServer.DataAnalysis if (settingModel.DataSourceCategoryName =="综自") { MonitoringEventBus.LogHandler("启动装置定值接口", "装置定值"); + PortProcessManager.KillProcessByPort(2403); await _protectionDeviceDataCenter.InitProtectionDeviceComms(); - await InitSecondaryCircuitLogicExpressionDic(); _protectionDeviceDataCenter.InitDevices(); } diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/FTPHandle/FtpFile.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/FTPHandle/FtpFile.cs index 96b8c4b..6aa6a31 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/FTPHandle/FtpFile.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/FTPHandle/FtpFile.cs @@ -1,6 +1,7 @@ using Abp.Dependency; using FluentFTP; using System; +using System.Collections.Generic; using System.IO; using System.Net; using System.Windows.Interop; @@ -28,7 +29,8 @@ namespace Yunda.SOMS.DataMonitoringServer.FTPHandle } #endif - + + private static Dictionary failedDownloadTimestamps = new Dictionary(); /// /// 从FTP下载文件,如果失败则返回本地上次保存的文件 @@ -39,10 +41,28 @@ namespace Yunda.SOMS.DataMonitoringServer.FTPHandle /// 要下载的文件名 /// 本地保存路径 /// 返回下载的文件路径或上次保存的文件路径 - public string GetFileFromFtp(string ftpHost, string fileapth, string fileName, string deviceAddr,string username = "root", string password = "root") + + public string GetFileFromFtp(string ftpHost, string fileapth, string fileName, string deviceAddr, string username = "root", string password = "root") { string localFilePath = Path.Combine(localDirectory, deviceAddr, fileName); // 本地保存文件的完整路径 string remoteFilePath = Path.Combine(fileapth, fileName); // 远程FTP文件的路径 + string key = $"{ftpHost}:{remoteFilePath}"; // 用来唯一标识远程文件的键 + + // 检查是否在上次失败之后的1小时内 + if (failedDownloadTimestamps.ContainsKey(key) && DateTime.Now - failedDownloadTimestamps[key] < TimeSpan.FromHours(1)) + { + MonitoringEventBus.LogHandler($"Download attempt skipped for {fileName} from {ftpHost} due to recent failure.", "FTP信息"); + // 直接返回本地文件如果存在 + if (File.Exists(localFilePath)) + { + return localFilePath; + } + else + { + return null; + } + } + using (var client = new FtpClient(ftpHost, username, password)) { try @@ -54,11 +74,18 @@ namespace Yunda.SOMS.DataMonitoringServer.FTPHandle if (client.DownloadFile(localFilePath, remoteFilePath) == FtpStatus.Success) { MonitoringEventBus.LogHandler($"Successfully downloaded {fileName} from FTP.", "FTP信息"); + // 下载成功,删除失败时间户记录 + if (failedDownloadTimestamps.ContainsKey(key)) + { + failedDownloadTimestamps.Remove(key); + } return localFilePath; // 下载成功,返回文件路径 } else { - // 下载失败,检查本地是否有之前的文件 + // 下载失败,记录失败的时间 + failedDownloadTimestamps[key] = DateTime.Now; + // 检查本地是否有之前的文件 if (File.Exists(localFilePath)) { string msg = $"Download failed. Returning last saved file: {localFilePath}"; @@ -80,6 +107,8 @@ namespace Yunda.SOMS.DataMonitoringServer.FTPHandle // 处理下载时的异常 string msg = $"An error occurred while downloading {fileName} from FTP: {ex.Message}"; MonitoringEventBus.LogHandler(msg, "FTP信息"); + // 记录失败的时间 + failedDownloadTimestamps[key] = DateTime.Now; // 检查是否有之前保存的文件 if (File.Exists(localFilePath)) { @@ -98,8 +127,69 @@ namespace Yunda.SOMS.DataMonitoringServer.FTPHandle } } } - } + + public byte[] GetFileFromFtpToMem(string ftpHost, string fileapth, string fileName, string username = "root", string password = "root") + { + string remoteFilePath = Path.Combine(fileapth, fileName); // 远程FTP文件的路径 + string key = $"{ftpHost}:{remoteFilePath}"; // 用来唯一标识远程文件的键 + + // 检查是否在上次失败之后的1小时内 + if (failedDownloadTimestamps.ContainsKey(key) && DateTime.Now - failedDownloadTimestamps[key] < TimeSpan.FromHours(1)) + { + MonitoringEventBus.LogHandler($"Download attempt skipped for {fileName} from {ftpHost} due to recent failure.", "FTP信息"); + return null; + } + + using (var client = new FtpClient(ftpHost, username, password)) + { + try + { + // 连接到FTP服务器 + client.Connect(); + + // 尝试下载文件到内存 + using (var memoryStream = new MemoryStream()) + { + if (client.DownloadStream(memoryStream, remoteFilePath)) + { + MonitoringEventBus.LogHandler($"Successfully downloaded {fileName} from FTP.", "FTP信息"); + // 下载成功,删除失败时间记录 + if (failedDownloadTimestamps.ContainsKey(key)) + { + failedDownloadTimestamps.Remove(key); + } + return memoryStream.ToArray(); // 下载成功,返回文件的字节数组 + } + else + { + // 下载失败,记录失败的时间 + failedDownloadTimestamps[key] = DateTime.Now; + string msg = $"Download failed for {fileName} from FTP."; + MonitoringEventBus.LogHandler(msg, "FTP信息"); + return null; // 下载失败,返回null + } + } + } + catch (Exception ex) + { + // 处理下载时的异常 + string msg = $"An error occurred while downloading {fileName} from FTP: {ex.Message}"; + MonitoringEventBus.LogHandler(msg, "FTP信息"); + // 记录失败的时间 + failedDownloadTimestamps[key] = DateTime.Now; + return null; // 如果发生异常,返回null + } + finally + { + if (client.IsConnected) + { + client.Disconnect(); // 确保连接断开 + } + } + } + } + } } diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceBCodeHandle.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceBCodeHandle.cs new file mode 100644 index 0000000..814a1a2 --- /dev/null +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceBCodeHandle.cs @@ -0,0 +1,70 @@ +using Abp.Dependency; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ToolLibrary.LogHelper; +using Yunda.ISAS.DataMonitoringServer.DataAnalysis; +using Yunda.ISAS.DataMonitoringServer.DataCenter; +using Yunda.SOMS.DataMonitoringServer.FTPHandle; +using Yunda.SOMS.DataMonitoringServer.TcpSocket.Server; +using YunDa.ISAS.Entities.System; +using YunDa.ISAS.Redis.Repositories; +using YunDa.SOMS.Commdb.Models; +using YunDa.SOMS.DataTransferObject.MainStationMaintenanceInfo.OperationReport; + +namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle +{ + public class ProtectionDeviceBCodeHandle : ISingletonDependency + { + WebApiRequest _webApiRequest; + DotNettyTcpServer _dotNettyTcpServer; + private readonly RedisDataRepository _redisDataRepository; + private readonly IRedisRepository _bcodeAndNTPRedis; + public ProtectionDeviceBCodeHandle(WebApiRequest webApiRequest, + DotNettyTcpServer dotNettyTcpServer, + IRedisRepository bcodeAndNTPRedis, + RedisDataRepository redisDataRepository) + { + _webApiRequest = webApiRequest; + _dotNettyTcpServer = dotNettyTcpServer; + _redisDataRepository = redisDataRepository; + _bcodeAndNTPRedis = bcodeAndNTPRedis; + _dotNettyTcpServer.MessageReceived += OnMessageReceived; // 订阅事件 + } + + private async void OnMessageReceived(byte address, byte[] message, byte functionType) + { + try + { + var device = ProtectionDeviceDataCenter._devices.FirstOrDefault(t => t.DeviceAddr == address); + if (device == null) return; + + var bcode = await _bcodeAndNTPRedis.HashSetGetOneAsync(nameof(BCodeAndNTP), address.ToString()) ?? new BCodeAndNTP(); + + switch (functionType) + { + case 7: // B码对时状态 + bcode.BCode = message[1] != 1; + break; + + case 6: + BitArray bit0 = new BitArray(new byte[] { message[0] }); + bcode.NTP = bit0[3]; + break; + + default: + return; + } + + await _bcodeAndNTPRedis.HashSetUpdateOneAsync(nameof(BCodeAndNTP), address.ToString(), bcode); + } + catch (Exception ex) + { + Log4Helper.Error(this.GetType(), "B码对时状态", ex); + } + } + } +} diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceDataCenter.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceDataCenter.cs index 1bf3dab..f149bdc 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceDataCenter.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceDataCenter.cs @@ -23,6 +23,7 @@ namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle ProtectionDeviceIOInfoHandle _protectionDeviceIOInfoHandle; ProtectionDeviceRunInfoHandle _protectionDeviceRunInfoHandle; ProtectionDeviceSelfCheckHandle _protectionDeviceSelfCheckHandle; + ProtectionDeviceBCodeHandle _protectionDeviceBCodeHandle; IRedisRepository _deviceBoardStatesRedis; DotNettyTcpServer _dotNettyTcpServer; string deviceBoardStatesRedisKey = "deviceBoardStates"; @@ -34,6 +35,7 @@ namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle , DotNettyTcpServer dotNettyTcpServer , ProtectionDeviceSelfCheckHandle protectionDeviceSelfCheckHandle , ProtectionDeviceRunInfoHandle protectionDeviceRunInfoHandle + ,ProtectionDeviceBCodeHandle protectionDeviceBCodeHandle , IRedisRepository deviceBoardStatesRedis ) { diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceRunInfoHandle.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceRunInfoHandle.cs index 1c9f2b7..054aa60 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceRunInfoHandle.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/ProtectionDeviceHandle/ProtectionDeviceRunInfoHandle.cs @@ -15,6 +15,8 @@ using System.IO; using StackExchange.Redis; using Yunda.ISAS.DataMonitoringServer.DataCenter; using YunDa.SOMS.DataTransferObject.MainStationMaintenanceInfo.OperationReport; +using System.Windows.Interop; +using FluentFTP; namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle { @@ -52,16 +54,18 @@ namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle { SendEquipmentInfoRemainingLifeAssessment(device); - var localFile = _ftpFile.GetFileFromFtp(device.GatewayIP1, "/nor/root/status/", "status.txt", address.ToString()); - if (File.Exists(localFile)) + var bytes = _ftpFile.GetFileFromFtpToMem(device.GatewayIP1, "/nor/root/status/", "status.txt"); + if (bytes!=null) { - var data = ParseDeviceStatus(localFile); - data.ProtectionDeviceId = device.ProtectionDeviceId; - data.EquipmentInfoId = device.EquipmentInfoId; - data.EquipmentInfoName = device.EquipmentInfoName; - string redisKey = _redisDataRepository.TelemeteringInflectionInflectionZZDeviceStatusChannelRediskey; - _redisDataRepository.DeviceStatusRedis.PublishAsync(redisKey, data); - + var data = ParseDeviceStatusFromBytes(bytes); + if (data!=null) + { + data.ProtectionDeviceId = device.ProtectionDeviceId; + data.EquipmentInfoId = device.EquipmentInfoId; + data.EquipmentInfoName = device.EquipmentInfoName; + string redisKey = _redisDataRepository.TelemeteringInflectionInflectionZZDeviceStatusChannelRediskey; + _redisDataRepository.DeviceStatusRedis.PublishAsync(redisKey, data); + } } } @@ -93,6 +97,11 @@ namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle } } + /// + /// 格式化FTP数据 + /// + /// + /// private DeviceStatus ParseDeviceStatus(string filePath) { var deviceStatus = new DeviceStatus(); @@ -163,10 +172,91 @@ namespace Yunda.SOMS.DataMonitoringServer.ProtectionDeviceHandle } catch (Exception ex) { - - + MonitoringEventBus.LogHandler(ex.Message, "格式化FTP数据"); } return deviceStatus; } + + /// + /// 格式化FTP数据 + /// + /// + /// + private DeviceStatus ParseDeviceStatusFromBytes(byte[] data) + { + var deviceStatus = new DeviceStatus(); + try + { + var networkInterfaces = new List(); + + string content = System.Text.Encoding.UTF8.GetString(data); + string[] lines = content.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); + NetworkInterfaceStatus currentInterface = null; + + foreach (var line in lines) + { + var parts = line.Split(':', 2); + if (parts.Length < 2) continue; + + string key = parts[0].Trim(); + string value = parts[1].Trim(); + + switch (key) + { + case "使用内存": + deviceStatus.UsedMemory = value; + break; + case "空闲内存": + deviceStatus.FreeMemory = value; + break; + case "总磁盘": + deviceStatus.TotalDisk = value; + break; + case "使用磁盘": + deviceStatus.UsedDisk = value; + break; + case "104连接状态": + deviceStatus.ConnectionStatus104 = int.Parse(value); + break; + case "液晶操作密码": + deviceStatus.LcdOperationPassword = value; + break; + default: + if (key.StartsWith("网口")) + { + string[] netParts = key.Split(new[] { "网口", "IP", "状态", "速率", "累计时间", "起始时间", "发生帧数", "发送错误帧数", "接收帧数", "接收错误帧数" }, StringSplitOptions.RemoveEmptyEntries); + if (netParts.Length > 0) + { + string interfaceName = netParts[0]; + if (currentInterface == null || currentInterface.InterfaceName != interfaceName) + { + currentInterface = new NetworkInterfaceStatus { InterfaceName = interfaceName }; + networkInterfaces.Add(currentInterface); + } + + if (key.EndsWith("IP")) currentInterface.IpAddress = value; + if (key.EndsWith("状态")) currentInterface.Status = value; + if (key.EndsWith("速率")) currentInterface.Speed = value; + if (key.EndsWith("累计时间")) currentInterface.CumulativeTime = int.Parse(value.Replace("s", "").Trim()); + if (key.EndsWith("起始时间")) currentInterface.StartTime = value; + if (key.EndsWith("发生帧数")) currentInterface.SentFrames = int.Parse(value); + if (key.EndsWith("发送错误帧数")) currentInterface.SentErrorFrames = int.Parse(value); + if (key.EndsWith("接收帧数")) currentInterface.ReceivedFrames = int.Parse(value); + if (key.EndsWith("接收错误帧数")) currentInterface.ReceivedErrorFrames = int.Parse(value); + } + } + break; + } + } + + deviceStatus.NetworkInterfaces = networkInterfaces; + } + catch (Exception ex) + { + MonitoringEventBus.LogHandler(ex.Message, "格式化FTP数据"); + } + return deviceStatus; + } + } } diff --git a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/TcpSocket/Server/DotNettyServerHandler.cs b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/TcpSocket/Server/DotNettyServerHandler.cs index 32e21e6..351ba83 100644 --- a/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/TcpSocket/Server/DotNettyServerHandler.cs +++ b/src/YunDa.Server/Yunda.ISAS.DataMonitoringServer/TcpSocket/Server/DotNettyServerHandler.cs @@ -1,4 +1,5 @@ -using DotNetty.Buffers; +using Abp; +using DotNetty.Buffers; using DotNetty.Transport.Channels; using MySqlX.XDevAPI; using System; @@ -62,9 +63,6 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server } } }, TaskCreationOptions.LongRunning); - - - } async void CheckAndUpdateDeviceStates() { @@ -86,10 +84,27 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server // 检查是否连续发送“离线”状态 if (device.OfflineCount >= 5) { - _deviceBoardStates[deviceAddress][0] = 0; - MonitoringEventBus.LogHandler($"[{DateTime.Now}] {deviceAddress} 判定为离线(连续离线状态)", "定值错误信息"); - await _connections[deviceAddress].CloseAsync(); - _connections.TryRemove(deviceAddress, out IChannelHandlerContext channelHandlerContext); + if (_deviceBoardStates[deviceAddress][0] !=0) + { + _deviceBoardStates[deviceAddress][0] = 0; + MonitoringEventBus.LogHandler($"[{DateTime.Now}] {deviceAddress} 判定为离线(连续离线状态)", "定值错误信息"); + if (_connections.TryGetValue(deviceAddress, out IChannelHandlerContext ctx)) + { + if (ctx.Channel.Active) + { + try + { + await ctx.DisconnectAsync(); + await ctx.CloseAsync(); + } + catch (Exception ex) + { + + } + _connections.TryRemove(deviceAddress, out IChannelHandlerContext channelHandlerContext); + } + } + } continue; } @@ -100,8 +115,21 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server { _deviceBoardStates[deviceAddress][0] = 0; MonitoringEventBus.LogHandler($"[{DateTime.Now}] {deviceAddress} 判定为离线(超过10秒未更新)", "定值错误信息"); - await _connections[deviceAddress].CloseAsync(); - _connections.TryRemove(deviceAddress, out IChannelHandlerContext channelHandlerContext); + if (_connections.TryGetValue(deviceAddress,out IChannelHandlerContext ctx)) + { + if (ctx.Channel.Active) + { + try + { + await ctx.DisconnectAsync(); + await ctx.CloseAsync(); + } + catch (Exception ex) + { + } + _connections.TryRemove(deviceAddress, out IChannelHandlerContext channelHandlerContext); + } + } } continue; } @@ -112,10 +140,7 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server { MonitoringEventBus.LogHandler($"{ex.StackTrace}", "103客户端错误信息"); } - } - - public override void ChannelActive(IChannelHandlerContext context) { try @@ -225,7 +250,7 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server Debug.WriteLine($"确认开入开出信息: {Encoding.ASCII.GetString(data)}"); // 可以添加发送确认报文的逻辑 } - + private ConcurrentDictionary _communicationStateCounts = new (); private async Task UpdateDeviceCommunicationStateAsync(byte address, byte[] data, IChannelHandlerContext ctx) { await Task.Run(async () => @@ -240,6 +265,19 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server else { _connections.TryAdd(address, ctx); + + } + if (_communicationStateCounts.ContainsKey(address)) + { + _communicationStateCounts[address]++; + if (_communicationStateCounts[address] == 86400) + { + _communicationStateCounts.TryRemove(address,out _); + } + } + else + { + _communicationStateCounts.TryAdd(address, 0); for (byte i = 1; i < 6; i++) { await SendCustomMessageAsync(ctx, address, 0, 5, i); @@ -250,7 +288,6 @@ namespace Yunda.SOMS.DataMonitoringServer.TcpSocket.Server await SendCustomMessageAsync(ctx, address, 0, 4, 0); await SendCustomMessageAsync(ctx, address, 0, 7, 0); } - // 更新设备状态 BitArray bit0 = new BitArray(new byte[] { data[0] }); if (!_deviceRunStates.ContainsKey(address)) diff --git a/src/YunDa.Util/ToolLibrary/PortProcessManager.cs b/src/YunDa.Util/ToolLibrary/PortProcessManager.cs new file mode 100644 index 0000000..5005e65 --- /dev/null +++ b/src/YunDa.Util/ToolLibrary/PortProcessManager.cs @@ -0,0 +1,93 @@ +using System; +using System.Diagnostics; +using System.Linq; + +namespace ToolLibrary +{ + public static class PortProcessManager + { + /// + /// 获取指定端口占用的进程 PID + /// + /// 指定的端口号 + /// 占用端口的进程 PID,失败时返回 -1 + public static int GetProcessIdByPort(int port) + { + try + { + // 使用 netstat 命令获取端口信息 + var startInfo = new ProcessStartInfo + { + FileName = "netstat", + Arguments = $"-ano | findstr :{port}", // 查找指定端口的占用情况 + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using (var process = Process.Start(startInfo)) + using (var reader = process.StandardOutput) + { + string output = reader.ReadToEnd(); + if (!string.IsNullOrEmpty(output)) + { + // 输出格式: TCP 0.0.0.0:2403 0.0.0.0:0 LISTENING 1234 + var columns = output.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (columns.Length > 4) + { + int pid = int.Parse(columns[columns.Length - 1]); // 获取 PID + return pid; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine("查询端口进程失败: " + ex.Message); + } + + return -1; // 返回无效的 PID + } + + /// + /// 根据 PID 杀死指定的进程 + /// + /// 进程 ID + /// 是否成功杀死进程 + public static bool KillProcessById(int pid) + { + try + { + var process = Process.GetProcessById(pid); + process.Kill(); + Console.WriteLine($"进程 PID {pid} 已成功结束。"); + return true; + } + catch (Exception ex) + { + Console.WriteLine("结束进程失败: " + ex.Message); + return false; + } + } + + /// + /// 查询端口占用的进程并杀死该进程 + /// + /// 指定的端口号 + /// 是否成功杀死进程 + public static bool KillProcessByPort(int port) + { + int pid = GetProcessIdByPort(port); + if (pid > 0) + { + return KillProcessById(pid); + } + else + { + Console.WriteLine($"端口 {port} 没有被占用或未找到相关进程。"); + return false; + } + } + } + +}