diff --git a/DMS.Application/DTOs/DeviceDto.cs b/DMS.Application/DTOs/DeviceDto.cs index 2b1aa0a..fbdcd37 100644 --- a/DMS.Application/DTOs/DeviceDto.cs +++ b/DMS.Application/DTOs/DeviceDto.cs @@ -1,3 +1,5 @@ +using DMS.Core.Enums; + namespace DMS.Application.DTOs; /// diff --git a/DMS.Core/Enums/ProtocolType.cs b/DMS.Core/Enums/ProtocolType.cs index 7823873..775fa36 100644 --- a/DMS.Core/Enums/ProtocolType.cs +++ b/DMS.Core/Enums/ProtocolType.cs @@ -1,4 +1,8 @@ -/// + +namespace DMS.Core.Enums; + + +/// /// 定义了设备支持的通信协议类型。 /// public enum ProtocolType diff --git a/DMS.Core/Models/VariableMqtt.cs b/DMS.Core/Models/VariableMqtt.cs new file mode 100644 index 0000000..1a758b2 --- /dev/null +++ b/DMS.Core/Models/VariableMqtt.cs @@ -0,0 +1,10 @@ +using DMS.Core.Models; + +namespace DMS.Core.Models; + +public class VariableMqtt +{ + public Variable Variable { get; set; } + public MqttServer Mqtt { get; set; } + public int MqttId { get; set; } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Entities/DbVariable.cs b/DMS.Infrastructure/Entities/DbVariable.cs index c69834f..3685fd5 100644 --- a/DMS.Infrastructure/Entities/DbVariable.cs +++ b/DMS.Infrastructure/Entities/DbVariable.cs @@ -1,5 +1,7 @@ -using SqlSugar; +using DMS.Core.Enums; +using SqlSugar; using SqlSugar.DbConvert; +using CSharpDataType = SqlSugar.CSharpDataType; namespace DMS.Infrastructure.Entities; diff --git a/DMS.Infrastructure/Helper/ExcelHelper.cs b/DMS.Infrastructure/Helper/ExcelHelper.cs new file mode 100644 index 0000000..0a22cac --- /dev/null +++ b/DMS.Infrastructure/Helper/ExcelHelper.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using DMS.Core.Enums; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; + +namespace DMS.Infrastructure.Helper; + +/// +/// Excel 操作帮助类 +/// +public static class ExcelHelper +{ + /// + /// 将数据列表导出到 Excel 文件。 + /// + /// 数据类型。 + /// 要导出的数据列表。 + /// Excel 文件的保存路径。 + /// 工作表的名称。 + public static void ExportToExcel(IEnumerable data, string filePath, string sheetName = "Sheet1") where T : class + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + IWorkbook workbook = new XSSFWorkbook(); + ISheet sheet = workbook.CreateSheet(sheetName); + + // 获取T类型的属性 + PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + + // 创建表头 + IRow headerRow = sheet.CreateRow(0); + for (int i = 0; i < properties.Length; i++) + { + headerRow.CreateCell(i).SetCellValue(properties[i].Name); + } + + // 填充数据 + int rowIndex = 1; + foreach (var item in data) + { + IRow dataRow = sheet.CreateRow(rowIndex++); + for (int i = 0; i < properties.Length; i++) + { + object value = properties[i].GetValue(item, null); + dataRow.CreateCell(i).SetCellValue(value?.ToString() ?? string.Empty); + } + } + + workbook.Write(fs); + } + } + + /// + /// 将 DataTable 导出到 Excel 文件。 + /// + /// 要导出的 DataTable。 + /// Excel 文件的保存路径。 + /// 工作表的名称。 + public static void ExportToExcel(DataTable dataTable, string filePath, string sheetName = "Sheet1") + { + if (dataTable == null) + { + throw new ArgumentNullException(nameof(dataTable)); + } + + using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) + { + IWorkbook workbook = new XSSFWorkbook(); + ISheet sheet = workbook.CreateSheet(sheetName); + + // 创建表头 + IRow headerRow = sheet.CreateRow(0); + for (int i = 0; i < dataTable.Columns.Count; i++) + { + headerRow.CreateCell(i).SetCellValue(dataTable.Columns[i].ColumnName); + } + + // 填充数据 + for (int i = 0; i < dataTable.Rows.Count; i++) + { + IRow dataRow = sheet.CreateRow(i + 1); + for (int j = 0; j < dataTable.Columns.Count; j++) + { + dataRow.CreateCell(j).SetCellValue(dataTable.Rows[i][j].ToString()); + } + } + + workbook.Write(fs); + } + } + + /// + /// 从 Excel 文件导入数据到 DataTable。 + /// + /// Excel 文件的路径。 + /// 工作表的名称。 + /// 是否包含表头行。 + /// 包含导入数据的 DataTable。 + public static DataTable ImportFromExcel(string filePath, string sheetName = "Sheet1", bool hasHeaderRow = true) + { + if (!File.Exists(filePath)) + { + throw new FileNotFoundException("The specified file does not exist.", filePath); + } + + var dt = new DataTable(); + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + IWorkbook workbook = new XSSFWorkbook(fs); + ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0); + + if (sheet == null) + { + throw new Exception($"Sheet with name '{sheetName}' not found."); + } + + IRow headerRow = hasHeaderRow ? sheet.GetRow(0) : null; + int firstRow = hasHeaderRow ? 1 : 0; + int cellCount = headerRow?.LastCellNum ?? sheet.GetRow(sheet.FirstRowNum).LastCellNum; + + // 创建列 + for (int i = 0; i < cellCount; i++) + { + string columnName = hasHeaderRow ? headerRow.GetCell(i)?.ToString() ?? $"Column{i + 1}" : $"Column{i + 1}"; + dt.Columns.Add(columnName); + } + + // 填充数据 + for (int i = firstRow; i <= sheet.LastRowNum; i++) + { + IRow row = sheet.GetRow(i); + if (row == null) continue; + + DataRow dataRow = dt.NewRow(); + for (int j = 0; j < cellCount; j++) + { + ICell cell = row.GetCell(j); + dataRow[j] = cell?.ToString() ?? string.Empty; + } + dt.Rows.Add(dataRow); + } + } + return dt; + } + + /// + /// 从 Excel 文件导入数据到对象列表。 + /// + /// 要转换的目标类型。 + /// Excel 文件的路径。 + /// 工作表的名称。 + /// 包含导入数据的对象列表。 + public static List ImportFromExcel(string filePath, string sheetName = "Sheet1") where T : class, new() + { + if (!File.Exists(filePath)) + { + throw new FileNotFoundException("The specified file does not exist.", filePath); + } + + var list = new List(); + PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + IWorkbook workbook = new XSSFWorkbook(fs); + ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0); + + if (sheet == null) + { + throw new Exception($"Sheet with name '{sheetName}' not found."); + } + + IRow headerRow = sheet.GetRow(0); + if (headerRow == null) + { + throw new Exception("Header row not found."); + } + + // 创建列名到属性的映射 + var columnMap = new Dictionary(); + for (int i = 0; i < headerRow.LastCellNum; i++) + { + string columnName = headerRow.GetCell(i)?.ToString(); + if (!string.IsNullOrEmpty(columnName)) + { + var prop = properties.FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase)); + if (prop != null) + { + columnMap[i] = prop; + } + } + } + + // 读取数据行 + for (int i = 1; i <= sheet.LastRowNum; i++) + { + IRow row = sheet.GetRow(i); + if (row == null) continue; + + var item = new T(); + foreach (var map in columnMap) + { + ICell cell = row.GetCell(map.Key); + if (cell != null) + { + try + { + // 尝试进行类型转换 + object value = Convert.ChangeType(cell.ToString(), map.Value.PropertyType); + map.Value.SetValue(item, value, null); + } + catch (Exception) + { + // 转换失败时可以记录日志或设置默认值 + } + } + } + list.Add(item); + } + } + return list; + } + + /// + /// 从博途的变量表中导如变量 + /// + /// + /// + /// + public static List ImprotFromTiaVariableTable(string excelFilePath) + { + // Act + // _testFilePath = "C:\Users\Administrator\Desktop\浓度变量.xlsx"; + var dataTable = ExcelHelper.ImportFromExcel(excelFilePath); + // 判断表头的名字 + if (dataTable.Columns[0].ColumnName != "Name" || dataTable.Columns[2].ColumnName != "Data Type" && + dataTable.Columns[3].ColumnName != "Logical Address") + throw new AggregateException( + "Excel表格式不正确:第一列的名字是:Name,第三列的名字是:Data Type,Data Type,第四列的名字是:Logical Address,请检查"); + + + List variableDatas = new List(); + foreach (DataRow dataRow in dataTable.Rows) + { + DMS.Core.Models.Variable variable = new DMS.Core.Models.Variable(); + variable.Name = dataRow["Name"].ToString(); + variable.DataType = (DMS.Core.Enums.SignalType)Enum.Parse(typeof(DMS.Core.Enums.SignalType), SiemensHelper.S7ToCSharpTypeString(dataRow["Data Type"].ToString())); + var exS7Addr = dataRow["Logical Address"].ToString(); + if (exS7Addr.StartsWith("%")) + { + variable.S7Address = exS7Addr.Substring(1); + } + + variable.OpcUaNodeId = ""; + variable.Protocol = ProtocolType.S7; + variable.PollLevel = PollLevelType.ThirtySeconds; + variableDatas.Add(variable); + } + + return variableDatas; + } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Helper/SiemensHelper.cs b/DMS.Infrastructure/Helper/SiemensHelper.cs new file mode 100644 index 0000000..7afb65e --- /dev/null +++ b/DMS.Infrastructure/Helper/SiemensHelper.cs @@ -0,0 +1,70 @@ +namespace DMS.Infrastructure.Helper; + +/// +/// 西门子帮助类 +/// +public static class SiemensHelper +{ + /// + /// 将S7数据类型字符串转换为C#数据类型字符串 + /// + /// S7数据类型字符串 + /// 对应的C#数据类型字符串 + public static string S7ToCSharpTypeString(string s7Type) + { + switch (s7Type.ToUpper()) + { + case "BOOL": + return "bool"; + case "BYTE": + return "byte"; + case "WORD": + return "ushort"; + case "DWORD": + return "uint"; + case "INT": + return "short"; + case "DINT": + return "int"; + case "REAL": + return "float"; + case "LREAL": + return "double"; + case "CHAR": + return "char"; + case "STRING": + return "string"; + case "TIMER": + case "TIME": + return "TimeSpan"; + case "COUNTER": + return "ushort"; + case "DATE": + return "DateTime"; + case "TIME_OF_DAY": + case "TOD": + return "DateTime"; + case "DATE_AND_TIME": + case "DT": + return "DateTime"; + default: + return "object"; + } + } + + /// + /// 将S7读取到的值转换为显示值 + /// + /// S7读取到的原始值 + /// 变量的数据类型 + /// 转换规则 + /// 显示值 + public static string ConvertS7Value(object value, string dataType, string conversion) + { + if (value == null) return string.Empty; + + // For now, a simple conversion to string. More complex logic can be added here. + // Based on dataType and conversion, you might parse, format, or apply formulas. + return value.ToString(); + } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Services/MqttBackgroundService.cs b/DMS.Infrastructure/Services/MqttBackgroundService.cs new file mode 100644 index 0000000..6c36991 --- /dev/null +++ b/DMS.Infrastructure/Services/MqttBackgroundService.cs @@ -0,0 +1,347 @@ +using System.Collections.Concurrent; +using System.Text; +using System.Threading.Channels; +using DMS.Core.Models; +using Microsoft.Extensions.Hosting; +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Client.Connecting; +using MQTTnet.Client.Disconnecting; +using MQTTnet.Client.Options; +using Microsoft.Extensions.Logging; +using DMS.Application.Interfaces; +using DMS.Core.Interfaces; + +namespace DMS.Infrastructure.Services; + +/// +/// MQTT后台服务,继承自BackgroundService,用于在后台管理MQTT连接和数据发布。 +/// +public class MqttBackgroundService : BackgroundService +{ + private readonly IDeviceDataService _deviceDataService; + private readonly IRepositoryManager _repositoryManager; + private readonly ILogger _logger; + + private readonly ConcurrentDictionary _mqttClients; + private readonly ConcurrentDictionary _mqttConfigDic; + private readonly ConcurrentDictionary _reconnectAttempts; + + private readonly SemaphoreSlim _reloadSemaphore = new(0); + private readonly Channel _messageChannel; + + /// + /// 构造函数,注入DataServices。 + /// + public MqttBackgroundService(IDeviceDataService deviceDataService, IRepositoryManager repositoryManager, ILogger logger) + { + _deviceDataService = deviceDataService; + _repositoryManager = repositoryManager; + _logger = logger; + _mqttClients = new ConcurrentDictionary(); + _mqttConfigDic = new ConcurrentDictionary(); + _reconnectAttempts = new ConcurrentDictionary(); + _messageChannel = Channel.CreateUnbounded(); + + // _deviceDataService.OnMqttListChanged += HandleMqttListChanged; + } + + /// + /// 将待发送的变量数据异步推入队列。 + /// + /// 包含MQTT别名和变量数据的对象。 + public async Task SendVariableAsync(VariableMqtt data) + { + await _messageChannel.Writer.WriteAsync(data); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("Mqtt后台服务正在启动。"); + _reloadSemaphore.Release(); + + var processQueueTask = ProcessMessageQueueAsync(stoppingToken); + + try + { + while (!stoppingToken.IsCancellationRequested) + { + await _reloadSemaphore.WaitAsync(stoppingToken); + + if (stoppingToken.IsCancellationRequested) break; + + // if (_deviceDataService.Mqtts == null || _deviceDataService.Mqtts.Count == 0) + // { + // _logger.LogInformation("没有可用的Mqtt配置,等待Mqtt列表更新..."); + // continue; + // } + + if (!LoadMqttConfigurations()) + { + _logger.LogInformation("加载Mqtt配置过程中发生了错误,停止后面的操作。"); + continue; + } + + await ConnectMqttList(stoppingToken); + _logger.LogInformation("Mqtt后台服务已启动。"); + + while (!stoppingToken.IsCancellationRequested && _reloadSemaphore.CurrentCount == 0) + { + await Task.Delay(1000, stoppingToken); + } + } + } + catch (OperationCanceledException) + { + _logger.LogInformation("Mqtt后台服务正在停止。"); + } + catch (Exception e) + { + _logger.LogError(e, $"Mqtt后台服务运行中发生了错误:{e.Message}"); + } + finally + { + _messageChannel.Writer.Complete(); + await processQueueTask; // 等待消息队列处理完成 + await DisconnectAll(stoppingToken); + // _deviceDataService.OnMqttListChanged -= HandleMqttListChanged; + _logger.LogInformation("Mqtt后台服务已停止。"); + } + } + + private async Task ProcessMessageQueueAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("MQTT消息发送队列处理器已启动。"); + var batch = new List(); + var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); + + while (!stoppingToken.IsCancellationRequested) + { + try + { + // 等待信号:要么是新消息到达,要么是1秒定时器触发 + await Task.WhenAny( + _messageChannel.Reader.WaitToReadAsync(stoppingToken).AsTask(), + timer.WaitForNextTickAsync(stoppingToken).AsTask() + ); + + // 尽可能多地读取消息,直到达到批次上限 + while (batch.Count < 50 && _messageChannel.Reader.TryRead(out var message)) + { + batch.Add(message); + } + + if (batch.Any()) + { + await SendBatchAsync(batch, stoppingToken); + batch.Clear(); + } + } + catch (OperationCanceledException) + { + _logger.LogInformation("MQTT消息发送队列处理器已停止。"); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, $"处理MQTT消息队列时发生错误: {ex.Message}"); + await Task.Delay(5000, stoppingToken); // 发生未知错误时,延迟一段时间再重试 + } + } + } + + private async Task SendBatchAsync(List batch, CancellationToken stoppingToken) + { + _logger.LogInformation($"准备发送一批 {batch.Count} 条MQTT消息。"); + // 按MQTT服务器ID进行分组 + var groupedByMqtt = batch.GroupBy(vm => vm.Mqtt.Id); + + foreach (var group in groupedByMqtt) + { + var mqttId = group.Key; + if (!_mqttClients.TryGetValue(mqttId, out var client) || !client.IsConnected) + { + _logger.LogWarning($"MQTT客户端 (ID: {mqttId}) 未连接或不存在,跳过 {group.Count()} 条消息。"); + continue; + } + + var messages = group.Select(vm => new MqttApplicationMessageBuilder() + .WithTopic(vm.Mqtt.PublishTopic) + .WithPayload(vm.Variable?.DataValue?.ToString() ?? string.Empty) + .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce) + .Build()) + .ToList(); + try + { + foreach (var message in messages) + { + await client.PublishAsync(message, stoppingToken); + } + _logger.LogInformation($"成功向MQTT客户端 (ID: {mqttId}) 发送 {messages.Count} 条消息。"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"向MQTT客户端 (ID: {mqttId}) 批量发送消息时发生错误: {ex.Message}"); + } + } + } + + private async Task DisconnectAll(CancellationToken stoppingToken) + { + var disconnectTasks = _mqttClients.Values.Select(client => client.DisconnectAsync(new MqttClientDisconnectOptions(), stoppingToken)); + await Task.WhenAll(disconnectTasks); + _mqttClients.Clear(); + } + + private bool LoadMqttConfigurations() + { + try + { + _logger.LogInformation("开始加载Mqtt配置文件..."); + _mqttConfigDic.Clear(); + // var mqttConfigList = _deviceDataService.Mqtts.Where(m => m.IsActive).ToList(); + // + // foreach (var mqtt in mqttConfigList) + // { + // // mqtt.OnMqttIsActiveChanged += OnMqttIsActiveChangedHandler; // 移除此行,因为MqttServer没有这个事件 + // _mqttConfigDic.TryAdd(mqtt.Id, mqtt); + // // mqtt.ConnectMessage = "配置加载成功."; // 移除此行,因为MqttServer没有这个属性 + // } + // + // _logger.LogInformation($"Mqtt配置文件加载成功,开启的Mqtt客户端:{mqttConfigList.Count}个。"); + return true; + } + catch (Exception e) + { + _logger.LogError(e, $"Mqtt后台服务在加载配置的过程中发生了错误:{e.Message}"); + return false; + } + } + + // private async void OnMqttIsActiveChangedHandler(MqttServer mqtt) // 移除此方法,因为MqttServer没有这个事件 + // { + // try + // { + // if (mqtt.IsActive) + // { + // await ConnectMqtt(mqtt, CancellationToken.None); + // } + // else + // { + // if (_mqttClients.TryRemove(mqtt.Id, out var client) && client.IsConnected) + // { + // await client.DisconnectAsync(); + // _logger.LogInformation($"{mqtt.Name}的客户端,与服务器断开连接."); + // } + // mqtt.IsConnected = false; + // mqtt.ConnectMessage = "已断开连接."; + // } + // + // await _repositoryManager.MqttServers.UpdateAsync(mqtt); + // _logger.LogInformation($"Mqtt客户端:{mqtt.Name},激活状态修改成功。"); + // } + // catch (Exception e) + // { + // _logger.LogError(e, $"{mqtt.Name}客户端,开启或关闭的过程中发生了错误:{e.Message}"); + // } + // } + + private async Task ConnectMqttList(CancellationToken stoppingToken) + { + var connectTasks = _mqttConfigDic.Values.Select(mqtt => ConnectMqtt(mqtt, stoppingToken)); + await Task.WhenAll(connectTasks); + } + + private async Task ConnectMqtt(MqttServer mqtt, CancellationToken stoppingToken) + { + if (_mqttClients.TryGetValue(mqtt.Id, out var existingClient) && existingClient.IsConnected) + { + _logger.LogInformation($"{mqtt.ServerName}的Mqtt服务器连接已存在。"); + return; + } + + _logger.LogInformation($"开始连接:{mqtt.ServerName}的服务器..."); + // mqtt.ConnectMessage = "开始连接服务器..."; // 移除此行,因为MqttServer没有这个属性 + + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + + var options = new MqttClientOptionsBuilder() + .WithClientId(mqtt.ClientId) + .WithTcpServer(mqtt.BrokerAddress, mqtt.Port) + .WithCredentials(mqtt.Username, mqtt.Password) + .WithCleanSession() + .Build(); + + client.UseConnectedHandler(async e => await HandleConnected(e, client, mqtt)); + client.UseApplicationMessageReceivedHandler(e => HandleMessageReceived(e, mqtt)); + client.UseDisconnectedHandler(async e => await HandleDisconnected(e, options, client, mqtt, stoppingToken)); + + try + { + await client.ConnectAsync(options, stoppingToken); + _mqttClients.AddOrUpdate(mqtt.Id, client, (id, oldClient) => client); + } + catch (Exception ex) + { + // mqtt.ConnectMessage = $"连接MQTT服务器失败: {ex.Message}"; // 移除此行,因为MqttServer没有这个属性 + _logger.LogError(ex, $"连接MQTT服务器失败: {mqtt.ServerName}"); + } + } + + private static void HandleMessageReceived(MqttApplicationMessageReceivedEventArgs e, MqttServer mqtt) + { + var topic = e.ApplicationMessage.Topic; + var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + // _logger.LogInformation($"MQTT客户端 {mqtt.ServerName} 收到消息: 主题={topic}, 消息={payload}"); + } + + private async Task HandleDisconnected(MqttClientDisconnectedEventArgs args, IMqttClientOptions options, IMqttClient client, MqttServer mqtt, CancellationToken stoppingToken) + { + _logger.LogWarning($"与MQTT服务器断开连接: {mqtt.ServerName}"); + // mqtt.ConnectMessage = "断开连接."; // 移除此行,因为MqttServer没有这个属性 + // mqtt.IsConnected = false; // 移除此行,因为MqttServer没有这个属性 + + if (stoppingToken.IsCancellationRequested || !mqtt.IsActive) return; + + _reconnectAttempts.AddOrUpdate(mqtt.Id, 1, (id, count) => count + 1); + var attempt = _reconnectAttempts[mqtt.Id]; + + var delay = TimeSpan.FromSeconds(Math.Min(60, Math.Pow(2, attempt))); + _logger.LogInformation($"与MQTT服务器:{mqtt.ServerName} 的连接已断开。将在 {delay.TotalSeconds} 秒后尝试第 {attempt} 次重新连接..."); + // mqtt.ConnectMessage = $"连接已断开,{delay.TotalSeconds}秒后尝试重连..."; // 移除此行,因为MqttServer没有这个属性 + + await Task.Delay(delay, stoppingToken); + + try + { + // mqtt.ConnectMessage = "开始重新连接服务器..."; // 移除此行,因为MqttServer没有这个属性 + await client.ConnectAsync(options, stoppingToken); + } + catch (Exception ex) + { + // mqtt.ConnectMessage = "重新连接失败."; // 移除此行,因为MqttServer没有这个属性 + _logger.LogError(ex, $"重新与Mqtt服务器连接失败: {mqtt.ServerName}"); + } + } + + private async Task HandleConnected(MqttClientConnectedEventArgs args, IMqttClient client, MqttServer mqtt) + { + _reconnectAttempts.TryRemove(mqtt.Id, out _); + _logger.LogInformation($"已连接到MQTT服务器: {mqtt.ServerName}"); + // mqtt.IsConnected = true; // 移除此行,因为MqttServer没有这个属性 + // mqtt.ConnectMessage = "连接成功."; // 移除此行,因为MqttServer没有这个属性 + + if (!string.IsNullOrEmpty(mqtt.SubscribeTopic)) + { + await client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(mqtt.SubscribeTopic).Build()); + _logger.LogInformation($"MQTT客户端 {mqtt.ServerName} 已订阅主题: {mqtt.SubscribeTopic}"); + } + } + + private void HandleMqttListChanged(List mqtts) + { + _logger.LogInformation("Mqtt列表发生了变化,正在重新加载数据..."); + _reloadSemaphore.Release(); + } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Services/OpcUaBackgroundService.cs b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs new file mode 100644 index 0000000..3f09aae --- /dev/null +++ b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs @@ -0,0 +1,585 @@ +using System.Collections.Concurrent; +using DMS.Core.Enums; +using DMS.Core.Models; +using Microsoft.Extensions.Hosting; +using Opc.Ua; +using Opc.Ua.Client; +using Microsoft.Extensions.Logging; +using DMS.Application.Interfaces; +using DMS.Core.Interfaces; + +namespace DMS.Infrastructure.Services; + +public class OpcUaBackgroundService : BackgroundService +{ + private readonly IDeviceDataService _deviceDataService; + private readonly IDataProcessingService _dataProcessingService; + private readonly ILogger _logger; + + // 存储 OPC UA 设备,键为设备Id,值为会话对象。 + private readonly ConcurrentDictionary _opcUaDevices; + + // 存储 OPC UA 会话,键为终结点 URL,值为会话对象。 + private readonly ConcurrentDictionary _opcUaSessions; + + // 存储 OPC UA 订阅,键为终结点 URL,值为订阅对象。 + private readonly ConcurrentDictionary _opcUaSubscriptions; + + // 存储活动的 OPC UA 变量,键为变量的OpcNodeId + private readonly ConcurrentDictionary _opcUaPollVariablesByNodeId; + + // 储存所有要轮询更新的变量,键是Device.Id,值是这个设备所有要轮询的变量 + private readonly ConcurrentDictionary> _opcUaPollVariablesByDeviceId; + + // 储存所有要订阅更新的变量,键是Device.Id,值是这个设备所有要轮询的变量 + private readonly ConcurrentDictionary> _opcUaSubVariablesByDeviceId; + + private readonly SemaphoreSlim _reloadSemaphore = new SemaphoreSlim(0); + + // OPC UA 轮询间隔(毫秒) + private readonly int _opcUaPollIntervalMs = 100; + + // OPC UA 订阅发布间隔(毫秒) + private readonly int _opcUaSubscriptionPublishingIntervalMs = 1000; + + // OPC UA 订阅采样间隔(毫秒) + private readonly int _opcUaSubscriptionSamplingIntervalMs = 1000; + + // 模拟 PollingIntervals,实际应用中可能从配置或数据库加载 + private static readonly Dictionary PollingIntervals = new Dictionary + { + { PollLevelType.TenMilliseconds, TimeSpan.FromMilliseconds((int)PollLevelType.TenMilliseconds) }, + { PollLevelType.HundredMilliseconds, TimeSpan.FromMilliseconds((int)PollLevelType.HundredMilliseconds) }, + { PollLevelType.FiveHundredMilliseconds, TimeSpan.FromMilliseconds((int)PollLevelType.FiveHundredMilliseconds) }, + { PollLevelType.OneSecond, TimeSpan.FromMilliseconds((int)PollLevelType.OneSecond) }, + { PollLevelType.FiveSeconds, TimeSpan.FromMilliseconds((int)PollLevelType.FiveSeconds) }, + { PollLevelType.TenSeconds, TimeSpan.FromMilliseconds((int)PollLevelType.TenSeconds) }, + { PollLevelType.TwentySeconds, TimeSpan.FromMilliseconds((int)PollLevelType.TwentySeconds) }, + { PollLevelType.ThirtySeconds, TimeSpan.FromMilliseconds((int)PollLevelType.ThirtySeconds) }, + { PollLevelType.OneMinute, TimeSpan.FromMilliseconds((int)PollLevelType.OneMinute) }, + { PollLevelType.ThreeMinutes, TimeSpan.FromMilliseconds((int)PollLevelType.ThreeMinutes) }, + { PollLevelType.FiveMinutes, TimeSpan.FromMilliseconds((int)PollLevelType.FiveMinutes) }, + { PollLevelType.TenMinutes, TimeSpan.FromMilliseconds((int)PollLevelType.TenMinutes) }, + { PollLevelType.ThirtyMinutes, TimeSpan.FromMilliseconds((int)PollLevelType.ThirtyMinutes) } + }; + + public OpcUaBackgroundService(IDeviceDataService deviceDataService, IDataProcessingService dataProcessingService, ILogger logger) + { + _deviceDataService = deviceDataService; + _dataProcessingService = dataProcessingService; + _logger = logger; + _opcUaDevices = new ConcurrentDictionary(); + _opcUaSessions = new ConcurrentDictionary(); + _opcUaSubscriptions = new ConcurrentDictionary(); + _opcUaPollVariablesByNodeId = new ConcurrentDictionary(); + _opcUaPollVariablesByDeviceId = new ConcurrentDictionary>(); + _opcUaSubVariablesByDeviceId = new ConcurrentDictionary>(); + + _deviceDataService.OnDeviceListChanged += HandleDeviceListChanged; + _deviceDataService.OnDeviceIsActiveChanged += HandleDeviceIsActiveChanged; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("OPC UA 后台服务正在启动。"); + _reloadSemaphore.Release(); // Initial trigger to load variables and connect + + try + { + while (!stoppingToken.IsCancellationRequested) + { + await _reloadSemaphore.WaitAsync(stoppingToken); // Wait for a reload signal + + if (stoppingToken.IsCancellationRequested) + { + break; + } + + if (_deviceDataService.Devices == null || _deviceDataService.Devices.Count == 0) + { + _logger.LogInformation("没有可用的OPC UA设备,等待设备列表更新..."); + continue; + } + + var isLoaded = LoadVariables(); + if (!isLoaded) + { + _logger.LogInformation("加载变量过程中发生了错误,停止后面的操作。"); + continue; + } + + await ConnectOpcUaServiceAsync(stoppingToken); + await SetupOpcUaSubscriptionAsync(stoppingToken); + _logger.LogInformation("OPC UA 后台服务已启动。"); + + // 持续轮询,直到取消请求或需要重新加载 + while (!stoppingToken.IsCancellationRequested && _reloadSemaphore.CurrentCount == 0) + { + await PollOpcUaVariableOnceAsync(stoppingToken); + await Task.Delay(_opcUaPollIntervalMs, stoppingToken); + } + } + } + catch (OperationCanceledException) + { + _logger.LogInformation("OPC UA 后台服务已停止。"); + } + catch (Exception e) + { + _logger.LogError(e, $"OPC UA 后台服务运行中发生了错误:{e.Message}"); + } + finally + { + await DisconnectAllOpcUaSessionsAsync(); + _deviceDataService.OnDeviceListChanged -= HandleDeviceListChanged; + _deviceDataService.OnDeviceIsActiveChanged -= HandleDeviceIsActiveChanged; + } + } + + private void HandleDeviceListChanged(List devices) + { + _logger.LogInformation("设备列表已更改。OPC UA 客户端可能需要重新初始化。"); + _reloadSemaphore.Release(); // 触发ExecuteAsync中的全面重新加载 + } + + private async void HandleDeviceIsActiveChanged(Device device, bool isActive) + { + if (device.Protocol != ProtocolType.OpcUA) + return; + + _logger.LogInformation($"设备 {device.Name} (ID: {device.Id}) 的IsActive状态改变为 {isActive}。"); + + if (!isActive) + { + // 设备变为非活动状态,断开连接 + if (_opcUaSessions.TryRemove(device.OpcUaServerUrl, out var session)) + { + try + { + if (_opcUaSubscriptions.TryRemove(device.OpcUaServerUrl, out var subscription)) + { + // 删除订阅。 + await subscription.DeleteAsync(true); + _logger.LogInformation($"已删除设备 {device.Name} ({device.OpcUaServerUrl}) 的订阅。"); + } + + if (session.Connected) + { + await session.CloseAsync(); + _logger.LogInformation($"已断开设备 {device.Name} ({device.OpcUaServerUrl}) 的连接。"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"断开设备 {device.Name} ({device.OpcUaServerUrl}) 连接时发生错误:{ex.Message}"); + } + } + } + + // 触发重新加载,让LoadVariables和ConnectOpcUaServiceAsync处理设备列表的更新 + _reloadSemaphore.Release(); + } + + /// + /// 从数据库加载所有活动的 OPC UA 变量,并进行相应的连接和订阅管理。 + /// + private bool LoadVariables() + { + try + { + _opcUaDevices.Clear(); + _opcUaPollVariablesByDeviceId.Clear(); + _opcUaSubVariablesByDeviceId.Clear(); + _opcUaPollVariablesByNodeId.Clear(); + + _logger.LogInformation("开始加载OPC UA变量...."); + var opcUaDevices = _deviceDataService + .Devices.Where(d => d.Protocol == ProtocolType.OpcUA && d.IsActive == true) + .ToList(); + + if (opcUaDevices.Count == 0) + { + _logger.LogInformation("没有找到活动的OPC UA设备。"); + return true; // No active devices, but not an error + } + + int totalPollVariableCount = 0; + int totalSubVariableCount = 0; + + foreach (var opcUaDevice in opcUaDevices) + { + _opcUaDevices.AddOrUpdate(opcUaDevice.Id, opcUaDevice, (key, oldValue) => opcUaDevice); + + //查找设备中所有要轮询的变量 + var dPollList = opcUaDevice.VariableTables?.SelectMany(vt => vt.Variables) + .Where(vd => vd.IsActive == true && + vd.Protocol == ProtocolType.OpcUA && + vd.OpcUaUpdateType == OpcUaUpdateType.OpcUaPoll) + .ToList(); + // 将变量保存到字典中,方便Read后还原 + foreach (var variable in dPollList) + { + _opcUaPollVariablesByNodeId.AddOrUpdate(variable.OpcUaNodeId, variable, + (key, oldValue) => variable); + } + + totalPollVariableCount += dPollList.Count; + _opcUaPollVariablesByDeviceId.AddOrUpdate(opcUaDevice.Id, dPollList, (key, oldValue) => dPollList); + + //查找设备中所有要订阅的变量 + var dSubList = opcUaDevice.VariableTables?.SelectMany(vt => vt.Variables) + .Where(vd => vd.IsActive == true && + vd.Protocol == ProtocolType.OpcUA && + vd.OpcUaUpdateType == OpcUaUpdateType.OpcUaSubscription) + .ToList(); + totalSubVariableCount += dSubList.Count; + _opcUaSubVariablesByDeviceId.AddOrUpdate(opcUaDevice.Id, dSubList, (key, oldValue) => dSubList); + } + + _logger.LogInformation( + $"OPC UA 变量加载成功,共加载OPC UA设备:{opcUaDevices.Count}个,轮询变量数:{totalPollVariableCount},订阅变量数:{totalSubVariableCount}"); + return true; + } + catch (Exception e) + { + _logger.LogError(e, $"加载OPC UA变量的过程中发生了错误:{e.Message}"); + return false; + } + } + + /// + /// 连接到 OPC UA 服务器并订阅或轮询指定的变量。 + /// + private async Task ConnectOpcUaServiceAsync(CancellationToken stoppingToken) + { + if (stoppingToken.IsCancellationRequested) + { + return; + } + + var connectTasks = new List(); + + // 遍历_opcUaDevices中的所有设备,尝试连接 + foreach (var device in _opcUaDevices.Values.ToList()) + { + connectTasks.Add(ConnectSingleOpcUaDeviceAsync(device, stoppingToken)); + } + + await Task.WhenAll(connectTasks); + } + + /// + /// 连接单个OPC UA设备。 + /// + /// 要连接的设备。 + /// 取消令牌。 + private async Task ConnectSingleOpcUaDeviceAsync(Device device, CancellationToken stoppingToken = default) + { + if (stoppingToken.IsCancellationRequested) + { + return; + } + + // Check if already connected + if (_opcUaSessions.TryGetValue(device.OpcUaServerUrl, out var existingSession)) + { + if (existingSession.Connected) + { + _logger.LogInformation($"已连接到 OPC UA 服务器: {device.OpcUaServerUrl}"); + return; + } + else + { + // Remove disconnected session from dictionary to attempt reconnection + _opcUaSessions.TryRemove(device.OpcUaServerUrl, out _); + } + } + + _logger.LogInformation($"开始连接OPC UA服务器: {device.Name} ({device.OpcUaServerUrl})"); + try + { + var session = await OpcUaHelper.CreateOpcUaSessionAsync(device.OpcUaServerUrl, stoppingToken); + if (session == null) + { + _logger.LogWarning($"创建OPC UA会话失败: {device.OpcUaServerUrl}"); + return; // 连接失败,直接返回 + } + + _opcUaSessions.AddOrUpdate(device.OpcUaServerUrl, session, (key, oldValue) => session); + _logger.LogInformation($"已连接到OPC UA服务器: {device.Name} ({device.OpcUaServerUrl})"); + } + catch (Exception e) + { + _logger.LogError(e, $"OPC UA服务连接 {device.Name} ({device.OpcUaServerUrl}) 过程中发生错误:{e.Message}"); + } + } + + private async Task PollOpcUaVariableOnceAsync(CancellationToken stoppingToken) + { + try + { + var deviceIdsToPoll = _opcUaPollVariablesByDeviceId.Keys.ToList(); + + var pollingTasks = deviceIdsToPoll.Select(deviceId => PollSingleDeviceVariablesAsync(deviceId, stoppingToken)).ToList(); + + await Task.WhenAll(pollingTasks); + } + catch (OperationCanceledException) + { + _logger.LogInformation("OPC UA 后台服务轮询变量被取消。"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"OPC UA 后台服务在轮询变量过程中发生错误:{ex.Message}"); + } + } + + /// + /// 轮询单个设备的所有 OPC UA 变量。 + /// + /// 设备的 ID。 + /// 取消令牌。 + private async Task PollSingleDeviceVariablesAsync(int deviceId, CancellationToken stoppingToken) + { + if (stoppingToken.IsCancellationRequested) return; + + if (!_opcUaDevices.TryGetValue(deviceId, out var device) || device.OpcUaServerUrl == null) + { + _logger.LogWarning($"OpcUa轮询变量时,在deviceDic中未找到ID为 {deviceId} 的设备,或其服务器地址为空,请检查!"); + return; + } + + if (!device.IsActive) return; + + if (!_opcUaSessions.TryGetValue(device.OpcUaServerUrl, out var session) || !session.Connected) + { + if (device.IsActive) + { + _logger.LogWarning($"用于 {device.OpcUaServerUrl} 的 OPC UA 会话未连接。正在尝试重新连接..."); + await ConnectSingleOpcUaDeviceAsync(device, stoppingToken); + } + return; + } + + if (!_opcUaPollVariablesByDeviceId.TryGetValue(deviceId, out var variableList) || variableList.Count == 0) + { + return; + } + + foreach (var variable in variableList) + { + if (stoppingToken.IsCancellationRequested) return; + + if (!PollingIntervals.TryGetValue(variable.PollLevelType, out var interval) || (DateTime.Now - variable.UpdateTime) < interval) + { + continue; + } + + await ReadAndProcessOpcUaVariableAsync(session, variable, stoppingToken); + } + } + + /// + /// 读取单个 OPC UA 变量并处理其数据。 + /// + /// OPC UA 会话。 + /// 要读取的变量。 + /// 取消令牌。 + private async Task ReadAndProcessOpcUaVariableAsync(Session session, Variable variable, CancellationToken stoppingToken) + { + var nodesToRead = new ReadValueIdCollection + { + new ReadValueId + { + NodeId = new NodeId(variable.OpcUaNodeId), + AttributeId = Attributes.Value + } + }; + + try + { + var readResponse = await session.ReadAsync(null, 0, TimestampsToReturn.Both, nodesToRead, stoppingToken); + var result = readResponse.Results?.FirstOrDefault(); + if (result == null) return; + + if (!StatusCode.IsGood(result.StatusCode)) + { + _logger.LogWarning($"读取 OPC UA 变量 {variable.Name} ({variable.OpcUaNodeId}) 失败: {result.StatusCode}"); + return; + } + + await UpdateAndEnqueueVariable(variable, result.Value); + } + catch (ServiceResultException ex) when (ex.StatusCode == StatusCodes.BadSessionIdInvalid) + { + _logger.LogError(ex, $"OPC UA会话ID无效,变量: {variable.Name} ({variable.OpcUaNodeId})。正在尝试重新连接..."); + // Assuming device can be retrieved from variable or passed as parameter if needed for ConnectSingleOpcUaDeviceAsync + // For now, I'll just log and let the outer loop handle reconnection if the session is truly invalid for the device. + // If a full device object is needed here, it would need to be passed down from PollSingleDeviceVariablesAsync. + // For simplicity, I'll remove the direct reconnection attempt here and rely on the outer loop. + // await ConnectSingleOpcUaDeviceAsync(variable.VariableTable.Device, stoppingToken); + } + catch (Exception ex) + { + _logger.LogError(ex, $"轮询OPC UA变量 {variable.Name} ({variable.OpcUaNodeId}) 时发生未知错误: {ex.Message}"); + } + } + + /// + /// 更新变量数据,并将其推送到数据处理队列。 + /// + /// 要更新的变量。 + /// 读取到的数据值。 + private async Task UpdateAndEnqueueVariable(Variable variable, object value) + { + try + { + // 更新变量的原始数据值和显示值。 + variable.DataValue = value.ToString(); + variable.DisplayValue = value.ToString(); // 或者根据需要进行格式化 + variable.UpdateTime = DateTime.Now; + // Console.WriteLine($"OpcUa后台服务轮询变量:{variable.Name},值:{variable.DataValue}"); + // 将更新后的数据推入处理队列。 + await _dataProcessingService.EnqueueAsync(variable); + } + catch (Exception ex) + { + _logger.LogError(ex, $"更新变量 {variable.Name} 并入队失败:{ex.Message}"); + } + } + + /// + /// 设置 OPC UA 订阅并添加监控项。 + /// + /// 取消令牌。 + private async Task SetupOpcUaSubscriptionAsync(CancellationToken stoppingToken) + { + if (stoppingToken.IsCancellationRequested) + { + return; + } + + var setupSubscriptionTasks = new List(); + + foreach (var deviceId in _opcUaSubVariablesByDeviceId.Keys.ToList()) + { + setupSubscriptionTasks.Add(Task.Run(async () => + { + if (stoppingToken.IsCancellationRequested) + { + return; // 任务被取消,退出循环 + } + + var device = _deviceDataService.Devices.FirstOrDefault(d => d.Id == deviceId); + if (device == null) + { + _logger.LogWarning($"未找到ID为 {deviceId} 的设备,无法设置订阅。"); + return; + } + + Subscription subscription = null; + // 得到session + if (!_opcUaSessions.TryGetValue(device.OpcUaServerUrl, out var session)) + { + _logger.LogInformation($"从OpcUa会话字典中获取会话失败: {device.OpcUaServerUrl} "); + return; + } + + // 判断设备是否已经添加了订阅 + if (_opcUaSubscriptions.TryGetValue(device.OpcUaServerUrl, out subscription)) + { + _logger.LogInformation($"OPC UA 终结点 {device.OpcUaServerUrl} 已存在订阅。"); + } + else + { + subscription = new Subscription(session.DefaultSubscription); + subscription.PublishingInterval = _opcUaSubscriptionPublishingIntervalMs; // 发布间隔(毫秒) + session.AddSubscription(subscription); + subscription.Create(); + _opcUaSubscriptions.AddOrUpdate(device.OpcUaServerUrl, subscription, + (key, oldValue) => subscription); + } + + // 将变量添加到订阅 + if (_opcUaSubVariablesByDeviceId.TryGetValue(deviceId, out var variablesToSubscribe)) + { + foreach (Variable variable in variablesToSubscribe) + { + // 7. 创建监控项并添加到订阅中。 + MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem); + monitoredItem.DisplayName = variable.Name; + monitoredItem.StartNodeId = new NodeId(variable.OpcUaNodeId); // 设置要监控的节点 ID + monitoredItem.AttributeId = Attributes.Value; // 监控节点的值属性 + monitoredItem.SamplingInterval = _opcUaSubscriptionSamplingIntervalMs; // 采样间隔(毫秒) + monitoredItem.QueueSize = 1; // 队列大小 + monitoredItem.DiscardOldest = true; // 丢弃最旧的数据 + // 注册数据变化通知事件。 + monitoredItem.Notification += (sender, e) => OnSubNotification(variable, monitoredItem, e); + + subscription.AddItem(monitoredItem); + } + + subscription.ApplyChanges(); // 应用更改 + _logger.LogInformation($"设备: {device.Name}, 添加了 {variablesToSubscribe.Count} 个订阅变量。"); + } + })); + } + + await Task.WhenAll(setupSubscriptionTasks); + } + + /// + /// 订阅变量变化的通知 + /// + /// 发生变化的变量。 + /// + /// + private async void OnSubNotification(Variable variable, MonitoredItem monitoredItem, + MonitoredItemNotificationEventArgs e) + { + + foreach (var value in monitoredItem.DequeueValues()) + { + _logger.LogInformation( + $"[OPC UA 通知] {monitoredItem.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}"); + if (StatusCode.IsGood(value.StatusCode)) + { + await UpdateAndEnqueueVariable(variable, value.Value); + } + } + } + + /// + /// 断开所有 OPC UA 会话。 + /// + private async Task DisconnectAllOpcUaSessionsAsync() + { + if (_opcUaSessions.IsEmpty) + return; + + _logger.LogInformation("正在断开所有 OPC UA 会话..."); + var closeTasks = new List(); + + foreach (var endpointUrl in _opcUaSessions.Keys.ToList()) + { + closeTasks.Add(Task.Run(async () => + { + _logger.LogInformation($"正在断开 OPC UA 会话: {endpointUrl}"); + if (_opcUaSessions.TryRemove(endpointUrl, out var session)) + { + if (_opcUaSubscriptions.TryRemove(endpointUrl, out var subscription)) + { + // 删除订阅。 + await subscription.DeleteAsync(true); + } + + // 关闭会话。 + await session.CloseAsync(); + _logger.LogInformation($"已从 OPC UA 服务器断开连接: {endpointUrl}"); + } + })); + } + + await Task.WhenAll(closeTasks); + } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Services/OpcUaHelper.cs b/DMS.Infrastructure/Services/OpcUaHelper.cs new file mode 100644 index 0000000..b896adf --- /dev/null +++ b/DMS.Infrastructure/Services/OpcUaHelper.cs @@ -0,0 +1,85 @@ +using Opc.Ua; +using Opc.Ua.Client; +using Opc.Ua.Configuration; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace DMS.Infrastructure.Services; + +public static class OpcUaHelper +{ + /// + /// 创建并配置 OPC UA 会话。 + /// + /// OPC UA 服务器的终结点 URL。 + /// + /// 创建的 Session 对象,如果失败则返回 null。 + public static async Task CreateOpcUaSessionAsync(string endpointUrl, CancellationToken stoppingToken = default) + { + // 1. 创建应用程序配置 + var application = new ApplicationInstance + { + ApplicationName = "OpcUADemoClient", + ApplicationType = ApplicationType.Client, + ConfigSectionName = "Opc.Ua.Client" + }; + + var config = new ApplicationConfiguration() + { + ApplicationName = application.ApplicationName, + ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OpcUADemoClient", + ApplicationType = application.ApplicationType, + SecurityConfiguration = new SecurityConfiguration + { + ApplicationCertificate = new CertificateIdentifier + { + StoreType = "Directory", + StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", + SubjectName = application.ApplicationName + }, + TrustedIssuerCertificates = new CertificateTrustList + { + StoreType = "Directory", + StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Certificate Authorities" + }, + TrustedPeerCertificates = new CertificateTrustList + { + StoreType = "Directory", + StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Applications" + }, + RejectedCertificateStore = new CertificateTrustList + { + StoreType = "Directory", + StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" + }, + AutoAcceptUntrustedCertificates = true // 自动接受不受信任的证书 (仅用于测试) + }, + TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, + ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }, + TraceConfiguration = new TraceConfiguration + { + OutputFilePath = "./Logs/OpcUaClient.log", + DeleteOnLoad = true, + TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security + } + }; + application.ApplicationConfiguration = config; + + // 验证并检查证书 + await config.Validate(ApplicationType.Client); + + // 2. 查找并选择端点 (将 useSecurity 设置为 false 以进行诊断) + var selectedEndpoint = CoreClientUtils.SelectEndpoint(config, endpointUrl, false); + + var session = await Session.Create( + config, + new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)), + false, + "DMS OPC UA Session", + 60000, + new UserIdentity(new AnonymousIdentityToken()), + null, stoppingToken); + return session; + } +} \ No newline at end of file diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs index a806bc5..0c0812d 100644 --- a/DMS.WPF/App.xaml.cs +++ b/DMS.WPF/App.xaml.cs @@ -1,6 +1,4 @@ using System.Windows; -using DMS.Infrastructure.Entities; -using DMS.Infrastructure.Repositories; using DMS.Core.Enums; using DMS.Helper; using DMS.Services; @@ -11,19 +9,12 @@ using iNKORE.UI.WPF.Modern.Common.IconKeys; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog; -using NLog.Extensions.Logging; using DMS.Extensions; using Microsoft.Extensions.Hosting; -using DMS.Config; -using DMS.Infrastructure.Data; using DMS.WPF.Helper; using DMS.WPF.Services; using DMS.WPF.Services.Processors; -using DMS.WPF.ViewModels.DMS.WPF.ViewModels; -using SqlSugar; using LogLevel = Microsoft.Extensions.Logging.LogLevel; -using DMS.Infrastructure.Services; -using DMS.Infrastructure.Interfaces; namespace DMS; @@ -33,7 +24,7 @@ namespace DMS; public partial class App : System.Windows.Application { public IServiceProvider Services { get; } - public AppSettings Settings { get; private set; } + // public AppSettings Settings { get; private set; } public App() @@ -63,10 +54,10 @@ public partial class App : System.Windows.Application try { - var databaseInitializer = Host.Services.GetRequiredService(); - databaseInitializer.InitializeDataBase(); - await databaseInitializer.InitializeMenu(); - Settings = AppSettings.Load(); + // var databaseInitializer = Host.Services.GetRequiredService(); + // databaseInitializer.InitializeDataBase(); + // await databaseInitializer.InitializeMenu(); + // Settings = AppSettings.Load(); Host.Services.GetRequiredService(); // 初始化数据处理链 @@ -108,17 +99,17 @@ public partial class App : System.Windows.Application private void ConfigureServices(IServiceCollection services) { - services.AddTransient(); - - - services.AddSingleton(); - services.AddSingleton(); - //services.AddSingleton(); - services.AddSingleton(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); - + // services.AddTransient(); + // + // + // services.AddSingleton(); + // services.AddSingleton(); + // //services.AddSingleton(); + // services.AddSingleton(); + // services.AddHostedService(); + // services.AddHostedService(); + // services.AddHostedService(); + // // 注册 AutoMapper services.AddAutoMapper(typeof(App).Assembly); @@ -132,14 +123,7 @@ public partial class App : System.Windows.Application services.AddSingleton(); services.AddSingleton(); - // 注册数据仓库 - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + // 注册视图模型 services.AddSingleton(); services.AddSingleton(); @@ -166,7 +150,7 @@ public partial class App : System.Windows.Application LogManager.Setup().LoadConfigurationFromFile("Config/nlog.config"); loggingBuilder.ClearProviders(); loggingBuilder.SetMinimumLevel(LogLevel.Trace); - loggingBuilder.AddNLog(); + // loggingBuilder.AddNLog(); // 捕获未处理的异常并记录 AppDomain.CurrentDomain.UnhandledException += (sender, args) => diff --git a/DMS.WPF/DMS.WPF.csproj b/DMS.WPF/DMS.WPF.csproj index 27a0b90..408e63b 100644 --- a/DMS.WPF/DMS.WPF.csproj +++ b/DMS.WPF/DMS.WPF.csproj @@ -154,4 +154,9 @@ + + + + + diff --git a/DMS.WPF/Helper/DataServicesHelper.cs b/DMS.WPF/Helper/DataServicesHelper.cs index 7ceeaf0..732f7dd 100644 --- a/DMS.WPF/Helper/DataServicesHelper.cs +++ b/DMS.WPF/Helper/DataServicesHelper.cs @@ -1,8 +1,7 @@ using DMS.Core.Enums; -using DMS.WPF.Models; +using DMS.Core.Models; using DMS.WPF.ViewModels; -using DMS.WPF.Models; using DMS.WPF.ViewModels; using Microsoft.Extensions.DependencyInjection; @@ -34,26 +33,26 @@ public class DataServicesHelper public static MenuBean FindMenusForDevice(Device device, IEnumerable menus) { - if (menus == null) - { - return null; - } - - foreach (var menu in menus) - { - // 检查当前菜单项是否匹配 - if (menu.Type==MenuType.DeviceMenu && menu.DataId ==device.Id) - { - return menu; - } - - // 递归搜索子菜单 - var foundInSubMenu = FindMenusForDevice(device, menu.Items); - if (foundInSubMenu != null) - { - return foundInSubMenu; - } - } + // if (menus == null) + // { + // return null; + // } + // + // foreach (var menu in menus) + // { + // // 检查当前菜单项是否匹配 + // if (menu.Type==MenuType.DeviceMenu && menu.DataId ==device.Id) + // { + // return menu; + // } + // + // // 递归搜索子菜单 + // var foundInSubMenu = FindMenusForDevice(device, menu.Items); + // if (foundInSubMenu != null) + // { + // return foundInSubMenu; + // } + // } return null; } @@ -64,15 +63,15 @@ public class DataServicesHelper /// public static void SortMenus(MenuBean menu) { - if (menu.Items == null || menu.Items.Count() == 0) - return; - menu.Items.Sort((a, b) => - a.Type.ToString().Length.CompareTo(b.Type.ToString().Length) - ); - foreach (var menuItem in menu.Items) - { - SortMenus(menuItem); - } + // if (menu.Items == null || menu.Items.Count() == 0) + // return; + // menu.Items.Sort((a, b) => + // a.Type.ToString().Length.CompareTo(b.Type.ToString().Length) + // ); + // foreach (var menuItem in menu.Items) + // { + // SortMenus(menuItem); + // } } public static ViewModelBase GetMainViewModel(string name) @@ -102,26 +101,26 @@ public class DataServicesHelper public static MenuBean FindVarTableMenu(int varTableId, List menus) { - if (menus == null) - { - return null; - } - - foreach (var menu in menus) - { - // 检查当前菜单项是否匹配 - if (menu.Type==MenuType.VariableTableMenu && menu.DataId ==varTableId) - { - return menu; - } - - // 递归搜索子菜单 - var foundInSubMenu = FindVarTableMenu(varTableId, menu.Items); - if (foundInSubMenu != null) - { - return foundInSubMenu; - } - } + // if (menus == null) + // { + // return null; + // } + // + // foreach (var menu in menus) + // { + // // 检查当前菜单项是否匹配 + // if (menu.Type==MenuType.VariableTableMenu && menu.DataId ==varTableId) + // { + // return menu; + // } + // + // // 递归搜索子菜单 + // var foundInSubMenu = FindVarTableMenu(varTableId, menu.Items); + // if (foundInSubMenu != null) + // { + // return foundInSubMenu; + // } + // } return null; } diff --git a/DMS.WPF/Helper/ExcelHelper.cs b/DMS.WPF/Helper/ExcelHelper.cs deleted file mode 100644 index efebd2a..0000000 --- a/DMS.WPF/Helper/ExcelHelper.cs +++ /dev/null @@ -1,275 +0,0 @@ - -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Linq; -using System.Reflection; -using DMS.Core.Enums; -using DMS.WPF.Models; -using NPOI.SS.UserModel; -using NPOI.XSSF.UserModel; - -namespace DMS.Helper -{ - /// - /// Excel 操作帮助类 - /// - public static class ExcelHelper - { - /// - /// 将数据列表导出到 Excel 文件。 - /// - /// 数据类型。 - /// 要导出的数据列表。 - /// Excel 文件的保存路径。 - /// 工作表的名称。 - public static void ExportToExcel(IEnumerable data, string filePath, string sheetName = "Sheet1") where T : class - { - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } - - using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - IWorkbook workbook = new XSSFWorkbook(); - ISheet sheet = workbook.CreateSheet(sheetName); - - // 获取T类型的属性 - PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); - - // 创建表头 - IRow headerRow = sheet.CreateRow(0); - for (int i = 0; i < properties.Length; i++) - { - headerRow.CreateCell(i).SetCellValue(properties[i].Name); - } - - // 填充数据 - int rowIndex = 1; - foreach (var item in data) - { - IRow dataRow = sheet.CreateRow(rowIndex++); - for (int i = 0; i < properties.Length; i++) - { - object value = properties[i].GetValue(item, null); - dataRow.CreateCell(i).SetCellValue(value?.ToString() ?? string.Empty); - } - } - - workbook.Write(fs); - } - } - - /// - /// 将 DataTable 导出到 Excel 文件。 - /// - /// 要导出的 DataTable。 - /// Excel 文件的保存路径。 - /// 工作表的名称。 - public static void ExportToExcel(DataTable dataTable, string filePath, string sheetName = "Sheet1") - { - if (dataTable == null) - { - throw new ArgumentNullException(nameof(dataTable)); - } - - using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - IWorkbook workbook = new XSSFWorkbook(); - ISheet sheet = workbook.CreateSheet(sheetName); - - // 创建表头 - IRow headerRow = sheet.CreateRow(0); - for (int i = 0; i < dataTable.Columns.Count; i++) - { - headerRow.CreateCell(i).SetCellValue(dataTable.Columns[i].ColumnName); - } - - // 填充数据 - for (int i = 0; i < dataTable.Rows.Count; i++) - { - IRow dataRow = sheet.CreateRow(i + 1); - for (int j = 0; j < dataTable.Columns.Count; j++) - { - dataRow.CreateCell(j).SetCellValue(dataTable.Rows[i][j].ToString()); - } - } - - workbook.Write(fs); - } - } - - /// - /// 从 Excel 文件导入数据到 DataTable。 - /// - /// Excel 文件的路径。 - /// 工作表的名称。 - /// 是否包含表头行。 - /// 包含导入数据的 DataTable。 - public static DataTable ImportFromExcel(string filePath, string sheetName = "Sheet1", bool hasHeaderRow = true) - { - if (!File.Exists(filePath)) - { - throw new FileNotFoundException("The specified file does not exist.", filePath); - } - - var dt = new DataTable(); - using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - IWorkbook workbook = new XSSFWorkbook(fs); - ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0); - - if (sheet == null) - { - throw new Exception($"Sheet with name '{sheetName}' not found."); - } - - IRow headerRow = hasHeaderRow ? sheet.GetRow(0) : null; - int firstRow = hasHeaderRow ? 1 : 0; - int cellCount = headerRow?.LastCellNum ?? sheet.GetRow(sheet.FirstRowNum).LastCellNum; - - // 创建列 - for (int i = 0; i < cellCount; i++) - { - string columnName = hasHeaderRow ? headerRow.GetCell(i)?.ToString() ?? $"Column{i + 1}" : $"Column{i + 1}"; - dt.Columns.Add(columnName); - } - - // 填充数据 - for (int i = firstRow; i <= sheet.LastRowNum; i++) - { - IRow row = sheet.GetRow(i); - if (row == null) continue; - - DataRow dataRow = dt.NewRow(); - for (int j = 0; j < cellCount; j++) - { - ICell cell = row.GetCell(j); - dataRow[j] = cell?.ToString() ?? string.Empty; - } - dt.Rows.Add(dataRow); - } - } - return dt; - } - - /// - /// 从博途的变量表中导如变量 - /// - /// - /// - /// - public static List ImprotFromTiaVariableTable(string excelFilePath) - { - // Act - // _testFilePath = "C:\\Users\\Administrator\\Desktop\\浓度变量.xlsx"; - var dataTable = ExcelHelper.ImportFromExcel(excelFilePath); - // 判断表头的名字 - if (dataTable.Columns[0].ColumnName != "Name" || dataTable.Columns[2].ColumnName != "Data Type" && - dataTable.Columns[3].ColumnName != "Logical Address") - throw new AggregateException( - "Excel表格式不正确:第一列的名字是:Name,第三列的名字是:Data Type,Data Type,第四列的名字是:Logical Address,请检查"); - - - List variableDatas = new List(); - foreach (DataRow dataRow in dataTable.Rows) - { - Variable variable = new Variable(); - variable.Name=dataRow["Name"].ToString(); - variable.DataType=SiemensHelper.S7ToCSharpTypeString(dataRow["Data Type"].ToString()) ; - var exS7Addr=dataRow["Logical Address"].ToString(); - if (exS7Addr.StartsWith("%")) - { - variable.S7Address = exS7Addr.Substring(1); - } - - variable.NodeId = ""; - variable.ProtocolType = ProtocolType.S7; - variable.SignalType = SignalType.OtherASignal; - variableDatas.Add(variable); - } - - return variableDatas; - } - - /// - /// 从 Excel 文件导入数据到对象列表。 - /// - /// 要转换的目标类型。 - /// Excel 文件的路径。 - /// 工作表的名称。 - /// 包含导入数据的对象列表。 - public static List ImportFromExcel(string filePath, string sheetName = "Sheet1") where T : class, new() - { - if (!File.Exists(filePath)) - { - throw new FileNotFoundException("The specified file does not exist.", filePath); - } - - var list = new List(); - PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); - - using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - IWorkbook workbook = new XSSFWorkbook(fs); - ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0); - - if (sheet == null) - { - throw new Exception($"Sheet with name '{sheetName}' not found."); - } - - IRow headerRow = sheet.GetRow(0); - if (headerRow == null) - { - throw new Exception("Header row not found."); - } - - // 创建列名到属性的映射 - var columnMap = new Dictionary(); - for (int i = 0; i < headerRow.LastCellNum; i++) - { - string columnName = headerRow.GetCell(i)?.ToString(); - if (!string.IsNullOrEmpty(columnName)) - { - var prop = properties.FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase)); - if (prop != null) - { - columnMap[i] = prop; - } - } - } - - // 读取数据行 - for (int i = 1; i <= sheet.LastRowNum; i++) - { - IRow row = sheet.GetRow(i); - if (row == null) continue; - - var item = new T(); - foreach (var map in columnMap) - { - ICell cell = row.GetCell(map.Key); - if (cell != null) - { - try - { - // 尝试进行类型转换 - object value = Convert.ChangeType(cell.ToString(), map.Value.PropertyType); - map.Value.SetValue(item, value, null); - } - catch (Exception) - { - // 转换失败时可以记录日志或设置默认值 - } - } - } - list.Add(item); - } - } - return list; - } - } -} diff --git a/DMS.WPF/Helper/MenuHelper.cs b/DMS.WPF/Helper/MenuHelper.cs index 53e6021..78d86bf 100644 --- a/DMS.WPF/Helper/MenuHelper.cs +++ b/DMS.WPF/Helper/MenuHelper.cs @@ -1,4 +1,5 @@ -using DMS.WPF.Models; + +using DMS.Core.Models; namespace DMS.Helper; @@ -6,15 +7,15 @@ public class MenuHelper { public static void MenuAddParent(MenuBean menu) { - if (menu.Items==null || menu.Items.Count==0) - return; - foreach (MenuBean menuItem in menu.Items) - { - menuItem.Parent=menu; - if (menuItem.Items!= null && menuItem.Items.Count>0) - { - MenuAddParent(menuItem); - } - } + // if (menu.Items==null || menu.Items.Count==0) + // return; + // foreach (MenuBean menuItem in menu.Items) + // { + // menuItem.Parent=menu; + // if (menuItem.Items!= null && menuItem.Items.Count>0) + // { + // MenuAddParent(menuItem); + // } + // } } } \ No newline at end of file diff --git a/DMS.WPF/Helper/MessageHelper.cs b/DMS.WPF/Helper/MessageHelper.cs index 1360df4..7655a93 100644 --- a/DMS.WPF/Helper/MessageHelper.cs +++ b/DMS.WPF/Helper/MessageHelper.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.Messaging; using DMS.Core.Enums; -using DMS.WPF.Message; +using DMS.Message; using DMS.WPF.ViewModels; namespace DMS.WPF.Helper; diff --git a/DMS.WPF/Helper/NotificationHelper.cs b/DMS.WPF/Helper/NotificationHelper.cs index 6a28cf9..c43ebb5 100644 --- a/DMS.WPF/Helper/NotificationHelper.cs +++ b/DMS.WPF/Helper/NotificationHelper.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Threading; using CommunityToolkit.Mvvm.Messaging; using DMS.Core.Enums; +using DMS.Core.Helper; using DMS.Message; namespace DMS.Helper; diff --git a/DMS.WPF/Helper/ServiceHelper.cs b/DMS.WPF/Helper/ServiceHelper.cs deleted file mode 100644 index 7ee58ce..0000000 --- a/DMS.WPF/Helper/ServiceHelper.cs +++ /dev/null @@ -1,161 +0,0 @@ -using DMS.Core.Enums; -using Opc.Ua; -using Opc.Ua.Client; -using Opc.Ua.Configuration; - -namespace DMS.Helper; - -public static class ServiceHelper -{ - // 定义不同轮询级别的间隔时间。 - public static Dictionary PollingIntervals = new Dictionary - { - { - PollLevelType.TenMilliseconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType.TenMilliseconds) - }, - { - PollLevelType.HundredMilliseconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType - .HundredMilliseconds) - }, - { - PollLevelType.FiveHundredMilliseconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType - .FiveHundredMilliseconds) - }, - { - PollLevelType.OneSecond, - TimeSpan.FromMilliseconds( - (int)PollLevelType.OneSecond) - }, - { - PollLevelType.FiveSeconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType.FiveSeconds) - }, - { - PollLevelType.TenSeconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType.TenSeconds) - }, - { - PollLevelType.TwentySeconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType.TwentySeconds) - }, - { - PollLevelType.ThirtySeconds, - TimeSpan.FromMilliseconds( - (int)PollLevelType.ThirtySeconds) - }, - { - PollLevelType.OneMinute, - TimeSpan.FromMilliseconds( - (int)PollLevelType.OneMinute) - }, - { - PollLevelType.ThreeMinutes, - TimeSpan.FromMilliseconds( - (int)PollLevelType.ThreeMinutes) - }, - { - PollLevelType.FiveMinutes, - TimeSpan.FromMilliseconds( - (int)PollLevelType.FiveMinutes) - }, - { - PollLevelType.TenMinutes, - TimeSpan.FromMilliseconds( - (int)PollLevelType.TenMinutes) - }, - { - PollLevelType.ThirtyMinutes, - TimeSpan.FromMilliseconds( - (int)PollLevelType.ThirtyMinutes) - } - }; - /// - /// 创建并配置 OPC UA 会话。 - /// - /// OPC UA 服务器的终结点 URL。 - /// - /// 创建的 Session 对象,如果失败则返回 null。 - public static async Task CreateOpcUaSessionAsync(string endpointUrl, CancellationToken stoppingToken = default) - { - // 1. 创建应用程序配置 - var application = new ApplicationInstance - { - ApplicationName = "OpcUADemoClient", - ApplicationType = ApplicationType.Client, - ConfigSectionName = "Opc.Ua.Client" - }; - - var config = new ApplicationConfiguration() - { - ApplicationName = application.ApplicationName, - ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OpcUADemoClient", - ApplicationType = application.ApplicationType, - SecurityConfiguration = new SecurityConfiguration - { - ApplicationCertificate = new CertificateIdentifier - { - StoreType = "Directory", - StorePath - = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", - SubjectName = application.ApplicationName - }, - TrustedIssuerCertificates = new CertificateTrustList - { - StoreType = "Directory", - StorePath - = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Certificate Authorities" - }, - TrustedPeerCertificates = new CertificateTrustList - { - StoreType = "Directory", - StorePath - = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Applications" - }, - RejectedCertificateStore = new CertificateTrustList - { - StoreType = "Directory", - StorePath - = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" - }, - AutoAcceptUntrustedCertificates - = true // 自动接受不受信任的证书 (仅用于测试) - }, - TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, - ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }, - TraceConfiguration = new TraceConfiguration - { - OutputFilePath = "./Logs/OpcUaClient.log", - DeleteOnLoad = true, - TraceMasks = Utils.TraceMasks.Error | - Utils.TraceMasks.Security - } - }; - application.ApplicationConfiguration = config; - - // 验证并检查证书 - await config.Validate(ApplicationType.Client); - - - // 2. 查找并选择端点 (将 useSecurity 设置为 false 以进行诊断) - var selectedEndpoint = CoreClientUtils.SelectEndpoint(config, endpointUrl, false); - - var session = await Session.Create( - config, - new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)), - false, - "DMS OPC UA Session", - 60000, - new UserIdentity(new AnonymousIdentityToken()), - null,stoppingToken); - return session; - } -} \ No newline at end of file diff --git a/DMS.WPF/Helper/ThemeHelper.cs b/DMS.WPF/Helper/ThemeHelper.cs index e1fde4c..a606d77 100644 --- a/DMS.WPF/Helper/ThemeHelper.cs +++ b/DMS.WPF/Helper/ThemeHelper.cs @@ -50,18 +50,18 @@ public static class ThemeHelper public static void InitializeTheme() { // 从应用设置中读取保存的主题并应用 - ApplyTheme(App.Current.Settings.Theme); - - // 监听系统主题变化事件 - SystemEvents.UserPreferenceChanged += (s, e) => - { - // 当用户偏好设置中的"常规"类别发生变化,并且应用主题设置为"跟随系统"时 - if (e.Category == UserPreferenceCategory.General && App.Current.Settings.Theme == "跟随系统") - { - // 在UI线程上调用ApplyTheme来更新主题,以响应系统主题的变化 - App.Current.Dispatcher.Invoke(() => { ApplyTheme("跟随系统"); }); - } - }; + // ApplyTheme(App.Current.Settings.Theme); + // + // // 监听系统主题变化事件 + // SystemEvents.UserPreferenceChanged += (s, e) => + // { + // // 当用户偏好设置中的"常规"类别发生变化,并且应用主题设置为"跟随系统"时 + // if (e.Category == UserPreferenceCategory.General && App.Current.Settings.Theme == "跟随系统") + // { + // // 在UI线程上调用ApplyTheme来更新主题,以响应系统主题的变化 + // App.Current.Dispatcher.Invoke(() => { ApplyTheme("跟随系统"); }); + // } + // }; } /// diff --git a/DMS.WPF/Models/Device.cs b/DMS.WPF/Models/Device.cs deleted file mode 100644 index d395e86..0000000 --- a/DMS.WPF/Models/Device.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Collections.ObjectModel; -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; -using SqlSugar; -using SqlSugar.DbConvert; -using S7.Net; // AddAsync this using directive - -namespace DMS.WPF.Models; - -/// -/// 表示设备信息。 -/// -public partial class Device : ObservableObject -{ - /// - /// 设备的描述信息。 - /// - [ObservableProperty] - private string description; - - /// - /// 设备的类型。 - /// - [SugarColumn(ColumnDataType = "varchar(20)", SqlParameterDbType = typeof(EnumToStringConvert))] - public DeviceType DeviceType { get; set; } - - /// - /// 设备的唯一标识符。 - /// - [ObservableProperty] - private int id; - - /// - /// 设备的IP地址。 - /// - [ObservableProperty] - private string ip; - - /// - /// 表示设备是否处于活动状态。 - /// - [ObservableProperty] - private bool isActive = true; - - public event Action OnDeviceIsActiveChanged ; - - partial void OnIsActiveChanged(bool isActive){ - OnDeviceIsActiveChanged?.Invoke(this,isActive); - } - - /// - /// 表示是否添加默认变量表。 - /// - [ObservableProperty] - private bool isAddDefVarTable = true; - - /// - /// 表示设备是否正在运行。 - /// - [ObservableProperty] - private bool isRuning; - - /// - /// 设备的名称。 - /// - [ObservableProperty] - private string name; - - /// - /// 设备的端口号。 - /// - [ObservableProperty] - private int prot; - - /// - /// PLC的CPU类型。 - /// - [ObservableProperty] - private CpuType cpuType; - - /// - /// PLC的机架号。 - /// - [ObservableProperty] - private short rack; - - /// - /// PLC的槽号。 - /// - [ObservableProperty] - private short slot; - - /// - /// 设备的通信协议类型。 - /// - public ProtocolType ProtocolType { get; set; } - - /// - /// OPC UA Endpoint URL。 - /// - [ObservableProperty] - private string? opcUaEndpointUrl; - - /// - /// 设备关联的变量表列表。 - /// - public ObservableCollection? VariableTables { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Models/MenuBean.cs b/DMS.WPF/Models/MenuBean.cs deleted file mode 100644 index 5df2cef..0000000 --- a/DMS.WPF/Models/MenuBean.cs +++ /dev/null @@ -1,61 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; -using DMS.WPF.ViewModels; - -namespace DMS.WPF.Models; - -/// -/// 表示菜单项。 -/// -public class MenuBean -{ - /// - /// 菜单项关联的数据。 - /// - public object? Data { get; set; } - - /// - /// 菜单项关联的数据ID。 - /// - public int DataId { get; set; } - - /// - /// 菜单项的图标。 - /// - public string Icon { get; set; } - - /// - /// 菜单项的唯一标识符。 - /// - public int Id { get; set; } - - /// - /// 子菜单项列表。 - /// - public List Items { get; set; } - - /// - /// 菜单项的名称。 - /// - public string Name { get; set; } - - /// - /// 父菜单项。 - /// - public MenuBean Parent { get; set; } - - /// - /// 父菜单项的ID。 - /// - public int ParentId { get; set; } - - /// - /// 菜单项的类型。 - /// - public MenuType Type { get; set; } - - /// - /// 菜单项关联的ViewModel。 - /// - public ViewModelBase ViewModel { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Models/Mqtt.cs b/DMS.WPF/Models/Mqtt.cs deleted file mode 100644 index 485d37b..0000000 --- a/DMS.WPF/Models/Mqtt.cs +++ /dev/null @@ -1,105 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; - -namespace DMS.WPF.Models; - -/// -/// 表示MQTT配置信息。 -/// -public partial class Mqtt : ObservableObject -{ - public event Action OnMqttIsActiveChanged; - - /// - /// MQTT客户端ID。 - /// - public string ClientID { get; set; } - - /// - /// 连接时间。 - /// - public DateTime ConnTime { get; set; } - - /// - /// MQTT主机。 - /// - public string Host { get; set; } - - /// - /// MQTT配置的唯一标识符。 - /// - public int Id { get; set; } - - /// - /// 是否启用此MQTT配置。 - /// - [ObservableProperty] - private bool _isActive; - - partial void OnIsActiveChanged(bool value) - { - OnMqttIsActiveChanged?.Invoke(this); - } - - /// - /// 显示连接的消息: - /// - [ObservableProperty] - private string connectMessage; - - /// - /// 是否设置为默认MQTT客户端。 - /// - public int IsDefault { get; set; } - - /// - /// MQTT客户端名字。 - /// - public string Name { get; set; } - - /// - /// MQTT客户端登录密码。 - /// - public string PassWord { get; set; } - - /// - /// Mqtt平台类型。 - /// - public MqttPlatform Platform { get; set; } - - /// - /// MQTT主机端口。 - /// - public int Port { get; set; } - - /// - /// MQTT发布主题。 - /// - public string PublishTopic { get; set; } - - /// - /// MQTT备注。 - /// - public string Remark { get; set; } = String.Empty; - - /// - /// MQTT订阅主题。 - /// - public string SubTopic { get; set; } - - /// - /// MQTT客户端登录用户名。 - /// - public string UserName { get; set; } - - /// - /// 关联的变量数据列表。 - /// - public List? VariableMqtts { get; set; } - - /// - /// 是否连接。 - /// - [ObservableProperty] - private bool _isConnected; -} \ No newline at end of file diff --git a/DMS.WPF/Models/Notification.cs b/DMS.WPF/Models/Notification.cs deleted file mode 100644 index a42e60b..0000000 --- a/DMS.WPF/Models/Notification.cs +++ /dev/null @@ -1,24 +0,0 @@ -using DMS.Core.Enums; - -namespace DMS.WPF.Models; - -/// -/// 表示通知信息。 -/// -public class Notification -{ - /// - /// 通知是否为全局通知。 - /// - public bool IsGlobal { get; set; } - - /// - /// 通知消息内容。 - /// - public string Message { get; set; } - - /// - /// 通知类型。 - /// - public NotificationType Type { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Models/OpcUaNode.cs b/DMS.WPF/Models/OpcUaNode.cs deleted file mode 100644 index 889b67e..0000000 --- a/DMS.WPF/Models/OpcUaNode.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.ObjectModel; -using CommunityToolkit.Mvvm.ComponentModel; -using Opc.Ua; - -namespace DMS.WPF.Models; - -/// -/// 表示OPC UA节点,用于构建节点树。 -/// -public partial class OpcUaNode : ObservableObject -{ - /// - /// 节点的显示名称。 - /// - public string DisplayName { get; set; } - - /// - /// 节点的唯一标识符。 - /// - public NodeId NodeId { get; set; } - - /// - /// 节点的类型(例如,文件夹、变量)。 - /// - public NodeType NodeType { get; set; } - - /// - /// 子节点集合。 - /// - [ObservableProperty] - private ObservableCollection _children; - - /// - /// 指示节点是否已加载子节点。 - /// - [ObservableProperty] - private bool _isLoaded; - - /// - /// 指示节点是否正在加载子节点。 - /// - [ObservableProperty] - private bool _isLoading; - - /// - /// 节点的完整路径(可选,用于调试或显示)。 - /// - public string Path { get; set; } - - /// - /// 构造函数。 - /// - /// 显示名称。 - /// 节点ID。 - /// 节点类型。 - public OpcUaNode(string displayName, NodeId nodeId, NodeType nodeType) - { - DisplayName = displayName; - NodeId = nodeId; - NodeType = nodeType; - Children = new ObservableCollection(); - } -} - -/// -/// OPC UA节点类型枚举。 -/// -public enum NodeType -{ - Folder, - Object, - Variable -} diff --git a/DMS.WPF/Models/User.cs b/DMS.WPF/Models/User.cs deleted file mode 100644 index e18bb4e..0000000 --- a/DMS.WPF/Models/User.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace DMS.WPF.Models; - -public class User -{ - - public int Id { get; set; } - - public string Name { get; set; } - - public string Password { get; set; } - public string Email { get; set; } - public string PhoneNumber { get; set; } - public string Address { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Models/Variable.cs b/DMS.WPF/Models/Variable.cs deleted file mode 100644 index 6b91d46..0000000 --- a/DMS.WPF/Models/Variable.cs +++ /dev/null @@ -1,250 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; - -namespace DMS.WPF.Models; - -/// -/// 表示变量数据信息。 -/// -public partial class Variable : ObservableObject, IEquatable -{ - /// - /// 变量唯一标识符。 - /// - public int Id { get; set; } - - /// - /// 变量名称。 - /// - [ObservableProperty] - private string name; - - /// - /// 节点ID,用于标识变量在设备或系统中的唯一路径。 - /// - public string NodeId { get; set; } - - /// - /// 节点ID,用于标识变量在设备或系统中的唯一路径。 - /// - public string S7Address { get; set; } - - /// - /// OPC UA Endpoint URL。 - /// - public string? OpcUaEndpointUrl { get; set; } - - /// - /// OPC UA Node ID。 - /// - public string? OpcUaNodeId { get; set; } - - - /// - /// 变量描述。 - /// - [ObservableProperty] - private string description = String.Empty; - - /// - /// 协议类型,例如Modbus、OPC UA等。 - /// - public ProtocolType ProtocolType { get; set; } - - /// - /// 信号类型,例如模拟量、数字量等。 - /// - public SignalType SignalType { get; set; } - - /// - /// 数据类型,例如Int、Float、String等。 - /// - public string DataType { get; set; } - - /// - /// 变量当前原始数据值。 - /// - [ObservableProperty] - private string dataValue = String.Empty; - - /// - /// 变量经过转换或格式化后的显示值。 - /// - [ObservableProperty] - private string displayValue = String.Empty; - - /// - /// 指示变量是否处于激活状态。 - /// - [ObservableProperty] - public bool isActive; - - /// - /// 指示变量是否被选中 - /// - [ObservableProperty] - public bool isSelect; - - /// - /// 指示是否需要保存变量数据。 - /// - public bool IsSave { get; set; } - - /// - /// 指示是否需要对变量进行报警监测。 - /// - public bool IsAlarm { get; set; } - - /// - /// 轮询级别,例如1秒、5秒等。 - /// - [ObservableProperty] - private PollLevelType pollLevelType = PollLevelType.ThirtySeconds; - - /// - /// OPC UA更新类型,例如轮询或订阅。 - /// - [ObservableProperty] - private OpcUaUpdateType opcUaUpdateType = OpcUaUpdateType.OpcUaPoll; - - /// - /// 最后一次轮询时间。 - /// - public DateTime LastPollTime { get; set; } = DateTime.MinValue; - - /// - /// 指示变量是否已被逻辑删除。 - /// - public bool IsDeleted { get; set; } - - /// - /// 指示变量是否已被修改了。 - /// - [ObservableProperty] - private bool isModified; - - /// - /// 报警的最大值阈值。 - /// - public double AlarmMax { get; set; } - - /// - /// 报警的最小值阈值。 - /// - public double AlarmMin { get; set; } - - /// - /// 数据转换规则或表达式。 - /// - [ObservableProperty] - private string converstion = String.Empty; - - /// - /// 数据保存的范围或阈值。 - /// - public double SaveRange { get; set; } - - /// - /// 变量数据创建时间。 - /// - [ObservableProperty] - private DateTime createTime; - - - /// - /// 变量数据最后更新时间。 - /// - [ObservableProperty] - private DateTime updateTime = DateTime.Now; - - /// - /// 最后更新变量数据的用户。 - /// - public User UpdateUser { get; set; } - - /// - /// 关联的变量表ID。 - /// - public int VariableTableId { get; set; } - - /// - /// 关联的变量表实体。 - /// - public VariableTable? VariableTable { get; set; } - - /// - /// 关联的MQTT配置列表。 - /// - public List? VariableMqtts { get; set; } - - partial void OnPollLevelTypeChanged(PollLevelType value) - { - IsModified = true; - } - - public override bool Equals(object? obj) - { - return Equals(obj as Variable); - } - - public bool Equals(Variable? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - - // Compare all relevant properties for equality - return Name == other.Name && - NodeId == other.NodeId && - S7Address == other.S7Address && - OpcUaEndpointUrl == other.OpcUaEndpointUrl && - OpcUaNodeId == other.OpcUaNodeId && - Description == other.Description && - ProtocolType == other.ProtocolType && - SignalType == other.SignalType && - DataType == other.DataType && - DataValue == other.DataValue && - DisplayValue == other.DisplayValue && - IsActive == other.IsActive && - IsSave == other.IsSave && - IsAlarm == other.IsAlarm && - PollLevelType == other.PollLevelType && - OpcUaUpdateType == other.OpcUaUpdateType && - IsDeleted == other.IsDeleted && - AlarmMax == other.AlarmMax && - AlarmMin == other.AlarmMin && - Converstion == other.Converstion && - SaveRange == other.SaveRange && - CreateTime == other.CreateTime && - VariableTableId == other.VariableTableId; - } - - public override int GetHashCode() - { - // Combine hash codes of all relevant properties - var hash = new HashCode(); - hash.Add(Name); - hash.Add(NodeId); - hash.Add(S7Address); - hash.Add(OpcUaEndpointUrl); - hash.Add(OpcUaNodeId); - hash.Add(Description); - hash.Add(ProtocolType); - hash.Add(SignalType); - hash.Add(DataType); - hash.Add(DataValue); - hash.Add(DisplayValue); - hash.Add(IsActive); - hash.Add(IsSave); - hash.Add(IsAlarm); - hash.Add(PollLevelType); - hash.Add(OpcUaUpdateType); - hash.Add(IsDeleted); - hash.Add(AlarmMax); - hash.Add(AlarmMin); - hash.Add(Converstion); - hash.Add(SaveRange); - hash.Add(CreateTime); - hash.Add(VariableTableId); - return hash.ToHashCode(); - } -} \ No newline at end of file diff --git a/DMS.WPF/Models/VariableContext.cs b/DMS.WPF/Models/VariableContext.cs deleted file mode 100644 index 628c9ff..0000000 --- a/DMS.WPF/Models/VariableContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using DMS.WPF.Models; - -namespace DMS.Models -{ - public class VariableContext - { - public Variable Data { get; set; } - public bool IsHandled { get; set; } - - public VariableContext(Variable data) - { - Data = data; - IsHandled = false; // 默认未处理 - } - } -} \ No newline at end of file diff --git a/DMS.WPF/Models/VariableMqtt.cs b/DMS.WPF/Models/VariableMqtt.cs deleted file mode 100644 index cb84c35..0000000 --- a/DMS.WPF/Models/VariableMqtt.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; - -using DMS.WPF.Models; - -namespace DMS.WPF.Models; - -/// -/// 表示变量数据与MQTT服务器之间的关联模型,包含MQTT别名。 -/// -public partial class VariableMqtt : ObservableObject -{ - public VariableMqtt() - { - } - - public VariableMqtt(Variable variable, Mqtt mqtt) - { - if (mqtt != null && variable != null) - { - Variable = variable; - Mqtt = mqtt; - MqttAlias = MqttAlias != String.Empty ? MqttAlias : variable.Name; - } - - } - - /// - /// 关联的唯一标识符。 - /// - public int Id { get; set; } - - /// - /// 关联的变量数据ID。 - /// - public int VariableId { get; set; } - - /// - /// 关联的MQTT服务器ID。 - /// - public int MqttId { get; set; } - - /// - /// 变量在该MQTT服务器上的别名。 - /// - [ObservableProperty] - private string _mqttAlias = string.Empty; - - /// - /// 变量的唯一标识符(S7地址或OPC UA NodeId)。 - /// - public string Identifier - { - get - { - if (Variable!=null) - { - - - if (Variable.ProtocolType == ProtocolType.S7) - { - return Variable.S7Address; - } - else if (Variable.ProtocolType == ProtocolType.OpcUA) - { - return Variable.OpcUaNodeId; - } - } - return string.Empty; - } - } - - /// - /// 创建时间。 - /// - public DateTime CreateTime { get; set; } = DateTime.Now; - - /// - /// 更新时间。 - /// - public DateTime UpdateTime { get; set; } = DateTime.Now; - - /// - /// 导航属性:关联的变量数据。 - /// - public Variable Variable { get; set; } - - /// - /// 导航属性:关联的MQTT服务器。 - /// - public Mqtt Mqtt { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Models/VariableTable.cs b/DMS.WPF/Models/VariableTable.cs deleted file mode 100644 index 22c09df..0000000 --- a/DMS.WPF/Models/VariableTable.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Core.Enums; - -namespace DMS.WPF.Models; - -/// -/// 表示变量表信息。 -/// -public partial class VariableTable : ObservableObject -{ - /// - /// 变量表关联的设备。 - /// - public Device? Device { get; set; } - - /// - /// 变量表关联的设备ID。 - /// - public int? DeviceId { get; set; } - - /// - /// 变量表描述。 - /// - [ObservableProperty] - private string description; - - /// - /// 变量表中包含的数据变量列表。 - /// - [ObservableProperty] - public ObservableCollection variables; - - /// - /// 变量表的唯一标识符。 - /// - public int Id { get; set; } - - /// - /// 表示变量表是否处于活动状态。 - /// - [ObservableProperty] - private bool isActive; - - /// - /// 变量表名称。 - /// - [ObservableProperty] - private string name; - - /// - /// 变量表使用的协议类型。 - /// - public ProtocolType ProtocolType { get; set; } -} \ No newline at end of file diff --git a/DMS.WPF/Services/DataProcessingService.cs b/DMS.WPF/Services/DataProcessingService.cs index c1a583a..bc37da8 100644 --- a/DMS.WPF/Services/DataProcessingService.cs +++ b/DMS.WPF/Services/DataProcessingService.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Threading; using System.Threading.Channels; -using System.Threading.Tasks; -using DMS.Helper; -using DMS.WPF.Models; +using DMS.Core.Helper; +using DMS.Core.Models; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; namespace DMS.Services; diff --git a/DMS.WPF/Services/DataServices.cs b/DMS.WPF/Services/DataServices.cs index b6ffc6f..b0a0c5e 100644 --- a/DMS.WPF/Services/DataServices.cs +++ b/DMS.WPF/Services/DataServices.cs @@ -2,15 +2,16 @@ using System.Collections.Concurrent; using AutoMapper; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; -using DMS.WPF.Models; -using DMS.WPF.Message; -using DMS.WPF.ViewModels; +using DMS.Application.DTOs; using DMS.Core.Helper; -using DMS.Infrastructure.Repositories; using DMS.Core.Enums; +using DMS.Core.Models; +using DMS.Helper; +using DMS.Message; +using DMS.WPF.Helper; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using SqlSugar; + namespace DMS.WPF.Services; @@ -38,23 +39,23 @@ public partial class DataServices : ObservableRecipient, IRecipient private List menuTrees; // MQTT配置列表。 - [ObservableProperty] - private List _mqtts; + // [ObservableProperty] + // private List _mqtts; public ConcurrentDictionary AllVariables; // 设备数据仓库,用于设备数据的CRUD操作。 - private readonly DeviceRepository _deviceRepository; - - // 菜单数据仓库,用于菜单数据的CRUD操作。 - private readonly MenuRepository _menuRepository; - - // MQTT数据仓库,用于MQTT配置数据的CRUD操作。 - private readonly MqttRepository _mqttRepository; - - // 变量数据仓库,用于变量数据的CRUD操作。 - private readonly VarDataRepository _varDataRepository; + // private readonly DeviceRepository _deviceRepository; + // + // // 菜单数据仓库,用于菜单数据的CRUD操作。 + // private readonly MenuRepository _menuRepository; + // + // // MQTT数据仓库,用于MQTT配置数据的CRUD操作。 + // private readonly MqttRepository _mqttRepository; + // + // // 变量数据仓库,用于变量数据的CRUD操作。 + // private readonly VarDataRepository _varDataRepository; // 设备列表变更事件,当设备列表数据更新时触发。 public event Action> OnDeviceListChanged; @@ -63,77 +64,74 @@ public partial class DataServices : ObservableRecipient, IRecipient public event Action> OnMenuTreeListChanged; // MQTT列表变更事件,当MQTT配置数据更新时触发。 - public event Action> OnMqttListChanged; + // public event Action> OnMqttListChanged; // 设备IsActive状态变更事件,当单个设备的IsActive状态改变时触发。 public event Action OnDeviceIsActiveChanged; - /// - /// 当_mqtts属性值改变时触发的局部方法,用于调用OnMqttListChanged事件。 - /// - /// 新的MQTT配置列表。 - partial void OnMqttsChanged(List mqtts) - { - OnMqttListChanged?.Invoke(mqtts); - } + // /// + // /// 当_mqtts属性值改变时触发的局部方法,用于调用OnMqttListChanged事件。 + // /// + // /// 新的MQTT配置列表。 + // partial void OnMqttsChanged(List mqtts) + // { + // OnMqttListChanged?.Invoke(mqtts); + // } // 注释掉的代码块,可能用于变量数据变更事件的触发,但目前未启用。 // { // OnVariableDataChanged?.Invoke(this, value); // } - /// - /// DataServices类的构造函数。 - /// 注入ILogger,并初始化各个数据仓库。 - /// - /// AutoMapper 实例。 - /// - public DataServices(IMapper mapper,DeviceRepository deviceRepository,MenuRepository menuRepository,MqttRepository mqttRepository,VarDataRepository varDataRepository) - { - _mapper = mapper; - IsActive = true; // 激活消息接收器 - _deviceRepository = deviceRepository; - _menuRepository = menuRepository; - _mqttRepository = mqttRepository; - _varDataRepository = varDataRepository; - _variables = new List(); - AllVariables = new ConcurrentDictionary(); - } + // /// + // /// DataServices类的构造函数。 + // /// 注入ILogger,并初始化各个数据仓库。 + // /// + // /// AutoMapper 实例。 + // /// + // public DataServices(IMapper mapper,DeviceRepository deviceRepository,MenuRepository menuRepository,MqttRepository mqttRepository,VarDataRepository varDataRepository) + // { + // _mapper = mapper; + // IsActive = true; // 激活消息接收器 + // + // _variables = new List(); + // AllVariables = new ConcurrentDictionary(); + // } - /// - /// 接收加载消息,根据消息类型从数据库加载对应的数据。 - /// - /// 加载消息,包含要加载的数据类型。 - /// 如果加载类型未知,可能会抛出此异常(尽管当前实现中未显式抛出)。 - public async void Receive(LoadMessage message) - { - try - { - switch (message.LoadType) - { - case LoadTypes.All: // 加载所有数据 - await LoadDevices(); - await LoadMenus(); - await LoadMqtts(); - break; - case LoadTypes.Devices: // 仅加载设备数据 - await LoadDevices(); - break; - case LoadTypes.Menu: // 仅加载菜单数据 - await LoadMenus(); - break; - case LoadTypes.Mqtts: // 仅加载MQTT配置数据 - await LoadMqtts(); - break; - } - } - catch (Exception e) - { - // 捕获加载数据时发生的异常,并通过通知和日志记录错误信息。 - NotificationHelper.ShowError($"加载数据出现了错误:{e.Message}", e); - } - } + // /// + // /// 接收加载消息,根据消息类型从数据库加载对应的数据。 + // /// + // /// 加载消息,包含要加载的数据类型。 + // /// 如果加载类型未知,可能会抛出此异常(尽管当前实现中未显式抛出)。 + // public async void Receive(LoadMessage message) + // { + // try + // { + // switch (message.LoadType) + // { + // case LoadTypes.All: // 加载所有数据 + // await LoadDevices(); + // await LoadMenus(); + // await LoadMqtts(); + // break; + // case LoadTypes.Devices: // 仅加载设备数据 + // await LoadDevices(); + // break; + // case LoadTypes.Menu: // 仅加载菜单数据 + // await LoadMenus(); + // break; + // case LoadTypes.Mqtts: // 仅加载MQTT配置数据 + // await LoadMqtts(); + // break; + // } + // } + // catch (Exception e) + // { + // // 捕获加载数据时发生的异常,并通过通知和日志记录错误信息。 + // NotificationHelper.ShowError($"加载数据出现了错误:{e.Message}", e); + // } + // } /// /// 异步加载设备数据。 @@ -142,33 +140,33 @@ public partial class DataServices : ObservableRecipient, IRecipient private async Task LoadDevices() { // 取消订阅旧设备的属性变更事件,防止内存泄漏 - if (Devices != null) - { - foreach (var device in Devices) - { - device.PropertyChanged -= Device_PropertyChanged; - } - } - - Devices = await _deviceRepository.GetAllAsync(); - - // 订阅新设备的属性变更事件 - if (Devices != null) - { - foreach (var device in Devices) - { - device.PropertyChanged += Device_PropertyChanged; - } - - var allVar = await _varDataRepository.GetAllAsync(); - foreach (var variable in allVar) - { - AllVariables.AddOrUpdate(variable.Id, variable, (key, old) => variable); - } - - } - - OnDeviceListChanged?.Invoke(Devices); + // if (Devices != null) + // { + // foreach (var device in Devices) + // { + // device.PropertyChanged -= Device_PropertyChanged; + // } + // } + // + // Devices = await _deviceRepository.GetAllAsync(); + // + // // 订阅新设备的属性变更事件 + // if (Devices != null) + // { + // foreach (var device in Devices) + // { + // device.PropertyChanged += Device_PropertyChanged; + // } + // + // var allVar = await _varDataRepository.GetAllAsync(); + // foreach (var variable in allVar) + // { + // AllVariables.AddOrUpdate(variable.Id, variable, (key, old) => variable); + // } + // + // } + // + // OnDeviceListChanged?.Invoke(Devices); } private void Device_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -189,64 +187,68 @@ public partial class DataServices : ObservableRecipient, IRecipient /// 表示异步操作的任务。 private async Task LoadMenus() { - MenuTrees = await _menuRepository.GetMenuTreesAsync(); - foreach (MenuBean menu in MenuTrees) - { - MenuHelper.MenuAddParent(menu); // 为菜单添加父级引用 - DataServicesHelper.SortMenus(menu); // 排序菜单 - } - - OnMenuTreeListChanged?.Invoke(MenuTrees); + // MenuTrees = await _menuRepository.GetMenuTreesAsync(); + // foreach (MenuBean menu in MenuTrees) + // { + // MenuHelper.MenuAddParent(menu); // 为菜单添加父级引用 + // DataServicesHelper.SortMenus(menu); // 排序菜单 + // } + // + // OnMenuTreeListChanged?.Invoke(MenuTrees); } - /// - /// 异步获取所有MQTT配置。 - /// - /// 包含所有MQTT配置的列表。 - public async Task> GetMqttsAsync() + // /// + // /// 异步获取所有MQTT配置。 + // /// + // /// 包含所有MQTT配置的列表。 + // public async Task> GetMqttsAsync() + // { + // var mqtts = await _mqttRepository.GetAllAsync(); + // OnMqttListChanged?.Invoke(mqtts); + // return mqtts; + // } + + // /// + // /// 异步加载MQTT配置数据。 + // /// + // /// 表示异步操作的任务。 + // private async Task LoadMqtts() + // { + // Mqtts = await _mqttRepository.GetAllAsync(); + // } + + + // /// + // /// 异步根据ID获取设备数据。 + // /// + // /// 设备ID。 + // /// 设备对象,如果不存在则为null。 + // public async Task GetDeviceByIdAsync(int id) + // { + // return await _deviceRepository.GetByIdAsync(id); + // } + // + // /// + // /// 异步加载变量数据。 + // /// + // /// 表示异步操作的任务。 + // private async Task LoadVariables() + // { + // Variables = await _varDataRepository.GetAllAsync(); + // } + + // /// + // /// 异步更新变量数据。 + // /// + // /// 要更新的变量数据。 + // /// 表示异步操作的任务。 + // public async Task UpdateVariableAsync(Variable variable) + // { + // await _variableAppService.UpdateVariableAsync(_mapper.Map(variable)); + // } + + public void Receive(LoadMessage message) { - var mqtts = await _mqttRepository.GetAllAsync(); - OnMqttListChanged?.Invoke(mqtts); - return mqtts; + } - - /// - /// 异步加载MQTT配置数据。 - /// - /// 表示异步操作的任务。 - private async Task LoadMqtts() - { - Mqtts = await _mqttRepository.GetAllAsync(); - } - - - /// - /// 异步根据ID获取设备数据。 - /// - /// 设备ID。 - /// 设备对象,如果不存在则为null。 - public async Task GetDeviceByIdAsync(int id) - { - return await _deviceRepository.GetByIdAsync(id); - } - - /// - /// 异步加载变量数据。 - /// - /// 表示异步操作的任务。 - private async Task LoadVariables() - { - Variables = await _varDataRepository.GetAllAsync(); - } - - /// - /// 异步更新变量数据。 - /// - /// 要更新的变量数据。 - /// 表示异步操作的任务。 - public async Task UpdateVariableAsync(Variable variable) - { - await _varDataRepository.UpdateAsync(variable); - } - } \ No newline at end of file diff --git a/DMS.WPF/Services/DialogService.cs b/DMS.WPF/Services/DialogService.cs index 1ad4060..0bccccd 100644 --- a/DMS.WPF/Services/DialogService.cs +++ b/DMS.WPF/Services/DialogService.cs @@ -1,264 +1,263 @@ using DMS.Core.Enums; -using DMS.WPF.Models; +using DMS.Core.Models; +using DMS.Services; using DMS.WPF.ViewModels.Dialogs; using DMS.WPF.Views.Dialogs; using HandyControl.Tools.Extension; using iNKORE.UI.WPF.Modern.Controls; -using NPOI.SS.Formula.Functions; -using DMS.Infrastructure.Repositories; namespace DMS.WPF.Services; -public class DialogService :IDialogService +public class DialogService { - private readonly DataServices _dataServices; - - public DialogService(DataServices dataServices) - { - _dataServices = dataServices; - } - - public async Task ShowAddDeviceDialog() - { - var device = new Device(); - DeviceDialogViewModel vm = new DeviceDialogViewModel(device); - vm.Title = "添加设备"; - vm.PrimaryButContent = "添加设备"; - return await ShowConentDialog(vm,device); - } - - private static async Task ShowConentDialog(DeviceDialogViewModel viewModel,Device device) - { - var dialog = new DeviceDialog(viewModel); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return device; - } - return null; - } - - public async Task ShowEditDeviceDialog(Device device) - { - DeviceDialogViewModel vm = new DeviceDialogViewModel(device); - vm.Title = "编辑设备"; - vm.PrimaryButContent = "编辑设备"; - return await ShowConentDialog(vm,device); - - } - - public async Task ShowAddMqttDialog() - { - var mqtt = new Mqtt(); - MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); - vm.Title = "添加MQTT"; - vm.PrimaryButContent = "添加MQTT"; - return await ShowConentDialog(vm, mqtt); - } - - public async Task ShowEditMqttDialog(Mqtt mqtt) - { - MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); - vm.Title = "编辑MQTT"; - vm.PrimaryButContent = "编辑MQTT"; - return await ShowConentDialog(vm, mqtt); - } - - private static async Task ShowConentDialog(MqttDialogViewModel viewModel, Mqtt mqtt) - { - var dialog = new MqttDialog(viewModel); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return mqtt; - } - return null; - } - - public async Task ShowConfrimeDialog(string title, string message,string buttonText="确认") - { - ConfrimDialogViewModel vm = new ConfrimDialogViewModel(); - vm.Title = title; - vm.Message = message; - vm.PrimaryButtonText = buttonText; - var dialog = new ConfirmDialog(vm); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return true; - } - return false; - } - - public async Task ShowAddVarTableDialog() - { - VarTableDialogViewModel vm = new(); - vm.Title = "添加变量表"; - vm.PrimaryButtonText = "添加变量表"; - vm.VariableTable = new VariableTable(); - var dialog = new VarTableDialog(vm); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return vm.VariableTable; - } - return null; - } - - public async Task ShowEditVarTableDialog(VariableTable variableTable) - { - VarTableDialogViewModel vm = new(); - vm.Title = "编辑变量表"; - vm.PrimaryButtonText = "编辑变量表"; - vm.VariableTable = variableTable; - var dialog = new VarTableDialog(vm); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return vm.VariableTable; - } - return null; - } - - public async Task ShowAddVarDataDialog() - { - VarDataDialogViewModel vm = new(); - vm.Title = "添加变量"; - vm.PrimaryButtonText = "添加变量"; - vm.Variable = new Variable(); - var dialog = new VarDataDialog(vm); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return vm.Variable; - } - return null; - } - - - public void ShowMessageDialog(string title, string message) - { - MessageBox.Show(message); - } - - public async Task ShowEditVarDataDialog(Variable variable) - { - VarDataDialogViewModel vm = new(); - vm.Title = "编辑变量"; - vm.PrimaryButtonText = "编辑变量"; - vm.Variable = variable; - var dialog = new VarDataDialog(vm); - var res = await dialog.ShowAsync(); - if (res == ContentDialogResult.Primary) - { - return vm.Variable; - } - return null; - } - - public async Task ShowImportExcelDialog() - { - var vm = new ImportExcelDialogViewModel(); - var dialog = new ImportExcelDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.FilePath; - } - return null; - } - - public ContentDialog ShowProcessingDialog(string title, string message) - { - ProcessingDialogViewModel vm = new ProcessingDialogViewModel(); - vm.Title = title; - vm.Message = message; - var dialog = new ProcessingDialog(vm); - _ = dialog.ShowAsync(); // 不await,让它在后台显示 - return dialog; - } - - public async Task ShowPollLevelDialog(PollLevelType pollLevelType) - { - var vm = new PollLevelDialogViewModel(pollLevelType); - var dialog = new PollLevelDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.SelectedPollLevelType; - } - return null; - } - - public async Task ShowMqttSelectionDialog() - { - var vm = new MqttSelectionDialogViewModel(_dataServices); - var dialog = new MqttSelectionDialog(vm); - var result = await dialog.ShowAsync(); - return result == ContentDialogResult.Primary ? vm.SelectedMqtt : null; - } - - public async Task> ShowOpcUaImportDialog(string endpointUrl) - { - var vm= new OpcUaImportDialogViewModel(); - vm.EndpointUrl = endpointUrl; - var dialog = new OpcUaImportDialog(vm); - var result = await dialog.ShowAsync(); - return result == ContentDialogResult.Primary ? vm.GetSelectedVariables().ToList() : null; - } - - public async Task ShowOpcUaUpdateTypeDialog() - { - var vm = new OpcUaUpdateTypeDialogViewModel(); - var dialog = new OpcUaUpdateTypeDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.SelectedUpdateType; - } - return null; - } - - public async Task ShowIsActiveDialog(bool currentIsActive) - { - var vm = new IsActiveDialogViewModel(currentIsActive); - var dialog = new IsActiveDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.SelectedIsActive; - } - return null; - } - - public async Task ShowImportResultDialog(List importedVariables, List existingVariables) - { - var vm = new ImportResultDialogViewModel(importedVariables, existingVariables); - var dialog = new ImportResultDialog(vm); - await dialog.ShowAsync(); - } - - public async Task ShowMqttAliasDialog(string variableName, string mqttServerName) - { - var vm = new MqttAliasDialogViewModel(variableName, mqttServerName); - var dialog = new MqttAliasDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.MqttAlias; - } - return null; - } - - public async Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt) - { - var vm = new MqttAliasBatchEditDialogViewModel(selectedVariables, selectedMqtt); - var dialog = new MqttAliasBatchEditDialog(vm); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - return vm.VariablesToEdit.ToList(); - } - return null; - } + // private readonly DataServices _dataServices; + // + // public DialogService(DataServices dataServices) + // { + // _dataServices = dataServices; + // } + // + // public async Task ShowAddDeviceDialog() + // { + // var device = new Device(); + // DeviceDialogViewModel vm = new DeviceDialogViewModel(device); + // vm.Title = "添加设备"; + // vm.PrimaryButContent = "添加设备"; + // return await ShowConentDialog(vm,device); + // } + // + // private static async Task ShowConentDialog(DeviceDialogViewModel viewModel,Device device) + // { + // var dialog = new DeviceDialog(viewModel); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return device; + // } + // return null; + // } + // + // public async Task ShowEditDeviceDialog(Device device) + // { + // DeviceDialogViewModel vm = new DeviceDialogViewModel(device); + // vm.Title = "编辑设备"; + // vm.PrimaryButContent = "编辑设备"; + // return await ShowConentDialog(vm,device); + // + // } + // + // public async Task ShowAddMqttDialog() + // { + // var mqtt = new Mqtt(); + // MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); + // vm.Title = "添加MQTT"; + // vm.PrimaryButContent = "添加MQTT"; + // return await ShowConentDialog(vm, mqtt); + // } + // + // public async Task ShowEditMqttDialog(Mqtt mqtt) + // { + // MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); + // vm.Title = "编辑MQTT"; + // vm.PrimaryButContent = "编辑MQTT"; + // return await ShowConentDialog(vm, mqtt); + // } + // + // private static async Task ShowConentDialog(MqttDialogViewModel viewModel, Mqtt mqtt) + // { + // var dialog = new MqttDialog(viewModel); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return mqtt; + // } + // return null; + // } + // + // public async Task ShowConfrimeDialog(string title, string message,string buttonText="确认") + // { + // ConfrimDialogViewModel vm = new ConfrimDialogViewModel(); + // vm.Title = title; + // vm.Message = message; + // vm.PrimaryButtonText = buttonText; + // var dialog = new ConfirmDialog(vm); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return true; + // } + // return false; + // } + // + // public async Task ShowAddVarTableDialog() + // { + // VarTableDialogViewModel vm = new(); + // vm.Title = "添加变量表"; + // vm.PrimaryButtonText = "添加变量表"; + // vm.VariableTable = new VariableTable(); + // var dialog = new VarTableDialog(vm); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return vm.VariableTable; + // } + // return null; + // } + // + // public async Task ShowEditVarTableDialog(VariableTable variableTable) + // { + // VarTableDialogViewModel vm = new(); + // vm.Title = "编辑变量表"; + // vm.PrimaryButtonText = "编辑变量表"; + // vm.VariableTable = variableTable; + // var dialog = new VarTableDialog(vm); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return vm.VariableTable; + // } + // return null; + // } + // + // public async Task ShowAddVarDataDialog() + // { + // VarDataDialogViewModel vm = new(); + // vm.Title = "添加变量"; + // vm.PrimaryButtonText = "添加变量"; + // vm.Variable = new Variable(); + // var dialog = new VarDataDialog(vm); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return vm.Variable; + // } + // return null; + // } + // + // + // public void ShowMessageDialog(string title, string message) + // { + // MessageBox.Show(message); + // } + // + // public async Task ShowEditVarDataDialog(Variable variable) + // { + // VarDataDialogViewModel vm = new(); + // vm.Title = "编辑变量"; + // vm.PrimaryButtonText = "编辑变量"; + // vm.Variable = variable; + // var dialog = new VarDataDialog(vm); + // var res = await dialog.ShowAsync(); + // if (res == ContentDialogResult.Primary) + // { + // return vm.Variable; + // } + // return null; + // } + // + // public async Task ShowImportExcelDialog() + // { + // var vm = new ImportExcelDialogViewModel(); + // var dialog = new ImportExcelDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.FilePath; + // } + // return null; + // } + // + // public ContentDialog ShowProcessingDialog(string title, string message) + // { + // ProcessingDialogViewModel vm = new ProcessingDialogViewModel(); + // vm.Title = title; + // vm.Message = message; + // var dialog = new ProcessingDialog(vm); + // _ = dialog.ShowAsync(); // 不await,让它在后台显示 + // return dialog; + // } + // + // public async Task ShowPollLevelDialog(PollLevelType pollLevelType) + // { + // var vm = new PollLevelDialogViewModel(pollLevelType); + // var dialog = new PollLevelDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.SelectedPollLevelType; + // } + // return null; + // } + // + // public async Task ShowMqttSelectionDialog() + // { + // var vm = new MqttSelectionDialogViewModel(_dataServices); + // var dialog = new MqttSelectionDialog(vm); + // var result = await dialog.ShowAsync(); + // return result == ContentDialogResult.Primary ? vm.SelectedMqtt : null; + // } + // + // public async Task> ShowOpcUaImportDialog(string endpointUrl) + // { + // var vm= new OpcUaImportDialogViewModel(); + // vm.EndpointUrl = endpointUrl; + // var dialog = new OpcUaImportDialog(vm); + // var result = await dialog.ShowAsync(); + // return result == ContentDialogResult.Primary ? vm.GetSelectedVariables().ToList() : null; + // } + // + // public async Task ShowOpcUaUpdateTypeDialog() + // { + // var vm = new OpcUaUpdateTypeDialogViewModel(); + // var dialog = new OpcUaUpdateTypeDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.SelectedUpdateType; + // } + // return null; + // } + // + // public async Task ShowIsActiveDialog(bool currentIsActive) + // { + // var vm = new IsActiveDialogViewModel(currentIsActive); + // var dialog = new IsActiveDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.SelectedIsActive; + // } + // return null; + // } + // + // public async Task ShowImportResultDialog(List importedVariables, List existingVariables) + // { + // var vm = new ImportResultDialogViewModel(importedVariables, existingVariables); + // var dialog = new ImportResultDialog(vm); + // await dialog.ShowAsync(); + // } + // + // public async Task ShowMqttAliasDialog(string variableName, string mqttServerName) + // { + // var vm = new MqttAliasDialogViewModel(variableName, mqttServerName); + // var dialog = new MqttAliasDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.MqttAlias; + // } + // return null; + // } + // + // public async Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt) + // { + // var vm = new MqttAliasBatchEditDialogViewModel(selectedVariables, selectedMqtt); + // var dialog = new MqttAliasBatchEditDialog(vm); + // var result = await dialog.ShowAsync(); + // if (result == ContentDialogResult.Primary) + // { + // return vm.VariablesToEdit.ToList(); + // } + // return null; + // } } \ No newline at end of file diff --git a/DMS.WPF/Services/GrowlNotificationService.cs b/DMS.WPF/Services/GrowlNotificationService.cs index 0803c57..fcd9cd3 100644 --- a/DMS.WPF/Services/GrowlNotificationService.cs +++ b/DMS.WPF/Services/GrowlNotificationService.cs @@ -3,8 +3,6 @@ using CommunityToolkit.Mvvm.Messaging; using DMS.Core.Enums; using DMS.Message; using HandyControl.Controls; -using Models_Notification = DMS.Models.Notification; -using Notification = DMS.Models.Notification; namespace DMS.Services; @@ -15,69 +13,73 @@ public class GrowlNotificationService : ObservableRecipient, IRecipient ShowAddDeviceDialog(); - Task ShowEditDeviceDialog(Device device); - Task ShowAddMqttDialog(); - Task ShowEditMqttDialog(Mqtt mqtt); - Task ShowConfrimeDialog(string title, string message,string buttonText="确认"); - - Task ShowAddVarTableDialog(); - Task ShowEditVarTableDialog(VariableTable variableTable); - - Task ShowAddVarDataDialog(); - - void ShowMessageDialog(string title, string message); - Task ShowEditVarDataDialog(Variable variable); - Task ShowImportExcelDialog(); - ContentDialog ShowProcessingDialog(string title, string message); - Task ShowPollLevelDialog(PollLevelType pollLevelType); - Task ShowMqttSelectionDialog(); - Task> ShowOpcUaImportDialog(string endpointUrl); - Task ShowOpcUaUpdateTypeDialog(); - Task ShowIsActiveDialog(bool currentIsActive); - Task ShowImportResultDialog(List importedVariables, List existingVariables); - Task ShowMqttAliasDialog(string variableName, string mqttServerName); - Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt); + // Task ShowAddDeviceDialog(); + // Task ShowEditDeviceDialog(Device device); + // Task ShowAddMqttDialog(); + // Task ShowEditMqttDialog(Mqtt mqtt); + // Task ShowConfrimeDialog(string title, string message,string buttonText="确认"); + // + // Task ShowAddVarTableDialog(); + // Task ShowEditVarTableDialog(VariableTable variableTable); + // + // Task ShowAddVarDataDialog(); + // + // void ShowMessageDialog(string title, string message); + // Task ShowEditVarDataDialog(Variable variable); + // Task ShowImportExcelDialog(); + // ContentDialog ShowProcessingDialog(string title, string message); + // Task ShowPollLevelDialog(PollLevelType pollLevelType); + // Task ShowMqttSelectionDialog(); + // Task> ShowOpcUaImportDialog(string endpointUrl); + // Task ShowOpcUaUpdateTypeDialog(); + // Task ShowIsActiveDialog(bool currentIsActive); + // Task ShowImportResultDialog(List importedVariables, List existingVariables); + // Task ShowMqttAliasDialog(string variableName, string mqttServerName); + // Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt); } \ No newline at end of file diff --git a/DMS.WPF/Services/INotificationService.cs b/DMS.WPF/Services/INotificationService.cs index 56bace5..5662106 100644 --- a/DMS.WPF/Services/INotificationService.cs +++ b/DMS.WPF/Services/INotificationService.cs @@ -1,10 +1,9 @@ using DMS.Core.Enums; -using DMS.WPF.Models; namespace DMS.Services; public interface INotificationService { - void Show(Notification notification); - void Show(string message, NotificationType type = NotificationType.Info, bool IsGlobal = true); + // void Show(Notification notification); + // void Show(string message, NotificationType type = NotificationType.Info, bool IsGlobal = true); } \ No newline at end of file diff --git a/DMS.WPF/Services/IVariableProcessor.cs b/DMS.WPF/Services/IVariableProcessor.cs index 4e31c26..c171369 100644 --- a/DMS.WPF/Services/IVariableProcessor.cs +++ b/DMS.WPF/Services/IVariableProcessor.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using DMS.WPF.Models; +using DMS.Core.Models; namespace DMS.Services; diff --git a/DMS.WPF/Services/Processors/CheckValueChangedProcessor.cs b/DMS.WPF/Services/Processors/CheckValueChangedProcessor.cs index ded0cf4..3de91bc 100644 --- a/DMS.WPF/Services/Processors/CheckValueChangedProcessor.cs +++ b/DMS.WPF/Services/Processors/CheckValueChangedProcessor.cs @@ -1,5 +1,7 @@ +using DMS.Core.Helper; +using DMS.Core.Models; using DMS.Helper; -using DMS.WPF.Models; +using DMS.WPF.Services; namespace DMS.Services.Processors; @@ -14,19 +16,19 @@ public class CheckValueChangedProcessor : IVariableProcessor public Task ProcessAsync(VariableContext context) { Variable newVariable = context.Data; - if (!_dataServices.AllVariables.TryGetValue(newVariable.Id, out Variable oldVariable)) - { - NlogHelper.Warn($"检查变量值是否改变时在_dataServices.AllVariables中找不到Id:{newVariable.Id},Name:{newVariable.Name}的变量。"); - context.IsHandled = true; - return Task.CompletedTask; - } + // if (!_dataServices.AllVariables.TryGetValue(newVariable.Id, out Variable oldVariable)) + // { + // NlogHelper.Warn($"检查变量值是否改变时在_dataServices.AllVariables中找不到Id:{newVariable.Id},Name:{newVariable.Name}的变量。"); + // context.IsHandled = true; + // return Task.CompletedTask; + // } - if (newVariable.DataValue == oldVariable.DataValue) - { - // 值没有变化,直接完成 - context.IsHandled = true; - } - + // if (newVariable.DataValue == oldVariable.DataValue) + // { + // // 值没有变化,直接完成 + // context.IsHandled = true; + // } + // // 在这里处理 context.Data return Task.CompletedTask; } diff --git a/DMS.WPF/Services/Processors/HistoryProcessor.cs b/DMS.WPF/Services/Processors/HistoryProcessor.cs index b64a7c3..adea7b1 100644 --- a/DMS.WPF/Services/Processors/HistoryProcessor.cs +++ b/DMS.WPF/Services/Processors/HistoryProcessor.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using DMS.Infrastructure.Data; -using DMS.Infrastructure.Entities; using DMS.Core.Helper; -using DMS.WPF.Models; +using DMS.Core.Models; +using DMS.Services; using Microsoft.Extensions.Logging; namespace DMS.WPF.Services.Processors; @@ -17,7 +10,7 @@ public class HistoryProcessor : IVariableProcessor, IDisposable private const int BATCH_SIZE = 50; // 批量写入的阈值 private const int TIMER_INTERVAL_MS = 30 * 1000; // 30秒 - private readonly ConcurrentQueue _queue = new(); + // private readonly ConcurrentQueue _queue = new(); private readonly Timer _timer; public HistoryProcessor() @@ -30,55 +23,55 @@ public class HistoryProcessor : IVariableProcessor, IDisposable public async Task ProcessAsync(VariableContext context) { // 只有当数据发生变化时才记录历史 - if (!context.Data.IsSave) // 如果数据已经被其他处理器处理过或者不需要保存,则跳过 - { - return; - } - - // 将 Variable 转换为 DbVariableHistory - var historyData = new DbVariableHistory - { - Name = context.Data.Name, - NodeId = context.Data.NodeId, - DataValue = context.Data.DataValue, - VariableId = context.Data.Id, - Timestamp = DateTime.Now // 记录当前时间 - }; - - _queue.Enqueue(historyData); - - if (_queue.Count >= BATCH_SIZE) - { - await FlushQueueToDatabase(); - } + // if (!context.Data.IsSave) // 如果数据已经被其他处理器处理过或者不需要保存,则跳过 + // { + // return; + // } + // + // // 将 Variable 转换为 DbVariableHistory + // var historyData = new DbVariableHistory + // { + // Name = context.Data.Name, + // NodeId = context.Data.NodeId, + // DataValue = context.Data.DataValue, + // VariableId = context.Data.Id, + // Timestamp = DateTime.Now // 记录当前时间 + // }; + // + // _queue.Enqueue(historyData); + // + // if (_queue.Count >= BATCH_SIZE) + // { + // await FlushQueueToDatabase(); + // } } private async Task FlushQueueToDatabase() { // 停止定时器,防止在写入过程中再次触发 - _timer.Change(Timeout.Infinite, Timeout.Infinite); - - var itemsToProcess = new List(); - while (_queue.TryDequeue(out var item)) - { - itemsToProcess.Add(item); - } - - if (itemsToProcess.Any()) - { - try - { - using var db = DbContext.GetInstance(); - await db.Insertable(itemsToProcess).ExecuteCommandAsync(); - NlogHelper.Info($"成功批量插入 {itemsToProcess.Count} 条历史变量数据。"); - } - catch (Exception ex) - { - NlogHelper.Error( $"批量插入历史变量数据时发生错误: {ex.Message}",ex); - // 错误处理:可以将未成功插入的数据重新放回队列,或者记录到日志中以便后续处理 - // 为了简单起见,这里不重新入队,避免无限循环 - } - } + // _timer.Change(Timeout.Infinite, Timeout.Infinite); + // + // var itemsToProcess = new List(); + // while (_queue.TryDequeue(out var item)) + // { + // itemsToProcess.Add(item); + // } + // + // if (itemsToProcess.Any()) + // { + // try + // { + // using var db = DbContext.GetInstance(); + // await db.Insertable(itemsToProcess).ExecuteCommandAsync(); + // NlogHelper.Info($"成功批量插入 {itemsToProcess.Count} 条历史变量数据。"); + // } + // catch (Exception ex) + // { + // NlogHelper.Error( $"批量插入历史变量数据时发生错误: {ex.Message}",ex); + // // 错误处理:可以将未成功插入的数据重新放回队列,或者记录到日志中以便后续处理 + // // 为了简单起见,这里不重新入队,避免无限循环 + // } + // } // 重新启动定时器 _timer.Change(TIMER_INTERVAL_MS, TIMER_INTERVAL_MS); diff --git a/DMS.WPF/Services/Processors/LoggingProcessor.cs b/DMS.WPF/Services/Processors/LoggingProcessor.cs index a9ac817..403dad8 100644 --- a/DMS.WPF/Services/Processors/LoggingProcessor.cs +++ b/DMS.WPF/Services/Processors/LoggingProcessor.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using DMS.WPF.Models; +using DMS.Core.Models; using Microsoft.Extensions.Logging; using DMS.Helper; diff --git a/DMS.WPF/Services/Processors/MqttPublishProcessor.cs b/DMS.WPF/Services/Processors/MqttPublishProcessor.cs index e3ebd74..0c99cec 100644 --- a/DMS.WPF/Services/Processors/MqttPublishProcessor.cs +++ b/DMS.WPF/Services/Processors/MqttPublishProcessor.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using DMS.WPF.Models; +using DMS.Core.Models; namespace DMS.Services.Processors { @@ -8,12 +8,12 @@ namespace DMS.Services.Processors /// public class MqttPublishProcessor : IVariableProcessor { - private readonly MqttBackgroundService _mqttBackgroundService; - - public MqttPublishProcessor(MqttBackgroundService mqttBackgroundService) - { - _mqttBackgroundService = mqttBackgroundService; - } + // private readonly MqttBackgroundService _mqttBackgroundService; + // + // public MqttPublishProcessor(MqttBackgroundService mqttBackgroundService) + // { + // _mqttBackgroundService = mqttBackgroundService; + // } /// /// 处理单个变量上下文,如果有关联的MQTT配置,则将其推送到发送队列。 @@ -21,19 +21,19 @@ namespace DMS.Services.Processors /// 包含变量及其元数据的上下文对象。 public async Task ProcessAsync(VariableContext context) { - var variable = context.Data; - if (variable?.VariableMqtts == null || variable.VariableMqtts.Count == 0) - { - return; // 没有关联的MQTT配置,直接返回 - } - - // 遍历所有关联的MQTT配置,并将其推入发送队列 - foreach (var variableMqtt in variable.VariableMqtts) - { - // 确保VariableMqtt对象中包含了最新的Variable数据 - variableMqtt.Variable = variable; - await _mqttBackgroundService.SendVariableAsync(variableMqtt); - } + // var variable = context.Data; + // if (variable?.VariableMqtts == null || variable.VariableMqtts.Count == 0) + // { + // return; // 没有关联的MQTT配置,直接返回 + // } + // + // // 遍历所有关联的MQTT配置,并将其推入发送队列 + // foreach (var variableMqtt in variable.VariableMqtts) + // { + // // 确保VariableMqtt对象中包含了最新的Variable数据 + // variableMqtt.Variable = variable; + // await _mqttBackgroundService.SendVariableAsync(variableMqtt); + // } } } } diff --git a/DMS.WPF/Services/Processors/UpdateDbVariableProcessor.cs b/DMS.WPF/Services/Processors/UpdateDbVariableProcessor.cs index ae2db60..a5b378d 100644 --- a/DMS.WPF/Services/Processors/UpdateDbVariableProcessor.cs +++ b/DMS.WPF/Services/Processors/UpdateDbVariableProcessor.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; +using DMS.Core.Helper; +using DMS.Core.Models; using DMS.Helper; -using DMS.WPF.Models; +using DMS.WPF.Services; namespace DMS.Services.Processors { @@ -18,15 +20,15 @@ namespace DMS.Services.Processors try { // 假设 DataServices 有一个方法来更新 Variable - await _dataServices.UpdateVariableAsync(context.Data); + // await _dataServices.UpdateVariableAsync(context.Data); // NlogHelper.Info($"数据库变量 {context.Data.Name} 更新成功,值为: {context.Data.DataValue}"); - if (!_dataServices.AllVariables.TryGetValue(context.Data.Id, out Variable oldVariable)) - { - NlogHelper.Warn($"数据库更新完成修改变量值是否改变时在_dataServices.AllVariables中找不到Id:{context.Data.Id},Name:{context.Data.Name}的变量。"); - context.IsHandled = true; - } - oldVariable.DataValue = context.Data.DataValue; + // if (!_dataServices.AllVariables.TryGetValue(context.Data.Id, out Variable oldVariable)) + // { + // NlogHelper.Warn($"数据库更新完成修改变量值是否改变时在_dataServices.AllVariables中找不到Id:{context.Data.Id},Name:{context.Data.Name}的变量。"); + // context.IsHandled = true; + // } + // oldVariable.DataValue = context.Data.DataValue; } catch (Exception ex) { diff --git a/DMS.WPF/ViewModels/DeviceDetailViewModel.cs b/DMS.WPF/ViewModels/DeviceDetailViewModel.cs index f946e9b..acc0189 100644 --- a/DMS.WPF/ViewModels/DeviceDetailViewModel.cs +++ b/DMS.WPF/ViewModels/DeviceDetailViewModel.cs @@ -1,36 +1,25 @@ -using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.WPF.Models; using DMS.WPF.Services; -using DMS.Core.Helper; -using DMS.Infrastructure.Data; -using DMS.Infrastructure.Repositories; - -using Microsoft.Extensions.DependencyInjection; using DMS.Services; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels; public partial class DeviceDetailViewModel : ViewModelBase { private readonly IDialogService _dialogService; - private readonly VarTableRepository _varTableRepository; - private readonly MenuRepository _menuRepository; private readonly DataServices _dataServices; [ObservableProperty] - private Device _currentDevice; + private DeviceItemViewModel _currentDevice; [ObservableProperty] - private VariableTable _selectedVariableTable; + private VariableItemViewModel _selectedVariableTable; - public DeviceDetailViewModel(IDialogService dialogService, VarTableRepository varTableRepository, - MenuRepository menuRepository, DataServices dataServices) + public DeviceDetailViewModel(IDialogService dialogService, DataServices dataServices) { _dialogService = dialogService; - _varTableRepository = varTableRepository; - _menuRepository = menuRepository; _dataServices = dataServices; } @@ -51,165 +40,165 @@ public partial class DeviceDetailViewModel : ViewModelBase [RelayCommand] private async Task AddVariableTable() { - using var db = DbContext.GetInstance(); - try - { - // 1. Show dialog to get new variable table details - var newVarTable = await _dialogService.ShowAddVarTableDialog(); - if (newVarTable == null) return; // User cancelled - - // 2. Set properties for the new variable table - newVarTable.DeviceId = CurrentDevice.Id; - newVarTable.ProtocolType = CurrentDevice.ProtocolType; - newVarTable.IsActive = true; - - // 3. Find the parent menu for the current device - var parentMenu = DataServicesHelper.FindMenusForDevice(CurrentDevice, _dataServices.MenuTrees); - if (parentMenu == null) - { - //NotificationHelper.ShowError("无法找到当前设备的父级菜单,无法添加变量表菜单。"); - return; - } - - // 4. Start database transaction - await db.BeginTranAsync(); - - // 5. AddAsync variable table to the database - var addedVarTable = await _varTableRepository.AddAsync(newVarTable, db); - - // 6. Create and add the corresponding menu item - var newMenu = new MenuBean - { - Name = addedVarTable.Name, - DataId = addedVarTable.Id, - Type = MenuType.VariableTableMenu, - ParentId = parentMenu.Id, - Icon = iNKORE.UI.WPF.Modern.Common.IconKeys.SegoeFluentIcons.Tablet.Glyph - }; - await _menuRepository.AddAsync(newMenu, db); - - // 7. Commit transaction - await db.CommitTranAsync(); - - // 8. Update UI - CurrentDevice?.VariableTables?.Add(addedVarTable); - MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu - //NotificationHelper.ShowSuccess($"变量表 {addedVarTable.Name} 添加成功。"); - } - catch (Exception ex) - { - await db.RollbackTranAsync(); - //NotificationHelper.ShowError($"添加变量表时发生错误: {ex.Message}", ex); - } + // using var db = DbContext.GetInstance(); + // try + // { + // // 1. Show dialog to get new variable table details + // var newVarTable = await _dialogService.ShowAddVarTableDialog(); + // if (newVarTable == null) return; // User cancelled + // + // // 2. Set properties for the new variable table + // newVarTable.DeviceId = CurrentDevice.Id; + // newVarTable.ProtocolType = CurrentDevice.ProtocolType; + // newVarTable.IsActive = true; + // + // // 3. Find the parent menu for the current device + // var parentMenu = DataServicesHelper.FindMenusForDevice(CurrentDevice, _dataServices.MenuTrees); + // if (parentMenu == null) + // { + // //NotificationHelper.ShowError("无法找到当前设备的父级菜单,无法添加变量表菜单。"); + // return; + // } + // + // // 4. Start database transaction + // await db.BeginTranAsync(); + // + // // 5. AddAsync variable table to the database + // var addedVarTable = await _varTableRepository.AddAsync(newVarTable, db); + // + // // 6. Create and add the corresponding menu item + // var newMenu = new MenuBean + // { + // Name = addedVarTable.Name, + // DataId = addedVarTable.Id, + // Type = MenuType.VariableTableMenu, + // ParentId = parentMenu.Id, + // Icon = iNKORE.UI.WPF.Modern.Common.IconKeys.SegoeFluentIcons.Tablet.Glyph + // }; + // await _menuRepository.AddAsync(newMenu, db); + // + // // 7. Commit transaction + // await db.CommitTranAsync(); + // + // // 8. Update UI + // CurrentDevice?.VariableTables?.Add(addedVarTable); + // MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu + // //NotificationHelper.ShowSuccess($"变量表 {addedVarTable.Name} 添加成功。"); + // } + // catch (Exception ex) + // { + // await db.RollbackTranAsync(); + // //NotificationHelper.ShowError($"添加变量表时发生错误: {ex.Message}", ex); + // } } [RelayCommand] private async Task EditVariableTable() { - if (SelectedVariableTable == null) - { - //NotificationHelper.ShowInfo("请选择要编辑的变量表。"); - return; - } - - using var db = DbContext.GetInstance(); - try - { - var originalName = SelectedVariableTable.Name; // Store original name for comparison - var editedVarTable = await _dialogService.ShowEditVarTableDialog(SelectedVariableTable); - if (editedVarTable == null) return; - - await db.BeginTranAsync(); - - // Update variable table in DB - var result = await _varTableRepository.UpdateAsync(SelectedVariableTable, db); - - if (result > 0) - { - // Update corresponding menu item if name changed - if (originalName != SelectedVariableTable.Name) - { - var menu = DataServicesHelper.FindVarTableMenu(SelectedVariableTable.Id, _dataServices.MenuTrees); - if (menu != null) - { - menu.Name = SelectedVariableTable.Name; - await _menuRepository.UpdateAsync(menu, db); - } - } - - await db.CommitTranAsync(); - //NotificationHelper.ShowSuccess($"变量表 {SelectedVariableTable.Name} 编辑成功。"); - MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu - } - else - { - await db.RollbackTranAsync(); - //NotificationHelper.ShowError($"变量表 {SelectedVariableTable.Name} 编辑失败。"); - } - } - catch (Exception ex) - { - await db.RollbackTranAsync(); - //NotificationHelper.ShowError($"编辑变量表时发生错误: {ex.Message}", ex); - } + // if (SelectedVariableTable == null) + // { + // //NotificationHelper.ShowInfo("请选择要编辑的变量表。"); + // return; + // } + // + // using var db = DbContext.GetInstance(); + // try + // { + // var originalName = SelectedVariableTable.Name; // Store original name for comparison + // var editedVarTable = await _dialogService.ShowEditVarTableDialog(SelectedVariableTable); + // if (editedVarTable == null) return; + // + // await db.BeginTranAsync(); + // + // // Update variable table in DB + // var result = await _varTableRepository.UpdateAsync(SelectedVariableTable, db); + // + // if (result > 0) + // { + // // Update corresponding menu item if name changed + // if (originalName != SelectedVariableTable.Name) + // { + // var menu = DataServicesHelper.FindVarTableMenu(SelectedVariableTable.Id, _dataServices.MenuTrees); + // if (menu != null) + // { + // menu.Name = SelectedVariableTable.Name; + // await _menuRepository.UpdateAsync(menu, db); + // } + // } + // + // await db.CommitTranAsync(); + // //NotificationHelper.ShowSuccess($"变量表 {SelectedVariableTable.Name} 编辑成功。"); + // MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu + // } + // else + // { + // await db.RollbackTranAsync(); + // //NotificationHelper.ShowError($"变量表 {SelectedVariableTable.Name} 编辑失败。"); + // } + // } + // catch (Exception ex) + // { + // await db.RollbackTranAsync(); + // //NotificationHelper.ShowError($"编辑变量表时发生错误: {ex.Message}", ex); + // } } [RelayCommand] private async Task DeleteVariableTable() { - if (SelectedVariableTable == null) - { - //NotificationHelper.ShowInfo("请选择要删除的变量表。"); - return; - } - - var confirm = await _dialogService.ShowConfrimeDialog( - "删除确认", - $"确定要删除变量表 \"{SelectedVariableTable.Name}\" 吗?\n\n此操作将同时删除该变量表下的所有变量数据,且无法恢复!", - "删除"); - - if (!confirm) return; - - using var db = DbContext.GetInstance(); - try - { - await db.BeginTranAsync(); - - // Find the corresponding menu item - MenuBean menuToDelete = null; - if (_dataServices.MenuTrees != null) - { - menuToDelete = DataServicesHelper.FindVarTableMenu( SelectedVariableTable.Id,_dataServices.MenuTrees); - } - - // Delete variable table from DB - var result = await _varTableRepository.DeleteAsync(SelectedVariableTable, db); - - if (result > 0) - { - // Delete corresponding menu item - if (menuToDelete != null) - { - await _menuRepository.DeleteAsync(menuToDelete, db); - } - - await db.CommitTranAsync(); - var delVarTableName = SelectedVariableTable.Name; - CurrentDevice?.VariableTables?.Remove(SelectedVariableTable); - //NotificationHelper.ShowSuccess($"变量表 {delVarTableName} 删除成功。"); - MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu - } - else - { - await db.RollbackTranAsync(); - //NotificationHelper.ShowError($"变量表 {SelectedVariableTable.Name} 删除失败。"); - } - } - catch (Exception ex) - { - await db.RollbackTranAsync(); - //NotificationHelper.ShowError($"删除变量表时发生错误: {ex.Message}", ex); - } + // if (SelectedVariableTable == null) + // { + // //NotificationHelper.ShowInfo("请选择要删除的变量表。"); + // return; + // } + // + // var confirm = await _dialogService.ShowConfrimeDialog( + // "删除确认", + // $"确定要删除变量表 \"{SelectedVariableTable.Name}\" 吗?\n\n此操作将同时删除该变量表下的所有变量数据,且无法恢复!", + // "删除"); + // + // if (!confirm) return; + // + // using var db = DbContext.GetInstance(); + // try + // { + // await db.BeginTranAsync(); + // + // // Find the corresponding menu item + // MenuBean menuToDelete = null; + // if (_dataServices.MenuTrees != null) + // { + // menuToDelete = DataServicesHelper.FindVarTableMenu( SelectedVariableTable.Id,_dataServices.MenuTrees); + // } + // + // // Delete variable table from DB + // var result = await _varTableRepository.DeleteAsync(SelectedVariableTable, db); + // + // if (result > 0) + // { + // // Delete corresponding menu item + // if (menuToDelete != null) + // { + // await _menuRepository.DeleteAsync(menuToDelete, db); + // } + // + // await db.CommitTranAsync(); + // var delVarTableName = SelectedVariableTable.Name; + // CurrentDevice?.VariableTables?.Remove(SelectedVariableTable); + // //NotificationHelper.ShowSuccess($"变量表 {delVarTableName} 删除成功。"); + // MessageHelper.SendLoadMessage(Enums.LoadTypes.Menu); // Refresh the main navigation menu + // } + // else + // { + // await db.RollbackTranAsync(); + // //NotificationHelper.ShowError($"变量表 {SelectedVariableTable.Name} 删除失败。"); + // } + // } + // catch (Exception ex) + // { + // await db.RollbackTranAsync(); + // //NotificationHelper.ShowError($"删除变量表时发生错误: {ex.Message}", ex); + // } } // Placeholder for EditDeviceCommand and DeleteDeviceCommand if they are needed here @@ -232,10 +221,10 @@ public partial class DeviceDetailViewModel : ViewModelBase [RelayCommand] private void NavigateToVariableTable() { - if (SelectedVariableTable == null) return; - - var variableTableVm = App.Current.Services.GetRequiredService(); - variableTableVm.VariableTable = SelectedVariableTable; - MessageHelper.SendNavgatorMessage(variableTableVm); + // if (SelectedVariableTable == null) return; + // + // var variableTableVm = App.Current.Services.GetRequiredService(); + // variableTableVm.VariableTable = SelectedVariableTable; + // MessageHelper.SendNavgatorMessage(variableTableVm); } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/DevicesViewModel.cs b/DMS.WPF/ViewModels/DevicesViewModel.cs index a404985..3815c6d 100644 --- a/DMS.WPF/ViewModels/DevicesViewModel.cs +++ b/DMS.WPF/ViewModels/DevicesViewModel.cs @@ -1,17 +1,16 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.Data.Repositories; using DMS.Core.Enums; +using DMS.Core.Helper; +using DMS.Core.Models; using DMS.Helper; -using DMS.WPF.Models; using DMS.Services; +using DMS.WPF.Helper; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using DMS.Data; using DMS.WPF.Services; -using DMS.Infrastructure.Repositories; -using DMS.WPF.Models; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels; @@ -22,23 +21,20 @@ public partial class DevicesViewModel : ViewModelBase { private readonly DataServices _dataServices; private readonly IDialogService _dialogService; - private readonly DeviceRepository _deviceRepository; - - private readonly MenuRepository _menuRepository; - private readonly VarTableRepository _varTableRepository; + /// /// 设备列表。 /// [ObservableProperty] - private ObservableCollection _devices; + private ObservableCollection _devices; /// /// 当前选中的设备。 /// [ObservableProperty] - private Device _selectedDevice; + private DeviceItemViewModel _selectedDevice; /// /// 初始化 类的新实例。 @@ -47,59 +43,55 @@ public partial class DevicesViewModel : ViewModelBase /// 对话框服务。 /// 数据服务。 public DevicesViewModel( - IDialogService dialogService, DataServices dataServices,DeviceRepository deviceRepository,VarTableRepository varTableRepository,MenuRepository menuRepository - ) + IDialogService dialogService, DataServices dataServices) { - _deviceRepository = deviceRepository; - _varTableRepository = varTableRepository; - _menuRepository = menuRepository; _dialogService = dialogService; _dataServices = dataServices; - + Devices = new ObservableCollection(); _dataServices.OnDeviceListChanged += (devices) => { - Devices = new ObservableCollection(devices); + }; } - public override void OnLoaded() - { - if (_dataServices.Devices!=null && _dataServices.Devices.Count>0) - { - Devices=new ObservableCollection(_dataServices.Devices); - foreach (var device in Devices) - { - device.OnDeviceIsActiveChanged += HandleDeviceIsActiveChanged; - - } - } - } + // public override void OnLoaded() + // { + // if (_dataServices.Devices!=null && _dataServices.Devices.Count>0) + // { + // Devices=new ObservableCollection(_dataServices.Devices); + // foreach (var device in Devices) + // { + // device.OnDeviceIsActiveChanged += HandleDeviceIsActiveChanged; + // + // } + // } + // } public override Task OnExitAsync() { - if (_dataServices.Devices!=null && _dataServices.Devices.Count>0) - { - foreach (var device in Devices) - { - device.OnDeviceIsActiveChanged -= HandleDeviceIsActiveChanged; - } - } + // if (_dataServices.Devices!=null && _dataServices.Devices.Count>0) + // { + // foreach (var device in Devices) + // { + // device.OnDeviceIsActiveChanged -= HandleDeviceIsActiveChanged; + // } + // } return Task.FromResult(true); } private async void HandleDeviceIsActiveChanged(Device device, bool isActive) { - try - { - await _deviceRepository.UpdateAsync(device); - NotificationHelper.ShowSuccess($"设备 {device.Name} 的激活状态已更新。"); - } - catch (Exception ex) - { - NotificationHelper.ShowError($"更新设备 {device.Name} 激活状态失败: {ex.Message}", ex); - } + // try + // { + // await _deviceRepository.UpdateAsync(device); + // NotificationHelper.ShowSuccess($"设备 {device.Name} 的激活状态已更新。"); + // } + // catch (Exception ex) + // { + // NotificationHelper.ShowError($"更新设备 {device.Name} 激活状态失败: {ex.Message}", ex); + // } } /// @@ -108,26 +100,26 @@ public partial class DevicesViewModel : ViewModelBase [RelayCommand] public async void AddDevice() { - try - { - // 1. 显示添加设备对话框 - var device = await _dialogService.ShowAddDeviceDialog(); - // 如果用户取消或对话框未返回设备,则直接返回 - if (device == null) - { - NlogHelper.Info("用户取消了添加设备操作。"); - return; - } - - if (device.ProtocolType == ProtocolType.OpcUA) - device.OpcUaEndpointUrl = $"opc.tcp://{device.Ip}:{device.Prot}"; - - await _deviceRepository.AddAsync(device); - } - catch (Exception e) - { - NotificationHelper.ShowError($"添加设备的过程中发生错误:{e.Message}", e); - } + // try + // { + // // 1. 显示添加设备对话框 + // var device = await _dialogService.ShowAddDeviceDialog(); + // // 如果用户取消或对话框未返回设备,则直接返回 + // if (device == null) + // { + // NlogHelper.Info("用户取消了添加设备操作。"); + // return; + // } + // + // if (device.ProtocolType == ProtocolType.OpcUA) + // device.OpcUaEndpointUrl = $"opc.tcp://{device.Ip}:{device.Prot}"; + // + // await _deviceRepository.AddAsync(device); + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"添加设备的过程中发生错误:{e.Message}", e); + // } } /// @@ -137,31 +129,31 @@ public partial class DevicesViewModel : ViewModelBase public async void DeleteDevice() { - try - { - if (SelectedDevice == null) - { - NotificationHelper.ShowError("你没有选择任何设备,请选择设备后再点击删除设备"); - return; - } - - string msg = $"确认要删除设备名为:{SelectedDevice.Name}"; - var isDel = await _dialogService.ShowConfrimeDialog("删除设备", msg, "删除设备"); - if (isDel) - { - - // 删除设备 - await _deviceRepository.DeleteAsync(SelectedDevice ,_dataServices.MenuTrees); - - MessageHelper.SendLoadMessage(LoadTypes.Menu); - MessageHelper.SendLoadMessage(LoadTypes.Devices); - NotificationHelper.ShowSuccess($"删除设备成功,设备名:{SelectedDevice.Name}"); - } - } - catch (Exception e) - { - NotificationHelper.ShowError($"删除设备的过程中发生错误:{e.Message}", e); - } + // try + // { + // if (SelectedDevice == null) + // { + // NotificationHelper.ShowError("你没有选择任何设备,请选择设备后再点击删除设备"); + // return; + // } + // + // string msg = $"确认要删除设备名为:{SelectedDevice.Name}"; + // var isDel = await _dialogService.ShowConfrimeDialog("删除设备", msg, "删除设备"); + // if (isDel) + // { + // + // // 删除设备 + // await _deviceRepository.DeleteAsync(SelectedDevice ,_dataServices.MenuTrees); + // + // MessageHelper.SendLoadMessage(LoadTypes.Menu); + // MessageHelper.SendLoadMessage(LoadTypes.Devices); + // NotificationHelper.ShowSuccess($"删除设备成功,设备名:{SelectedDevice.Name}"); + // } + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"删除设备的过程中发生错误:{e.Message}", e); + // } } /// @@ -170,31 +162,31 @@ public partial class DevicesViewModel : ViewModelBase [RelayCommand] public async void EditDevice() { - try - { - if (SelectedDevice == null) - { - NotificationHelper.ShowError("你没有选择任何设备,请选择设备后再点击编辑设备"); - return; - } - - var editDievce = await _dialogService.ShowEditDeviceDialog(SelectedDevice); - if (editDievce != null) - { - // 更新菜单 - var res = await _deviceRepository.UpdateAsync(editDievce); - var menu = DataServicesHelper.FindMenusForDevice(editDievce, _dataServices.MenuTrees); - if (menu != null) - await _menuRepository.UpdateAsync(menu); - - MessageHelper.SendLoadMessage(LoadTypes.Menu); - MessageHelper.SendLoadMessage(LoadTypes.Devices); - } - } - catch (Exception e) - { - NotificationHelper.ShowError($"编辑设备的过程中发生错误:{e.Message}", e); - } + // try + // { + // if (SelectedDevice == null) + // { + // NotificationHelper.ShowError("你没有选择任何设备,请选择设备后再点击编辑设备"); + // return; + // } + // + // var editDievce = await _dialogService.ShowEditDeviceDialog(SelectedDevice); + // if (editDievce != null) + // { + // // 更新菜单 + // var res = await _deviceRepository.UpdateAsync(editDievce); + // var menu = DataServicesHelper.FindMenusForDevice(editDievce, _dataServices.MenuTrees); + // if (menu != null) + // await _menuRepository.UpdateAsync(menu); + // + // MessageHelper.SendLoadMessage(LoadTypes.Menu); + // MessageHelper.SendLoadMessage(LoadTypes.Devices); + // } + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"编辑设备的过程中发生错误:{e.Message}", e); + // } } [RelayCommand] diff --git a/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs index a7ebebd..d85893e 100644 --- a/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs @@ -1,32 +1,31 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.WPF.Models; -using S7.Net; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels.Dialogs; public partial class DeviceDialogViewModel : ObservableObject { [ObservableProperty] - private Device _device; - partial void OnDeviceChanged(Device value) + private DeviceItemViewModel _device; + partial void OnDeviceChanged(DeviceItemViewModel value) { - if (value != null) - { - System.Diagnostics.Debug.WriteLine($"Device ProtocolType changed to: {value.ProtocolType}"); - } + // if (value != null) + // { + // System.Diagnostics.Debug.WriteLine($"Device ProtocolType changed to: {value.ProtocolType}"); + // } } [ObservableProperty] private string title ; [ObservableProperty] private string primaryButContent ; - public DeviceDialogViewModel(Device device) + public DeviceDialogViewModel(DeviceItemViewModel device) { _device = device; } // AddAsync a property to expose CpuType enum values for ComboBox - public Array CpuTypes => Enum.GetValues(typeof(CpuType)); + // public Array CpuTypes => Enum.GetValues(typeof(CpuType)); [RelayCommand] diff --git a/DMS.WPF/ViewModels/Dialogs/ImportExcelDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/ImportExcelDialogViewModel.cs index 2496709..3f991f2 100644 --- a/DMS.WPF/ViewModels/Dialogs/ImportExcelDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/ImportExcelDialogViewModel.cs @@ -1,8 +1,5 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Helper; -using DMS.WPF.Models; -using DMS.WPF.Models; namespace DMS.WPF.ViewModels.Dialogs; @@ -12,23 +9,23 @@ public partial class ImportExcelDialogViewModel : ObservableObject private string? _filePath; [ObservableProperty] - private ObservableCollection _variables = new(); + private ObservableCollection _variables = new(); partial void OnFilePathChanged(string? value) { - if (string.IsNullOrEmpty(value)) - { - return; - } - - try - { - var data = ExcelHelper.ImprotFromTiaVariableTable(value); - Variables = new ObservableCollection(data); - } - catch (System.Exception ex) - { - // Handle exception - } + // if (string.IsNullOrEmpty(value)) + // { + // return; + // } + // + // try + // { + // var data = ExcelHelper.ImprotFromTiaVariableTable(value); + // Variables = new ObservableCollection(data); + // } + // catch (System.Exception ex) + // { + // // Handle exception + // } } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/ImportResultDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/ImportResultDialogViewModel.cs index 083c18e..7d231c9 100644 --- a/DMS.WPF/ViewModels/Dialogs/ImportResultDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/ImportResultDialogViewModel.cs @@ -1,7 +1,6 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.WPF.Models; namespace DMS.WPF.ViewModels.Dialogs; diff --git a/DMS.WPF/ViewModels/Dialogs/MqttAliasBatchEditDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/MqttAliasBatchEditDialogViewModel.cs index e751a04..f6446f4 100644 --- a/DMS.WPF/ViewModels/Dialogs/MqttAliasBatchEditDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/MqttAliasBatchEditDialogViewModel.cs @@ -3,8 +3,7 @@ using CommunityToolkit.Mvvm.Input; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Linq; -using DMS.WPF.Models; -using DMS.WPF.Models; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels.Dialogs; @@ -14,22 +13,22 @@ public partial class MqttAliasBatchEditDialogViewModel : ObservableObject private string _title = "批量设置MQTT别名"; [ObservableProperty] - private ObservableCollection _variablesToEdit; + private ObservableCollection _variablesToEdit; - public Mqtt SelectedMqtt { get; private set; } + public MqttServerItemViewModel SelectedMqtt { get; private set; } - public MqttAliasBatchEditDialogViewModel(List selectedVariables, Mqtt selectedMqtt) + public MqttAliasBatchEditDialogViewModel(List selectedVariables, MqttServerItemViewModel selectedMqtt) { SelectedMqtt = selectedMqtt; - Title=$"设置:{SelectedMqtt.Name}-MQTT服务器关联变量的别名"; - VariablesToEdit = new ObservableCollection( - selectedVariables.Select(v => new VariableMqtt(v, selectedMqtt)) - ); + Title=$"设置:{SelectedMqtt.ServerName}-MQTT服务器关联变量的别名"; + // VariablesToEdit = new ObservableCollection( + // selectedVariables.Select(v => new VariableMqttAlias(v, selectedMqtt)) + // ); } public MqttAliasBatchEditDialogViewModel() { // For design time - VariablesToEdit = new ObservableCollection(); + // VariablesToEdit = new ObservableCollection(); } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/MqttDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/MqttDialogViewModel.cs index d4d1eb4..c72488a 100644 --- a/DMS.WPF/ViewModels/Dialogs/MqttDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/MqttDialogViewModel.cs @@ -1,27 +1,25 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.WPF.Models; -using DMS.WPF.Models; namespace DMS.WPF.ViewModels.Dialogs; public partial class MqttDialogViewModel : ObservableObject { - [ObservableProperty] - private Mqtt _mqtt; - - [ObservableProperty] private string title ; - [ObservableProperty] private string primaryButContent ; - - public MqttDialogViewModel(Mqtt mqtt) - { - _mqtt = mqtt; - } - - - [RelayCommand] - public void AddMqtt() - { - - } + // [ObservableProperty] + // private Mqtt _mqtt; + // + // [ObservableProperty] private string title ; + // [ObservableProperty] private string primaryButContent ; + // + // public MqttDialogViewModel(Mqtt mqtt) + // { + // _mqtt = mqtt; + // } + // + // + // [RelayCommand] + // public void AddMqtt() + // { + // + // } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/MqttSelectionDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/MqttSelectionDialogViewModel.cs index 7ca9367..72d88d0 100644 --- a/DMS.WPF/ViewModels/Dialogs/MqttSelectionDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/MqttSelectionDialogViewModel.cs @@ -1,27 +1,22 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; -using DMS.Data.Repositories; -using System.Threading.Tasks; -using DMS.WPF.Models; -using DMS.Services; using DMS.WPF.Services; -using DMS.WPF.Models; namespace DMS.WPF.ViewModels.Dialogs; public partial class MqttSelectionDialogViewModel : ObservableObject { - [ObservableProperty] - private ObservableCollection mqtts; - - [ObservableProperty] - private Mqtt? selectedMqtt; - - public MqttSelectionDialogViewModel(DataServices dataServices) - { - Mqtts = new ObservableCollection(dataServices.Mqtts); - } + // [ObservableProperty] + // private ObservableCollection mqtts; + // + // [ObservableProperty] + // private Mqtt? selectedMqtt; + // + // public MqttSelectionDialogViewModel(DataServices dataServices) + // { + // Mqtts = new ObservableCollection(dataServices.Mqtts); + // } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/OpcUaImportDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/OpcUaImportDialogViewModel.cs index dc46e9e..79294e1 100644 --- a/DMS.WPF/ViewModels/Dialogs/OpcUaImportDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/OpcUaImportDialogViewModel.cs @@ -5,258 +5,253 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DMS.Core.Enums; using DMS.Helper; -using DMS.WPF.Models; -using DMS.WPF.Models; -using Opc.Ua; -using Opc.Ua.Client; -using Opc.Ua.Configuration; namespace DMS.WPF.ViewModels.Dialogs; public partial class OpcUaImportDialogViewModel : ObservableObject { - [ObservableProperty] - private string _endpointUrl = "opc.tcp://127.0.0.1:4855"; // 默认值 - - [ObservableProperty] - private ObservableCollection _opcUaNodes; - - [ObservableProperty] - private ObservableCollection _selectedNodeVariables; - - public List SelectedVariables { get; set; }=new List(); - - [ObservableProperty] - private bool _selectAllVariables; - - [ObservableProperty] - private bool _isConnected; - - private Session _session; - - public OpcUaImportDialogViewModel() - { - OpcUaNodes = new ObservableCollection(); - SelectedNodeVariables = new ObservableCollection(); - // Automatically connect when the ViewModel is created - ConnectCommand.Execute(null); - - } - - [RelayCommand] - private async Task Connect() - { - try - { - // 断开现有连接 - if (_session != null && _session.Connected) - { - await _session.CloseAsync(); - _session.Dispose(); - _session = null; - } - - IsConnected = false; - OpcUaNodes.Clear(); - SelectedNodeVariables.Clear(); - - _session = await ServiceHelper.CreateOpcUaSessionAsync(EndpointUrl); - - NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {EndpointUrl}"); - IsConnected = true; - - // 浏览根节点 - await BrowseNodes(OpcUaNodes, ObjectIds.ObjectsFolder); - } - catch (Exception ex) - { - IsConnected = false; - NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex); - } - } - - /// - /// 处理来自服务器的数据变化通知 - /// - private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e) - { - foreach (var value in item.DequeueValues()) - { - Console.WriteLine( - $"[通知] {item.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}"); - } - } - - private async Task BrowseNodes(ObservableCollection nodes, NodeId parentNodeId) - { - try - { - Opc.Ua.ReferenceDescriptionCollection references; - byte[] continuationPoint = null; - - _session.Browse( - null, // RequestHeader - new ViewDescription(), - parentNodeId, - 0u, - BrowseDirection.Forward, - Opc.Ua.ReferenceTypeIds.HierarchicalReferences, - true, - (uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable, - out continuationPoint, - out references - ); - - foreach (var rd in references) - { - NodeType nodeType = NodeType.Folder; // 默认是文件夹 - if ((rd.NodeClass & NodeClass.Variable) != 0) - { - nodeType = NodeType.Variable; - } - else if ((rd.NodeClass & NodeClass.Object) != 0) - { - nodeType = NodeType.Object; - } - - var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType); - nodes.Add(opcUaNode); - - // 如果是文件夹或对象,添加一个虚拟子节点,用于懒加载 - if (nodeType == NodeType.Folder || nodeType == NodeType.Object) - { - opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点 - } - } - } - catch (Exception ex) - { - NlogHelper.Error($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex); - NotificationHelper.ShowError($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex); - } - } - - public async Task LoadNodeVariables(OpcUaNode node) - { - if (node.NodeType == NodeType.Variable) - { - // 如果是变量节点,直接显示它 - SelectedNodeVariables.Clear(); - SelectedNodeVariables.Add(new Variable - { - Name = node.DisplayName, - NodeId = node.NodeId.ToString(), - OpcUaNodeId = node.NodeId.ToString(), - ProtocolType = ProtocolType.OpcUA, - IsActive = true // 默认选中 - }); - return; - } - - if (node.IsLoaded || node.IsLoading) - { - return; // 已经加载或正在加载 - } - - node.IsLoading = true; - node.Children.Clear(); // 清除虚拟节点 - - try - { - Opc.Ua.ReferenceDescriptionCollection references; - byte[] continuationPoint = null; - - _session.Browse( - null, // RequestHeader - new ViewDescription(), - node.NodeId, - 0u, - BrowseDirection.Forward, - Opc.Ua.ReferenceTypeIds.HierarchicalReferences, - true, - (uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable, - out continuationPoint, - out references - ); - - foreach (var rd in references) - { - NodeType nodeType = NodeType.Folder; - if ((rd.NodeClass & NodeClass.Variable) != 0) - { - nodeType = NodeType.Variable; - } - else if ((rd.NodeClass & NodeClass.Object) != 0) - { - nodeType = NodeType.Object; - } - - var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType); - node.Children.Add(opcUaNode); - - if (nodeType == NodeType.Folder || nodeType == NodeType.Object) - { - opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点 - } - - // 如果是变量,添加到右侧列表 - if (nodeType == NodeType.Variable) - { - // Read the DataType attribute - ReadValueId readValueId = new ReadValueId - { - NodeId = opcUaNode.NodeId, - AttributeId = Attributes.DataType, - // You might need to specify IndexRange and DataEncoding if dealing with arrays or specific encodings - }; - - DataValueCollection results; - DiagnosticInfoCollection diagnosticInfos; - - _session.Read( - null, // RequestHeader - 0, // MaxAge - TimestampsToReturn.Source, - new ReadValueIdCollection { readValueId }, - out results, - out diagnosticInfos - ); - - string dataType = string.Empty; - - if (results != null && results.Count > 0 && results[0].Value != null) - { - // Convert the NodeId of the DataType to a readable string - NodeId dataTypeNodeId = (NodeId)results[0].Value; - dataType = _session.NodeCache.GetDisplayText(dataTypeNodeId); - } - - SelectedNodeVariables.Add(new Variable - { - Name = opcUaNode.DisplayName, - OpcUaNodeId = opcUaNode.NodeId.ToString(), - ProtocolType = ProtocolType.OpcUA, - IsActive = true, // Default selected - DataType = dataType // Assign the read DataType - }); - } - } - - node.IsLoaded = true; - } - catch (Exception ex) - { - NlogHelper.Error($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex); - NotificationHelper.ShowError($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex); - } - finally - { - node.IsLoading = false; - } - } - - public ObservableCollection GetSelectedVariables() - { - return new ObservableCollection(SelectedVariables); - } + // [ObservableProperty] + // private string _endpointUrl = "opc.tcp://127.0.0.1:4855"; // 默认值 + // + // [ObservableProperty] + // private ObservableCollection _opcUaNodes; + // + // [ObservableProperty] + // private ObservableCollection _selectedNodeVariables; + // + // public List SelectedVariables { get; set; }=new List(); + // + // [ObservableProperty] + // private bool _selectAllVariables; + // + // [ObservableProperty] + // private bool _isConnected; + // + // private Session _session; + // + // public OpcUaImportDialogViewModel() + // { + // OpcUaNodes = new ObservableCollection(); + // SelectedNodeVariables = new ObservableCollection(); + // // Automatically connect when the ViewModel is created + // ConnectCommand.Execute(null); + // + // } + // + // [RelayCommand] + // private async Task Connect() + // { + // try + // { + // // 断开现有连接 + // if (_session != null && _session.Connected) + // { + // await _session.CloseAsync(); + // _session.Dispose(); + // _session = null; + // } + // + // IsConnected = false; + // OpcUaNodes.Clear(); + // SelectedNodeVariables.Clear(); + // + // _session = await ServiceHelper.CreateOpcUaSessionAsync(EndpointUrl); + // + // NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {EndpointUrl}"); + // IsConnected = true; + // + // // 浏览根节点 + // await BrowseNodes(OpcUaNodes, ObjectIds.ObjectsFolder); + // } + // catch (Exception ex) + // { + // IsConnected = false; + // NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex); + // } + // } + // + // /// + // /// 处理来自服务器的数据变化通知 + // /// + // private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e) + // { + // foreach (var value in item.DequeueValues()) + // { + // Console.WriteLine( + // $"[通知] {item.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}"); + // } + // } + // + // private async Task BrowseNodes(ObservableCollection nodes, NodeId parentNodeId) + // { + // try + // { + // Opc.Ua.ReferenceDescriptionCollection references; + // byte[] continuationPoint = null; + // + // _session.Browse( + // null, // RequestHeader + // new ViewDescription(), + // parentNodeId, + // 0u, + // BrowseDirection.Forward, + // Opc.Ua.ReferenceTypeIds.HierarchicalReferences, + // true, + // (uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable, + // out continuationPoint, + // out references + // ); + // + // foreach (var rd in references) + // { + // NodeType nodeType = NodeType.Folder; // 默认是文件夹 + // if ((rd.NodeClass & NodeClass.Variable) != 0) + // { + // nodeType = NodeType.Variable; + // } + // else if ((rd.NodeClass & NodeClass.Object) != 0) + // { + // nodeType = NodeType.Object; + // } + // + // var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType); + // nodes.Add(opcUaNode); + // + // // 如果是文件夹或对象,添加一个虚拟子节点,用于懒加载 + // if (nodeType == NodeType.Folder || nodeType == NodeType.Object) + // { + // opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点 + // } + // } + // } + // catch (Exception ex) + // { + // NlogHelper.Error($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex); + // NotificationHelper.ShowError($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex); + // } + // } + // + // public async Task LoadNodeVariables(OpcUaNode node) + // { + // if (node.NodeType == NodeType.Variable) + // { + // // 如果是变量节点,直接显示它 + // SelectedNodeVariables.Clear(); + // SelectedNodeVariables.Add(new Variable + // { + // Name = node.DisplayName, + // NodeId = node.NodeId.ToString(), + // OpcUaNodeId = node.NodeId.ToString(), + // ProtocolType = ProtocolType.OpcUA, + // IsActive = true // 默认选中 + // }); + // return; + // } + // + // if (node.IsLoaded || node.IsLoading) + // { + // return; // 已经加载或正在加载 + // } + // + // node.IsLoading = true; + // node.Children.Clear(); // 清除虚拟节点 + // + // try + // { + // Opc.Ua.ReferenceDescriptionCollection references; + // byte[] continuationPoint = null; + // + // _session.Browse( + // null, // RequestHeader + // new ViewDescription(), + // node.NodeId, + // 0u, + // BrowseDirection.Forward, + // Opc.Ua.ReferenceTypeIds.HierarchicalReferences, + // true, + // (uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable, + // out continuationPoint, + // out references + // ); + // + // foreach (var rd in references) + // { + // NodeType nodeType = NodeType.Folder; + // if ((rd.NodeClass & NodeClass.Variable) != 0) + // { + // nodeType = NodeType.Variable; + // } + // else if ((rd.NodeClass & NodeClass.Object) != 0) + // { + // nodeType = NodeType.Object; + // } + // + // var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType); + // node.Children.Add(opcUaNode); + // + // if (nodeType == NodeType.Folder || nodeType == NodeType.Object) + // { + // opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点 + // } + // + // // 如果是变量,添加到右侧列表 + // if (nodeType == NodeType.Variable) + // { + // // Read the DataType attribute + // ReadValueId readValueId = new ReadValueId + // { + // NodeId = opcUaNode.NodeId, + // AttributeId = Attributes.DataType, + // // You might need to specify IndexRange and DataEncoding if dealing with arrays or specific encodings + // }; + // + // DataValueCollection results; + // DiagnosticInfoCollection diagnosticInfos; + // + // _session.Read( + // null, // RequestHeader + // 0, // MaxAge + // TimestampsToReturn.Source, + // new ReadValueIdCollection { readValueId }, + // out results, + // out diagnosticInfos + // ); + // + // string dataType = string.Empty; + // + // if (results != null && results.Count > 0 && results[0].Value != null) + // { + // // Convert the NodeId of the DataType to a readable string + // NodeId dataTypeNodeId = (NodeId)results[0].Value; + // dataType = _session.NodeCache.GetDisplayText(dataTypeNodeId); + // } + // + // SelectedNodeVariables.Add(new Variable + // { + // Name = opcUaNode.DisplayName, + // OpcUaNodeId = opcUaNode.NodeId.ToString(), + // ProtocolType = ProtocolType.OpcUA, + // IsActive = true, // Default selected + // DataType = dataType // Assign the read DataType + // }); + // } + // } + // + // node.IsLoaded = true; + // } + // catch (Exception ex) + // { + // NlogHelper.Error($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex); + // NotificationHelper.ShowError($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex); + // } + // finally + // { + // node.IsLoading = false; + // } + // } + // + // public ObservableCollection GetSelectedVariables() + // { + // return new ObservableCollection(SelectedVariables); + // } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/VarDataDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/VarDataDialogViewModel.cs index ac7f4af..fa0dcf3 100644 --- a/DMS.WPF/ViewModels/Dialogs/VarDataDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/VarDataDialogViewModel.cs @@ -1,13 +1,12 @@ using CommunityToolkit.Mvvm.ComponentModel; -using DMS.WPF.Models; -using DMS.WPF.Models; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels.Dialogs; public partial class VarDataDialogViewModel : ObservableObject { [ObservableProperty] - private Variable _variable; + private VariableItemViewModel _variable; [ObservableProperty] private string title; [ObservableProperty] diff --git a/DMS.WPF/ViewModels/Dialogs/VarTableDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/VarTableDialogViewModel.cs index cb0af4e..8646ac0 100644 --- a/DMS.WPF/ViewModels/Dialogs/VarTableDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/VarTableDialogViewModel.cs @@ -1,12 +1,12 @@ using CommunityToolkit.Mvvm.ComponentModel; -using DMS.WPF.Models; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels.Dialogs; public partial class VarTableDialogViewModel:ObservableObject { [ObservableProperty] - private VariableTable variableTable; + private VariableTableItemViewModel variableTable; [ObservableProperty] private string title; [ObservableProperty] diff --git a/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs b/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs new file mode 100644 index 0000000..9165949 --- /dev/null +++ b/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs @@ -0,0 +1,73 @@ +// 文件: DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using DMS.Core.Enums; + +namespace DMS.WPF.ViewModels.Items; + +/// +/// 代表设备列表中的单个设备项的ViewModel。 +/// 实现了INotifyPropertyChanged,其任何属性变化都会自动通知UI。 +/// +public partial class DeviceItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private string _name; + + [ObservableProperty] + private ProtocolType _protocol; + + [ObservableProperty] + private string _ipAddress; + + [ObservableProperty] + private int _port; + + [ObservableProperty] + private int _rack; + + [ObservableProperty] + private int _slot; + + [ObservableProperty] + private string _opcUaServerUrl; + + [ObservableProperty] + private bool _isActive; + + [ObservableProperty] + private string _status; + + public DeviceItemViewModel(DeviceDto dto) + { + Id = dto.Id; + _name = dto.Name; + _protocol = dto.Protocol; + _ipAddress = dto.IpAddress; + _port = dto.Port; + _rack = dto.Rack; + _slot = dto.Slot; + _opcUaServerUrl = dto.OpcUaServerUrl; + _isActive = dto.IsActive; + _status = dto.Status; + } + + public DeviceDto ToDto() + { + return new DeviceDto + { + Id = this.Id, + Name = this.Name, + Protocol = this.Protocol, + IpAddress = this.IpAddress, + Port = this.Port, + Rack = this.Rack, + Slot = this.Slot, + OpcUaServerUrl = this.OpcUaServerUrl, + IsActive = this.IsActive, + Status = this.Status + }; + } +} diff --git a/DMS.WPF/ViewModels/Items/MenuBeanItemViewModel.cs b/DMS.WPF/ViewModels/Items/MenuBeanItemViewModel.cs new file mode 100644 index 0000000..97122cb --- /dev/null +++ b/DMS.WPF/ViewModels/Items/MenuBeanItemViewModel.cs @@ -0,0 +1,43 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using DMS.Core.Enums; + +namespace DMS.WPF.ViewModels.Items; + +public partial class MenuBeanItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private int? _parentId; + + [ObservableProperty] + private string _header; + + [ObservableProperty] + private string _icon; + + [ObservableProperty] + private MenuType _menuType; + + [ObservableProperty] + private int _targetId; + + [ObservableProperty] + private string _navigationParameter; + + [ObservableProperty] + private int _displayOrder; + + public MenuBeanItemViewModel(MenuBeanDto dto) + { + Id = dto.Id; + _parentId = dto.ParentId; + _header = dto.Header; + _icon = dto.Icon; + _menuType = dto.MenuType; + _targetId = dto.TargetId; + _navigationParameter = dto.NavigationParameter; + _displayOrder = dto.DisplayOrder; + } +} diff --git a/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs b/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs new file mode 100644 index 0000000..09a297e --- /dev/null +++ b/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs @@ -0,0 +1,67 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using System; + +namespace DMS.WPF.ViewModels.Items; + +public partial class MqttServerItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private string _serverName; + + [ObservableProperty] + private string _brokerAddress; + + [ObservableProperty] + private int _port; + + [ObservableProperty] + private string _username; + + [ObservableProperty] + private string _password; + + [ObservableProperty] + private bool _isActive; + + [ObservableProperty] + private string _subscribeTopic; + + [ObservableProperty] + private string _publishTopic; + + [ObservableProperty] + private string _clientId; + + [ObservableProperty] + private DateTime _createdAt; + + [ObservableProperty] + private DateTime? _connectedAt; + + [ObservableProperty] + private long _connectionDuration; + + [ObservableProperty] + private string _messageFormat; + + public MqttServerItemViewModel(MqttServerDto dto) + { + Id = dto.Id; + _serverName = dto.ServerName; + _brokerAddress = dto.BrokerAddress; + _port = dto.Port; + _username = dto.Username; + _password = dto.Password; + _isActive = dto.IsActive; + _subscribeTopic = dto.SubscribeTopic; + _publishTopic = dto.PublishTopic; + _clientId = dto.ClientId; + _createdAt = dto.CreatedAt; + _connectedAt = dto.ConnectedAt; + _connectionDuration = dto.ConnectionDuration; + _messageFormat = dto.MessageFormat; + } +} diff --git a/DMS.WPF/ViewModels/Items/UserItemViewModel.cs b/DMS.WPF/ViewModels/Items/UserItemViewModel.cs new file mode 100644 index 0000000..9282f7d --- /dev/null +++ b/DMS.WPF/ViewModels/Items/UserItemViewModel.cs @@ -0,0 +1,26 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; + +namespace DMS.WPF.ViewModels.Items; + +public partial class UserItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private string _username; + + [ObservableProperty] + private string _role; + + [ObservableProperty] + private bool _isActive; + + public UserItemViewModel(UserDto dto) + { + Id = dto.Id; + _username = dto.Username; + _role = dto.Role; + _isActive = dto.IsActive; + } +} diff --git a/DMS.WPF/ViewModels/Items/VariableHistoryItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableHistoryItemViewModel.cs new file mode 100644 index 0000000..e49eaa6 --- /dev/null +++ b/DMS.WPF/ViewModels/Items/VariableHistoryItemViewModel.cs @@ -0,0 +1,27 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using System; + +namespace DMS.WPF.ViewModels.Items; + +public partial class VariableHistoryItemViewModel : ObservableObject +{ + public long Id { get; } + + [ObservableProperty] + private int _variableId; + + [ObservableProperty] + private string _value; + + [ObservableProperty] + private DateTime _timestamp; + + public VariableHistoryItemViewModel(VariableHistoryDto dto) + { + Id = dto.Id; + _variableId = dto.VariableId; + _value = dto.Value; + _timestamp = dto.Timestamp; + } +} diff --git a/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs new file mode 100644 index 0000000..5aa57d8 --- /dev/null +++ b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs @@ -0,0 +1,117 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using DMS.Core.Enums; +using System; +using System.Collections.Generic; + +namespace DMS.WPF.ViewModels.Items; + +public partial class VariableItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private string _name; + + [ObservableProperty] + private string? _s7Address; + + [ObservableProperty] + private string? _dataValue; + + [ObservableProperty] + private string? _displayValue; + + [ObservableProperty] + private VariableTableDto? _variableTable; + + [ObservableProperty] + private List? _mqttAliases; + + [ObservableProperty] + private SignalType _dataType; + + [ObservableProperty] + private PollLevelType _pollLevel; + + [ObservableProperty] + private bool _isActive; + + [ObservableProperty] + private int _variableTableId; + + [ObservableProperty] + private string _opcUaNodeId; + + [ObservableProperty] + private bool _isHistoryEnabled; + + [ObservableProperty] + private double _historyDeadband; + + [ObservableProperty] + private bool _isAlarmEnabled; + + [ObservableProperty] + private double _alarmMinValue; + + [ObservableProperty] + private double _alarmMaxValue; + + [ObservableProperty] + private double _alarmDeadband; + + [ObservableProperty] + private ProtocolType _protocol; + + [ObservableProperty] + private CSharpDataType _cSharpDataType; + + [ObservableProperty] + private string _conversionFormula; + + [ObservableProperty] + private DateTime _createdAt; + + [ObservableProperty] + private DateTime _updatedAt; + + [ObservableProperty] + private string _updatedBy; + + [ObservableProperty] + private bool _isModified; + + [ObservableProperty] + private string _description; + + public VariableItemViewModel(VariableDto dto) + { + Id = dto.Id; + _name = dto.Name; + _s7Address = dto.S7Address; + _dataValue = dto.DataValue; + _displayValue = dto.DisplayValue; + _variableTable = dto.VariableTable; + _mqttAliases = dto.MqttAliases; + _dataType = dto.DataType; + _pollLevel = dto.PollLevel; + _isActive = dto.IsActive; + _variableTableId = dto.VariableTableId; + _opcUaNodeId = dto.OpcUaNodeId; + _isHistoryEnabled = dto.IsHistoryEnabled; + _historyDeadband = dto.HistoryDeadband; + _isAlarmEnabled = dto.IsAlarmEnabled; + _alarmMinValue = dto.AlarmMinValue; + _alarmMaxValue = dto.AlarmMaxValue; + _alarmDeadband = dto.AlarmDeadband; + _protocol = dto.Protocol; + _cSharpDataType = dto.CSharpDataType; + _conversionFormula = dto.ConversionFormula; + _createdAt = dto.CreatedAt; + _updatedAt = dto.UpdatedAt; + _updatedBy = dto.UpdatedBy; + _isModified = dto.IsModified; + _description = dto.Description; + } +} diff --git a/DMS.WPF/ViewModels/Items/VariableMqttAliasItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableMqttAliasItemViewModel.cs new file mode 100644 index 0000000..7bde250 --- /dev/null +++ b/DMS.WPF/ViewModels/Items/VariableMqttAliasItemViewModel.cs @@ -0,0 +1,30 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; + +namespace DMS.WPF.ViewModels.Items; + +public partial class VariableMqttAliasItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private int _variableId; + + [ObservableProperty] + private int _mqttServerId; + + [ObservableProperty] + private string _mqttServerName; + + [ObservableProperty] + private string _alias; + + public VariableMqttAliasItemViewModel(VariableMqttAliasDto dto) + { + Id = dto.Id; + _variableId = dto.VariableId; + _mqttServerId = dto.MqttServerId; + _mqttServerName = dto.MqttServerName; + _alias = dto.Alias; + } +} diff --git a/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs new file mode 100644 index 0000000..3dc6928 --- /dev/null +++ b/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs @@ -0,0 +1,35 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using DMS.Application.DTOs; +using DMS.Core.Enums; + +namespace DMS.WPF.ViewModels.Items; + +public partial class VariableTableItemViewModel : ObservableObject +{ + public int Id { get; } + + [ObservableProperty] + private string _name; + + [ObservableProperty] + private string _description; + + [ObservableProperty] + private bool _isActive; + + [ObservableProperty] + private int _deviceId; + + [ObservableProperty] + private ProtocolType _protocol; + + public VariableTableItemViewModel(VariableTableDto dto) + { + Id = dto.Id; + _name = dto.Name; + _description = dto.Description; + _isActive = dto.IsActive; + _deviceId = dto.DeviceId; + _protocol = dto.Protocol; + } +} diff --git a/DMS.WPF/ViewModels/MainViewModel.cs b/DMS.WPF/ViewModels/MainViewModel.cs index 4cac95b..e6a7ace 100644 --- a/DMS.WPF/ViewModels/MainViewModel.cs +++ b/DMS.WPF/ViewModels/MainViewModel.cs @@ -2,14 +2,15 @@ using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.WPF.Models; +using DMS.Core.Enums; using DMS.WPF.Services; using DMS.WPF.Views; using DMS.Core.Helper; -using DMS.Infrastructure.Data; -using DMS.Infrastructure.Repositories; - -namespace DMS.WPF.ViewModels; +using DMS.Core.Models; +using DMS.Helper; +using DMS.Services; +using DMS.ViewModels; +using DMS.WPF.Helper; using iNKORE.UI.WPF.Modern.Common.IconKeys; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -27,9 +28,7 @@ public partial class MainViewModel : ViewModelBase private readonly DataServices _dataServices; private readonly IDialogService _dialogService; private readonly ILogger _logger; - private readonly MenuRepository _menuRepository; private readonly NavgatorServices _navgatorServices; - private readonly VarTableRepository _varTableRepository; /// /// 当前显示的视图模型。 @@ -51,15 +50,12 @@ public partial class MainViewModel : ViewModelBase /// 对话框服务。 /// 日志记录器。 public MainViewModel(NavgatorServices navgatorServices, DataServices dataServices, IDialogService dialogService, - ILogger logger, VarTableRepository varTableRepository, - MenuRepository menuRepository) + ILogger logger) { _navgatorServices = navgatorServices; _dataServices = dataServices; _dialogService = dialogService; _logger = logger; - _varTableRepository = varTableRepository; - _menuRepository = menuRepository; _navgatorServices.OnViewModelChanged += () => { CurrentViewModel = _navgatorServices.CurrentViewModel; }; @@ -77,10 +73,10 @@ public partial class MainViewModel : ViewModelBase [RelayCommand] private void ShowWindow() { - if (Application.Current.MainWindow is MainView mainWindow) - { - mainWindow.ShowApplication(); - } + // if (Application.Current.MainWindow is MainView mainWindow) + // { + // mainWindow.ShowApplication(); + // } } /// @@ -89,7 +85,7 @@ public partial class MainViewModel : ViewModelBase [RelayCommand] private void ExitApplication() { - Application.Current.Shutdown(); + // Application.Current.Shutdown(); } /// @@ -98,72 +94,72 @@ public partial class MainViewModel : ViewModelBase /// 当前菜单项,用于获取父级设备信息。 private async Task AddVariableTable(MenuBean menu) { - var db = DbContext.GetInstance(); - try - { - // 1. 检查父级设备信息 - if (menu.Parent?.Data is not Device device) - { - _logger.LogWarning("尝试添加变量表时,Parent 或 Parent.Data 为空,或 Parent.Data 不是 Device 类型。"); - NotificationHelper.ShowError("操作失败:无法获取有效的设备信息。"); - return; - } - - - // 2. 显示添加变量表对话框 - var varTable = await _dialogService.ShowAddVarTableDialog(); - if (varTable == null) - { - // 用户取消或未选择 - return; - } - - // 3. 设置变量表属性 - varTable.IsActive = true; - varTable.DeviceId = device.Id; - varTable.ProtocolType = device.ProtocolType; - - // 4. 添加变量表到数据库 - // 假设 _varTableRepository.AddAsync 返回一个布尔值表示成功,或者一个表示 ID 的整数 - // 这里为了演示,我们假设它返回新添加的ID,如果失败则返回0 - await db.BeginTranAsync(); - var addVarTable = await _varTableRepository.AddAsync(varTable, db); - - // 5. 添加变量表菜单 - MenuBean newMenu = new MenuBean - { - Icon = SegoeFluentIcons.Tablet.Glyph, - Name = varTable.Name, - DataId = addVarTable.Id, // 使用实际添加的ID - Type = MenuType.VariableTableMenu, - ParentId = menu.Parent.Id - }; - - var addMenuRes = await _menuRepository.AddAsync(newMenu, db); - if (addMenuRes > 0) - { - await db.CommitTranAsync(); - // 变量表和菜单都添加成功 - MessageHelper.SendLoadMessage(LoadTypes.Menu); - MessageHelper.SendLoadMessage(LoadTypes.Devices); - NotificationHelper.ShowSuccess($"变量表:{varTable.Name},添加成功"); - _logger.LogInformation($"变量表:{varTable.Name},添加成功"); - } - else - { - await db.RollbackTranAsync(); - // 变量表菜单添加失败 (此时变量表可能已添加成功,需要根据业务决定是否回滚) - NotificationHelper.ShowError($"变量表:{varTable.Name},添加菜单失败"); - _logger.LogError($"变量表:{varTable.Name},添加菜单失败"); - // 考虑:如果菜单添加失败,是否需要删除之前添加的变量表? - // 例如:await _varTableRepository.DeleteAsync(addVarTableId); - } - } - catch (Exception e) - { - await db.RollbackTranAsync(); - NotificationHelper.ShowError($"添加变量表时出现了错误:{e.Message}", e); - } + // var db = DbContext.GetInstance(); + // try + // { + // // 1. 检查父级设备信息 + // if (menu.Parent?.Data is not Device device) + // { + // _logger.LogWarning("尝试添加变量表时,Parent 或 Parent.Data 为空,或 Parent.Data 不是 Device 类型。"); + // NotificationHelper.ShowError("操作失败:无法获取有效的设备信息。"); + // return; + // } + // + // + // // 2. 显示添加变量表对话框 + // var varTable = await _dialogService.ShowAddVarTableDialog(); + // if (varTable == null) + // { + // // 用户取消或未选择 + // return; + // } + // + // // 3. 设置变量表属性 + // varTable.IsActive = true; + // varTable.DeviceId = device.Id; + // varTable.ProtocolType = device.ProtocolType; + // + // // 4. 添加变量表到数据库 + // // 假设 _varTableRepository.AddAsync 返回一个布尔值表示成功,或者一个表示 ID 的整数 + // // 这里为了演示,我们假设它返回新添加的ID,如果失败则返回0 + // await db.BeginTranAsync(); + // var addVarTable = await _varTableRepository.AddAsync(varTable, db); + // + // // 5. 添加变量表菜单 + // MenuBean newMenu = new MenuBean + // { + // Icon = SegoeFluentIcons.Tablet.Glyph, + // Name = varTable.Name, + // DataId = addVarTable.Id, // 使用实际添加的ID + // Type = MenuType.VariableTableMenu, + // ParentId = menu.Parent.Id + // }; + // + // var addMenuRes = await _menuRepository.AddAsync(newMenu, db); + // if (addMenuRes > 0) + // { + // await db.CommitTranAsync(); + // // 变量表和菜单都添加成功 + // MessageHelper.SendLoadMessage(LoadTypes.Menu); + // MessageHelper.SendLoadMessage(LoadTypes.Devices); + // NotificationHelper.ShowSuccess($"变量表:{varTable.Name},添加成功"); + // _logger.LogInformation($"变量表:{varTable.Name},添加成功"); + // } + // else + // { + // await db.RollbackTranAsync(); + // // 变量表菜单添加失败 (此时变量表可能已添加成功,需要根据业务决定是否回滚) + // NotificationHelper.ShowError($"变量表:{varTable.Name},添加菜单失败"); + // _logger.LogError($"变量表:{varTable.Name},添加菜单失败"); + // // 考虑:如果菜单添加失败,是否需要删除之前添加的变量表? + // // 例如:await _varTableRepository.DeleteAsync(addVarTableId); + // } + // } + // catch (Exception e) + // { + // await db.RollbackTranAsync(); + // NotificationHelper.ShowError($"添加变量表时出现了错误:{e.Message}", e); + // } } /// @@ -172,61 +168,61 @@ public partial class MainViewModel : ViewModelBase /// 被选中的菜单项。 public async Task MenuSelectionChanged(MenuBean menu) { - try - { - switch (menu.Type) - { - // 导航到一级菜单 - case MenuType.MainMenu: - menu.ViewModel = DataServicesHelper.GetMainViewModel(menu.Name); - break; - // 导航到设备下面的菜单 - case MenuType.DeviceMenu: - var deviceDetailVm = App.Current.Services.GetRequiredService(); - var currentDevice = _dataServices.Devices.FirstOrDefault(d => d.Id == menu.DataId); - deviceDetailVm.CurrentDevice = currentDevice; - menu.ViewModel = deviceDetailVm; - menu.Data = currentDevice; - break; - // 导航到变量表菜单 - case MenuType.VariableTableMenu: - VariableTableViewModel varTableVM = - App.Current.Services.GetRequiredService(); - varTableVM.VariableTable = - DataServicesHelper.FindVarTableForDevice(_dataServices.Devices, menu.DataId); - - varTableVM.IsLoadCompletion = false; - menu.ViewModel = varTableVM; - menu.Data = varTableVM.VariableTable; - break; - // 导航到添加变量表的菜单 - case MenuType.AddVariableTableMenu: - await AddVariableTable(menu); - return; - break; - // 导航到Mqtt服务器 - case MenuType.MqttMenu: - var mqttVM = App.Current.Services.GetRequiredService(); - mqttVM.CurrentMqtt = _dataServices.Mqtts.FirstOrDefault(d => d.Id == menu.DataId); - menu.ViewModel = mqttVM; - break; - } - - - if (menu.ViewModel != null) - { - MessageHelper.SendNavgatorMessage(menu.ViewModel); - _logger.LogInformation($"导航到:{menu.Name}"); - } - else - { - NotificationHelper.ShowInfo($"菜单:{menu.Name},没有对应的ViewModel."); - _logger.LogInformation($"菜单:{menu.Name},没有对应的ViewModel."); - } - } - catch (Exception e) - { - NotificationHelper.ShowError($"菜单切换时出现了错误:{e.Message}", e); - } + // try + // { + // switch (menu.Type) + // { + // // 导航到一级菜单 + // case MenuType.MainMenu: + // menu.ViewModel = DataServicesHelper.GetMainViewModel(menu.Name); + // break; + // // 导航到设备下面的菜单 + // case MenuType.DeviceMenu: + // var deviceDetailVm = App.Current.Services.GetRequiredService(); + // var currentDevice = _dataServices.Devices.FirstOrDefault(d => d.Id == menu.DataId); + // deviceDetailVm.CurrentDevice = currentDevice; + // menu.ViewModel = deviceDetailVm; + // menu.Data = currentDevice; + // break; + // // 导航到变量表菜单 + // case MenuType.VariableTableMenu: + // VariableTableViewModel varTableVM = + // App.Current.Services.GetRequiredService(); + // varTableVM.VariableTable = + // DataServicesHelper.FindVarTableForDevice(_dataServices.Devices, menu.DataId); + // + // varTableVM.IsLoadCompletion = false; + // menu.ViewModel = varTableVM; + // menu.Data = varTableVM.VariableTable; + // break; + // // 导航到添加变量表的菜单 + // case MenuType.AddVariableTableMenu: + // await AddVariableTable(menu); + // return; + // break; + // // 导航到Mqtt服务器 + // case MenuType.MqttMenu: + // var mqttVM = App.Current.Services.GetRequiredService(); + // mqttVM.CurrentMqtt = _dataServices.Mqtts.FirstOrDefault(d => d.Id == menu.DataId); + // menu.ViewModel = mqttVM; + // break; + // } + // + // + // if (menu.ViewModel != null) + // { + // MessageHelper.SendNavgatorMessage(menu.ViewModel); + // _logger.LogInformation($"导航到:{menu.Name}"); + // } + // else + // { + // NotificationHelper.ShowInfo($"菜单:{menu.Name},没有对应的ViewModel."); + // _logger.LogInformation($"菜单:{menu.Name},没有对应的ViewModel."); + // } + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"菜单切换时出现了错误:{e.Message}", e); + // } } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/MqttServerDetailViewModel.cs b/DMS.WPF/ViewModels/MqttServerDetailViewModel.cs index f960b73..91829a1 100644 --- a/DMS.WPF/ViewModels/MqttServerDetailViewModel.cs +++ b/DMS.WPF/ViewModels/MqttServerDetailViewModel.cs @@ -1,15 +1,13 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; -using DMS.Core.Enums; using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using System.Collections.Generic; -using DMS.Data.Repositories; +using DMS.Core.Models; using DMS.Helper; -using DMS.WPF.Models; using DMS.Services; +using DMS.WPF.Services; +using DMS.WPF.ViewModels; +using DMS.WPF.ViewModels.Items; namespace DMS.ViewModels { @@ -27,15 +25,14 @@ namespace DMS.ViewModels /// 当前正在编辑的MQTT服务器对象。 /// [ObservableProperty] - private Mqtt _currentMqtt; + private MqttServerItemViewModel _currentMqtt; /// /// 与当前MQTT服务器关联的变量数据集合。 /// [ObservableProperty] - private ObservableCollection _associatedVariables; + private ObservableCollection _associatedVariables; - private readonly VariableMqttAliasRepository _variableMqttAliasRepository; /// /// 构造函数。 @@ -44,20 +41,19 @@ namespace DMS.ViewModels /// 数据服务。 /// 对话框服务。 public MqttServerDetailViewModel(ILogger logger, DataServices dataServices, - IDialogService dialogService, VariableMqttAliasRepository variableMqttAliasRepository) + IDialogService dialogService) { _logger = logger; _dataServices = dataServices; _dialogService = dialogService; - _variableMqttAliasRepository = variableMqttAliasRepository; } public override void OnLoaded() { - if (CurrentMqtt.VariableMqtts != null) - { - AssociatedVariables =new ObservableCollection(CurrentMqtt.VariableMqtts) ; - } + // if (CurrentMqtt.VariableMqtts != null) + // { + // AssociatedVariables =new ObservableCollection(CurrentMqtt.VariableMqtts) ; + // } } @@ -95,32 +91,32 @@ namespace DMS.ViewModels [RelayCommand] private async Task RemoveVariables(System.Collections.IList variablesToRemove) { - if (CurrentMqtt == null || variablesToRemove == null || variablesToRemove.Count == 0) - { - NotificationHelper.ShowInfo("请选择要移除的变量。"); - return; - } - - var variablesList = variablesToRemove.Cast() - .ToList(); - - var result = await _dialogService.ShowConfrimeDialog( - "确认移除", $"确定要从MQTT服务器 '{CurrentMqtt.Name}' 中移除选定的 {variablesList.Count} 个变量吗?"); - if (result != true) return; - - foreach (var variable in variablesList) // 使用ToList()避免在迭代时修改集合 - { - // 移除变量与当前MQTT服务器的关联 - // variable.Mqtts?.Remove(CurrentMqtt); - // // 标记变量为已修改,以便保存时更新数据库 - // variable.IsModified = true; - // AssociatedVariables.Remove(variable); - // _logger.LogInformation($"Removed variable {variable.Name} from MQTT server {CurrentMqtt.Name}."); - } - - // TODO: 这里需要调用DataServices来更新数据库中VariableData的Mqtt关联 - // 例如:await _dataServices.UpdateVariableDataAssociationsAsync(variablesToRemove); - NotificationHelper.ShowSuccess("变量移除成功,请记得保存更改。"); + // if (CurrentMqtt == null || variablesToRemove == null || variablesToRemove.Count == 0) + // { + // NotificationHelper.ShowInfo("请选择要移除的变量。"); + // return; + // } + // + // var variablesList = variablesToRemove.Cast() + // .ToList(); + // + // var result = await _dialogService.ShowConfrimeDialog( + // "确认移除", $"确定要从MQTT服务器 '{CurrentMqtt.ServerName}' 中移除选定的 {variablesList.Count} 个变量吗?"); + // if (result != true) return; + // + // foreach (var variable in variablesList) // 使用ToList()避免在迭代时修改集合 + // { + // // 移除变量与当前MQTT服务器的关联 + // // variable.Mqtts?.Remove(CurrentMqtt); + // // // 标记变量为已修改,以便保存时更新数据库 + // // variable.IsModified = true; + // // AssociatedVariables.Remove(variable); + // // _logger.LogInformation($"Removed variable {variable.Name} from MQTT server {CurrentMqtt.Name}."); + // } + // + // // + // // 例如:await _dataServices.UpdateVariableDataAssociationsAsync(variablesToRemove); + // NotificationHelper.ShowSuccess("变量移除成功,请记得保存更改。"); } /// diff --git a/DMS.WPF/ViewModels/MqttsViewModel.cs b/DMS.WPF/ViewModels/MqttsViewModel.cs index b69cb94..934f941 100644 --- a/DMS.WPF/ViewModels/MqttsViewModel.cs +++ b/DMS.WPF/ViewModels/MqttsViewModel.cs @@ -1,13 +1,13 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.Data.Repositories; using DMS.Core.Enums; using DMS.Helper; -using DMS.WPF.Models; using DMS.Services; +using DMS.WPF.Helper; +using DMS.WPF.Services; +using DMS.WPF.ViewModels.Items; using Microsoft.Extensions.Logging; -using DMS.Data; using DMS.WPF.Views; namespace DMS.WPF.ViewModels; @@ -16,11 +16,10 @@ public partial class MqttsViewModel : ViewModelBase { private readonly DataServices _dataServices; private readonly IDialogService _dialogService; - private readonly MqttRepository _mqttRepository; private readonly ILogger _logger; private readonly NavgatorServices _navgatorServices; [ObservableProperty] - private ObservableCollection _mqtts; + private ObservableCollection _mqtts; // public ObservableCollection Mqtts // { @@ -48,125 +47,124 @@ public partial class MqttsViewModel : ViewModelBase // } [ObservableProperty] - private Mqtt _selectedMqtt; + private MqttServerItemViewModel _selectedMqtt; public MqttsViewModel( - ILogger logger, IDialogService dialogService, DataServices dataServices, NavgatorServices navgatorServices,MqttRepository mqttRepository + ILogger logger, IDialogService dialogService, DataServices dataServices, NavgatorServices navgatorServices ) { - _mqttRepository = mqttRepository; _logger = logger; _dialogService = dialogService; _dataServices = dataServices; _navgatorServices = navgatorServices; - if (dataServices.Mqtts == null || dataServices.Mqtts.Count == 0) - { - MessageHelper.SendLoadMessage(LoadTypes.Mqtts); - } - else - { - Mqtts = new ObservableCollection(dataServices.Mqtts); - } - - - _dataServices.OnMqttListChanged += ( mqtts) => - { - Mqtts = new ObservableCollection(mqtts); - }; + // if (dataServices.Mqtts == null || dataServices.Mqtts.Count == 0) + // { + // MessageHelper.SendLoadMessage(LoadTypes.Mqtts); + // } + // else + // { + // Mqtts = new ObservableCollection(dataServices.Mqtts); + // } + // + // + // _dataServices.OnMqttListChanged += ( mqtts) => + // { + // Mqtts = new ObservableCollection(mqtts); + // }; } private async void Mqtt_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Mqtt.IsActive)) - { - if (sender is Mqtt mqtt) - { - try - { - await _mqttRepository.UpdateAsync(mqtt); - NotificationHelper.ShowSuccess($"MQTT: {mqtt.Name} 的启用状态已更新。"); - MessageHelper.SendLoadMessage(LoadTypes.Mqtts); - } - catch (Exception ex) - { - NotificationHelper.ShowError($"更新MQTT启用状态失败: {mqtt.Name} - {ex.Message}", ex); - } - } - } + // if (e.PropertyName == nameof(Mqtt.IsActive)) + // { + // if (sender is Mqtt mqtt) + // { + // try + // { + // await _mqttRepository.UpdateAsync(mqtt); + // NotificationHelper.ShowSuccess($"MQTT: {mqtt.Name} 的启用状态已更新。"); + // MessageHelper.SendLoadMessage(LoadTypes.Mqtts); + // } + // catch (Exception ex) + // { + // NotificationHelper.ShowError($"更新MQTT启用状态失败: {mqtt.Name} - {ex.Message}", ex); + // } + // } + // } } [RelayCommand] public async void AddMqtt() { - try - { - var mqtt = await _dialogService.ShowAddMqttDialog(); - if (mqtt == null) - { - _logger.LogInformation("用户取消了添加MQTT操作。"); - return; - } - - await _mqttRepository.AddAsync(mqtt); - MessageHelper.SendLoadMessage(LoadTypes.Mqtts); - MessageHelper.SendLoadMessage(LoadTypes.Menu); - } - catch (Exception e) - { - NotificationHelper.ShowError($"添加MQTT的过程中发生错误:{e.Message}", e); - } + // try + // { + // var mqtt = await _dialogService.ShowAddMqttDialog(); + // if (mqtt == null) + // { + // _logger.LogInformation("用户取消了添加MQTT操作。"); + // return; + // } + // + // await _mqttRepository.AddAsync(mqtt); + // MessageHelper.SendLoadMessage(LoadTypes.Mqtts); + // MessageHelper.SendLoadMessage(LoadTypes.Menu); + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"添加MQTT的过程中发生错误:{e.Message}", e); + // } } [RelayCommand] public async void DeleteMqtt() { - try - { - if (SelectedMqtt == null) - { - NotificationHelper.ShowError("你没有选择任何MQTT,请选择MQTT后再点击删除"); - return; - } - - string msg = $"确认要删除MQTT名为:{SelectedMqtt.Name}"; - var isDel = await _dialogService.ShowConfrimeDialog("删除MQTT", msg, "删除MQTT"); - if (isDel) - { - await _mqttRepository.DeleteAsync(SelectedMqtt); - MessageHelper.SendLoadMessage(LoadTypes.Mqtts); - MessageHelper.SendLoadMessage(LoadTypes.Menu); - NotificationHelper.ShowSuccess($"删除MQTT成功,MQTT名:{SelectedMqtt.Name}"); - } - } - catch (Exception e) - { - NotificationHelper.ShowError($"删除MQTT的过程中发生错误:{e.Message}", e); - } + // try + // { + // if (SelectedMqtt == null) + // { + // NotificationHelper.ShowError("你没有选择任何MQTT,请选择MQTT后再点击删除"); + // return; + // } + // + // string msg = $"确认要删除MQTT名为:{SelectedMqtt.Name}"; + // var isDel = await _dialogService.ShowConfrimeDialog("删除MQTT", msg, "删除MQTT"); + // if (isDel) + // { + // await _mqttRepository.DeleteAsync(SelectedMqtt); + // MessageHelper.SendLoadMessage(LoadTypes.Mqtts); + // MessageHelper.SendLoadMessage(LoadTypes.Menu); + // NotificationHelper.ShowSuccess($"删除MQTT成功,MQTT名:{SelectedMqtt.Name}"); + // } + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"删除MQTT的过程中发生错误:{e.Message}", e); + // } } [RelayCommand] public async void EditMqtt() { - try - { - if (SelectedMqtt == null) - { - NotificationHelper.ShowError("你没有选择任何MQTT,请选择MQTT后再点击编辑"); - return; - } - - var editMqtt = await _dialogService.ShowEditMqttDialog(SelectedMqtt); - if (editMqtt != null) - { - var res = await _mqttRepository.UpdateAsync(editMqtt); - MessageHelper.SendLoadMessage(LoadTypes.Mqtts); - } - } - catch (Exception e) - { - NotificationHelper.ShowError($"编辑MQTT的过程中发生错误:{e.Message}", e); - } + // try + // { + // if (SelectedMqtt == null) + // { + // NotificationHelper.ShowError("你没有选择任何MQTT,请选择MQTT后再点击编辑"); + // return; + // } + // + // var editMqtt = await _dialogService.ShowEditMqttDialog(SelectedMqtt); + // if (editMqtt != null) + // { + // var res = await _mqttRepository.UpdateAsync(editMqtt); + // MessageHelper.SendLoadMessage(LoadTypes.Mqtts); + // } + // } + // catch (Exception e) + // { + // NotificationHelper.ShowError($"编辑MQTT的过程中发生错误:{e.Message}", e); + // } } /// diff --git a/DMS.WPF/ViewModels/SettingViewModel.cs b/DMS.WPF/ViewModels/SettingViewModel.cs index 12b87c0..13cbb40 100644 --- a/DMS.WPF/ViewModels/SettingViewModel.cs +++ b/DMS.WPF/ViewModels/SettingViewModel.cs @@ -1,161 +1,152 @@ using CommunityToolkit.Mvvm.Input; -using SqlSugar; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DMS.Config; -using DMS.Services; using DMS.WPF.Helper; -using DMS.Infrastructure.Interfaces; using DMS.Helper; namespace DMS.WPF.ViewModels; public partial class SettingViewModel : ViewModelBase { - private readonly IDbContext transaction; - private AppSettings _settings; + // private AppSettings _settings; - public SettingViewModel(IDbContext transaction) + public SettingViewModel() { - _settings = AppSettings.Load(); - AvailableDbTypes = Enum.GetNames(typeof(SqlSugar.DbType)).ToList(); - Themes = new List { "浅色", "深色", "跟随系统" }; - this.transaction = transaction; + // _settings = AppSettings.Load(); + // AvailableDbTypes = Enum.GetNames(typeof(SqlSugar.DbType)).ToList(); + // Themes = new List { "浅色", "深色", "跟随系统" }; + // this.transaction = transaction; } public List Themes { get; } - public string SelectedTheme - { - get => _settings.Theme; - set - { - if (_settings.Theme != value) - { - _settings.Theme = value; - OnPropertyChanged(); - _settings.Save(); - ThemeHelper.ApplyTheme(value); - } - } - } + // public string SelectedTheme + // { + // get => _settings.Theme; + // set + // { + // if (_settings.Theme != value) + // { + // _settings.Theme = value; + // OnPropertyChanged(); + // _settings.Save(); + // ThemeHelper.ApplyTheme(value); + // } + // } + // } public List AvailableDbTypes { get; set; } - public string SelectedDbType - { - get => _settings.Database.DbType; - set - { - if (_settings.Database.DbType != value) - { - _settings.Database.DbType = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } + // public string SelectedDbType + // { + // get => _settings.Database.DbType; + // set + // { + // if (_settings.Database.DbType != value) + // { + // _settings.Database.DbType = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } - public string Server - { - get => _settings.Database.Server; - set - { - if (_settings.Database.Server != value) - { - _settings.Database.Server = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } + // public string Server + // { + // get => _settings.Database.Server; + // set + // { + // if (_settings.Database.Server != value) + // { + // _settings.Database.Server = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } - public int Port - { - get => _settings.Database.Port; - set - { - if (_settings.Database.Port != value) - { - _settings.Database.Port = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } + // public int Port + // { + // get => _settings.Database.Port; + // set + // { + // if (_settings.Database.Port != value) + // { + // _settings.Database.Port = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } - public string UserId - { - get => _settings.Database.UserId; - set - { - if (_settings.Database.UserId != value) - { - _settings.Database.UserId = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } + // public string UserId + // { + // get => _settings.Database.UserId; + // set + // { + // if (_settings.Database.UserId != value) + // { + // _settings.Database.UserId = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } + // + // public string Password + // { + // get => _settings.Database.Password; + // set + // { + // if (_settings.Database.Password != value) + // { + // _settings.Database.Password = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } + // + // public string Database + // { + // get => _settings.Database.Database; + // set + // { + // if (_settings.Database.Database != value) + // { + // _settings.Database.Database = value; + // OnPropertyChanged(); + // _settings.Save(); + // } + // } + // } - public string Password - { - get => _settings.Database.Password; - set - { - if (_settings.Database.Password != value) - { - _settings.Database.Password = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } - - public string Database - { - get => _settings.Database.Database; - set - { - if (_settings.Database.Database != value) - { - _settings.Database.Database = value; - OnPropertyChanged(); - _settings.Save(); - } - } - } - - public bool MinimizeToTrayOnClose - { - get => _settings.MinimizeToTrayOnClose; - set - { - if (_settings.MinimizeToTrayOnClose != value) - { - _settings.MinimizeToTrayOnClose = value; - OnPropertyChanged(nameof(MinimizeToTrayOnClose)); - _settings.Save(); - } - } - } + // public bool MinimizeToTrayOnClose + // { + // get => _settings.MinimizeToTrayOnClose; + // set + // { + // if (_settings.MinimizeToTrayOnClose != value) + // { + // _settings.MinimizeToTrayOnClose = value; + // OnPropertyChanged(nameof(MinimizeToTrayOnClose)); + // _settings.Save(); + // } + // } + // } [RelayCommand] private async Task TestConnection() { - try - { - using (var db = transaction.GetInstance()) - { - await db.Ado.OpenAsync(); - NotificationHelper.ShowSuccess("连接成功!"); - } - } - catch (Exception ex) - { - NotificationHelper.ShowError($"连接失败:{ex.Message}", ex); - } + // try + // { + // using (var db = transaction.GetInstance()) + // { + // await db.Ado.OpenAsync(); + // NotificationHelper.ShowSuccess("连接成功!"); + // } + // } + // catch (Exception ex) + // { + // NotificationHelper.ShowError($"连接失败:{ex.Message}", ex); + // } } } diff --git a/DMS.WPF/ViewModels/VariableTableViewModel.cs b/DMS.WPF/ViewModels/VariableTableViewModel.cs index 658bc2c..e762333 100644 --- a/DMS.WPF/ViewModels/VariableTableViewModel.cs +++ b/DMS.WPF/ViewModels/VariableTableViewModel.cs @@ -5,15 +5,14 @@ using System.Windows.Input; using AutoMapper; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using DMS.Data.Repositories; -using DMS.Core.Enums; +using DMS.Core.Models; using DMS.Helper; -using DMS.WPF.Models; using DMS.Services; using iNKORE.UI.WPF.Modern.Controls; using Newtonsoft.Json; using DMS.Extensions; -using SqlSugar; +using DMS.WPF.Services; +using DMS.WPF.ViewModels.Items; namespace DMS.WPF.ViewModels; @@ -63,21 +62,21 @@ partial class VariableTableViewModel : ViewModelBase /// 通过 ObservableProperty 自动生成 VariableTable 属性和 OnVariableTableChanged 方法。 /// [ObservableProperty] - private VariableTable variableTable; + private VariableItemViewModel variableTable; /// /// 存储当前变量表中的所有变量数据的集合。 /// 通过 ObservableProperty 自动生成 Variables 属性和 OnVariablesChanged 方法。 /// [ObservableProperty] - private ObservableCollection _variables; + private ObservableCollection _variables; /// /// 当前选中的变量数据。 /// 通过 ObservableProperty 自动生成 SelectedVariable 属性和 OnSelectedVariableDataChanged 方法。 /// [ObservableProperty] - private Variable _selectedVariable; + private VariableItemViewModel _selectedVariable; /// /// 用于过滤变量数据的搜索文本。 @@ -101,17 +100,17 @@ partial class VariableTableViewModel : ViewModelBase /// /// 变量表数据仓库,用于与变量表相关的数据库操作。 /// - private readonly VarTableRepository _varTableRepository; - - /// - /// 变量数据仓库,用于与变量数据相关的数据库操作。 - /// - private readonly VarDataRepository _varDataRepository; + // private readonly VarTableRepository _varTableRepository; + // + // /// + // /// 变量数据仓库,用于与变量数据相关的数据库操作。 + // /// + // private readonly VarDataRepository _varDataRepository; /// /// 原始变量数据的深拷贝备份,用于在用户取消保存时还原数据。 /// - private ObservableCollection? _originalVariables; + private ObservableCollection? _originalVariables; /// /// 指示当前变量表是否使用S7协议。 @@ -134,16 +133,13 @@ partial class VariableTableViewModel : ViewModelBase /// 对话服务接口的实例。 private readonly DataServices _dataServices; - public VariableTableViewModel(IMapper mapper, IDialogService dialogService, VarTableRepository varTableRepository, - VarDataRepository varDataRepository, DataServices dataServices) + public VariableTableViewModel(IMapper mapper, IDialogService dialogService, DataServices dataServices) { _mapper = mapper; _dialogService = dialogService; _dataServices = dataServices; IsLoadCompletion = false; // 初始设置为 false,表示未完成加载 - _varTableRepository = varTableRepository; - _varDataRepository = varDataRepository; - _variables = new ObservableCollection(); // 初始化集合 + _variables = new ObservableCollection(); // 初始化集合 VariableView = CollectionViewSource.GetDefaultView(Variables); // 获取集合视图 VariableView.Filter = FilterVariables; // 设置过滤方法 } @@ -163,23 +159,23 @@ partial class VariableTableViewModel : ViewModelBase } // 尝试将项转换为 Variable 类型 - if (item is Variable variable) - { - var searchTextLower = SearchText.ToLower(); - // 检查变量的名称、描述、NodeId、S7地址、数据值或显示值是否包含搜索文本 - return variable.Name?.ToLower() - .Contains(searchTextLower) == true || - variable.Description?.ToLower() - .Contains(searchTextLower) == true || - variable.NodeId?.ToLower() - .Contains(searchTextLower) == true || - variable.S7Address?.ToLower() - .Contains(searchTextLower) == true || - variable.DataValue?.ToLower() - .Contains(searchTextLower) == true || - variable.DisplayValue?.ToLower() - .Contains(searchTextLower) == true; - } + // if (item is Variable variable) + // { + // var searchTextLower = SearchText.ToLower(); + // // 检查变量的名称、描述、NodeId、S7地址、数据值或显示值是否包含搜索文本 + // return variable.Name?.ToLower() + // .Contains(searchTextLower) == true || + // variable.Description?.ToLower() + // .Contains(searchTextLower) == true || + // variable.NodeId?.ToLower() + // .Contains(searchTextLower) == true || + // variable.S7Address?.ToLower() + // .Contains(searchTextLower) == true || + // variable.DataValue?.ToLower() + // .Contains(searchTextLower) == true || + // variable.DisplayValue?.ToLower() + // .Contains(searchTextLower) == true; + // } return false; } @@ -201,36 +197,36 @@ partial class VariableTableViewModel : ViewModelBase public override void OnLoaded() { // 根据变量表的协议类型设置对应的布尔属性 - IsS7ProtocolSelected = VariableTable.ProtocolType == ProtocolType.S7; - IsOpcUaProtocolSelected = VariableTable.ProtocolType == ProtocolType.OpcUA; - - // 如果变量表包含数据变量,则进行初始化 - if (VariableTable.Variables != null) - { - // // 将变量表中的数据变量复制到可观察集合中 - Variables.Clear(); // 清空现有集合 - foreach (var item in VariableTable.Variables) - { - Variables.Add(item); // 添加新项 - } - - - VariableView.Refresh(); // 刷新视图以应用过滤和排序 - - // 创建原始数据的深拷贝备份,用于在取消保存时还原 - var settings = new JsonSerializerSettings - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore - }; - var serialized = JsonConvert.SerializeObject(Variables, settings); - _originalVariables = JsonConvert.DeserializeObject>(serialized); - - // 在数据加载完成后,将所有变量的 IsModified 状态重置为 false - foreach (var variable in Variables) - { - variable.IsModified = false; - } - } + // IsS7ProtocolSelected = VariableTable.ProtocolType == ProtocolType.S7; + // IsOpcUaProtocolSelected = VariableTable.ProtocolType == ProtocolType.OpcUA; + // + // // 如果变量表包含数据变量,则进行初始化 + // if (VariableTable.Variables != null) + // { + // // // 将变量表中的数据变量复制到可观察集合中 + // Variables.Clear(); // 清空现有集合 + // foreach (var item in VariableTable.Variables) + // { + // Variables.Add(item); // 添加新项 + // } + // + // + // VariableView.Refresh(); // 刷新视图以应用过滤和排序 + // + // // 创建原始数据的深拷贝备份,用于在取消保存时还原 + // var settings = new JsonSerializerSettings + // { + // ReferenceLoopHandling = ReferenceLoopHandling.Ignore + // }; + // var serialized = JsonConvert.SerializeObject(Variables, settings); + // _originalVariables = JsonConvert.DeserializeObject>(serialized); + // + // // 在数据加载完成后,将所有变量的 IsModified 状态重置为 false + // foreach (var variable in Variables) + // { + // variable.IsModified = false; + // } + // } // 标记加载完成 IsLoadCompletion = true; @@ -251,23 +247,23 @@ partial class VariableTableViewModel : ViewModelBase return true; // 提示用户有未保存的数据,询问是否离开 - var isExit = await _dialogService.ShowConfrimeDialog( - "数据未保存", $"你有{modifiedDatas.Count}个修改的变量没有保存,离开后这些数据就可能丢失了确认要离开吗?", "离开"); + // var isExit = await _dialogService.ShowConfrimeDialog( + // "数据未保存", $"你有{modifiedDatas.Count}个修改的变量没有保存,离开后这些数据就可能丢失了确认要离开吗?", "离开"); - // 如果用户选择不离开(即不保存),则还原数据 - if (!isExit) - { - // 遍历所有已修改的数据,从原始备份中还原 - foreach (var modifiedData in modifiedDatas) - { - var oldData = _originalVariables.First(od => od.Id == modifiedData.Id); - // 将原始数据复制回当前数据 - _mapper.Map(oldData, modifiedData); - modifiedData.IsModified = false; // 重置修改状态 - } - - return false; // 不允许退出 - } + // // 如果用户选择不离开(即不保存),则还原数据 + // if (!isExit) + // { + // // 遍历所有已修改的数据,从原始备份中还原 + // foreach (var modifiedData in modifiedDatas) + // { + // var oldData = _originalVariables.First(od => od.Id == modifiedData.Id); + // // 将原始数据复制回当前数据 + // _mapper.Map(oldData, modifiedData); + // modifiedData.IsModified = false; // 重置修改状态 + // } + // + // return false; // 不允许退出 + // } return true; // 允许退出 } @@ -279,20 +275,20 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] private async void SaveModifiedVarData() { - // 查找所有已标记为修改的变量数据 - var modifiedDatas = Variables.Where(d => d.IsModified == true) - .ToList(); - // 更新数据库中的这些数据 - await _varDataRepository.UpdateAsync(modifiedDatas); - - // 还原所有已保存数据的修改状态 - foreach (var modifiedData in modifiedDatas) - { - modifiedData.IsModified = false; - } - - // 显示成功通知 - NotificationHelper.ShowSuccess($"修改的{modifiedDatas.Count}变量保存成功."); + // // 查找所有已标记为修改的变量数据 + // var modifiedDatas = Variables.Where(d => d.IsModified == true) + // .ToList(); + // // 更新数据库中的这些数据 + // await _varDataRepository.UpdateAsync(modifiedDatas); + // + // // 还原所有已保存数据的修改状态 + // foreach (var modifiedData in modifiedDatas) + // { + // modifiedData.IsModified = false; + // } + // + // // 显示成功通知 + // NotificationHelper.ShowSuccess($"修改的{modifiedDatas.Count}变量保存成功."); } /// @@ -303,34 +299,34 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] private async void UpdateVariable(VariableTable variableTable) { - try - { - // 显示编辑变量数据的对话框,并传入当前选中的变量数据 - var varData = await _dialogService.ShowEditVarDataDialog(SelectedVariable); - - // 如果用户取消或对话框未返回数据,则直接返回 - if (varData == null) - return; - - // 设置变量数据的所属变量表ID - varData.VariableTableId = variableTable.Id; - - // 更新数据库中的变量数据 - await _varDataRepository.UpdateAsync(varData); - - // 更新当前页面显示的数据:找到原数据在集合中的索引并替换 - var index = variableTable.Variables.IndexOf(SelectedVariable); - if (index >= 0 && index < variableTable.Variables.Count) - variableTable.Variables[index] = varData; // 替换为编辑后的数据 - - // 显示成功通知 - NotificationHelper.ShowSuccess($"编辑变量成功:{varData?.Name}"); - } - catch (Exception e) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"编辑变量的过程中发生了不可预期的错误:{e.Message}", e); - } + // try + // { + // // 显示编辑变量数据的对话框,并传入当前选中的变量数据 + // var varData = await _dialogService.ShowEditVarDataDialog(SelectedVariable); + // + // // 如果用户取消或对话框未返回数据,则直接返回 + // if (varData == null) + // return; + // + // // 设置变量数据的所属变量表ID + // varData.VariableTableId = variableTable.Id; + // + // // 更新数据库中的变量数据 + // await _varDataRepository.UpdateAsync(varData); + // + // // 更新当前页面显示的数据:找到原数据在集合中的索引并替换 + // var index = variableTable.Variables.IndexOf(SelectedVariable); + // if (index >= 0 && index < variableTable.Variables.Count) + // variableTable.Variables[index] = varData; // 替换为编辑后的数据 + // + // // 显示成功通知 + // NotificationHelper.ShowSuccess($"编辑变量成功:{varData?.Name}"); + // } + // catch (Exception e) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"编辑变量的过程中发生了不可预期的错误:{e.Message}", e); + // } } /// @@ -340,76 +336,76 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] private async void ImprotFromTiaVarTable() { - ContentDialog processingDialog = null; // 用于显示处理中的对话框 - try - { - // 让用户选择要导入的Excel文件路径 - var filePath = await _dialogService.ShowImportExcelDialog(); - if (string.IsNullOrEmpty(filePath)) - return; // 如果用户取消选择,则返回 - - // 读取Excel文件并将其内容转换为 Variable 列表 - var importVarDataList = ExcelHelper.ImprotFromTiaVariableTable(filePath); - if (importVarDataList.Count == 0) - return; // 如果没有读取到数据,则返回 - - // 显示处理中的对话框 - processingDialog = _dialogService.ShowProcessingDialog("正在处理...", "正在导入变量,请稍等片刻...."); - - List newVariables = new List(); - List importedVariableNames = new List(); - List existingVariableNames = new List(); - - foreach (var variableData in importVarDataList) - { - // 判断是否存在重复变量 - // 判断是否存在重复变量,仅在当前 VariableTable 的 Variables 中查找 - bool isDuplicate = Variables.Any(existingVar => - (existingVar.Name == variableData.Name) || - (!string.IsNullOrEmpty(variableData.NodeId) && - existingVar.NodeId == variableData.NodeId) || - (!string.IsNullOrEmpty(variableData.S7Address) && - existingVar.S7Address == variableData.S7Address) - ); - - if (isDuplicate) - { - existingVariableNames.Add(variableData.Name); - } - else - { - variableData.CreateTime = DateTime.Now; - variableData.IsActive = true; - variableData.VariableTableId = VariableTable.Id; - newVariables.Add(variableData); - importedVariableNames.Add(variableData.Name); - } - } - - if (newVariables.Any()) - { - // 批量插入新变量数据到数据库 - var resVarDataCount = await _varDataRepository.AddAsync(newVariables); - NlogHelper.Info($"成功导入变量:{resVarDataCount}个。"); - } - - // 更新界面显示的数据:重新从数据库加载所有变量数据 - await RefreshDataView(); - - processingDialog?.Hide(); // 隐藏处理中的对话框 - - // 显示导入结果对话框 - await _dialogService.ShowImportResultDialog(importedVariableNames, existingVariableNames); - } - catch (Exception e) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"从TIA导入变量的过程中发生了不可预期的错误:{e.Message}", e); - } - finally - { - processingDialog?.Hide(); // 确保在任何情况下都隐藏对话框 - } + // ContentDialog processingDialog = null; // 用于显示处理中的对话框 + // try + // { + // // 让用户选择要导入的Excel文件路径 + // var filePath = await _dialogService.ShowImportExcelDialog(); + // if (string.IsNullOrEmpty(filePath)) + // return; // 如果用户取消选择,则返回 + // + // // 读取Excel文件并将其内容转换为 Variable 列表 + // var importVarDataList = DMS.Infrastructure.Helper.ExcelHelper.ImprotFromTiaVariableTable(filePath); + // if (importVarDataList.Count == 0) + // return; // 如果没有读取到数据,则返回 + // + // // 显示处理中的对话框 + // processingDialog = _dialogService.ShowProcessingDialog("正在处理...", "正在导入变量,请稍等片刻...."); + // + // List newVariables = new List(); + // List importedVariableNames = new List(); + // List existingVariableNames = new List(); + // + // foreach (var variableData in importVarDataList) + // { + // // 判断是否存在重复变量 + // // 判断是否存在重复变量,仅在当前 VariableTable 的 Variables 中查找 + // bool isDuplicate = Variables.Any(existingVar => + // (existingVar.Name == variableData.Name) || + // (!string.IsNullOrEmpty(variableData.NodeId) && + // existingVar.NodeId == variableData.NodeId) || + // (!string.IsNullOrEmpty(variableData.S7Address) && + // existingVar.S7Address == variableData.S7Address) + // ); + // + // if (isDuplicate) + // { + // existingVariableNames.Add(variableData.Name); + // } + // else + // { + // variableData.CreateTime = DateTime.Now; + // variableData.IsActive = true; + // variableData.VariableTableId = VariableTable.Id; + // newVariables.Add(variableData); + // importedVariableNames.Add(variableData.Name); + // } + // } + // + // if (newVariables.Any()) + // { + // // 批量插入新变量数据到数据库 + // var resVarDataCount = await _varDataRepository.AddAsync(newVariables); + // NlogHelper.Info($"成功导入变量:{resVarDataCount}个。"); + // } + // + // // 更新界面显示的数据:重新从数据库加载所有变量数据 + // await RefreshDataView(); + // + // processingDialog?.Hide(); // 隐藏处理中的对话框 + // + // // 显示导入结果对话框 + // await _dialogService.ShowImportResultDialog(importedVariableNames, existingVariableNames); + // } + // catch (Exception e) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"从TIA导入变量的过程中发生了不可预期的错误:{e.Message}", e); + // } + // finally + // { + // processingDialog?.Hide(); // 确保在任何情况下都隐藏对话框 + // } } /// @@ -419,84 +415,84 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] private async Task ImportFromOpcUaServer() { - ContentDialog processingDialog = null; // 用于显示处理中的对话框 - try - { - // 检查OPC UA Endpoint URL是否已设置 - string opcUaEndpointUrl = VariableTable?.Device?.OpcUaEndpointUrl; - if (string.IsNullOrEmpty(opcUaEndpointUrl)) - { - NotificationHelper.ShowError("OPC UA Endpoint URL 未设置。请在设备详情中配置。"); - return; - } - - // 显示OPC UA导入对话框,让用户选择要导入的变量 - var importedVariables = await _dialogService.ShowOpcUaImportDialog(opcUaEndpointUrl); - if (importedVariables == null || !importedVariables.Any()) - { - return; // 用户取消或没有选择任何变量 - } - - // 显示处理中的对话框 - processingDialog = _dialogService.ShowProcessingDialog("正在处理...", "正在导入OPC UA变量,请稍等片刻...."); - - // 在进行重复检查之前,先刷新 Variables 集合,确保其包含所有最新数据 - await RefreshDataView(); - - List newVariables = new List(); - List importedVariableNames = new List(); - List existingVariableNames = new List(); - - foreach (var variableData in importedVariables) - { - // 判断是否存在重复变量,仅在当前 VariableTable 的 Variables 中查找 - bool isDuplicate = Variables.Any(existingVar => - (existingVar.Name == variableData.Name) || - (!string.IsNullOrEmpty(variableData.NodeId) && - existingVar.NodeId == variableData.NodeId) || - (!string.IsNullOrEmpty(variableData.OpcUaNodeId) && - existingVar.OpcUaNodeId == variableData.OpcUaNodeId) - ); - - if (isDuplicate) - { - existingVariableNames.Add(variableData.Name); - } - else - { - variableData.CreateTime = DateTime.Now; - variableData.VariableTableId = VariableTable.Id; - variableData.ProtocolType = ProtocolType.OpcUA; // 确保协议类型正确 - variableData.IsModified = false; - newVariables.Add(variableData); - importedVariableNames.Add(variableData.Name); - } - } - - if (newVariables.Any()) - { - // 批量插入新变量数据到数据库 - var resVarDataCount = await _varDataRepository.AddAsync(newVariables); - NlogHelper.Info($"成功导入OPC UA变量:{resVarDataCount}个。"); - } - - // 再次刷新 Variables 集合,以反映新添加的数据 - await RefreshDataView(); - - processingDialog?.Hide(); // 隐藏处理中的对话框 - - // 显示导入结果对话框 - await _dialogService.ShowImportResultDialog(importedVariableNames, existingVariableNames); - } - catch (Exception e) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"从OPC UA服务器导入变量的过程中发生了不可预期的错误:{e.Message}", e); - } - finally - { - processingDialog?.Hide(); // 确保在任何情况下都隐藏对话框 - } + // ContentDialog processingDialog = null; // 用于显示处理中的对话框 + // try + // { + // // 检查OPC UA Endpoint URL是否已设置 + // string opcUaEndpointUrl = VariableTable?.Device?.OpcUaEndpointUrl; + // if (string.IsNullOrEmpty(opcUaEndpointUrl)) + // { + // NotificationHelper.ShowError("OPC UA Endpoint URL 未设置。请在设备详情中配置。"); + // return; + // } + // + // // 显示OPC UA导入对话框,让用户选择要导入的变量 + // var importedVariables = await _dialogService.ShowOpcUaImportDialog(opcUaEndpointUrl); + // if (importedVariables == null || !importedVariables.Any()) + // { + // return; // 用户取消或没有选择任何变量 + // } + // + // // 显示处理中的对话框 + // processingDialog = _dialogService.ShowProcessingDialog("正在处理...", "正在导入OPC UA变量,请稍等片刻...."); + // + // // 在进行重复检查之前,先刷新 Variables 集合,确保其包含所有最新数据 + // await RefreshDataView(); + // + // List newVariables = new List(); + // List importedVariableNames = new List(); + // List existingVariableNames = new List(); + // + // foreach (var variableData in importedVariables) + // { + // // 判断是否存在重复变量,仅在当前 VariableTable 的 Variables 中查找 + // bool isDuplicate = Variables.Any(existingVar => + // (existingVar.Name == variableData.Name) || + // (!string.IsNullOrEmpty(variableData.NodeId) && + // existingVar.NodeId == variableData.NodeId) || + // (!string.IsNullOrEmpty(variableData.OpcUaNodeId) && + // existingVar.OpcUaNodeId == variableData.OpcUaNodeId) + // ); + // + // if (isDuplicate) + // { + // existingVariableNames.Add(variableData.Name); + // } + // else + // { + // variableData.CreateTime = DateTime.Now; + // variableData.VariableTableId = VariableTable.Id; + // variableData.ProtocolType = ProtocolType.OpcUA; // 确保协议类型正确 + // variableData.IsModified = false; + // newVariables.Add(variableData); + // importedVariableNames.Add(variableData.Name); + // } + // } + // + // if (newVariables.Any()) + // { + // // 批量插入新变量数据到数据库 + // var resVarDataCount = await _varDataRepository.AddAsync(newVariables); + // NlogHelper.Info($"成功导入OPC UA变量:{resVarDataCount}个。"); + // } + // + // // 再次刷新 Variables 集合,以反映新添加的数据 + // await RefreshDataView(); + // + // processingDialog?.Hide(); // 隐藏处理中的对话框 + // + // // 显示导入结果对话框 + // await _dialogService.ShowImportResultDialog(importedVariableNames, existingVariableNames); + // } + // catch (Exception e) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"从OPC UA服务器导入变量的过程中发生了不可预期的错误:{e.Message}", e); + // } + // finally + // { + // processingDialog?.Hide(); // 确保在任何情况下都隐藏对话框 + // } } /// @@ -504,52 +500,52 @@ partial class VariableTableViewModel : ViewModelBase /// private async Task RefreshDataView() { - // 从数据库加载最新的变量数据 - var latestVariables = await _varDataRepository.GetByVariableTableIdAsync(VariableTable.Id); - - // 将最新数据转换为字典,以便快速查找 - var latestVariablesDict = latestVariables.ToDictionary(v => v.Id); - - // 用于存储需要从 Variables 中移除的项 - var itemsToRemove = new List(); - - // 遍历当前 Variables 集合,处理删除和更新 - for (int i = Variables.Count - 1; i >= 0; i--) - { - var currentVariable = Variables[i]; - if (latestVariablesDict.TryGetValue(currentVariable.Id, out var newVariable)) - { - // 如果存在于最新数据中,检查是否需要更新 - if (!currentVariable.Equals(newVariable)) - { - // 使用 AutoMapper 更新现有对象的属性,保持对象引用不变 - _mapper.Map(newVariable, currentVariable); - } - - // 从字典中移除已处理的项,剩余的将是新增项 - latestVariablesDict.Remove(currentVariable.Id); - } - else - { - // 如果不存在于最新数据中,则标记为删除 - itemsToRemove.Add(currentVariable); - } - } - - // 移除已标记的项 - foreach (var item in itemsToRemove) - { - Variables.Remove(item); - } - - // 添加所有剩余在 latestVariablesDict 中的项(这些是新增项) - foreach (var newVariable in latestVariablesDict.Values) - { - Variables.Add(newVariable); - } - - // 刷新视图以应用所有更改 - VariableView.Refresh(); + // // 从数据库加载最新的变量数据 + // var latestVariables = await _varDataRepository.GetByVariableTableIdAsync(VariableTable.Id); + // + // // 将最新数据转换为字典,以便快速查找 + // var latestVariablesDict = latestVariables.ToDictionary(v => v.Id); + // + // // 用于存储需要从 Variables 中移除的项 + // var itemsToRemove = new List(); + // + // // 遍历当前 Variables 集合,处理删除和更新 + // for (int i = Variables.Count - 1; i >= 0; i--) + // { + // var currentVariable = Variables[i]; + // if (latestVariablesDict.TryGetValue(currentVariable.Id, out var newVariable)) + // { + // // 如果存在于最新数据中,检查是否需要更新 + // if (!currentVariable.Equals(newVariable)) + // { + // // 使用 AutoMapper 更新现有对象的属性,保持对象引用不变 + // _mapper.Map(newVariable, currentVariable); + // } + // + // // 从字典中移除已处理的项,剩余的将是新增项 + // latestVariablesDict.Remove(currentVariable.Id); + // } + // else + // { + // // 如果不存在于最新数据中,则标记为删除 + // itemsToRemove.Add(currentVariable); + // } + // } + // + // // 移除已标记的项 + // foreach (var item in itemsToRemove) + // { + // Variables.Remove(item); + // } + // + // // 添加所有剩余在 latestVariablesDict 中的项(这些是新增项) + // foreach (var newVariable in latestVariablesDict.Values) + // { + // Variables.Add(newVariable); + // } + // + // // 刷新视图以应用所有更改 + // VariableView.Refresh(); } // @@ -561,76 +557,76 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] private async void AddVarData(VariableTable variableTable) { - try - { - // 显示添加变量数据的对话框 - var varData = await _dialogService.ShowAddVarDataDialog(); - - // 如果用户取消或对话框未返回数据,则直接返回 - if (varData == null) - return; - - // 设置新变量的所属变量表ID - varData.VariableTableId = variableTable.Id; - - // --- 重复性检查逻辑开始 --- - bool isDuplicate = false; - string duplicateReason = string.Empty; - - // 检查名称是否重复 - if (Variables.Any(v => v.Name == varData.Name)) - { - isDuplicate = true; - duplicateReason = $"名称 '{varData.Name}' 已存在。"; - } - else - { - // 根据协议类型检查S7地址或NodeId是否重复 - if (variableTable.ProtocolType == ProtocolType.S7) - { - if (!string.IsNullOrEmpty(varData.S7Address) && - Variables.Any(v => v.S7Address == varData.S7Address)) - { - isDuplicate = true; - duplicateReason = $"S7地址 '{varData.S7Address}' 已存在。"; - } - } - else if (variableTable.ProtocolType == ProtocolType.OpcUA) - { - if (!string.IsNullOrEmpty(varData.NodeId) && Variables.Any(v => v.NodeId == varData.NodeId)) - { - isDuplicate = true; - duplicateReason = $"OPC UA NodeId '{varData.NodeId}' 已存在。"; - } - } - } - - if (isDuplicate) - { - NotificationHelper.ShowError($"添加变量失败:{duplicateReason}"); - return; - } - // --- 重复性检查逻辑结束 --- - - // 添加变量数据到数据库 - var resVarData = await _varDataRepository.AddAsync(varData); - if (resVarData == null) - { - NotificationHelper.ShowError($"添加变量失败了:{varData?.Name}"); - return; - } - - // 更新当前页面显示的数据:将新变量添加到集合中 - Variables.Add(resVarData); - - // 显示成功通知 - NotificationHelper.ShowSuccess($"添加变量成功:{varData?.Name}"); - } - catch (Exception e) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"添加变量的过程中发生了不可预期的错误:{e.Message}", e); - } + // try + // { + // // 显示添加变量数据的对话框 + // var varData = await _dialogService.ShowAddVarDataDialog(); + // + // // 如果用户取消或对话框未返回数据,则直接返回 + // if (varData == null) + // return; + // + // // 设置新变量的所属变量表ID + // varData.VariableTableId = variableTable.Id; + // + // // --- 重复性检查逻辑开始 --- + // bool isDuplicate = false; + // string duplicateReason = string.Empty; + // + // // 检查名称是否重复 + // if (Variables.Any(v => v.Name == varData.Name)) + // { + // isDuplicate = true; + // duplicateReason = $"名称 '{varData.Name}' 已存在。"; + // } + // else + // { + // // 根据协议类型检查S7地址或NodeId是否重复 + // if (variableTable.ProtocolType == ProtocolType.S7) + // { + // if (!string.IsNullOrEmpty(varData.S7Address) && + // Variables.Any(v => v.S7Address == varData.S7Address)) + // { + // isDuplicate = true; + // duplicateReason = $"S7地址 '{varData.S7Address}' 已存在。"; + // } + // } + // else if (variableTable.ProtocolType == ProtocolType.OpcUA) + // { + // if (!string.IsNullOrEmpty(varData.NodeId) && Variables.Any(v => v.NodeId == varData.NodeId)) + // { + // isDuplicate = true; + // duplicateReason = $"OPC UA NodeId '{varData.NodeId}' 已存在。"; + // } + // } + // } + // + // if (isDuplicate) + // { + // NotificationHelper.ShowError($"添加变量失败:{duplicateReason}"); + // return; + // } + // // --- 重复性检查逻辑结束 --- + // + // // 添加变量数据到数据库 + // var resVarData = await _varDataRepository.AddAsync(varData); + // if (resVarData == null) + // { + // NotificationHelper.ShowError($"添加变量失败了:{varData?.Name}"); + // return; + // } + // + // // 更新当前页面显示的数据:将新变量添加到集合中 + // Variables.Add(resVarData); + // + // // 显示成功通知 + // NotificationHelper.ShowSuccess($"添加变量成功:{varData?.Name}"); + // } + // catch (Exception e) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"添加变量的过程中发生了不可预期的错误:{e.Message}", e); + // } } /// @@ -641,46 +637,46 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] public async Task DeleteVarData(List variablesToDelete) { - // 检查是否有变量被选中 - if (variablesToDelete == null || !variablesToDelete.Any()) - { - NotificationHelper.ShowInfo("请选择要删除的变量"); - return; - } - - // 拼接要删除的变量名称,用于确认提示 - var names = string.Join("、", variablesToDelete.Select(v => v.Name)); - - // 显示确认删除对话框 - var confirm = await _dialogService.ShowConfrimeDialog( - "删除确认", - $"确定要删除选中的 {variablesToDelete.Count} 个变量吗?\n\n{names}", - "删除"); - - if (!confirm) - return; // 如果用户取消删除,则返回 - - try - { - // 从数据库中删除变量数据 - var result = await _varDataRepository.DeleteAsync(variablesToDelete); - if (result > 0) - { - await RefreshDataView(); - // 显示成功通知 - NotificationHelper.ShowSuccess($"成功删除 {result} 个变量"); - } - else - { - // 显示删除失败通知 - NotificationHelper.ShowError("删除变量失败"); - } - } - catch (Exception e) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"删除变量的过程中发生了不可预期的错误:{e.Message}", e); - } + // // 检查是否有变量被选中 + // if (variablesToDelete == null || !variablesToDelete.Any()) + // { + // NotificationHelper.ShowInfo("请选择要删除的变量"); + // return; + // } + // + // // 拼接要删除的变量名称,用于确认提示 + // var names = string.Join("、", variablesToDelete.Select(v => v.Name)); + // + // // 显示确认删除对话框 + // var confirm = await _dialogService.ShowConfrimeDialog( + // "删除确认", + // $"确定要删除选中的 {variablesToDelete.Count} 个变量吗?\n\n{names}", + // "删除"); + // + // if (!confirm) + // return; // 如果用户取消删除,则返回 + // + // try + // { + // // 从数据库中删除变量数据 + // var result = await _varDataRepository.DeleteAsync(variablesToDelete); + // if (result > 0) + // { + // await RefreshDataView(); + // // 显示成功通知 + // NotificationHelper.ShowSuccess($"成功删除 {result} 个变量"); + // } + // else + // { + // // 显示删除失败通知 + // NotificationHelper.ShowError("删除变量失败"); + // } + // } + // catch (Exception e) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"删除变量的过程中发生了不可预期的错误:{e.Message}", e); + // } } /// @@ -691,35 +687,35 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] public async Task ChangePollLevel(IList variablesToChange) { - var validVariables = variablesToChange?.OfType() - .ToList(); - - // 检查是否有变量被选中 - if (validVariables == null || !validVariables.Any()) - { - NotificationHelper.ShowInfo("请选择要修改轮询频率的变量"); - return; - } - - // 显示轮询频率选择对话框,并传入第一个变量的当前轮询频率作为默认值 - var newPollLevelType = await _dialogService.ShowPollLevelDialog(validVariables.First() - .PollLevelType); - if (newPollLevelType.HasValue) - { - // 更新所有选定变量的轮询频率和修改状态 - foreach (var variable in validVariables) - { - variable.PollLevelType = newPollLevelType.Value; - variable.IsModified = false; // 标记为未修改,因为已保存到数据库 - } - - // 批量更新数据库中的变量数据 - await _varDataRepository.UpdateAsync(validVariables); - - await RefreshDataView(); - // 显示成功通知 - NotificationHelper.ShowSuccess($"已成功更新 {validVariables.Count} 个变量的轮询频率"); - } + // var validVariables = variablesToChange?.OfType() + // .ToList(); + // + // // 检查是否有变量被选中 + // if (validVariables == null || !validVariables.Any()) + // { + // NotificationHelper.ShowInfo("请选择要修改轮询频率的变量"); + // return; + // } + // + // // 显示轮询频率选择对话框,并传入第一个变量的当前轮询频率作为默认值 + // var newPollLevelType = await _dialogService.ShowPollLevelDialog(validVariables.First() + // .PollLevelType); + // if (newPollLevelType.HasValue) + // { + // // 更新所有选定变量的轮询频率和修改状态 + // foreach (var variable in validVariables) + // { + // variable.PollLevelType = newPollLevelType.Value; + // variable.IsModified = false; // 标记为未修改,因为已保存到数据库 + // } + // + // // 批量更新数据库中的变量数据 + // await _varDataRepository.UpdateAsync(validVariables); + // + // await RefreshDataView(); + // // 显示成功通知 + // NotificationHelper.ShowSuccess($"已成功更新 {validVariables.Count} 个变量的轮询频率"); + // } } /// @@ -729,31 +725,31 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] public async Task ModifyOpcUaUpdateType(IList variablesToChange) { - // 过滤出有效的VariableData对象 - var validVariables = variablesToChange?.OfType() - .ToList(); - - if (validVariables == null || !validVariables.Any()) - { - NotificationHelper.ShowInfo("请选择要修改更新方式的OPC UA变量"); - return; - } - - - // 显示更新方式选择对话框 - var newUpdateType = await _dialogService.ShowOpcUaUpdateTypeDialog(); - if (newUpdateType.HasValue) - { - // 更新所有选定变量的更新方式 - foreach (var variable in validVariables) - { - variable.OpcUaUpdateType = newUpdateType.Value; - } - - // 批量更新数据库 - await _varDataRepository.UpdateAsync(validVariables); - NotificationHelper.ShowSuccess($"已成功为 {validVariables.Count} 个变量更新OPC UA更新方式。"); - } + // // 过滤出有效的VariableData对象 + // var validVariables = variablesToChange?.OfType() + // .ToList(); + // + // if (validVariables == null || !validVariables.Any()) + // { + // NotificationHelper.ShowInfo("请选择要修改更新方式的OPC UA变量"); + // return; + // } + // + // + // // 显示更新方式选择对话框 + // var newUpdateType = await _dialogService.ShowOpcUaUpdateTypeDialog(); + // if (newUpdateType.HasValue) + // { + // // 更新所有选定变量的更新方式 + // foreach (var variable in validVariables) + // { + // variable.OpcUaUpdateType = newUpdateType.Value; + // } + // + // // 批量更新数据库 + // await _varDataRepository.UpdateAsync(validVariables); + // NotificationHelper.ShowSuccess($"已成功为 {validVariables.Count} 个变量更新OPC UA更新方式。"); + // } } /// @@ -764,101 +760,101 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] public async Task AddMqttServerToVariables(IList variablesToAddMqtt) { - var validVariables = variablesToAddMqtt?.OfType() - .ToList(); - - // 检查是否有变量被选中 - if (validVariables == null || !validVariables.Any()) - { - NotificationHelper.ShowInfo("请选择要添加MQTT服务器的变量"); - return; - } - - try - { - // 显示MQTT服务器选择对话框,让用户选择一个MQTT服务器 - var selectedMqtt = await _dialogService.ShowMqttSelectionDialog(); - if (selectedMqtt == null) - { - return; // 用户取消选择 - } - - // 显示批量编辑别名对话框 - var editedVariableMqtts = await _dialogService.ShowMqttAliasBatchEditDialog(validVariables, selectedMqtt); - - if (editedVariableMqtts == null || !editedVariableMqtts.Any()) - { - NotificationHelper.ShowInfo("没有变量别名被设置或已取消。"); - return; - } - - - int totalAffectedCount = 0; - // 调用仓库方法来添加或更新MQTT服务器关联和别名 - var resCount = await _varDataRepository.AddMqttToVariablesAsync(editedVariableMqtts); - totalAffectedCount += resCount; - - - //更新变量Variable的VariableMqtts列表 - foreach (var editedVariableMqtt in editedVariableMqtts) - { - // 更新内存中的 Variable 对象 - var originalVariable = VariableTable.Variables.FirstOrDefault(v=>v.Id==editedVariableMqtt.Variable.Id); - if (originalVariable == null) - { - NlogHelper.Warn($"没有在VariableTable.Variables中找到,ID:{editedVariableMqtt.Variable.Id},Name:{editedVariableMqtt.Variable.Name}的对象"); - continue; - } - - - if (originalVariable.VariableMqtts == null) - { - originalVariable.VariableMqtts = new List(); - } - - // 检查是否已存在该变量与该MQTT服务器的关联 - var existingVariableMqtt - = originalVariable.VariableMqtts.FirstOrDefault(vm => vm.MqttId == - editedVariableMqtt.Mqtt.Id); - - if (existingVariableMqtt == null) - { - // 如果不存在,则添加新的关联 - var variableMqtt = new VariableMqtt(originalVariable,editedVariableMqtt.Mqtt) - { - VariableId = originalVariable.Id, - MqttId = editedVariableMqtt.Mqtt.Id, - MqttAlias = editedVariableMqtt.MqttAlias, - Mqtt = editedVariableMqtt.Mqtt // 关联Mqtt对象,方便UI显示 - }; - originalVariable.VariableMqtts.Add(variableMqtt); - //更新MQTT服务器对应的的VariableMqtts列表 - selectedMqtt.VariableMqtts.Add(variableMqtt); - } - else - { - // 如果存在,则更新别名 - existingVariableMqtt.MqttAlias = editedVariableMqtt.MqttAlias; - } - } - - - if (totalAffectedCount > 0) - { - // 刷新界面以反映更改 - await RefreshDataView(); - NotificationHelper.ShowSuccess($"已成功为 {totalAffectedCount} 个变量添加/更新MQTT服务器: {selectedMqtt.Name} 的别名。"); - } - else - { - NotificationHelper.ShowInfo($"没有新的变量关联或别名更新到MQTT服务器: {selectedMqtt.Name}。"); - } - } - catch (Exception ex) - { - // 捕获并显示错误通知 - NotificationHelper.ShowError($"添加MQTT服务器失败: {ex.Message}", ex); - } + // var validVariables = variablesToAddMqtt?.OfType() + // .ToList(); + // + // // 检查是否有变量被选中 + // if (validVariables == null || !validVariables.Any()) + // { + // NotificationHelper.ShowInfo("请选择要添加MQTT服务器的变量"); + // return; + // } + // + // try + // { + // // 显示MQTT服务器选择对话框,让用户选择一个MQTT服务器 + // var selectedMqtt = await _dialogService.ShowMqttSelectionDialog(); + // if (selectedMqtt == null) + // { + // return; // 用户取消选择 + // } + // + // // 显示批量编辑别名对话框 + // var editedVariableMqtts = await _dialogService.ShowMqttAliasBatchEditDialog(validVariables, selectedMqtt); + // + // if (editedVariableMqtts == null || !editedVariableMqtts.Any()) + // { + // NotificationHelper.ShowInfo("没有变量别名被设置或已取消。"); + // return; + // } + // + // + // int totalAffectedCount = 0; + // // 调用仓库方法来添加或更新MQTT服务器关联和别名 + // var resCount = await _varDataRepository.AddMqttToVariablesAsync(editedVariableMqtts); + // totalAffectedCount += resCount; + // + // + // //更新变量Variable的VariableMqtts列表 + // foreach (var editedVariableMqtt in editedVariableMqtts) + // { + // // 更新内存中的 Variable 对象 + // var originalVariable = VariableTable.Variables.FirstOrDefault(v=>v.Id==editedVariableMqtt.Variable.Id); + // if (originalVariable == null) + // { + // NlogHelper.Warn($"没有在VariableTable.Variables中找到,ID:{editedVariableMqtt.Variable.Id},Name:{editedVariableMqtt.Variable.Name}的对象"); + // continue; + // } + // + // + // if (originalVariable.VariableMqtts == null) + // { + // originalVariable.VariableMqtts = new List(); + // } + // + // // 检查是否已存在该变量与该MQTT服务器的关联 + // var existingVariableMqtt + // = originalVariable.VariableMqtts.FirstOrDefault(vm => vm.MqttId == + // editedVariableMqtt.Mqtt.Id); + // + // if (existingVariableMqtt == null) + // { + // // 如果不存在,则添加新的关联 + // var variableMqtt = new VariableMqtt(originalVariable,editedVariableMqtt.Mqtt) + // { + // VariableId = originalVariable.Id, + // MqttId = editedVariableMqtt.Mqtt.Id, + // MqttAlias = editedVariableMqtt.MqttAlias, + // Mqtt = editedVariableMqtt.Mqtt // 关联Mqtt对象,方便UI显示 + // }; + // originalVariable.VariableMqtts.Add(variableMqtt); + // //更新MQTT服务器对应的的VariableMqtts列表 + // selectedMqtt.VariableMqtts.Add(variableMqtt); + // } + // else + // { + // // 如果存在,则更新别名 + // existingVariableMqtt.MqttAlias = editedVariableMqtt.MqttAlias; + // } + // } + // + // + // if (totalAffectedCount > 0) + // { + // // 刷新界面以反映更改 + // await RefreshDataView(); + // NotificationHelper.ShowSuccess($"已成功为 {totalAffectedCount} 个变量添加/更新MQTT服务器: {selectedMqtt.Name} 的别名。"); + // } + // else + // { + // NotificationHelper.ShowInfo($"没有新的变量关联或别名更新到MQTT服务器: {selectedMqtt.Name}。"); + // } + // } + // catch (Exception ex) + // { + // // 捕获并显示错误通知 + // NotificationHelper.ShowError($"添加MQTT服务器失败: {ex.Message}", ex); + // } } /// @@ -868,41 +864,41 @@ partial class VariableTableViewModel : ViewModelBase [RelayCommand] public async Task ModifyIsActive(IList variablesToChange) { - var validVariables = variablesToChange?.OfType() - .ToList(); - - if (validVariables == null || !validVariables.Any()) - { - NotificationHelper.ShowInfo("请选择要修改启用状态的变量"); - return; - } - - // 假设所有选中的变量都应该被设置为相同的状态,取第一个变量的当前状态的反值 - var currentIsActive = validVariables.First() - .IsActive; - var newIsActive = !currentIsActive; - - var confirm = await _dialogService.ShowIsActiveDialog(newIsActive); - - if (confirm.HasValue && confirm.Value == newIsActive) - { - foreach (var variable in validVariables) - { - variable.IsActive = newIsActive; - } - - await _varDataRepository.UpdateAsync(validVariables); - - // 更新界面 - await RefreshDataView(); - - - NotificationHelper.ShowSuccess($"已成功将 {validVariables.Count} 个变量的启用状态修改为 {newIsActive}"); - } - else - { - NotificationHelper.ShowInfo("操作已取消或状态未改变。"); - } + // var validVariables = variablesToChange?.OfType() + // .ToList(); + // + // if (validVariables == null || !validVariables.Any()) + // { + // NotificationHelper.ShowInfo("请选择要修改启用状态的变量"); + // return; + // } + // + // // 假设所有选中的变量都应该被设置为相同的状态,取第一个变量的当前状态的反值 + // var currentIsActive = validVariables.First() + // .IsActive; + // var newIsActive = !currentIsActive; + // + // var confirm = await _dialogService.ShowIsActiveDialog(newIsActive); + // + // if (confirm.HasValue && confirm.Value == newIsActive) + // { + // foreach (var variable in validVariables) + // { + // variable.IsActive = newIsActive; + // } + // + // await _varDataRepository.UpdateAsync(validVariables); + // + // // 更新界面 + // await RefreshDataView(); + // + // + // NotificationHelper.ShowSuccess($"已成功将 {validVariables.Count} 个变量的启用状态修改为 {newIsActive}"); + // } + // else + // { + // NotificationHelper.ShowInfo("操作已取消或状态未改变。"); + // } } /// @@ -912,19 +908,19 @@ partial class VariableTableViewModel : ViewModelBase /// 变量表的新激活状态(true为启用,false为禁用)。 public async Task OnIsActiveChanged(bool active) { - // 更新数据库中变量表的激活状态 - var res = await _varTableRepository.UpdateAsync(VariableTable); - if (res > 0) - { - // 根据激活状态显示成功通知 - var statusMessage = active ? "已启用" : "已停用"; - NotificationHelper.ShowSuccess($"变量表:{VariableTable.Name},{statusMessage}"); - } - else - { - // 显示失败通知 - NotificationHelper.ShowError($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); - // _logger.LogInformation($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); // 可以选择记录日志 - } + // // 更新数据库中变量表的激活状态 + // var res = await _varTableRepository.UpdateAsync(VariableTable); + // if (res > 0) + // { + // // 根据激活状态显示成功通知 + // var statusMessage = active ? "已启用" : "已停用"; + // NotificationHelper.ShowSuccess($"变量表:{VariableTable.Name},{statusMessage}"); + // } + // else + // { + // // 显示失败通知 + // NotificationHelper.ShowError($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); + // // _logger.LogInformation($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); // 可以选择记录日志 + // } } } \ No newline at end of file diff --git a/DMS.WPF/Views/DeviceDetailView.xaml b/DMS.WPF/Views/DeviceDetailView.xaml index 270a409..3e76c56 100644 --- a/DMS.WPF/Views/DeviceDetailView.xaml +++ b/DMS.WPF/Views/DeviceDetailView.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" - xmlns:vm="clr-namespace:DMS.ViewModels" + xmlns:vm="clr-namespace:DMS.WPF.ViewModels" xmlns:hc="https://handyorg.github.io/handycontrol" d:DataContext="{d:DesignInstance vm:DeviceDetailViewModel}" mc:Ignorable="d" diff --git a/DMS.WPF/Views/DevicesView.xaml b/DMS.WPF/Views/DevicesView.xaml index 22a7407..27700ec 100644 --- a/DMS.WPF/Views/DevicesView.xaml +++ b/DMS.WPF/Views/DevicesView.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" - xmlns:vm="clr-namespace:DMS.ViewModels" + xmlns:vm="clr-namespace:DMS.WPF.ViewModels" xmlns:hc="https://handyorg.github.io/handycontrol" d:DataContext="{d:DesignInstance vm:DevicesViewModel}" mc:Ignorable="d" diff --git a/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml b/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml index 103d592..b807cc5 100644 --- a/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml +++ b/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - xmlns:vmd="clr-namespace:DMS.ViewModels.Dialogs" + xmlns:vmd="clr-namespace:DMS.WPF.ViewModels.Dialogs" xmlns:vc="clr-namespace:DMS.ValueConverts" xmlns:ex="clr-namespace:DMS.Extensions" xmlns:en="clr-namespace:DMS.Core.Enums" diff --git a/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml.cs b/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml.cs index 18f92ee..4aeb3b7 100644 --- a/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml.cs +++ b/DMS.WPF/Views/Dialogs/ConfirmDialog.xaml.cs @@ -1,8 +1,7 @@ -using System.Windows.Controls; using DMS.WPF.ViewModels.Dialogs; using iNKORE.UI.WPF.Modern.Controls; -namespace DMS.Views.Dialogs; +namespace DMS.WPF.Views.Dialogs; public partial class ConfirmDialog : ContentDialog { diff --git a/DMS.WPF/Views/Dialogs/DeviceDialog.xaml b/DMS.WPF/Views/Dialogs/DeviceDialog.xaml index 7710294..90b6af3 100644 --- a/DMS.WPF/Views/Dialogs/DeviceDialog.xaml +++ b/DMS.WPF/Views/Dialogs/DeviceDialog.xaml @@ -5,10 +5,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - xmlns:vmd="clr-namespace:DMS.ViewModels.Dialogs" + xmlns:vmd="clr-namespace:DMS.WPF.ViewModels.Dialogs" xmlns:vc="clr-namespace:DMS.ValueConverts" xmlns:ex="clr-namespace:DMS.Extensions" xmlns:en="clr-namespace:DMS.Core.Enums" + xmlns:enums="clr-namespace:DMS.Core.Enums;assembly=DMS.Core" + xmlns:global="clr-namespace:;assembly=DMS.Core" Title="{Binding Title}" CloseButtonText="取消" DefaultButton="Primary" @@ -17,9 +19,9 @@ mc:Ignorable="d"> + EnumType="{x:Type enums:DeviceType}" /> + EnumType="{x:Type enums:ProtocolType}" /> @@ -103,7 +105,7 @@