using System.Collections;
using AutoMapper;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DMS.Application.DTOs;
using DMS.Application.Interfaces;
using DMS.Application.Interfaces.Database;
using DMS.Application.Interfaces.Management;
using DMS.Core.Enums;
using DMS.Core.Events;
using DMS.Core.Models;
using DMS.WPF.Interfaces;
using DMS.WPF.ViewModels.Dialogs;
using DMS.WPF.ItemViewModel;
using Microsoft.Extensions.DependencyInjection;
using ObservableCollections;
namespace DMS.WPF.ViewModels;
partial class VariableTableViewModel : ViewModelBase, INavigatable
{
private readonly IMapper _mapper;
///
/// 对话服务接口,用于显示各种对话框(如确认、编辑、导入等)。
///
private readonly IDialogService _dialogService;
private readonly IVariableManagementService _variableManagementService;
private readonly IAppDataService _appDataService;
private readonly IVariableViewService _variableViewService;
private readonly IEventService _eventService;
private readonly IMqttAliasAppService _mqttAliasAppService;
private readonly IMqttAppService _mqttAppService;
///
/// 当前正在操作的变量表实体。
/// 通过 ObservableProperty 自动生成 VariableTable 属性和 OnVariableTableChanged 方法。
///
[ObservableProperty]
private VariableTableItem currentVariableTable;
///
/// 当前选中的变量数据。
/// 通过 ObservableProperty 自动生成 SelectedVariable 属性和 OnSelectedVariableDataChanged 方法。
///
[ObservableProperty]
private VariableItem _selectedVariable;
[ObservableProperty]
private IList _selectedVariables = new ArrayList();
///
/// 用于过滤变量数据的搜索文本。
/// 通过 ObservableProperty 自动生成 SearchText 属性和 OnSearchTextChanged 方法。
///
[ObservableProperty]
private string _searchText;
///
/// 指示视图是否已完成首次加载。
/// 用于防止某些UI控件(如ToggleSwitch)在初始化时触发不必要的事件。
///
public bool IsLoadCompletion { get; set; } = false;
///
/// 指示当前变量表是否使用S7协议。
/// 通过 ObservableProperty 自动生成 IsS7ProtocolSelected 属性。
///
[ObservableProperty]
private bool _isS7ProtocolSelected;
///
/// 指示当前变量表是否使用OpcUA协议。
/// 通过 ObservableProperty 自动生成 IsOpcUaProtocolSelected 属性。
///
[ObservableProperty]
private bool _isOpcUaProtocolSelected;
///
/// VariableTableViewModel 的构造函数。
/// 初始化服务、数据仓库和变量数据集合视图。
///
/// 对话服务接口的实例。
private readonly IViewCenterService _viewCenterService;
private readonly IViewDataService _viewDataService;
private readonly ObservableList _variableItemList;
private readonly ISynchronizedView _synchronizedView;
public NotifyCollectionChangedSynchronizedViewList VariableItemListView { get; }
private readonly INotificationService _notificationService;
private readonly ITriggerAppService _triggerAppService;
private readonly ITriggerVariableAppService _triggerVariableAppService;
public VariableTableViewModel(IMapper mapper, IDialogService dialogService, IVariableManagementService variableManagementService,
IAppDataService appDataService,
IVariableViewService variableViewService,
IEventService eventService,
IMqttAliasAppService mqttAliasAppService, IMqttAppService mqttAppService,
IViewCenterService wpfDataService, IViewDataService dataStorageService,
INotificationService notificationService, ITriggerAppService triggerAppService,
ITriggerVariableAppService triggerVariableAppService)
{
_mapper = mapper;
_dialogService = dialogService;
_variableManagementService = variableManagementService;
_appDataService = appDataService;
_variableViewService = variableViewService;
_eventService = eventService;
_mqttAliasAppService = mqttAliasAppService;
_mqttAppService = mqttAppService;
_viewCenterService = wpfDataService;
_viewDataService = dataStorageService;
_notificationService = notificationService;
_triggerAppService = triggerAppService;
_triggerVariableAppService = triggerVariableAppService;
IsLoadCompletion = false; // 初始设置为 false,表示未完成加载
_variableItemList = new ObservableList();
_synchronizedView = _variableItemList.CreateView(v => v);
VariableItemListView = _synchronizedView.ToNotifyCollectionChanged();
}
///
/// 用于过滤 中的变量数据。
/// 根据 属性的值进行模糊匹配。
///
/// 要过滤的集合中的单个项。
/// 如果项匹配搜索条件则为 true,否则为 false。
private bool FilterVariables(VariableItem item)
{
// 尝试将项转换为 Variable 类型
var searchTextLower = SearchText.ToLower();
// 检查变量的名称、描述、NodeId、S7地址、数据值或显示值是否包含搜索文本
return item.Name?.ToLower()
.Contains(searchTextLower) == true ||
item.Description?.ToLower()
.Contains(searchTextLower) == true ||
item.OpcUaNodeId?.ToLower()
.Contains(searchTextLower) == true ||
item.S7Address?.ToLower()
.Contains(searchTextLower) == true ||
item.DataValue?.ToLower()
.Contains(searchTextLower) == true ||
item.DisplayValue?.ToLower()
.Contains(searchTextLower) == true;
}
///
/// 当 属性的值发生改变时自动调用。
/// 刷新 以应用新的过滤条件。
///
/// 新的搜索文本值。
partial void OnSearchTextChanged(string value)
{
// 如果搜索文本为空或空白,则显示所有项
if (string.IsNullOrWhiteSpace(SearchText))
{
_synchronizedView.ResetFilter();
}
else
{
_synchronizedView.AttachFilter(FilterVariables);
}
}
///
/// 编辑选定的变量数据。
/// 此命令通常绑定到UI中的"编辑"按钮或双击事件。
///
/// 当前操作的变量表,用于更新其内部的变量数据。
[RelayCommand]
private async Task UpdateVariable()
{
try
{
// 检查是否有变量被选中
if (SelectedVariable == null)
{
_notificationService.ShowInfo("请选择要编辑的变量");
return;
}
// 显示编辑变量数据的对话框,并传入当前选中的变量数据
VariableDialogViewModel variableDialogViewModel
= App.Current.Services.GetRequiredService();
variableDialogViewModel.Title = "编辑变量";
variableDialogViewModel.PrimaryButText = "保存修改";
variableDialogViewModel.IsAddModel = false;
// 创建一个副本用于编辑,避免直接修改原数据
var variableToEdit = new VariableItem();
_mapper.Map(SelectedVariable, variableToEdit);
variableDialogViewModel.Variable = variableToEdit;
var editedVariable = await _dialogService.ShowDialogAsync(variableDialogViewModel);
// 如果用户取消或对话框未返回数据,则直接返回
if (editedVariable == null)
return;
// 更新时间戳
editedVariable.UpdatedAt = DateTime.Now;
// 更新数据库中的变量数据
var updateResult = await _variableManagementService.UpdateVariableAsync(_mapper.Map(editedVariable));
if (updateResult > 0)
{
// 更新当前页面显示的数据:找到原数据在集合中的索引并替换
_mapper.Map(editedVariable, SelectedVariable); // 更新选中项的属性
// 显示成功通知
_notificationService.ShowSuccess($"编辑变量成功:{editedVariable.Name}");
}
else
{
_notificationService.ShowError("编辑变量失败");
}
}
catch (Exception e)
{
// 捕获并显示错误通知
_notificationService.ShowError($"编辑变量的过程中发生了不可预期的错误:{e.Message}", e);
}
}
///
/// 从TIA Portal导出的变量表Excel文件中导入变量数据。
/// 此命令通常绑定到UI中的"从TIA导入"按钮。
///
[RelayCommand]
private async Task ImprotFromTiaVarTable()
{
try
{
ImportExcelDialogViewModel
viewModel = App.Current.Services.GetRequiredService();
var improtVariableItems = await _dialogService.ShowDialogAsync(viewModel);
if (improtVariableItems == null || improtVariableItems.Count == 0) return;
bool flowControl = await CheckExist(improtVariableItems);
if (!flowControl)
{
return;
}
// 如果还有变量需要导入,则执行导入操作
if (!improtVariableItems.Any())
{
_notificationService.ShowSuccess("列表中没有要添加的变量了。");
return;
}
var resList = await _variableViewService.AddAsync(improtVariableItems, CurrentVariableTable);
if (resList is null || !resList.Any())
{
_notificationService.ShowError("从OPC UA服务器导入变量失败");
return;
}
_variableItemList.AddRange(resList);
_notificationService.ShowSuccess($"从TIA变量表导入变量成功,共导入变量:{improtVariableItems.Count}个");
}
catch (Exception e)
{
_notificationService.ShowError($"从TIA导入变量的过程中发生了不可预期的错误:{e.Message}", e);
}
}
///
/// 从OPC UA服务器导入变量数据。
/// 此命令通常绑定到UI中的"从OPC UA导入"按钮。
///
[RelayCommand]
private async Task ImportFromOpcUaServer()
{
try
{
if (CurrentVariableTable.Device == null)
{
_notificationService.ShowError("当前变量表的Device对象为空,请检查。");
return;
}
// 检查OPC UA Endpoint URL是否已设置
string opcUaEndpointUrl = CurrentVariableTable.Device.OpcUaServerUrl;
if (string.IsNullOrEmpty(opcUaEndpointUrl))
{
_notificationService.ShowError("OPC UA Endpoint URL 未设置。请在设备详情中配置。");
return;
}
// 显示OPC UA导入对话框,让用户选择要导入的变量
ImportOpcUaDialogViewModel importOpcUaDialogViewModel
= App.Current.Services.GetRequiredService();
importOpcUaDialogViewModel.EndpointUrl = opcUaEndpointUrl; // 设置Endpoint URL
var importedVariableItems = await _dialogService.ShowDialogAsync(importOpcUaDialogViewModel);
if (importedVariableItems == null || !importedVariableItems.Any()) return; // 用户取消或没有选择任何变量
//检查重复变量并询问是否继续
bool flowControl = await CheckExist(importedVariableItems);
if (!flowControl)
{
return;
}
// 如果还有变量需要导入,则执行导入操作
if (!importedVariableItems.Any())
{
_notificationService.ShowSuccess("列表中没有要添加的变量了。");
return;
}
var resList = await _variableViewService.AddAsync(importedVariableItems, CurrentVariableTable);
if (resList is null || !resList.Any())
{
_notificationService.ShowError("从OPC UA服务器导入变量失败");
return;
}
_variableItemList.AddRange(resList);
_notificationService.ShowSuccess($"从OPC UA服务器导入变量成功,共导入变量:{importedVariableItems.Count}个");
}
catch (Exception e)
{
_notificationService.ShowError($"从OPC UA服务器导入变量的过程中发生了不可预期的错误:{e.Message}", e);
}
}
private async Task CheckExist(List importedVariableItems)
{
// 检查是否存在同名变量
List existList = _variableViewService.CheckExistingVariables(importedVariableItems, CurrentVariableTable.Device);
if (existList.Count > 0)
{
// 拼接要删除的变量名称,用于确认提示
var existNames = string.Join("、", existList.Select(v => v.Name));
var confirmDialogViewModel
= new ConfirmDialogViewModel("存在已经添加的变量", $"变量名称:{existNames},已经存在,是否跳过继续添加其他的变量。取消则不添加任何变量", "继续");
var res = await _dialogService.ShowDialogAsync(confirmDialogViewModel);
if (!res) return false;
// 从导入列表中删除已经存在的变量
importedVariableItems.RemoveAll(variableDto => existList.Contains(variableDto));
}
return true;
}
//
///
/// 添加新的变量数据。
///
/// 此命令通常绑定到UI中的"添加"按钮。
/// 当前操作的变量表,用于设置新变量的所属ID。
[RelayCommand]
private async Task AddVariable()
{
try
{
// 显示添加变量数据的对话框
VariableDialogViewModel variableDialogViewModel
= App.Current.Services.GetRequiredService();
VariableItem variableItem = new VariableItem();
variableItem.Protocol = CurrentVariableTable.Protocol;
variableDialogViewModel.Title = "添加变量";
variableDialogViewModel.PrimaryButText = "添加变量";
variableDialogViewModel.Variable = variableItem;
var conVariableItem = await _dialogService.ShowDialogAsync(variableDialogViewModel);
// 如果用户取消或对话框未返回数据,则直接返回
if (conVariableItem == null)
return;
// // 添加变量数据到数据库
var resList
= await _variableViewService.AddAsync(new List() { conVariableItem }, CurrentVariableTable);
if (resList is null || !resList.Any())
{
_notificationService.ShowError("添加变量失败");
return;
}
// // 更新当前页面显示的数据:将新变量添加到集合中
_variableItemList.Add(variableItem);
_viewCenterService.VariableDataService.AddVariable(variableItem);
//
// // 显示成功通知
_notificationService.ShowSuccess($"添加变量成功:{variableItem.Name}");
}
catch (Exception e)
{
// 捕获并显示错误通知
_notificationService.ShowError($"添加变量的过程中发生了不可预期的错误:{e.Message}", e);
}
}
///
/// 删除选定的变量数据。
/// 此命令通常绑定到UI中的"删除"按钮。
///
/// 要删除的变量数据列表。
[RelayCommand]
public async Task DeleteVariable()
{
try
{
List variablesToDelete = SelectedVariables.Cast()
.ToList();
// 检查是否有变量被选中
if (variablesToDelete == null || !variablesToDelete.Any())
{
_notificationService.ShowInfo("请选择要删除的变量");
return;
}
// 拼接要删除的变量名称,用于确认提示
var names = string.Join("、", variablesToDelete.Select(v => v.Name));
// 显示确认删除对话框
string message = $"确认要删除变量:{names},删除后不可恢复,确认要删除吗?";
var viewModel
= new ConfirmDialogViewModel("删除变量", message, "删除变量");
// 如果用户取消删除,则返回
if (!await _dialogService.ShowDialogAsync(viewModel)) return;
// 从数据库中删除变量数据
var result = await _variableViewService.DeleteAsync(variablesToDelete);
if (result)
{
foreach (VariableItem item in variablesToDelete)
{
_variableItemList.Remove(item);
}
// 显示成功通知
_notificationService.ShowSuccess($"成功删除 {variablesToDelete.Count} 个变量");
}
else
{
// 显示删除失败通知
_notificationService.ShowError("删除变量失败");
}
}
catch (Exception e)
{
// 捕获并显示错误通知
_notificationService.ShowError($"删除变量的过程中发生了不可预期的错误:{e.Message}", e);
}
}
///
/// 更改选定变量的轮询频率。
/// 此命令通常绑定到UI中的"修改轮询频率"按钮。
///
/// 要修改轮询频率的变量数据列表。
[RelayCommand]
public async Task ChangePollingInterval()
{
// 检查是否有变量被选中
if (SelectedVariables.Count == 0)
{
_notificationService.ShowInfo("请选择要修改轮询间隔的变量");
return;
}
// 获取选中的变量列表
var validVariables = SelectedVariables.Cast()
.ToList();
// 显示轮询间隔选择对话框,并传入第一个变量的当前轮询间隔作为默认值
PollLevelDialogViewModel viewModel = new PollLevelDialogViewModel(validVariables.First()
.PollingInterval);
var newPollingInterval = await _dialogService.ShowDialogAsync(viewModel);
if (newPollingInterval.HasValue)
{
// 更新所有选定变量的轮询间隔和修改状态
foreach (var variable in validVariables)
{
variable.PollingInterval = newPollingInterval.Value;
variable.UpdatedAt = DateTime.Now;
}
// 批量更新数据库中的变量数据
var result = await _variableManagementService.UpdateVariablesAsync(_mapper.Map>(validVariables));
if (result > 0)
{
// 显示成功通知
_notificationService.ShowSuccess($"已成功更新 {validVariables.Count} 个变量的轮询间隔");
}
else
{
_notificationService.ShowError("更新轮询间隔失败");
}
}
}
[RelayCommand]
public async Task ModifyConversionFormula()
{
// 检查是否有变量被选中
if (SelectedVariables.Count == 0)
{
_notificationService.ShowInfo("请选择要修改转换公式的变量");
return;
}
// 获取选中的变量列表
var validVariables = SelectedVariables.Cast().ToList();
// --- 对话框调用 ---
var viewModel = new InputDialogViewModel("修改数值转换公式", "请输入新的转换公式 (使用'x'代表变量值):", validVariables.First().ConversionFormula);
string newFormula = await _dialogService.ShowDialogAsync(viewModel);
if (newFormula != null) // 当用户输入了新公式并确认后
{
// 更新所有选定变量的转换公式
foreach (var variable in validVariables)
{
variable.ConversionFormula = newFormula;
variable.UpdatedAt = DateTime.Now;
}
// 批量更新数据库中的变量数据
var variableDtos = _mapper.Map>(validVariables);
var result = await _variableManagementService.UpdateVariablesAsync(variableDtos);
if (result > 0)
{
// 显示成功通知
_notificationService.ShowSuccess($"已成功更新 {validVariables.Count} 个变量的转换公式");
}
else
{
_notificationService.ShowError("更新转换公式失败");
}
}
}
///
/// 为选定的变量添加MQTT服务器。
/// 此命令通常绑定到UI中的"添加MQTT服务器"按钮。
///
/// 要添加MQTT服务器的变量数据列表。
[RelayCommand]
public async Task AddMqttServerToVariables(IList