using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Newtonsoft.Json.Linq; using OxyPlot; using OxyPlot.Axes; using OxyPlot.Series; namespace YKC { public partial class RealtimePage : Page { private Timer _pollTimer; private int _currentPile = 1; private int _currentGun = 1; public RealtimePage() { InitializeComponent(); InitLinePlot(plotCurrent, OxyColor.Parse("#3B82F6")); InitLinePlot(plotVoltage, OxyColor.Parse("#8B5CF6")); InitLinePlot(plotPower, OxyColor.Parse("#F59E0B")); InitBarPlot(plotAmount); Loaded += (s, e) => { Dispatcher.BeginInvoke(new Action(RefreshData), System.Windows.Threading.DispatcherPriority.Background); _pollTimer = new Timer((_) => Dispatcher.BeginInvoke(new Action(RefreshData)), null, 2000, 2000); }; Unloaded += (s, e) => _pollTimer?.Dispose(); } private void InitLinePlot(OxyPlot.Wpf.PlotView pv, OxyColor color) { var model = new PlotModel { PlotAreaBorderColor = OxyColors.Transparent }; model.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, TextColor = OxyColor.Parse("#94A3B8"), AxislineColor = OxyColor.Parse("#E2E8F0"), TicklineColor = OxyColor.Parse("#E2E8F0"), MajorGridlineColor = OxyColor.Parse("#F1F5F9"), StringFormat = "HH:mm:ss", FontSize = 10 }); model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, TextColor = OxyColor.Parse("#94A3B8"), AxislineColor = OxyColors.Transparent, TicklineColor = OxyColors.Transparent, MajorGridlineStyle = LineStyle.Dash, MajorGridlineColor = OxyColor.Parse("#F1F5F9"), FontSize = 10, MinimumPadding = 0.1, MaximumPadding = 0.1 }); model.Series.Add(new LineSeries { Color = color, StrokeThickness = 2, MarkerType = MarkerType.None, CanTrackerInterpolatePoints = false }); pv.Model = model; } private void InitBarPlot(OxyPlot.Wpf.PlotView pv) { var model = new PlotModel { PlotAreaBorderColor = OxyColors.Transparent }; var catAxis = new CategoryAxis { Position = AxisPosition.Bottom, TextColor = OxyColor.Parse("#64748B"), FontSize = 12, AxislineColor = OxyColor.Parse("#E2E8F0"), TicklineColor = OxyColor.Parse("#E2E8F0") }; catAxis.Labels.Add("服务费"); catAxis.Labels.Add("电费"); catAxis.Labels.Add("总金额"); model.Axes.Add(catAxis); model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "元", TextColor = OxyColor.Parse("#94A3B8"), FontSize = 10, AxislineColor = OxyColors.Transparent, TicklineColor = OxyColors.Transparent, MajorGridlineStyle = LineStyle.Dash, MajorGridlineColor = OxyColor.Parse("#F1F5F9"), MinimumPadding = 0, MaximumPadding = 0.15 }); model.Series.Add(new RectangleBarSeries { FillColor = OxyColor.Parse("#1E9FFF") }); model.Series.Add(new RectangleBarSeries { FillColor = OxyColor.Parse("#10B981") }); model.Series.Add(new RectangleBarSeries { FillColor = OxyColor.Parse("#6366F1") }); pv.Model = model; } private void PileGun_Changed(object sender, SelectionChangedEventArgs e) { try { if (cmbPile == null || cmbGun == null) return; _currentPile = cmbPile.SelectedIndex + 1; _currentGun = cmbGun.SelectedIndex + 1; RefreshData(); } catch { } } private void RefreshData() { try { if (txtCurrent == null || plotCurrent == null || plotCurrent.Model == null) return; var latest = PilesStoreHolder.Instance.GetLatest(_currentPile, _currentGun); if (latest == null) return; txtCurrent.Text = FormatValue(latest.Value("current")) + " A"; txtVoltage.Text = FormatValue(latest.Value("voltage")) + " V"; txtPower.Text = FormatValue(latest.Value("power")) + " kW"; txtEnergy.Text = FormatValue(latest.Value("energy")) + " kWh"; txtTotalAmount.Text = "¥ " + FormatValue(latest.Value("total_amount")); int? st = latest.Value("status"); if (st == 0) { txtStatus.Text = "离线"; txtStatus.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#909399")); } else if (st == 1) { txtStatus.Text = "故障"; txtStatus.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#EF4444")); } else if (st == 2) { txtStatus.Text = "空闲"; txtStatus.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#10B981")); } else if (st == 3) { txtStatus.Text = "充电中"; txtStatus.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#3B82F6")); } else { txtStatus.Text = "--"; } txtOrderNo.Text = latest.Value("order_no") ?? "--"; txtSoc.Text = latest.Value("soc") != null ? latest.Value("soc").ToString("F1") + "%" : "--"; txtCumTime.Text = FormatMin(latest.Value("cumulative_time")); txtRemTime.Text = FormatMin(latest.Value("remaining_time")); UpdateCharts(); } catch { } } private void UpdateCharts() { try { if (plotCurrent == null || plotCurrent.Model == null || plotCurrent.Model.Series.Count == 0) return; if (plotAmount == null || plotAmount.Model == null || plotAmount.Model.Series.Count == 0) return; var chartData = PilesStoreHolder.Instance.GetChartData(_currentPile, _currentGun); if (chartData == null || chartData.Count == 0) return; var curPoints = new List(); var volPoints = new List(); var powPoints = new List(); foreach (var item in chartData) { long unixTs = item.Value("t"); var dt = DateTimeOffset.FromUnixTimeSeconds(unixTs).DateTime; double oxyTime = DateTimeAxis.ToDouble(dt); curPoints.Add(new DataPoint(oxyTime, item.Value("current") ?? 0)); volPoints.Add(new DataPoint(oxyTime, item.Value("voltage") ?? 0)); powPoints.Add(new DataPoint(oxyTime, item.Value("power") ?? 0)); } ((LineSeries)plotCurrent.Model.Series[0]).Points.Clear(); ((LineSeries)plotCurrent.Model.Series[0]).Points.AddRange(curPoints); ((LineSeries)plotVoltage.Model.Series[0]).Points.Clear(); ((LineSeries)plotVoltage.Model.Series[0]).Points.AddRange(volPoints); ((LineSeries)plotPower.Model.Series[0]).Points.Clear(); ((LineSeries)plotPower.Model.Series[0]).Points.AddRange(powPoints); plotCurrent.InvalidatePlot(true); plotVoltage.InvalidatePlot(true); plotPower.InvalidatePlot(true); var latest = PilesStoreHolder.Instance.GetLatest(_currentPile, _currentGun); if (latest != null) { double sf = latest.Value("service_fee") ?? 0; double ef = latest.Value("electricity_fee") ?? 0; double ta = latest.Value("total_amount") ?? 0; ((RectangleBarSeries)plotAmount.Model.Series[0]).Items.Clear(); ((RectangleBarSeries)plotAmount.Model.Series[0]).Items.Add(new RectangleBarItem(-0.25, 0, 0.25, sf)); ((RectangleBarSeries)plotAmount.Model.Series[1]).Items.Clear(); ((RectangleBarSeries)plotAmount.Model.Series[1]).Items.Add(new RectangleBarItem(0.75, 0, 1.25, ef)); ((RectangleBarSeries)plotAmount.Model.Series[2]).Items.Clear(); ((RectangleBarSeries)plotAmount.Model.Series[2]).Items.Add(new RectangleBarItem(1.75, 0, 2.25, ta)); plotAmount.InvalidatePlot(true); } } catch { } } private string FormatValue(double? v) { return v.HasValue ? v.Value.ToString("F2") : "--"; } private string FormatMin(double? v) { return v.HasValue ? Math.Round(v.Value).ToString() + " min" : "--"; } } }