Files
DMS/DMS.WPF/ViewModels/VariableHistoryViewModel.cs
David P.G a6fc543e4f 1 feat(mqtt): 实现MQTT服务器状态管理与事件系统
2
    3 1. 在MqttServer和MqttServerDto模型中添加IsConnect属性,用于跟踪连接状态
    4 2. 重构MqttManagementService服务,使用事件驱动方式管理服务器状态变化
    5 3. 实现MqttServerChangedEventArgs事件参数类,支持区分不同变更类型
    6 4. 在IEventService中添加OnMqttServerChanged事件,实现事件通知机制
    7 5. 优化数据存储结构,将MqttServers从ObservableCollection改为ObservableDictionary
    8 6. 更新MqttServiceManager以正确处理连接状态和事件触发
    9 7. 在WPF层更新UI以响应服务器状态变化
   10 8. 删除不再需要的Helper类(DataServicesHelper, MessageHelper, SiemensHelper)
   11 9. 在NLog配置中添加调试器输出目标以便调试
   12 10. 完善VariableHistoryViewModel防止空引用异常
2025-10-05 00:28:25 +08:00

364 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Windows.Media;
using AutoMapper;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DMS.Application.DTOs;
using DMS.Application.Events;
using DMS.Application.Interfaces;
using DMS.Application.Interfaces.Database;
using DMS.Core.Enums;
using DMS.Core.Events;
using DMS.Core.Models;
using DMS.WPF.Interfaces;
using DMS.WPF.ViewModels.Items;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.WPF;
using ObservableCollections;
using SkiaSharp;
namespace DMS.WPF.ViewModels;
partial class VariableHistoryViewModel : ViewModelBase, INavigatable
{
private readonly IMapper _mapper;
private readonly IDialogService _dialogService;
private readonly IHistoryAppService _historyAppService;
private readonly IWPFDataService _wpfDataService;
private readonly IDataStorageService _dataStorageService;
private readonly IEventService _eventService;
private readonly INotificationService _notificationService;
private readonly INavigationService _navigationService;
/// <summary>
/// 加载历史记录条数限制
/// </summary>
[ObservableProperty]
private int? _initHistoryLimit;
/// <summary>
/// 显示历史记录条数限制
/// </summary>
[ObservableProperty]
private int? _viewHistoryLimit;
/// <summary>
/// 历史记录开始时间
/// </summary>
[ObservableProperty]
private DateTime? _startTime;
/// <summary>
/// 历史记录结束时间
/// </summary>
[ObservableProperty]
private DateTime? _endTime;
/// <summary>
/// 选中的变量历史记录
/// </summary>
[ObservableProperty]
private VariableItemViewModel _currentVariable;
/// <summary>
/// 变量历史记录列表
/// </summary>
public NotifyCollectionChangedSynchronizedViewList<VariableHistoryDto> VariableHistories { get; }
// 折线图相关属性
public ISeries[] LineSeriesCollection { get; set; }
[ObservableProperty]
private Axis[] _lineAxisX;
[ObservableProperty]
private Axis[] _lineAxisY;
private readonly ObservableList<VariableHistoryDto> _variableHistoryList;
private readonly ISynchronizedView<VariableHistoryDto, VariableHistoryDto> _variableHistorySynchronizedView;
/// <summary>
/// 所有变量的缓存列表,用于搜索
/// </summary>
private List<VariableHistoryDto> _allVariableHistories;
public VariableHistoryViewModel(IMapper mapper, IDialogService dialogService, IHistoryAppService historyAppService,
IWPFDataService wpfDataService, IDataStorageService dataStorageService,
IEventService eventService, INotificationService notificationService,
INavigationService navigationService)
{
_mapper = mapper;
_dialogService = dialogService;
_historyAppService = historyAppService;
_wpfDataService = wpfDataService;
_dataStorageService = dataStorageService;
_eventService = eventService;
_notificationService = notificationService;
_navigationService = navigationService;
_variableHistoryList = new ObservableList<VariableHistoryDto>();
_variableHistorySynchronizedView = _variableHistoryList.CreateView(v => v);
VariableHistories = _variableHistorySynchronizedView.ToNotifyCollectionChanged();
_allVariableHistories = new List<VariableHistoryDto>();
// 初始化默认值
_initHistoryLimit = 50; // 默认限制1000条记录
_startTime = null;
_endTime = null;
// 初始化图表属性
LineAxisX = new Axis[] { new Axis() };
LineAxisY = new Axis[] { new Axis() };
LineSeriesCollection = new ISeries[0];
_eventService.OnVariableValueChanged += OnVariableValueChanged;
}
private void OnVariableValueChanged(object? sender, VariableValueChangedEventArgs e)
{
if (CurrentVariable is null)
{
return;
}
if (e.Variable.Id != CurrentVariable.Id)
{
return;
}
var variableHistory = new VariableHistoryDto()
{
VariableId = CurrentVariable.Id,
Timestamp = DateTime.Now,
Value = e.Variable.DisplayValue,
NumericValue = e.Variable.NumericValue
};
_variableHistoryList.Add(variableHistory);
// 限制历史记录数量以防止内存溢出
if (ViewHistoryLimit.HasValue && _variableHistoryList.Count > ViewHistoryLimit.Value)
{
// 移除最旧的记录
_variableHistoryList.RemoveAt(0);
}
// 更新图表数据 - 使用更高效的方法处理实时更新
UpdateChartData();
}
/// <summary>
/// 加载所有变量的历史记录
/// </summary>
/// <param name="limit">返回记录的最大数量null表示无限制</param>
/// <param name="startTime">开始时间null表示无限制</param>
/// <param name="endTime">结束时间null表示无限制</param>
private async void LoadAllVariableHistories(int variableId, int? limit = null, DateTime? startTime = null,
DateTime? endTime = null)
{
try
{
_variableHistoryList.Clear();
var allHistories
= await _historyAppService.GetVariableHistoriesAsync(variableId, limit, startTime, endTime);
_allVariableHistories = allHistories.OrderBy(v => v.Timestamp)
.ToList();
_variableHistoryList.AddRange(_allVariableHistories);
// 更新图表数据
UpdateChartData();
}
catch (Exception ex)
{
// 记录更详细的错误信息
_notificationService.ShowError($"加载变量历史记录失败: {ex.Message}", ex);
}
}
public override async Task OnNavigatedToAsync(NavigationParameter parameter)
{
if (_dataStorageService.Variables.TryGetValue(parameter.TargetId, out VariableItemViewModel variableItem))
{
CurrentVariable = variableItem;
// 加载所有变量的历史记录
LoadAllVariableHistories(variableItem.Id, InitHistoryLimit, StartTime, EndTime);
}
}
/// <summary>
/// 重新加载历史记录命令
/// </summary>
[RelayCommand]
private void Reload()
{
if (CurrentVariable != null)
{
LoadAllVariableHistories(CurrentVariable.Id, InitHistoryLimit, StartTime, EndTime);
}
}
/// <summary>
/// 返回变量表命令
/// </summary>
[RelayCommand]
private async Task NavigateToVariableTable()
{
try
{
// 导航到变量表页面
await _navigationService.NavigateToAsync(this,new NavigationParameter(nameof(VariableTableViewModel),CurrentVariable.VariableTableId,NavigationType.VariableTable));
}
catch (Exception ex)
{
_notificationService.ShowError($"导航到变量表失败: {ex.Message}", ex);
}
}
/// <summary>
/// 根据搜索文本过滤历史记录
/// </summary>
/// <param name="searchText"></param>
private void FilterHistoriesBySearchText(string searchText)
{
if (string.IsNullOrWhiteSpace(searchText))
{
// 如果搜索文本为空,显示所有历史记录
_variableHistoryList.Clear();
_variableHistoryList.AddRange(_allVariableHistories);
}
else
{
// 根据搜索文本过滤历史记录
var filteredHistories = _allVariableHistories
.Where(h =>
h.VariableName?.Contains(
searchText, StringComparison.OrdinalIgnoreCase) == true)
.ToList();
_variableHistoryList.Clear();
_variableHistoryList.AddRange(filteredHistories);
}
}
/// <summary>
/// 根据变量ID加载历史记录
/// </summary>
/// <param name="variableId">变量ID</param>
/// <param name="limit">返回记录的最大数量null表示无限制</param>
/// <param name="startTime">开始时间null表示无限制</param>
/// <param name="endTime">结束时间null表示无限制</param>
public async Task LoadVariableHistoriesAsync(int variableId, int? limit = null, DateTime? startTime = null,
DateTime? endTime = null)
{
try
{
_variableHistoryList.Clear();
var histories = await _historyAppService.GetVariableHistoriesAsync(variableId, limit, startTime, endTime);
_variableHistoryList.AddRange(histories);
// 更新图表数据
UpdateChartData();
}
catch (Exception ex)
{
// 记录更详细的错误信息
_notificationService.ShowError($"加载变量历史记录失败: {ex.Message}", ex);
}
}
// 添加字段来保存轴实例,以保持缩放状态
private Axis[] _lineAxisXInstance;
private Axis[] _lineAxisYInstance;
/// <summary>
/// 更新图表数据
/// </summary>
private void UpdateChartData()
{
if (_variableHistoryList == null || _variableHistoryList.Count == 0)
{
// 清空图表数据
LineSeriesCollection = new ISeries[0];
OnPropertyChanged(nameof(LineSeriesCollection));
return;
}
// 如果系列集合为空或没有有效的系列,则重新创建
if (LineSeriesCollection == null || LineSeriesCollection.Length == 0)
{
// 创建数值点集合
var values = new List<DateTimePoint>();
foreach (var history in _variableHistoryList)
{
values.Add(new DateTimePoint(history.Timestamp, history.NumericValue));
}
// 创建线性序列
var series = new LineSeries<DateTimePoint>
{
Name = CurrentVariable?.Name ?? "变量值",
Values = values,
Fill = null,
Stroke = new SolidColorPaint(new SKColor(41, 128, 185)) { StrokeThickness = 2 },
GeometrySize = 6, // 显示数据点圆点大小为6
LineSmoothness = 5 // 使用直线连接点,也可以设为其他值实现曲线
};
// 更新序列集合
LineSeriesCollection = new ISeries[] { series };
// 初始化坐标轴并保存实例引用
_lineAxisXInstance = new Axis[]
{
new Axis
{
Labeler = value => new DateTime((long)value).ToString("MM-dd HH:mm:ss"),
// 不设置固定范围,让图表根据数据自动调整
}
};
_lineAxisYInstance = new Axis[]
{
new Axis
{
Name = CurrentVariable?.Name ?? "值",
MinLimit = 0 // 设置Y轴从0开始
}
};
// 设置属性值
LineAxisX = _lineAxisXInstance;
LineAxisY = _lineAxisYInstance;
// 通知属性更改
OnPropertyChanged(nameof(LineSeriesCollection));
OnPropertyChanged(nameof(LineAxisX));
OnPropertyChanged(nameof(LineAxisY));
}
else
{
// 对于实时更新,保持原有完整的更新逻辑以确保数据一致性
// 创建数值点集合
var values = new List<DateTimePoint>();
foreach (var history in _variableHistoryList)
{
// 尝试将值转换为double
values.Add(new DateTimePoint(history.Timestamp, history.NumericValue));
}
// 更新当前系列
var currentSeries = (LineSeries<DateTimePoint>)LineSeriesCollection[0];
currentSeries.Values = values;
currentSeries.Name = CurrentVariable?.Name ?? "变量值";
// 通知系列更改,但保留当前轴的缩放状态(不需要更新轴)
// OnPropertyChanged(nameof(LineSeriesCollection));
}
}
}