From ace97f14a63b17b2c850525cdabce33319f9b157 Mon Sep 17 00:00:00 2001 From: hsc Date: Tue, 2 Dec 2025 09:59:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E7=AE=A1=E7=90=86ui=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BOB/App.xaml.cs | 1 + BOB/Services/BackfeedPollingRead.cs | 46 +++ BOB/Services/EAEL9080PollingRead.cs | 43 +++ BOB/Services/IT6724CPollingRead.cs | 43 +++ BOB/Services/LQ7500-DPollingRead.cs | 41 +++ BOB/Services/PSB11000PollingRead.cs | 46 +++ BOB/Services/PollingRead.cs | 75 +++++ BOB/Services/SQ0030G1DPollingRead.cs | 32 ++ BOB/Services/WS-68030-380TPollingRead.cs | 30 ++ BOB/Services/ZXKSPollingRead.cs | 32 ++ BOB/Singleton/Devices.cs | 93 +++++- BOB/SystemConfig.cs | 4 +- BOB/Tasks/HeartbeatManager.cs | 159 --------- BOB/ViewModels/Dialogs/BackfeedViewModel.cs | 103 ++++++ BOB/ViewModels/Dialogs/EAEL9080ViewModel.cs | 105 +++++- BOB/ViewModels/Dialogs/IT6724CViewModel.cs | 133 +++++++- BOB/ViewModels/Dialogs/PSB11000ViewModel.cs | 102 ++++++ BOB/ViewModels/MainViewModel.cs | 5 +- BOB/ViewModels/MonitorViewModel.cs | 269 ++++++++------- BOB/ViewModels/ParametersManagerViewModel.cs | 1 + BOB/ViewModels/ShellViewModel.cs | 2 +- BOB/ViewModels/UpdateInfoViewModel.cs | 46 +++ BOB/Views/Dialogs/BackfeedView.xaml | 18 +- BOB/Views/Dialogs/EAEL9080View.xaml | 9 +- BOB/Views/Dialogs/IOBoardView.xaml | 25 +- BOB/Views/Dialogs/IT6724CView.xaml | 26 +- BOB/Views/Dialogs/PSB11000View.xaml | 25 +- BOB/Views/Dialogs/SQ0030G1DView.xaml | 311 +++++++++--------- BOB/Views/Dialogs/WS-68030-380TView.xaml | 103 +++--- BOB/Views/ProgressView.xaml | 23 -- BOB/Views/ShellView.xaml | 25 +- BOB/Views/ShellView.xaml.cs | 10 +- BOB/Views/UpdateInfoView.xaml | 30 ++ ...essView.xaml.cs => UpdateInfoView.xaml.cs} | 7 +- BOB/Views/UserControls/NumericalDisplay.xaml | 2 +- Common/PubEvent/ChangeCurrentTagEvent.cs | 12 + Common/PubEvent/ConnectionChangeEvent.cs | 12 + Common/PubEvent/CurveDataEvent.cs | 13 + Common/PubEvent/StartProcessEvent.cs | 12 + DeviceCommand/Device/Backfeed.cs | 37 +++ DeviceCommand/Device/E36233A.cs | 97 ++++-- DeviceCommand/Device/IT6724C.cs | 3 +- DeviceCommand/Device/IT6724CReverse.cs | 31 +- Model/UpdateInfoModel.cs | 14 + ProcessManager/App.xaml.cs | 9 +- ProcessManager/ProcessManager.csproj | 16 + ProcessManager/ViewModels/DevicesViewModel.cs | 73 ++++ ProcessManager/ViewModels/MenuViewModel.cs | 163 +++++++++ ProcessManager/ViewModels/PMainViewModel.cs | 144 +++----- ProcessManager/Views/DeviceOther.xaml | 20 ++ ProcessManager/Views/DeviceOther.xaml.cs | 29 ++ ProcessManager/Views/DeviceThreeView.xaml | 20 ++ ProcessManager/Views/DeviceThreeView.xaml.cs | 29 ++ ProcessManager/Views/DeviceTwoView.xaml | 20 ++ ProcessManager/Views/DeviceTwoView.xaml.cs | 29 ++ ProcessManager/Views/DevicesView.xaml | 20 ++ ProcessManager/Views/DevicesView.xaml.cs | 29 ++ ProcessManager/Views/MainWindow.xaml | 213 ++++++++++++ ProcessManager/{ => Views}/MainWindow.xaml.cs | 2 +- .../{MainWindow.xaml => Views/MenuView.xaml} | 39 ++- ProcessManager/Views/MenuView.xaml.cs | 28 ++ ProcessManager/{ => Views}/Setting.xaml | 1 + ProcessManager/{ => Views}/Setting.xaml.cs | 0 ProcessManager/WindowEmbedHelper.cs | 93 ++++++ 64 files changed, 2505 insertions(+), 728 deletions(-) create mode 100644 BOB/Services/BackfeedPollingRead.cs create mode 100644 BOB/Services/EAEL9080PollingRead.cs create mode 100644 BOB/Services/IT6724CPollingRead.cs create mode 100644 BOB/Services/LQ7500-DPollingRead.cs create mode 100644 BOB/Services/PSB11000PollingRead.cs create mode 100644 BOB/Services/PollingRead.cs create mode 100644 BOB/Services/SQ0030G1DPollingRead.cs create mode 100644 BOB/Services/WS-68030-380TPollingRead.cs create mode 100644 BOB/Services/ZXKSPollingRead.cs delete mode 100644 BOB/Tasks/HeartbeatManager.cs create mode 100644 BOB/ViewModels/UpdateInfoViewModel.cs delete mode 100644 BOB/Views/ProgressView.xaml create mode 100644 BOB/Views/UpdateInfoView.xaml rename BOB/Views/{ProgressView.xaml.cs => UpdateInfoView.xaml.cs} (74%) create mode 100644 Common/PubEvent/ChangeCurrentTagEvent.cs create mode 100644 Common/PubEvent/ConnectionChangeEvent.cs create mode 100644 Common/PubEvent/CurveDataEvent.cs create mode 100644 Common/PubEvent/StartProcessEvent.cs create mode 100644 Model/UpdateInfoModel.cs create mode 100644 ProcessManager/ViewModels/DevicesViewModel.cs create mode 100644 ProcessManager/ViewModels/MenuViewModel.cs create mode 100644 ProcessManager/Views/DeviceOther.xaml create mode 100644 ProcessManager/Views/DeviceOther.xaml.cs create mode 100644 ProcessManager/Views/DeviceThreeView.xaml create mode 100644 ProcessManager/Views/DeviceThreeView.xaml.cs create mode 100644 ProcessManager/Views/DeviceTwoView.xaml create mode 100644 ProcessManager/Views/DeviceTwoView.xaml.cs create mode 100644 ProcessManager/Views/DevicesView.xaml create mode 100644 ProcessManager/Views/DevicesView.xaml.cs create mode 100644 ProcessManager/Views/MainWindow.xaml rename ProcessManager/{ => Views}/MainWindow.xaml.cs (88%) rename ProcessManager/{MainWindow.xaml => Views/MenuView.xaml} (72%) create mode 100644 ProcessManager/Views/MenuView.xaml.cs rename ProcessManager/{ => Views}/Setting.xaml (99%) rename ProcessManager/{ => Views}/Setting.xaml.cs (100%) create mode 100644 ProcessManager/WindowEmbedHelper.cs diff --git a/BOB/App.xaml.cs b/BOB/App.xaml.cs index 99f21c7..228538a 100644 --- a/BOB/App.xaml.cs +++ b/BOB/App.xaml.cs @@ -51,6 +51,7 @@ namespace BOB containerRegistry.RegisterForNavigation("MainView"); containerRegistry.RegisterForNavigation("MonitorView"); containerRegistry.RegisterForNavigation("DataView"); + containerRegistry.RegisterForNavigation("UpdateInfoView"); //注册弹窗 containerRegistry.RegisterDialog("MessageBox"); containerRegistry.RegisterDialog("ParameterSetting"); diff --git a/BOB/Services/BackfeedPollingRead.cs b/BOB/Services/BackfeedPollingRead.cs new file mode 100644 index 0000000..f559862 --- /dev/null +++ b/BOB/Services/BackfeedPollingRead.cs @@ -0,0 +1,46 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class BackfeedPollingRead : PollingRead + { + private Backfeed _Backfeed { get;set; } + private IEventAggregator _eventAggregator { get;set; } + public BackfeedPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "Backfeed"; + _Backfeed = _devices.DeviceDic["Backfeed"]as Backfeed; + } + public double 实时电流 { get; set; } = double.NaN; + public double 实时电压 { get; set; } = double.NaN; + public double 实时功率 { get; set; } = double.NaN; + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + 实时电流 = Random.Shared.NextDouble() * 10; + 实时电压 = Random.Shared.NextDouble() * 10; + 实时功率 = Random.Shared.NextDouble() * 10; + //实时电流 =await _Backfeed!.查询实时电流(ct); + //实时电压 = await _Backfeed!.查询实时电压(ct); + //实时功率 = await _Backfeed!.查询实时功率(ct); + var datalist = new List() { 实时电流, 实时电压, 实时功率 }; + var dataDic = new Dictionary + { + { "实时电流", 实时电流 }, + { "实时电压", 实时电压 }, + { "实时功率", 实时功率 } + }; + _eventAggregator.GetEvent().Publish(("Backfeed", dataDic)); + } + } +} diff --git a/BOB/Services/EAEL9080PollingRead.cs b/BOB/Services/EAEL9080PollingRead.cs new file mode 100644 index 0000000..52eb91d --- /dev/null +++ b/BOB/Services/EAEL9080PollingRead.cs @@ -0,0 +1,43 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class EAEL9080PollingRead:PollingRead + { + private EAEL9080 _EAEL9080 { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public EAEL9080PollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "EAEL9080"; + _EAEL9080 = _devices.DeviceDic["EAEL9080"] as EAEL9080; + } + public double 实时电流 { get; set; } = double.NaN; + public double 实时电压 { get; set; } = double.NaN; + public double 实时功率 { get; set; } = double.NaN; + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + 实时电流 = Random.Shared.NextDouble() * 10; + 实时电压 = Random.Shared.NextDouble() * 10; + 实时功率 = Random.Shared.NextDouble() * 10; + //实时电流 = await _EAEL9080!.查询电流(ct); + //实时电压 = await _EAEL9080!.查询电压(ct); + //实时功率 = await _EAEL9080!.查询功率(ct); + var dataDic = new Dictionary + { + { "实时电流", 实时电流 }, + { "实时电压", 实时电压 }, + { "实时功率", 实时功率 } + }; + _eventAggregator.GetEvent().Publish(("EAEL9080", dataDic)); + } + } +} diff --git a/BOB/Services/IT6724CPollingRead.cs b/BOB/Services/IT6724CPollingRead.cs new file mode 100644 index 0000000..403e951 --- /dev/null +++ b/BOB/Services/IT6724CPollingRead.cs @@ -0,0 +1,43 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class IT6724CPollingRead : PollingRead + { + private IT6724C _IT6724C { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public IT6724CPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "Backfeed"; + _IT6724C = _devices.DeviceDic["IT6724C"] as IT6724C; + } + public double 实时电流 { get; set; } = double.NaN; + public double 实时电压 { get; set; } = double.NaN; + public double 实时功率 { get; set; } = double.NaN; + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + 实时电流 = Random.Shared.NextDouble() * 10; + 实时电压 = Random.Shared.NextDouble() * 10; + 实时功率 = Random.Shared.NextDouble() * 10; + //实时电流 = await _IT6724C!.查询实时电流(ct); + //实时电压 = await _IT6724C!.查询实时电压(ct); + //实时功率 = await _IT6724C!.查询实时功率(ct); + var dataDic = new Dictionary + { + { "实时电流", 实时电流 }, + { "实时电压", 实时电压 }, + { "实时功率", 实时功率 } + }; + _eventAggregator.GetEvent().Publish(("IT6724C", dataDic)); + } + } +} diff --git a/BOB/Services/LQ7500-DPollingRead.cs b/BOB/Services/LQ7500-DPollingRead.cs new file mode 100644 index 0000000..8accec9 --- /dev/null +++ b/BOB/Services/LQ7500-DPollingRead.cs @@ -0,0 +1,41 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class LQ7500_DPollingRead : PollingRead + { + private LQ7500_D _LQ7500_D { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public LQ7500_DPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "LQ7500_D"; + _LQ7500_D = _devices.DeviceDic["LQ7500_D"] as LQ7500_D; + } + public double 内部传感器温度 { get; set; } = double.NaN; + public double 外部传感器温度 { get; set; } = double.NaN; + public double 水流量 { get; set; } = double.NaN; + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + 内部传感器温度 = await _LQ7500_D.读取内部传感器温度(); + 外部传感器温度 = await _LQ7500_D.读取外部传感器温度(); + 水流量 = await _LQ7500_D.读取流量(); + var dataDic = new Dictionary + { + { "内部传感器温度", 内部传感器温度 }, + { "外部传感器温度", 外部传感器温度 }, + { "水流量", 水流量 } + }; + _eventAggregator.GetEvent().Publish(("LQ7500_D", dataDic)); + } + } +} diff --git a/BOB/Services/PSB11000PollingRead.cs b/BOB/Services/PSB11000PollingRead.cs new file mode 100644 index 0000000..5004991 --- /dev/null +++ b/BOB/Services/PSB11000PollingRead.cs @@ -0,0 +1,46 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class PSB11000PollingRead : PollingRead + { + private PSB11000 _PSB11000 { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public PSB11000PollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "PSB11000"; + _PSB11000 = _devices.DeviceDic["PSB11000"] as PSB11000; + } + public double 实时电流 { get; set; } = double.NaN; + public double 实时电压 { get; set; } = double.NaN; + public double 实时功率 { get; set; } = double.NaN; + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + + 实时电流 = Random.Shared.NextDouble() * 10; + 实时电压 = Random.Shared.NextDouble() * 10; + 实时功率 = Random.Shared.NextDouble() * 10; + //实时电流 = await _PSB11000!.查询电流(ct); + //实时电压 = await _PSB11000!.查询电压(ct); + //实时功率 = await _PSB11000!.查询功率(ct); + var datalist = new List() { 实时电流, 实时电压, 实时功率 }; + var dataDic = new Dictionary + { + { "实时电流", 实时电流 }, + { "实时电压", 实时电压 }, + { "实时功率", 实时功率 } + }; + _eventAggregator.GetEvent().Publish(("PSB11000", dataDic)); + } + } +} diff --git a/BOB/Services/PollingRead.cs b/BOB/Services/PollingRead.cs new file mode 100644 index 0000000..07fdd53 --- /dev/null +++ b/BOB/Services/PollingRead.cs @@ -0,0 +1,75 @@ +using BOB.Converters; +using Common.PubEvent; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public abstract class PollingRead + { + private CancellationTokenSource _pollingCancellationTokenSource; + private Task? _pollingTask; + public int PollingInterval = 1000; + public string DeviceName{get;set;}=""; + private bool IsConnect{ get; set; }=false; + public PollingRead(IContainerProvider containerProvider) + { + var _eventAggregator = containerProvider.Resolve(); + _eventAggregator.GetEvent().Subscribe(PollingConnection); + } + + private void PollingConnection((string, bool) tuple) + { + var (device, isConnect) = tuple; + //System.Diagnostics.Debug.WriteLine($"PollingRead收到连接状态变化事件:设备={device},状态={isConnect}"); + if (device== DeviceName) + { + IsConnect = isConnect; + } + } + + public void StartPolling() + { + if (_pollingTask != null) + return; + + if (_pollingCancellationTokenSource == null || _pollingCancellationTokenSource.IsCancellationRequested) + _pollingCancellationTokenSource = new CancellationTokenSource(); + + _pollingTask = Task.Run(() => PollingLoop(_pollingCancellationTokenSource.Token)); + } + + public void StopPolling() + { + _pollingCancellationTokenSource?.Cancel(); + _pollingTask = null; + } + + private async Task PollingLoop(CancellationToken ct) + { + while (!ct.IsCancellationRequested) + { + if (IsConnect) + { + await Task.Delay(PollingInterval, ct); + + try + { + await ReadDeviceDataAsync(ct); + } + catch (Exception ex) + { + Console.WriteLine($"轮询过程中发生错误: {ex.Message}"); + } + } + } + } + public abstract Task ReadDeviceDataAsync(CancellationToken ct = default); + + + } +} diff --git a/BOB/Services/SQ0030G1DPollingRead.cs b/BOB/Services/SQ0030G1DPollingRead.cs new file mode 100644 index 0000000..08ea7d9 --- /dev/null +++ b/BOB/Services/SQ0030G1DPollingRead.cs @@ -0,0 +1,32 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class SQ0030G1DPollingRead : PollingRead + { + private SQ0030G1D _SQ0030G1D { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public SQ0030G1DPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "SQ0030G1D"; + _SQ0030G1D = _devices.DeviceDic["SQ0030G1D"] as SQ0030G1D; + } + + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + + + + } + } +} diff --git a/BOB/Services/WS-68030-380TPollingRead.cs b/BOB/Services/WS-68030-380TPollingRead.cs new file mode 100644 index 0000000..3f87e2b --- /dev/null +++ b/BOB/Services/WS-68030-380TPollingRead.cs @@ -0,0 +1,30 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class WS_68030_380TPollingRead : PollingRead + { + private WS_68030_380T _WS_68030_380T { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public WS_68030_380TPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "WS_68030_380T"; + _WS_68030_380T = _devices.DeviceDic["WS_68030_380T"] as WS_68030_380T; + } + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + + + } + } +} diff --git a/BOB/Services/ZXKSPollingRead.cs b/BOB/Services/ZXKSPollingRead.cs new file mode 100644 index 0000000..d112d8f --- /dev/null +++ b/BOB/Services/ZXKSPollingRead.cs @@ -0,0 +1,32 @@ +using BOB.Singleton; +using Common.PubEvent; +using DeviceCommand.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BOB.Services +{ + public class ZXKSPollingRead : PollingRead + { + private ZXKS _ZXKS { get; set; } + private IEventAggregator _eventAggregator { get; set; } + public ZXKSPollingRead(IContainerProvider containerProvider) : base(containerProvider) + { + _eventAggregator = containerProvider.Resolve(); + var _devices = containerProvider.Resolve(); + DeviceName = "ZXKS"; + _ZXKS = _devices.DeviceDic["ZXKS"] as ZXKS; + } + public double 实时电流 { get; set; } = double.NaN; + public double 实时电压 { get; set; } = double.NaN; + public double 实时功率 { get; set; } = double.NaN; + + public override async Task ReadDeviceDataAsync(CancellationToken ct = default) + { + + } + } +} diff --git a/BOB/Singleton/Devices.cs b/BOB/Singleton/Devices.cs index 8eb6750..8f98a98 100644 --- a/BOB/Singleton/Devices.cs +++ b/BOB/Singleton/Devices.cs @@ -1,4 +1,5 @@ -using Castle.DynamicProxy; +using BOB.Services; +using Castle.DynamicProxy; using DeviceCommand.Base; using DeviceCommand.Device; using Logger; @@ -13,10 +14,16 @@ namespace BOB.Singleton { public class Devices { + public Devices(IContainerProvider containerProvider) + { + _containerProvider=containerProvider; + } + private IContainerProvider _containerProvider; private CancellationTokenSource _appCancellationTokenSource = new(); public CancellationToken AppCancellationToken => _appCancellationTokenSource.Token; public Dictionary DeviceDic { get; private set; } = new Dictionary(); + public Dictionary PollingReadDic { get; private set; } = new Dictionary(); private readonly ProxyGenerator _proxyGen = new ProxyGenerator(); private readonly IInterceptor _loggingInterceptor = new LoggingInterceptor(); @@ -31,15 +38,11 @@ namespace BOB.Singleton private IModbusDevice ZXKSTDevice { get; set; } private Backfeed BackfeedDevice { get; set; } - public Devices() - { - } public void Init(List deviceList) { foreach (var device in deviceList) { if (!device.IsEnabled) continue; - switch (device.DeviceType) { case "DeviceCommand.Device.E36233A": @@ -235,6 +238,86 @@ namespace BOB.Singleton } } + public void StartPollingCollectionAsync() + { + foreach (var dc in DeviceDic) + { + var name = dc.Key; + + switch (name) + { + case "Backfeed": + { + var polling = new BackfeedPollingRead(_containerProvider); + PollingReadDic.Add("Backfeed", polling); + polling.StartPolling(); + break; + } + + case "IT6724C": + { + var polling = new IT6724CPollingRead(_containerProvider); + PollingReadDic.Add("IT6724C", polling); + polling.StartPolling(); + break; + } + + case "LQ7500_D": + { + var polling = new LQ7500_DPollingRead(_containerProvider); + PollingReadDic.Add("LQ7500_D", polling); + polling.StartPolling(); + break; + } + + case "EAEL9080": + { + var polling = new EAEL9080PollingRead(_containerProvider); + PollingReadDic.Add("EAEL9080", polling); + polling.StartPolling(); + break; + } + + case "IOBoard": + { + + break; + } + + case "PSB11000": + { + var polling = new PSB11000PollingRead(_containerProvider); + PollingReadDic.Add("PSB11000", polling); + polling.StartPolling(); + break; + } + + case "WS_68030_380T": + { + + break; + } + + case "SQ0030G1D": + { + + break; + } + + case "ZXKS": + { + var polling = new ZXKSPollingRead(_containerProvider); + PollingReadDic.Add("ZXKS", polling); + polling.StartPolling(); + break; + } + + default: + LoggerHelper.Warn($"未定义 {name} 的轮询处理器"); + break; + } + } + } public void Dispose() { diff --git a/BOB/SystemConfig.cs b/BOB/SystemConfig.cs index 5f1a6ab..92c882f 100644 --- a/BOB/SystemConfig.cs +++ b/BOB/SystemConfig.cs @@ -1,7 +1,8 @@ using BOB.Models; using Logger; -using Newtonsoft.Json; using Model; +using Newtonsoft.Json; +using System.Collections.ObjectModel; using System.IO; using System.Reflection; @@ -42,6 +43,7 @@ namespace BOB public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\"; public string DefaultSubProgramFilePath { get; set; } = ""; + public ObservableCollection CurSingleList { get; set; } public List DeviceList { get; set; } = new(); #region 配置加载方法 diff --git a/BOB/Tasks/HeartbeatManager.cs b/BOB/Tasks/HeartbeatManager.cs deleted file mode 100644 index 4bb5afe..0000000 --- a/BOB/Tasks/HeartbeatManager.cs +++ /dev/null @@ -1,159 +0,0 @@ -using DeviceCommand.Base; -using DeviceCommand.Device; -using Logger; - - -namespace BOB.Tasks -{ - public class HeartbeatManager where T : class - { - private readonly T _device; - private readonly CancellationTokenSource _cancellationTokenSource; - private Task? _heartbeatTask; - private const int HeartbeatInterval = 3000; - bool IsActive = false; - int ReConnectionAttempts = 0; - private const int MaxReconnectAttempts = 10; - - public HeartbeatManager(T device) - { - _device = device ?? throw new ArgumentNullException(nameof(device)); - _cancellationTokenSource = new CancellationTokenSource(); - } - - // 启动设备的心跳 - public void StartHeartbeat() - { - if (_heartbeatTask != null) - return; - - _heartbeatTask = Task.Run(() => HeartbeatLoop(_device, _cancellationTokenSource.Token)); - } - - // 停止设备的心跳 - public void StopHeartbeat() - { - IsActive = false; - _cancellationTokenSource.Cancel(); - _heartbeatTask?.Wait(); - _heartbeatTask = null; - } - - private async Task HeartbeatLoop(T device, CancellationToken ct) - { - while (!ct.IsCancellationRequested) - { - await Task.Delay(HeartbeatInterval, ct); - - try - { - switch (device) - { - case EAEL9080 eAEL9080: - await eAEL9080.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - break; - - case IT6724C iT6724C: - await iT6724C.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - break; - - case PSB11000 pSB11000: - await pSB11000.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - break; - - case SQ0030G1D sQ0030G1D: - await sQ0030G1D.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - break; - - case WS_68030_380T wS_68030_380T: - await wS_68030_380T.ReadCoilsAsync(1,0,1); - IsActive = true; - break; - - case IOBoard iOBoard: - await iOBoard.ReadCoilsAsync(1, 0, 1); - IsActive = true; - break; - - case LQ7500_D lQ7500_D: - await lQ7500_D.ReadCoilsAsync(1, 0, 1); - IsActive = true; - break; - - case ZXKS zXKS: - await zXKS.ReadCoilsAsync(1, 0, 1); - IsActive = true; - break; - - case Backfeed backfeed: - if (backfeed.CurrentInstance is E36233A e36233A) - { - await e36233A.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - } - else if (backfeed.CurrentInstance is IT6724CReverse iT6724CReverse) - { - await iT6724CReverse.SendAsync("SYSTem:REMote\r\n", ct); - IsActive = true; - } - break; - } - - } - catch (Exception ex) - { - IsActive = false; - ReConnectionAttempts++; - if (MaxReconnectAttempts < ReConnectionAttempts) - { - LoggerHelper.ErrorWithNotify($"重连次数超过上限,停止重连:{device.GetType().Name}"); - StopHeartbeat(); - return; - } - await ReconnectDevice(device, ct); - } - } - } - - private async Task ReconnectDevice(T device, CancellationToken ct) - { - try - { - bool result = false; - if (device is ITcp itcpDevice) - { - result = await itcpDevice.ConnectAsync(CancellationToken.None); - } - else if (device is IModbusDevice imodbusDevice) - { - result = await imodbusDevice.ConnectAsync(CancellationToken.None); - } - else if (device is ISerialPort iserialPortDevice) - { - result = await iserialPortDevice.ConnectAsync(CancellationToken.None); - } - else if (device is Backfeed backfeedDevice) - { - if (backfeedDevice.CurrentInstance is ITcp itcpDevice1) - { - result = await itcpDevice1.ConnectAsync(CancellationToken.None); - } - else if (backfeedDevice.CurrentInstance is ISerialPort iserialPortDevice1) - { - result = await iserialPortDevice1.ConnectAsync(CancellationToken.None); - } - } - if (result == false) { LoggerHelper.ErrorWithNotify($"重连失败:{device.GetType().Name}"); } - else ReConnectionAttempts=0; - } - catch (Exception ex) - { - LoggerHelper.ErrorWithNotify($"重连异常:{device.GetType().Name}"+ex.Message); - } - } - } -} diff --git a/BOB/ViewModels/Dialogs/BackfeedViewModel.cs b/BOB/ViewModels/Dialogs/BackfeedViewModel.cs index 3e8394d..f1bcb36 100644 --- a/BOB/ViewModels/Dialogs/BackfeedViewModel.cs +++ b/BOB/ViewModels/Dialogs/BackfeedViewModel.cs @@ -1,6 +1,12 @@ using BOB.Singleton; +using BOB.ViewModels.UserControls; +using Common.PubEvent; using DeviceCommand.Device; using Logger; +using OxyPlot; +using OxyPlot.Axes; +using OxyPlot.Legends; +using OxyPlot.Series; using Prism.Commands; using Prism.Events; using Prism.Mvvm; @@ -62,7 +68,54 @@ namespace BOB.ViewModels.Dialogs get { return _OPP功率; } set { SetProperty(ref _OPP功率, value); } } + private LineSeries _电压_lineSeries = new LineSeries { Title = "电压(V)", StrokeThickness = 2 }; + public LineSeries 电压_lineSeries + { + get => _电压_lineSeries; + set => SetProperty(ref _电压_lineSeries, value); + } + private LineSeries _电流_lineSeries = new LineSeries { Title = "电流(A)", StrokeThickness = 2 }; + public LineSeries 电流_lineSeries + { + get => _电流_lineSeries; + set => SetProperty(ref _电流_lineSeries, value); + } + + private LineSeries _功率_lineSeries = new LineSeries { Title = "功率(W)", StrokeThickness = 2 }; + public LineSeries 功率_lineSeries + { + get => _功率_lineSeries; + set => SetProperty(ref _功率_lineSeries, value); + } + private NumericalDisplayViewModel _电压MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电压MV + { + get => _电压MV; + set => SetProperty(ref _电压MV, value); + } + + private NumericalDisplayViewModel _电流MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电流MV + { + get => _电流MV; + set => SetProperty(ref _电流MV, value); + } + + private NumericalDisplayViewModel _功率MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 功率MV + { + get => _功率MV; + set => SetProperty(ref _功率MV, value); + } + private PlotModel _曲线Model = new PlotModel(); + public PlotModel 曲线Model + { + get => _曲线Model; + set => SetProperty(ref _曲线Model, value); + } + public DateTimeAxis TimeAxis { get; private set; } + public LinearAxis ValueAxis { get; private set; } #endregion #region 命令 @@ -82,6 +135,7 @@ namespace BOB.ViewModels.Dialogs public ICommand DisableOPPCommand { get; private set; } public ICommand SetOPPCommand { get; private set; } public ICommand CloseCommand { get; private set; } + public ICommand ResetViewCommand { get; private set; } #endregion @@ -113,6 +167,55 @@ namespace BOB.ViewModels.Dialogs DisableOPPCommand = new AsyncDelegateCommand(OnDisableOPP); SetOPPCommand = new AsyncDelegateCommand(OnSetOPP); CloseCommand = new DelegateCommand(OnClose); + ResetViewCommand = new DelegateCommand(ResetView); + _eventAggregator.GetEvent().Subscribe(UpdateCurve); + InitCurve(); + } + private void ResetView() + { + 曲线Model.ResetAllAxes(); + 曲线Model.InvalidatePlot(true); + } + private void InitCurve() + { + TimeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + StringFormat = "HH:mm:ss", + IntervalType = DateTimeIntervalType.Seconds, + }; + ValueAxis = new LinearAxis + { + Position = AxisPosition.Left, + }; + 曲线Model.Axes.Add(TimeAxis); + 曲线Model.Axes.Add(ValueAxis); + 曲线Model.Series.Add(电压_lineSeries); + 曲线Model.Series.Add(电流_lineSeries); + 曲线Model.Series.Add(功率_lineSeries); + 曲线Model.Legends.Add(new Legend() + { + LegendPosition = LegendPosition.TopRight, + LegendOrientation = LegendOrientation.Vertical, + LegendPlacement = LegendPlacement.Inside + }); + + 曲线Model.InvalidatePlot(true); + } + + private void UpdateCurve((string device, Dictionary data )tuple) + { + var (device, data) = tuple; + if (device != "IT6724C") return; + 电流MV.MonitorValue = data["实时电流"]; + 电压MV.MonitorValue = data["实时电压"]; + 功率MV.MonitorValue = data["实时功率"]; + + var now = DateTimeAxis.ToDouble(DateTime.Now); + 电流_lineSeries.Points.Add(new DataPoint(now, data["实时电流"])); + 电压_lineSeries.Points.Add(new DataPoint(now, data["实时电压"])); + 功率_lineSeries.Points.Add(new DataPoint(now, data["实时功率"])); + 曲线Model.InvalidatePlot(true); } diff --git a/BOB/ViewModels/Dialogs/EAEL9080ViewModel.cs b/BOB/ViewModels/Dialogs/EAEL9080ViewModel.cs index 65c1ea2..e89c303 100644 --- a/BOB/ViewModels/Dialogs/EAEL9080ViewModel.cs +++ b/BOB/ViewModels/Dialogs/EAEL9080ViewModel.cs @@ -1,13 +1,19 @@ using BOB.Singleton; +using BOB.ViewModels.UserControls; +using Common.PubEvent; +using DeviceCommand.Device; +using Logger; +using OxyPlot; +using OxyPlot.Annotations; +using OxyPlot.Axes; +using OxyPlot.Legends; +using OxyPlot.Series; using Prism.Commands; -using Prism.Mvvm; using Prism.Events; +using Prism.Mvvm; using System; using System.Threading.Tasks; using System.Windows.Input; -using DeviceCommand.Device; -using Logger; -using BOB.ViewModels.UserControls; namespace BOB.ViewModels.Dialogs { @@ -17,12 +23,31 @@ namespace BOB.ViewModels.Dialogs private string _Title = "低压直流负载"; private string _设备标识字符串; private string _负载模式; - private string _电流LA; - private string _电流LB; + private LineSeries _电压_lineSeries = new LineSeries + { + Title = "电压(V)", + StrokeThickness = 2, + }; + private LineSeries _电流_lineSeries = new LineSeries + { + Title = "电流(A)", + StrokeThickness = 2, + }; + private LineSeries _功率_lineSeries = new LineSeries + { + Title = "功率(W)", + StrokeThickness = 2, + }; private NumericalDisplayViewModel _电压MV=new(); private NumericalDisplayViewModel _电流MV = new(); private NumericalDisplayViewModel _功率MV=new(); + private PlotModel _曲线Model = new(); + public PlotModel 曲线Model + { + get { return _曲线Model; } + set { SetProperty(ref _曲线Model, value); } + } public string Title { get { return _Title; } @@ -41,16 +66,21 @@ namespace BOB.ViewModels.Dialogs set { SetProperty(ref _负载模式, value); } } - public string 电流LA + public LineSeries 电流_lineSeries { - get { return _电流LA; } - set { SetProperty(ref _电流LA, value); } + get { return _电流_lineSeries; } + set { SetProperty(ref _电流_lineSeries, value); } } - public string 电流LB + public LineSeries 电压_lineSeries { - get { return _电流LB; } - set { SetProperty(ref _电流LB, value); } + get { return _电压_lineSeries; } + set { SetProperty(ref _电压_lineSeries, value); } + } + public LineSeries 功率_lineSeries + { + get { return _功率_lineSeries; } + set { SetProperty(ref _功率_lineSeries, value); } } public NumericalDisplayViewModel 电压MV { @@ -67,6 +97,8 @@ namespace BOB.ViewModels.Dialogs get { return _功率MV; } set { SetProperty(ref _功率MV, value); } } + public DateTimeAxis TimeAxis { get; private set; } + public LinearAxis ValueAxis { get; private set; } #endregion #region 命令 @@ -78,6 +110,7 @@ namespace BOB.ViewModels.Dialogs public ICommand 电源输出关命令 { get; private set; } public ICommand 关闭命令 { get; private set; } public ICommand 查询设备信息命令 { get; private set; } + public ICommand ResetViewCommand { get; private set; } #endregion @@ -99,8 +132,56 @@ namespace BOB.ViewModels.Dialogs 电源输出关命令 = new AsyncDelegateCommand(OnPowerOff); 关闭命令 = new DelegateCommand(OnOff); 查询设备信息命令 = new AsyncDelegateCommand(OnQueryDeviceInfo); + ResetViewCommand = new DelegateCommand(ResetView); + _eventAggregator.GetEvent().Subscribe(UpdateCurve); + InitCurve(); + } + private void ResetView() + { + 曲线Model.ResetAllAxes(); + 曲线Model.InvalidatePlot(true); + } + private void InitCurve() + { + TimeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + StringFormat = "HH:mm:ss", + IntervalType = DateTimeIntervalType.Seconds, + }; + ValueAxis = new LinearAxis + { + Position = AxisPosition.Left, + }; + 曲线Model.Axes.Add(TimeAxis); + 曲线Model.Axes.Add(ValueAxis); + 曲线Model.Series.Add(电压_lineSeries); + 曲线Model.Series.Add(电流_lineSeries); + 曲线Model.Series.Add(功率_lineSeries); + 曲线Model.Legends.Add(new Legend() + { + LegendPosition = LegendPosition.TopRight, + LegendOrientation = LegendOrientation.Vertical, + LegendPlacement = LegendPlacement.Inside + }); + + 曲线Model.InvalidatePlot(true); } + private void UpdateCurve((string device, Dictionary data) tuple) + { + var (device, data) = tuple; + if (device != "EAEL9080") return; + 电流MV.MonitorValue = data["实时电流"]; + 电压MV.MonitorValue = data["实时电压"]; + 功率MV.MonitorValue = data["实时功率"]; + + var now = DateTimeAxis.ToDouble(DateTime.Now); + 电流_lineSeries.Points.Add(new DataPoint(now, data["实时电流"])); + 电压_lineSeries.Points.Add(new DataPoint(now, data["实时电压"])); + 功率_lineSeries.Points.Add(new DataPoint(now, data["实时功率"])); + 曲线Model.InvalidatePlot(true); + } private async Task OnQueryDeviceInfo() { 设备标识字符串 = await device.查询设备信息(); diff --git a/BOB/ViewModels/Dialogs/IT6724CViewModel.cs b/BOB/ViewModels/Dialogs/IT6724CViewModel.cs index 6a21d1c..5f8783b 100644 --- a/BOB/ViewModels/Dialogs/IT6724CViewModel.cs +++ b/BOB/ViewModels/Dialogs/IT6724CViewModel.cs @@ -1,6 +1,13 @@ using BOB.Singleton; +using BOB.ViewModels.UserControls; +using Common.PubEvent; using DeviceCommand.Device; using Logger; +using OxyPlot; +using OxyPlot.Annotations; +using OxyPlot.Axes; +using OxyPlot.Legends; +using OxyPlot.Series; using System; using System.Collections.Generic; using System.Linq; @@ -12,52 +19,97 @@ namespace BOB.ViewModels.Dialogs { public class IT6724CViewModel : BindableBase, IDialogAware { - #region 属性 - private string _title = "低压直流电源"; public string Title { - get { return _title; } - set { SetProperty(ref _title, value); } + get => _title; + set => SetProperty(ref _title, value); } - private string _设备标识字符串; public string 设备标识字符串 { - get { return _设备标识字符串; } - set { SetProperty(ref _设备标识字符串, value); } + get => _设备标识字符串; + set => SetProperty(ref _设备标识字符串, value); } - private string _输出电压; public string 输出电压 { - get { return _输出电压; } - set { SetProperty(ref _输出电压, value); } + get => _输出电压; + set => SetProperty(ref _输出电压, value); } private string _输出电流; public string 输出电流 { - get { return _输出电流; } - set { SetProperty(ref _输出电流, value); } + get => _输出电流; + set => SetProperty(ref _输出电流, value); } private string _OCP电流; public string OCP电流 { - get { return _OCP电流; } - set { SetProperty(ref _OCP电流, value); } + get => _OCP电流; + set => SetProperty(ref _OCP电流, value); } private string _OVP电压; public string OVP电压 { - get { return _OVP电压; } - set { SetProperty(ref _OVP电压, value); } + get => _OVP电压; + set => SetProperty(ref _OVP电压, value); } + private LineSeries _电压_lineSeries = new LineSeries { Title = "电压(V)", StrokeThickness = 2 }; + public LineSeries 电压_lineSeries + { + get => _电压_lineSeries; + set => SetProperty(ref _电压_lineSeries, value); + } + + private LineSeries _电流_lineSeries = new LineSeries { Title = "电流(A)", StrokeThickness = 2 }; + public LineSeries 电流_lineSeries + { + get => _电流_lineSeries; + set => SetProperty(ref _电流_lineSeries, value); + } + + private LineSeries _功率_lineSeries = new LineSeries { Title = "功率(W)", StrokeThickness = 2 }; + public LineSeries 功率_lineSeries + { + get => _功率_lineSeries; + set => SetProperty(ref _功率_lineSeries, value); + } + private NumericalDisplayViewModel _电压MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电压MV + { + get => _电压MV; + set => SetProperty(ref _电压MV, value); + } + + private NumericalDisplayViewModel _电流MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电流MV + { + get => _电流MV; + set => SetProperty(ref _电流MV, value); + } + + private NumericalDisplayViewModel _功率MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 功率MV + { + get => _功率MV; + set => SetProperty(ref _功率MV, value); + } + private PlotModel _曲线Model = new PlotModel(); + public PlotModel 曲线Model + { + get => _曲线Model; + set => SetProperty(ref _曲线Model, value); + } + public DateTimeAxis TimeAxis { get; private set; } + public LinearAxis ValueAxis { get; private set; } #endregion + #region 命令 public ICommand ConnectCommand { get; private set; } public ICommand DisconnectCommand { get; private set; } @@ -76,6 +128,7 @@ namespace BOB.ViewModels.Dialogs public ICommand ClearOVPAlarmCommand { get; private set; } public ICommand SetOVPCommand { get; private set; } public ICommand CloseCommand { get; private set; } + public ICommand ResetViewCommand { get; private set; } #endregion private IEventAggregator _eventAggregator { get; set; } @@ -104,8 +157,56 @@ namespace BOB.ViewModels.Dialogs _devices = containerProvider.Resolve(); _globalVariables = containerProvider.Resolve(); _eventAggregator = containerProvider.Resolve(); + ResetViewCommand = new DelegateCommand(ResetView); + _eventAggregator.GetEvent().Subscribe(UpdateCurve); + InitCurve(); + } + private void ResetView() + { + 曲线Model.ResetAllAxes(); + 曲线Model.InvalidatePlot(true); + } + private void InitCurve() + { + TimeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + StringFormat = "HH:mm:ss", + IntervalType = DateTimeIntervalType.Seconds, + }; + ValueAxis = new LinearAxis + { + Position = AxisPosition.Left, + }; + 曲线Model.Axes.Add(TimeAxis); + 曲线Model.Axes.Add(ValueAxis); + 曲线Model.Series.Add(电压_lineSeries); + 曲线Model.Series.Add(电流_lineSeries); + 曲线Model.Series.Add(功率_lineSeries); + 曲线Model.Legends.Add(new Legend() + { + LegendPosition = LegendPosition.TopRight, + LegendOrientation = LegendOrientation.Vertical, + LegendPlacement = LegendPlacement.Inside + }); + + 曲线Model.InvalidatePlot(true); } + private void UpdateCurve((string device, Dictionary data) tuple) + { + var (device, data) = tuple; + if (device != "IT6724C") return; + 电流MV.MonitorValue = data["实时电流"]; + 电压MV.MonitorValue = data["实时电压"]; + 功率MV.MonitorValue = data["实时功率"]; + + var now = DateTimeAxis.ToDouble(DateTime.Now); + 电流_lineSeries.Points.Add(new DataPoint(now, data["实时电流"])); + 电压_lineSeries.Points.Add(new DataPoint(now, data["实时电压"])); + 功率_lineSeries.Points.Add(new DataPoint(now, data["实时功率"])); + 曲线Model.InvalidatePlot(true); + } #region 命令处理方法 private void OnClose() { diff --git a/BOB/ViewModels/Dialogs/PSB11000ViewModel.cs b/BOB/ViewModels/Dialogs/PSB11000ViewModel.cs index fb593a0..f4a6a47 100644 --- a/BOB/ViewModels/Dialogs/PSB11000ViewModel.cs +++ b/BOB/ViewModels/Dialogs/PSB11000ViewModel.cs @@ -1,6 +1,12 @@ using BOB.Singleton; +using BOB.ViewModels.UserControls; +using Common.PubEvent; using DeviceCommand.Device; using Logger; +using OxyPlot; +using OxyPlot.Axes; +using OxyPlot.Legends; +using OxyPlot.Series; using System; using System.Collections.Generic; using System.Linq; @@ -48,7 +54,54 @@ namespace BOB.ViewModels.Dialogs get { return _OCP电流; } set { SetProperty(ref _OCP电流, value); } } + private LineSeries _电压_lineSeries = new LineSeries { Title = "电压(V)", StrokeThickness = 2 }; + public LineSeries 电压_lineSeries + { + get => _电压_lineSeries; + set => SetProperty(ref _电压_lineSeries, value); + } + private LineSeries _电流_lineSeries = new LineSeries { Title = "电流(A)", StrokeThickness = 2 }; + public LineSeries 电流_lineSeries + { + get => _电流_lineSeries; + set => SetProperty(ref _电流_lineSeries, value); + } + + private LineSeries _功率_lineSeries = new LineSeries { Title = "功率(W)", StrokeThickness = 2 }; + public LineSeries 功率_lineSeries + { + get => _功率_lineSeries; + set => SetProperty(ref _功率_lineSeries, value); + } + private NumericalDisplayViewModel _电压MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电压MV + { + get => _电压MV; + set => SetProperty(ref _电压MV, value); + } + + private NumericalDisplayViewModel _电流MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 电流MV + { + get => _电流MV; + set => SetProperty(ref _电流MV, value); + } + + private NumericalDisplayViewModel _功率MV = new NumericalDisplayViewModel(); + public NumericalDisplayViewModel 功率MV + { + get => _功率MV; + set => SetProperty(ref _功率MV, value); + } + private PlotModel _曲线Model = new PlotModel(); + public PlotModel 曲线Model + { + get => _曲线Model; + set => SetProperty(ref _曲线Model, value); + } + public DateTimeAxis TimeAxis { get; private set; } + public LinearAxis ValueAxis { get; private set; } #endregion #region 命令 @@ -66,6 +119,7 @@ namespace BOB.ViewModels.Dialogs public ICommand SetCCCurrentCommand { get; private set; } public ICommand SetOCPCurrentCommand { get; private set; } public ICommand CloseCommand { get; private set; } + public ICommand ResetViewCommand { get; private set; } #endregion private IEventAggregator _eventAggregator { get; set; } @@ -90,7 +144,55 @@ namespace BOB.ViewModels.Dialogs SetCCCurrentCommand = new AsyncDelegateCommand(OnSetCCCurrent); SetOCPCurrentCommand = new AsyncDelegateCommand(OnSetOCPCurrent); CloseCommand = new DelegateCommand(OnClose); + ResetViewCommand = new DelegateCommand(ResetView); + _eventAggregator.GetEvent().Subscribe(UpdateCurve); + InitCurve(); + } + private void ResetView() + { + 曲线Model.ResetAllAxes(); + 曲线Model.InvalidatePlot(true); + } + private void InitCurve() + { + TimeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + StringFormat = "HH:mm:ss", + IntervalType = DateTimeIntervalType.Seconds, + }; + ValueAxis = new LinearAxis + { + Position = AxisPosition.Left, + }; + 曲线Model.Axes.Add(TimeAxis); + 曲线Model.Axes.Add(ValueAxis); + 曲线Model.Series.Add(电压_lineSeries); + 曲线Model.Series.Add(电流_lineSeries); + 曲线Model.Series.Add(功率_lineSeries); + 曲线Model.Legends.Add(new Legend() + { + LegendPosition = LegendPosition.TopRight, + LegendOrientation = LegendOrientation.Vertical, + LegendPlacement = LegendPlacement.Inside + }); + 曲线Model.InvalidatePlot(true); + } + + private void UpdateCurve((string device, Dictionary data) tuple) + { + var (device, data) = tuple; + if (device != "IT6724C") return; + 电流MV.MonitorValue = data["实时电流"]; + 电压MV.MonitorValue = data["实时电压"]; + 功率MV.MonitorValue = data["实时功率"]; + + var now = DateTimeAxis.ToDouble(DateTime.Now); + 电流_lineSeries.Points.Add(new DataPoint(now, data["实时电流"])); + 电压_lineSeries.Points.Add(new DataPoint(now, data["实时电压"])); + 功率_lineSeries.Points.Add(new DataPoint(now, data["实时功率"])); + 曲线Model.InvalidatePlot(true); } #region Command Handlers private void OnClose() diff --git a/BOB/ViewModels/MainViewModel.cs b/BOB/ViewModels/MainViewModel.cs index 56e8486..8ea2e44 100644 --- a/BOB/ViewModels/MainViewModel.cs +++ b/BOB/ViewModels/MainViewModel.cs @@ -44,6 +44,7 @@ namespace BOB.ViewModels { _devices.Init(SystemConfig.Instance.DeviceList); await _devices.ConnectAllDevicesAsync(); + _devices.StartPollingCollectionAsync(); } catch (Exception ex) { @@ -51,7 +52,7 @@ namespace BOB.ViewModels } finally { - _eventAggregator.GetEvent().Publish(false); + _eventAggregator.GetEvent().Publish(false); } } #region 导航 @@ -61,7 +62,7 @@ namespace BOB.ViewModels { if (!_isInitialized) { - _eventAggregator.GetEvent().Publish(true); + _eventAggregator.GetEvent().Publish(true); _ = InitializeDevicesAsync(); _isInitialized = true; } diff --git a/BOB/ViewModels/MonitorViewModel.cs b/BOB/ViewModels/MonitorViewModel.cs index 88779fc..4aecc02 100644 --- a/BOB/ViewModels/MonitorViewModel.cs +++ b/BOB/ViewModels/MonitorViewModel.cs @@ -1,8 +1,11 @@ -using BOB.Singleton; +using BOB.Services; +using BOB.Singleton; +using Common.PubEvent; using Common.PubEvents; using Logger; using Microsoft.Win32; using OxyPlot; +using OxyPlot.Axes; using OxyPlot.Legends; using OxyPlot.Series; using Prism.Dialogs; @@ -10,6 +13,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Input; @@ -27,13 +31,6 @@ namespace BOB.ViewModels get => _CurveModel; set => SetProperty(ref _CurveModel, value); } - private List _signalSeries = new List(); - - public List SignalSeries - { - get => _signalSeries; - set => SetProperty(ref _signalSeries, value); - } private Dictionary _SeriesDic = new (); public Dictionary SeriesDic @@ -69,12 +66,19 @@ namespace BOB.ViewModels get => _SelectedSaveDays; set => SetProperty(ref _SelectedSaveDays, value); } - private ObservableCollection _CurSingleList=new(); + //private ObservableCollection _CurSingleList=new(); public ObservableCollection CurSingleList { - get => _CurSingleList; - set => SetProperty(ref _CurSingleList, value); + get => SystemConfig.Instance.CurSingleList; + set + { + if (SystemConfig.Instance.CurSingleList != value) + { + SystemConfig.Instance.CurSingleList = value; + RaisePropertyChanged(); + } + } } private ObservableCollection _DeviceSingleList = new(); @@ -83,8 +87,6 @@ namespace BOB.ViewModels get => _DeviceSingleList; set => SetProperty(ref _DeviceSingleList, value); } - - #endregion #region 命令 public ICommand ResetViewCommand { get; set; } @@ -97,135 +99,113 @@ namespace BOB.ViewModels private IEventAggregator _eventAggregator; private IDialogService _dialogService; private static Random _random = new Random(); + private DispatcherTimer _refreshTimer; private Devices _devices { get; set; } + private readonly IProgress<(string name, double value)> _progress; public MonitorViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IContainerProvider containerProvider ) { _dialogService = dialogService; _eventAggregator = eventAggregator; _devices = containerProvider.Resolve(); ResetViewCommand = new DelegateCommand(ResetView); - AddSignalCommand = new DelegateCommand(AddSignal); + AddSignalCommand = new DelegateCommand(AddSignal); RemoveSignalCommand = new DelegateCommand(RemoveSignal); ChanegPathCommand = new DelegateCommand(ChanegPath); SaveCommand = new DelegateCommand(Save); + _eventAggregator.GetEvent().Subscribe(OnCurveDataReceived); + _progress = new Progress<(string name, double value)>(UpdateCurveOnUI); InitData(); - - // 初始化模拟量曲线 - InitializeSimulatedSignals(); - - // 设置定时器定时更新模拟数据 - _timer = new DispatcherTimer - { - Interval = TimeSpan.FromSeconds(0.5) // 每1秒更新一次 - }; - _timer.Tick += (s, e) => UpdateSimulatedData(); - _timer.Start(); } + private void UpdateCurveOnUI((string name, double value) update) + { + var (signalName, value) = update; + + if (!SeriesDic.TryGetValue(signalName, out var line)) + return; + + line.Points.Add(DateTimeAxis.CreateDataPoint(DateTime.Now, value)); + } + + private async void OnCurveDataReceived((string device, Dictionary data) tuple) + { + await Task.Run(() => + { + var (device, data) = tuple; + foreach (var item in data) + { + string propname = item.Key; + double value = item.Value; + string signalName = device + "." + propname; + + if (CurSingleList.Contains(signalName)) + { + // 通过 Progress 安全更新 UI + _progress.Report((signalName, value)); + } + } + }); + } + private void InitData() { SelectedSaveDays=SystemConfig.Instance.SaveDay.ToString(); - FilePath= SystemConfig.Instance.DBSavePath; - } - - - - #region 模拟量 - - private DispatcherTimer _timer; - private double _time = 0; - - private void InitializeSimulatedSignals() - { - CurveModel.Legends.Add(new Legend + FilePath= SystemConfig.Instance.DBSavePath; + foreach (var _PollingRead in _devices.PollingReadDic) { - LegendPosition= LegendPosition.TopLeft, - IsLegendVisible=true - }); - // 模拟四个信号的初始化 + string deviceName = _PollingRead.Key; + PollingRead pollingRead = _PollingRead.Value; - // 模拟量1:正弦波 - var sineSignal = new LineSeries - { - Title = "正弦波信号", - MarkerType = MarkerType.Circle, - StrokeThickness = 2, - Color = OxyColors.Red - }; - _signalSeries.Add(sineSignal); - SeriesDic.Add(sineSignal.Title, sineSignal); + var props = pollingRead.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.PropertyType == typeof(double)); - // 模拟量2:线性增长 - var linearSignal = new LineSeries - { - Title = "线性增长信号", - MarkerType = MarkerType.Square, - StrokeThickness = 2, - Color = OxyColors.Green - }; - _signalSeries.Add(linearSignal); - SeriesDic.Add(linearSignal.Title, linearSignal); - - // 模拟量3:随机波动 - var randomSignal = new LineSeries - { - Title = "随机波动信号", - MarkerType = MarkerType.Triangle, - StrokeThickness = 2, - Color = OxyColors.Blue - }; - _signalSeries.Add(randomSignal); - SeriesDic.Add(randomSignal.Title, randomSignal); - - // 模拟量4:指数衰减 - var exponentialSignal = new LineSeries - { - Title = "指数衰减信号", - MarkerType = MarkerType.Diamond, - StrokeThickness = 2, - Color = OxyColors.Orange - }; - _signalSeries.Add(exponentialSignal); - SeriesDic.Add(exponentialSignal.Title, exponentialSignal); - - // 将模拟量曲线添加到图表中 - foreach (var signal in _signalSeries) - { - CurveModel.Series.Add(signal); + foreach (var prop in props) + { + DeviceSingleList.Add(deviceName + "." + prop.Name); + } } + foreach(var curve in CurSingleList) + { + AddSignal(curve); + } + InitCurveModel(); + InitTimer(); } - private void UpdateSimulatedData() + private void InitCurveModel() { - // 更新模拟量的值并添加新的数据点 + CurveModel.Axes.Clear(); - // 模拟量1:正弦波 - var sineSignal = _signalSeries[0]; - sineSignal.Points.Add(new DataPoint(_time, Math.Sin(_time))); - if (sineSignal.Points.Count > 1000) sineSignal.Points.RemoveAt(0); // 保持最多100个点 + var timeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + StringFormat = "HH:mm:ss", + Title = "时间", + IntervalType = DateTimeIntervalType.Seconds + }; + CurveModel.Axes.Add(timeAxis); - // 模拟量2:线性增长 - var linearSignal = _signalSeries[1]; - linearSignal.Points.Add(new DataPoint(_time, _time)); - if (linearSignal.Points.Count > 1000) linearSignal.Points.RemoveAt(0); // 保持最多100个点 - - // 模拟量3:随机波动 - var randomSignal = _signalSeries[2]; - var randomValue = 10 * (2 * new Random().NextDouble() - 1); // 随机波动范围 [-10, 10] - randomSignal.Points.Add(new DataPoint(_time, randomValue)); - if (randomSignal.Points.Count > 1000) randomSignal.Points.RemoveAt(0); // 保持最多100个点 - - // 模拟量4:指数衰减 - var exponentialSignal = _signalSeries[3]; - exponentialSignal.Points.Add(new DataPoint(_time, Math.Exp(-0.1 * _time))); - if (exponentialSignal.Points.Count > 1000) exponentialSignal.Points.RemoveAt(0); // 保持最多100个点 - - // 更新图表 - CurveModel.InvalidatePlot(true); - - // 时间前进 - _time += 0.5; // 每次增加0.1秒 + var valueAxis = new LinearAxis + { + Position = AxisPosition.Left, + Title = "数值" + }; + CurveModel.Axes.Add(valueAxis); } - #endregion + + private void InitTimer() + { + _refreshTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + _refreshTimer.Tick += (s, e) => + { + CurveModel.InvalidatePlot(true); + }; + _refreshTimer.Start(); + } + + private OxyColor GetRandomColor() { // 生成随机的红色、绿色、蓝色分量,范围是 0 到 255 @@ -237,28 +217,62 @@ namespace BOB.ViewModels return OxyColor.FromRgb(r, g, b); } - private void AddSignal() + private void AddSignal(string Curve="") { try { - if (_SeriesDic.ContainsKey(SelectCurve)) + if (Curve == null) { - return; + if (_SeriesDic.ContainsKey(SelectCurve)) + { + return; + } + else + { + var sineSignal = new LineSeries + { + Title = SelectCurve, + MarkerType = MarkerType.Circle, + StrokeThickness = 2, + Color = GetRandomColor() + }; + CurveModel.Series.Add(sineSignal); + CurveModel.Legends.Add(new Legend + { + LegendPosition = LegendPosition.TopCenter, // 图表顶部中间 + LegendOrientation = LegendOrientation.Horizontal, // 横向排列 + LegendPlacement = LegendPlacement.Inside, // 放在图上面 + LegendBorderThickness = 0, // 去掉边框,可选 + LegendBackground = OxyColors.WhiteSmoke // 背景色,可选 + }); + + SeriesDic.Add(SelectCurve, sineSignal); + CurSingleList.Add(SelectCurve); + SystemConfig.Instance.SaveToFile(); + CurveModel.InvalidatePlot(true); + } + } else { - // 开启资源 - var sineSignal = new LineSeries { - Title = SelectCurve, + Title = Curve, MarkerType = MarkerType.Circle, StrokeThickness = 2, Color = GetRandomColor() }; CurveModel.Series.Add(sineSignal); - SeriesDic.Add(SelectCurve, sineSignal); - CurSingleList.Add(SelectCurve); + CurveModel.Legends.Add(new Legend + { + LegendPosition = LegendPosition.TopCenter, // 图表顶部中间 + LegendOrientation = LegendOrientation.Horizontal, // 横向排列 + LegendPlacement = LegendPlacement.Inside, // 放在图上面 + LegendBorderThickness = 0, // 去掉边框,可选 + LegendBackground = OxyColors.WhiteSmoke // 背景色,可选 + }); + + SeriesDic.Add(Curve, sineSignal); CurveModel.InvalidatePlot(true); } } @@ -277,9 +291,8 @@ namespace BOB.ViewModels _SeriesDic.Remove(SelectDeleteCurve); CurveModel.Series.Remove(signalToRemove); CurSingleList.Remove(SelectDeleteCurve); + SystemConfig.Instance.SaveToFile(); CurveModel.InvalidatePlot(true); - - // 释放资源 } } catch (Exception ex) @@ -336,7 +349,7 @@ namespace BOB.ViewModels #region 导航 public void OnNavigatedTo(NavigationContext navigationContext) { - + } public bool IsNavigationTarget(NavigationContext navigationContext) diff --git a/BOB/ViewModels/ParametersManagerViewModel.cs b/BOB/ViewModels/ParametersManagerViewModel.cs index e0d34af..c7bf9e4 100644 --- a/BOB/ViewModels/ParametersManagerViewModel.cs +++ b/BOB/ViewModels/ParametersManagerViewModel.cs @@ -176,6 +176,7 @@ namespace BOB.ViewModels } break; } + _eventAggregator.GetEvent().Publish((DeviceList[i].Remark, isConnected)); } progress.Report((i, isConnected)); diff --git a/BOB/ViewModels/ShellViewModel.cs b/BOB/ViewModels/ShellViewModel.cs index 75f9e55..57e2217 100644 --- a/BOB/ViewModels/ShellViewModel.cs +++ b/BOB/ViewModels/ShellViewModel.cs @@ -145,7 +145,7 @@ namespace BOB.ViewModels break; case "更新信息": - + _regionManager.RequestNavigate("ShellViewManager", "UpdateInfoView"); break; default: diff --git a/BOB/ViewModels/UpdateInfoViewModel.cs b/BOB/ViewModels/UpdateInfoViewModel.cs new file mode 100644 index 0000000..520b7c0 --- /dev/null +++ b/BOB/ViewModels/UpdateInfoViewModel.cs @@ -0,0 +1,46 @@ +using Model; +using Newtonsoft.Json; +using Prism.Mvvm; +using Prism.Ioc; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace BOB.ViewModels +{ + public class UpdateInfoViewModel : BindableBase + { + private List _updateList; + public List UpdateList + { + get => _updateList; + set => SetProperty(ref _updateList, value); + } + + public UpdateInfoViewModel(IContainerProvider containerProvider) + { + string path = Path.Combine(SystemConfig.Instance.SystemPath, "UpdateInfo.json"); + + if (File.Exists(path)) + { + try + { + string json = File.ReadAllText(path, Encoding.UTF8); + UpdateList = JsonConvert.DeserializeObject>(json) + ?? new List(); + } + catch (Exception ex) + { + UpdateList = new List(); + Console.WriteLine("读取 UpdateInfo.json 出错: " + ex.Message); + } + } + else + { + + UpdateList = new List(); + } + } + } +} diff --git a/BOB/Views/Dialogs/BackfeedView.xaml b/BOB/Views/Dialogs/BackfeedView.xaml index 0901294..903baea 100644 --- a/BOB/Views/Dialogs/BackfeedView.xaml +++ b/BOB/Views/Dialogs/BackfeedView.xaml @@ -9,6 +9,7 @@ xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" xmlns:prism="http://prismlibrary.com/" + xmlns:ucs="clr-namespace:BOB.Views.UserControls" Background="White" prism:ViewModelLocator.AutoWireViewModel="True" Height="850" @@ -27,7 +28,6 @@ @@ -35,7 +35,12 @@ - + + + @@ -43,7 +48,14 @@ - + + + + + + + diff --git a/BOB/Views/Dialogs/EAEL9080View.xaml b/BOB/Views/Dialogs/EAEL9080View.xaml index c7297fd..fd2ab80 100644 --- a/BOB/Views/Dialogs/EAEL9080View.xaml +++ b/BOB/Views/Dialogs/EAEL9080View.xaml @@ -50,7 +50,14 @@ - + + + + + + + diff --git a/BOB/Views/Dialogs/IOBoardView.xaml b/BOB/Views/Dialogs/IOBoardView.xaml index 2f8cc12..f40d5d0 100644 --- a/BOB/Views/Dialogs/IOBoardView.xaml +++ b/BOB/Views/Dialogs/IOBoardView.xaml @@ -15,8 +15,10 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProcessManager/MainWindow.xaml.cs b/ProcessManager/Views/MainWindow.xaml.cs similarity index 88% rename from ProcessManager/MainWindow.xaml.cs rename to ProcessManager/Views/MainWindow.xaml.cs index 7b7a9a0..05c0d65 100644 --- a/ProcessManager/MainWindow.xaml.cs +++ b/ProcessManager/Views/MainWindow.xaml.cs @@ -12,7 +12,7 @@ namespace ProcessManager /// /// Interaction logic for MainWindow.xaml /// - public partial class MainWindow : MetroWindow + public partial class MainWindow : Window { public MainWindow() diff --git a/ProcessManager/MainWindow.xaml b/ProcessManager/Views/MenuView.xaml similarity index 72% rename from ProcessManager/MainWindow.xaml rename to ProcessManager/Views/MenuView.xaml index bc49d4c..0ea526a 100644 --- a/ProcessManager/MainWindow.xaml +++ b/ProcessManager/Views/MenuView.xaml @@ -1,24 +1,23 @@ - + - - - + + - + RelativeSource={RelativeSource AncestorType=ContextMenu}}" /> + RelativeSource={RelativeSource AncestorType=ContextMenu}}" /> @@ -75,5 +74,5 @@ - - + + diff --git a/ProcessManager/Views/MenuView.xaml.cs b/ProcessManager/Views/MenuView.xaml.cs new file mode 100644 index 0000000..f32646d --- /dev/null +++ b/ProcessManager/Views/MenuView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ProcessManager +{ + /// + /// MenuView.xaml 的交互逻辑 + /// + public partial class MenuView : UserControl + { + public MenuView() + { + InitializeComponent(); + } + } +} diff --git a/ProcessManager/Setting.xaml b/ProcessManager/Views/Setting.xaml similarity index 99% rename from ProcessManager/Setting.xaml rename to ProcessManager/Views/Setting.xaml index 01831d6..a01bf0b 100644 --- a/ProcessManager/Setting.xaml +++ b/ProcessManager/Views/Setting.xaml @@ -1,6 +1,7 @@  + { + IntPtr hWnd = IntPtr.Zero; + int elapsed = 0; + + while (elapsed < timeoutMs) + { + hWnd = FindWindow(null, windowTitle); + + if (hWnd != IntPtr.Zero) + break; + + await Task.Delay(200); + elapsed += 200; + } + + if (hWnd == IntPtr.Zero) + { + Application.Current.Dispatcher.Invoke(() => + { + MessageBox.Show($"在 {timeoutMs / 1000.0}s 内未找到窗口:{windowTitle}"); + }); + return; + } + + Application.Current.Dispatcher.Invoke(() => + { + EmbedWindow(hWnd, panel); + }); + }); + } + + public static void EmbedWindow(IntPtr childHwnd, System.Windows.Forms.Panel panel) + { + IntPtr parentHwnd = panel.Handle; + + // 设定父窗口 + SetParent(childHwnd, parentHwnd); + + // 去掉标题栏 + long style = GetWindowLong(childHwnd, GWL_STYLE); + style &= ~WS_CAPTION; + style &= ~WS_THICKFRAME; + style |= WS_CHILD; + SetWindowLong(childHwnd, GWL_STYLE, style); + + ResizeEmbeddedWindow(childHwnd, panel); + + // 自动resize + panel.Resize -= (s, e) => ResizeEmbeddedWindow(childHwnd, panel); + panel.Resize += (s, e) => ResizeEmbeddedWindow(childHwnd, panel); + } + + public static void ResizeEmbeddedWindow(IntPtr hWnd, System.Windows.Forms.Panel panel) + { + MoveWindow(hWnd, 0, 0, panel.Width, panel.Height, true); + } + + [DllImport("user32.dll")] + public static extern IntPtr FindWindow(string? lpClassName, string? lpWindowName); + + [DllImport("user32.dll")] + public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int W, int H, bool repaint); + + [DllImport("user32.dll")] + public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + [DllImport("user32.dll")] + public static extern long GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong); + } + +}