| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- using SWRIS.Extensions;
- using SWRIS.Models;
- using SWRIS.Models.ViewModel;
- using SWRIS.Repository;
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Threading;
- namespace SWRIS.Pages.RealTime
- {
- /// <summary>
- /// RealTimePage.xaml 的交互逻辑
- /// </summary>
- public partial class RealTimePage : Page
- {
- /// <summary>实时流超时隐藏阈值:超过该时长未收到新数据则隐藏实时值</summary>
- private const long LiveStreamTimeoutTicks = TimeSpan.TicksPerSecond;
- private readonly IRecordRepository _recordRepository;
- /// <summary>全部设备(小车过滤的原始全集)</summary>
- private readonly ObservableCollection<EquipmentDataModel> _allEquipments;
- private readonly DispatcherTimer _liveStreamTimer;
- /// <summary>记录各设备最后一次收到实时数据的时间(UtcNow.Ticks),超过 1 秒未更新时隐藏</summary>
- private readonly Dictionary<EquipmentDataModel, long> _lastLiveStreamTimes = new Dictionary<EquipmentDataModel, long>();
- public RealTimeViewModel ViewModel { get; set; }
- public RealTimePage()
- {
- InitializeComponent();
- _recordRepository = new RecordRepository();
- ViewModel = new RealTimeViewModel(_recordRepository.GetLastRecords().ToSimpleRecordDtos());
- _allEquipments = ViewModel.Equipments;
- FilterEquipmentsByTrolley(ViewModel.Trolleys.FirstOrDefault());
- DataContext = ViewModel;
- // 定时从各设备的实时流队列取出一项(取出即移出)填充 LiveStreamValues,供设备卡片显示
- _liveStreamTimer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(50)
- };
- _liveStreamTimer.Tick += LiveStreamTimer_Tick;
- _liveStreamTimer.Start();
- Unloaded += RealTimePage_Unloaded;
- }
- private void RealTimePage_Unloaded(object sender, RoutedEventArgs e)
- {
- _liveStreamTimer.Stop();
- }
- /// <summary>
- /// 从 LiveStreams 队列取出一项(取出即移出)并显示各传感器值;设备离线时不显示,
- /// 超过 1 秒未收到新数据则隐藏实时值,值未变化时跳过更新
- /// </summary>
- private void LiveStreamTimer_Tick(object sender, EventArgs e)
- {
- var equipments = ViewModel?.Equipments;
- if (equipments == null)
- return;
- // 用 UtcNow.Ticks 作为单调时钟,避免 DateTime.Now 的时区/日历换算开销
- long now = DateTime.UtcNow.Ticks;
- foreach (var equipmentData in equipments)
- {
- UpdateEquipmentLiveStream(equipmentData, now);
- }
- }
- private void UpdateEquipmentLiveStream(EquipmentDataModel equipmentData, long now)
- {
- var values = equipmentData.LiveStreamValues;
- // 设备离线时不显示
- if (!equipmentData.IsConnect)
- {
- ClearValues(values);
- return;
- }
- // 超过 1 秒未收到新数据 → 隐藏实时值(首次未见数据时 last 为 0,直接隐藏)
- if (!_lastLiveStreamTimes.TryGetValue(equipmentData, out long last) || now - last > LiveStreamTimeoutTicks)
- ClearValues(values);
- // 无新数据:保持当前显示状态
- if (!equipmentData.TryDequeueLiveStream(out int[] sample))
- return;
- // 有新数据,记录更新时间
- _lastLiveStreamTimes[equipmentData] = now;
- // 值未变化则跳过,减少无谓的集合通知与 UI 重绘
- if (ValuesEqual(values, sample))
- return;
- // 用索引器赋值(触发 Replace 通知,UI 容器复用)代替 Clear+Add 全量重建,减轻闪烁
- ApplySample(values, sample);
- }
- /// <summary>比较当前显示值与新样本是否一致(长度与逐项内容均相同)</summary>
- private static bool ValuesEqual(ObservableCollection<int> values, int[] sample)
- {
- if (values.Count != sample.Length)
- return false;
- for (int i = 0; i < sample.Length; i++)
- {
- if (values[i] != sample[i])
- return false;
- }
- return true;
- }
- /// <summary>用新样本更新集合:已有项索引器赋值(容器复用),数量差异用最小化 Add/RemoveAt 补齐</summary>
- private static void ApplySample(ObservableCollection<int> values, int[] sample)
- {
- int common = Math.Min(values.Count, sample.Length);
- for (int i = 0; i < common; i++)
- {
- values[i] = sample[i];
- }
- while (values.Count > sample.Length)
- {
- values.RemoveAt(values.Count - 1);
- }
- while (values.Count < sample.Length)
- {
- values.Add(sample[values.Count]);
- }
- }
- /// <summary>仅在集合非空时清空,避免无谓的集合通知触发 UI 重绘</summary>
- private static void ClearValues(ObservableCollection<int> values)
- {
- if (values.Count > 0)
- values.Clear();
- }
- private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- if ((sender as Border)?.DataContext is TrolleyActiveModel activedTrolley)
- {
- ViewModel.ActivedTrolly = activedTrolley;
- FilterEquipmentsByTrolley(activedTrolley);
- }
- }
- private void FilterEquipmentsByTrolley(TrolleyActiveModel trolley)
- {
- if (trolley == null)
- {
- ViewModel.Equipments = _allEquipments;
- return;
- }
- var filtered = _allEquipments
- .Where(e => e.TrolleyId == trolley.Id)
- .ConvertToObservableCollection();
- ViewModel.Equipments = filtered;
- }
- }
- }
|