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
{
///
/// RealTimePage.xaml 的交互逻辑
///
public partial class RealTimePage : Page
{
/// 实时流超时隐藏阈值:超过该时长未收到新数据则隐藏实时值
private const long LiveStreamTimeoutTicks = TimeSpan.TicksPerSecond;
private readonly IRecordRepository _recordRepository;
/// 全部设备(小车过滤的原始全集)
private readonly ObservableCollection _allEquipments;
private readonly DispatcherTimer _liveStreamTimer;
/// 记录各设备最后一次收到实时数据的时间(UtcNow.Ticks),超过 1 秒未更新时隐藏
private readonly Dictionary _lastLiveStreamTimes = new Dictionary();
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();
}
///
/// 从 LiveStreams 队列取出一项(取出即移出)并显示各传感器值;设备离线时不显示,
/// 超过 1 秒未收到新数据则隐藏实时值,值未变化时跳过更新
///
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);
}
/// 比较当前显示值与新样本是否一致(长度与逐项内容均相同)
private static bool ValuesEqual(ObservableCollection 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;
}
/// 用新样本更新集合:已有项索引器赋值(容器复用),数量差异用最小化 Add/RemoveAt 补齐
private static void ApplySample(ObservableCollection 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]);
}
}
/// 仅在集合非空时清空,避免无谓的集合通知触发 UI 重绘
private static void ClearValues(ObservableCollection 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;
}
}
}