进程管理ui优化

This commit is contained in:
hsc 2025-12-02 09:59:39 +08:00
parent 9264596831
commit ace97f14a6
64 changed files with 2505 additions and 728 deletions

View File

@ -51,6 +51,7 @@ namespace BOB
containerRegistry.RegisterForNavigation<MainView>("MainView"); containerRegistry.RegisterForNavigation<MainView>("MainView");
containerRegistry.RegisterForNavigation<MonitorView>("MonitorView"); containerRegistry.RegisterForNavigation<MonitorView>("MonitorView");
containerRegistry.RegisterForNavigation<Views.DataView>("DataView"); containerRegistry.RegisterForNavigation<Views.DataView>("DataView");
containerRegistry.RegisterForNavigation<UpdateInfoView>("UpdateInfoView");
//注册弹窗 //注册弹窗
containerRegistry.RegisterDialog<MessageBoxView, MessageBoxViewModel>("MessageBox"); containerRegistry.RegisterDialog<MessageBoxView, MessageBoxViewModel>("MessageBox");
containerRegistry.RegisterDialog<ParameterSetting, ParameterSettingViewModel>("ParameterSetting"); containerRegistry.RegisterDialog<ParameterSetting, ParameterSettingViewModel>("ParameterSetting");

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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<double>() { , , };
var dataDic = new Dictionary<string, double>
{
{ "实时电流", },
{ "实时电压", },
{ "实时功率", }
};
_eventAggregator.GetEvent<CurveDataEvent>().Publish(("Backfeed", dataDic));
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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<string, double>
{
{ "实时电流", },
{ "实时电压", },
{ "实时功率", }
};
_eventAggregator.GetEvent<CurveDataEvent>().Publish(("EAEL9080", dataDic));
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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<string, double>
{
{ "实时电流", },
{ "实时电压", },
{ "实时功率", }
};
_eventAggregator.GetEvent<CurveDataEvent>().Publish(("IT6724C", dataDic));
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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<string, double>
{
{ "内部传感器温度", },
{ "外部传感器温度", },
{ "水流量", }
};
_eventAggregator.GetEvent<CurveDataEvent>().Publish(("LQ7500_D", dataDic));
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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<double>() { , , };
var dataDic = new Dictionary<string, double>
{
{ "实时电流", },
{ "实时电压", },
{ "实时功率", }
};
_eventAggregator.GetEvent<CurveDataEvent>().Publish(("PSB11000", dataDic));
}
}
}

View File

@ -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<IEventAggregator>();
_eventAggregator.GetEvent<ConnectionChangeEvent>().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);
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
DeviceName = "SQ0030G1D";
_SQ0030G1D = _devices.DeviceDic["SQ0030G1D"] as SQ0030G1D;
}
public override async Task ReadDeviceDataAsync(CancellationToken ct = default)
{
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
DeviceName = "WS_68030_380T";
_WS_68030_380T = _devices.DeviceDic["WS_68030_380T"] as WS_68030_380T;
}
public override async Task ReadDeviceDataAsync(CancellationToken ct = default)
{
}
}
}

View File

@ -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<IEventAggregator>();
var _devices = containerProvider.Resolve<Devices>();
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)
{
}
}
}

View File

@ -1,4 +1,5 @@
using Castle.DynamicProxy; using BOB.Services;
using Castle.DynamicProxy;
using DeviceCommand.Base; using DeviceCommand.Base;
using DeviceCommand.Device; using DeviceCommand.Device;
using Logger; using Logger;
@ -13,10 +14,16 @@ namespace BOB.Singleton
{ {
public class Devices public class Devices
{ {
public Devices(IContainerProvider containerProvider)
{
_containerProvider=containerProvider;
}
private IContainerProvider _containerProvider;
private CancellationTokenSource _appCancellationTokenSource = new(); private CancellationTokenSource _appCancellationTokenSource = new();
public CancellationToken AppCancellationToken => _appCancellationTokenSource.Token; public CancellationToken AppCancellationToken => _appCancellationTokenSource.Token;
public Dictionary<string, object> DeviceDic { get; private set; } = new Dictionary<string, object>(); public Dictionary<string, object> DeviceDic { get; private set; } = new Dictionary<string, object>();
public Dictionary<string, PollingRead> PollingReadDic { get; private set; } = new Dictionary<string, PollingRead>();
private readonly ProxyGenerator _proxyGen = new ProxyGenerator(); private readonly ProxyGenerator _proxyGen = new ProxyGenerator();
private readonly IInterceptor _loggingInterceptor = new LoggingInterceptor(); private readonly IInterceptor _loggingInterceptor = new LoggingInterceptor();
@ -31,15 +38,11 @@ namespace BOB.Singleton
private IModbusDevice ZXKSTDevice { get; set; } private IModbusDevice ZXKSTDevice { get; set; }
private Backfeed BackfeedDevice { get; set; } private Backfeed BackfeedDevice { get; set; }
public Devices()
{
}
public void Init(List<DeviceConfigModel> deviceList) public void Init(List<DeviceConfigModel> deviceList)
{ {
foreach (var device in deviceList) foreach (var device in deviceList)
{ {
if (!device.IsEnabled) continue; if (!device.IsEnabled) continue;
switch (device.DeviceType) switch (device.DeviceType)
{ {
case "DeviceCommand.Device.E36233A": 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() public void Dispose()
{ {

View File

@ -1,7 +1,8 @@
using BOB.Models; using BOB.Models;
using Logger; using Logger;
using Newtonsoft.Json;
using Model; using Model;
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -42,6 +43,7 @@ namespace BOB
public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\"; public string SubProgramFilePath { get; set; } = @"D:\BOB\子程序\";
public string DefaultSubProgramFilePath { get; set; } = ""; public string DefaultSubProgramFilePath { get; set; } = "";
public ObservableCollection<string> CurSingleList { get; set; }
public List<DeviceConfigModel> DeviceList { get; set; } = new(); public List<DeviceConfigModel> DeviceList { get; set; } = new();
#region #region

View File

@ -1,159 +0,0 @@
using DeviceCommand.Base;
using DeviceCommand.Device;
using Logger;
namespace BOB.Tasks
{
public class HeartbeatManager<T> 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);
}
}
}
}

View File

@ -1,6 +1,12 @@
using BOB.Singleton; using BOB.Singleton;
using BOB.ViewModels.UserControls;
using Common.PubEvent;
using DeviceCommand.Device; using DeviceCommand.Device;
using Logger; using Logger;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using Prism.Commands; using Prism.Commands;
using Prism.Events; using Prism.Events;
using Prism.Mvvm; using Prism.Mvvm;
@ -62,7 +68,54 @@ namespace BOB.ViewModels.Dialogs
get { return _OPP功率; } get { return _OPP功率; }
set { SetProperty(ref _OPP功率, value); } 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 #endregion
#region #region
@ -82,6 +135,7 @@ namespace BOB.ViewModels.Dialogs
public ICommand DisableOPPCommand { get; private set; } public ICommand DisableOPPCommand { get; private set; }
public ICommand SetOPPCommand { get; private set; } public ICommand SetOPPCommand { get; private set; }
public ICommand CloseCommand { get; private set; } public ICommand CloseCommand { get; private set; }
public ICommand ResetViewCommand { get; private set; }
#endregion #endregion
@ -113,6 +167,55 @@ namespace BOB.ViewModels.Dialogs
DisableOPPCommand = new AsyncDelegateCommand(OnDisableOPP); DisableOPPCommand = new AsyncDelegateCommand(OnDisableOPP);
SetOPPCommand = new AsyncDelegateCommand(OnSetOPP); SetOPPCommand = new AsyncDelegateCommand(OnSetOPP);
CloseCommand = new DelegateCommand(OnClose); CloseCommand = new DelegateCommand(OnClose);
ResetViewCommand = new DelegateCommand(ResetView);
_eventAggregator.GetEvent<CurveDataEvent>().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<string, double> 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);
} }

View File

@ -1,13 +1,19 @@
using BOB.Singleton; 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.Commands;
using Prism.Mvvm;
using Prism.Events; using Prism.Events;
using Prism.Mvvm;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using DeviceCommand.Device;
using Logger;
using BOB.ViewModels.UserControls;
namespace BOB.ViewModels.Dialogs namespace BOB.ViewModels.Dialogs
{ {
@ -17,12 +23,31 @@ namespace BOB.ViewModels.Dialogs
private string _Title = "低压直流负载"; private string _Title = "低压直流负载";
private string _设备标识字符串; private string _设备标识字符串;
private string _负载模式; private string _负载模式;
private string _电流LA; private LineSeries _电压_lineSeries = new LineSeries
private string _电流LB; {
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 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 public string Title
{ {
get { return _Title; } get { return _Title; }
@ -41,16 +66,21 @@ namespace BOB.ViewModels.Dialogs
set { SetProperty(ref _负载模式, value); } set { SetProperty(ref _负载模式, value); }
} }
public string LA public LineSeries _lineSeries
{ {
get { return _电流LA; } get { return _电流_lineSeries; }
set { SetProperty(ref _电流LA, value); } set { SetProperty(ref _电流_lineSeries, value); }
} }
public string LB public LineSeries _lineSeries
{ {
get { return _电流LB; } get { return _电压_lineSeries; }
set { SetProperty(ref _电流LB, value); } set { SetProperty(ref _电压_lineSeries, value); }
}
public LineSeries _lineSeries
{
get { return _功率_lineSeries; }
set { SetProperty(ref _功率_lineSeries, value); }
} }
public NumericalDisplayViewModel MV public NumericalDisplayViewModel MV
{ {
@ -67,6 +97,8 @@ namespace BOB.ViewModels.Dialogs
get { return _功率MV; } get { return _功率MV; }
set { SetProperty(ref _功率MV, value); } set { SetProperty(ref _功率MV, value); }
} }
public DateTimeAxis TimeAxis { get; private set; }
public LinearAxis ValueAxis { get; private set; }
#endregion #endregion
#region #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 { get; private set; }
public ICommand { get; private set; } public ICommand { get; private set; }
public ICommand ResetViewCommand { get; private set; }
#endregion #endregion
@ -99,8 +132,56 @@ namespace BOB.ViewModels.Dialogs
= new AsyncDelegateCommand(OnPowerOff); = new AsyncDelegateCommand(OnPowerOff);
= new DelegateCommand(OnOff); = new DelegateCommand(OnOff);
= new AsyncDelegateCommand(OnQueryDeviceInfo); = new AsyncDelegateCommand(OnQueryDeviceInfo);
ResetViewCommand = new DelegateCommand(ResetView);
_eventAggregator.GetEvent<CurveDataEvent>().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<string,double> 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() private async Task OnQueryDeviceInfo()
{ {
= await device.(); = await device.();

View File

@ -1,6 +1,13 @@
using BOB.Singleton; using BOB.Singleton;
using BOB.ViewModels.UserControls;
using Common.PubEvent;
using DeviceCommand.Device; using DeviceCommand.Device;
using Logger; using Logger;
using OxyPlot;
using OxyPlot.Annotations;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -12,52 +19,97 @@ namespace BOB.ViewModels.Dialogs
{ {
public class IT6724CViewModel : BindableBase, IDialogAware public class IT6724CViewModel : BindableBase, IDialogAware
{ {
#region #region
private string _title = "低压直流电源"; private string _title = "低压直流电源";
public string Title public string Title
{ {
get { return _title; } get => _title;
set { SetProperty(ref _title, value); } set => SetProperty(ref _title, value);
} }
private string _设备标识字符串; private string _设备标识字符串;
public string public string
{ {
get { return _设备标识字符串; } get => _设备标识字符串;
set { SetProperty(ref _设备标识字符串, value); } set => SetProperty(ref _设备标识字符串, value);
} }
private string _输出电压; private string _输出电压;
public string public string
{ {
get { return _输出电压; } get => _输出电压;
set { SetProperty(ref _输出电压, value); } set => SetProperty(ref _输出电压, value);
} }
private string _输出电流; private string _输出电流;
public string public string
{ {
get { return _输出电流; } get => _输出电流;
set { SetProperty(ref _输出电流, value); } set => SetProperty(ref _输出电流, value);
} }
private string _OCP电流; private string _OCP电流;
public string OCP电流 public string OCP电流
{ {
get { return _OCP电流; } get => _OCP电流;
set { SetProperty(ref _OCP电流, value); } set => SetProperty(ref _OCP电流, value);
} }
private string _OVP电压; private string _OVP电压;
public string OVP电压 public string OVP电压
{ {
get { return _OVP电压; } get => _OVP电压;
set { SetProperty(ref _OVP电压, value); } 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 #endregion
#region #region
public ICommand ConnectCommand { get; private set; } public ICommand ConnectCommand { get; private set; }
public ICommand DisconnectCommand { 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 ClearOVPAlarmCommand { get; private set; }
public ICommand SetOVPCommand { get; private set; } public ICommand SetOVPCommand { get; private set; }
public ICommand CloseCommand { get; private set; } public ICommand CloseCommand { get; private set; }
public ICommand ResetViewCommand { get; private set; }
#endregion #endregion
private IEventAggregator _eventAggregator { get; set; } private IEventAggregator _eventAggregator { get; set; }
@ -104,8 +157,56 @@ namespace BOB.ViewModels.Dialogs
_devices = containerProvider.Resolve<Devices>(); _devices = containerProvider.Resolve<Devices>();
_globalVariables = containerProvider.Resolve<GlobalVariables>(); _globalVariables = containerProvider.Resolve<GlobalVariables>();
_eventAggregator = containerProvider.Resolve<IEventAggregator>(); _eventAggregator = containerProvider.Resolve<IEventAggregator>();
ResetViewCommand = new DelegateCommand(ResetView);
_eventAggregator.GetEvent<CurveDataEvent>().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<string, double> 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 #region
private void OnClose() private void OnClose()
{ {

View File

@ -1,6 +1,12 @@
using BOB.Singleton; using BOB.Singleton;
using BOB.ViewModels.UserControls;
using Common.PubEvent;
using DeviceCommand.Device; using DeviceCommand.Device;
using Logger; using Logger;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -48,7 +54,54 @@ namespace BOB.ViewModels.Dialogs
get { return _OCP电流; } get { return _OCP电流; }
set { SetProperty(ref _OCP电流, value); } 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 #endregion
#region #region
@ -66,6 +119,7 @@ namespace BOB.ViewModels.Dialogs
public ICommand SetCCCurrentCommand { get; private set; } public ICommand SetCCCurrentCommand { get; private set; }
public ICommand SetOCPCurrentCommand { get; private set; } public ICommand SetOCPCurrentCommand { get; private set; }
public ICommand CloseCommand { get; private set; } public ICommand CloseCommand { get; private set; }
public ICommand ResetViewCommand { get; private set; }
#endregion #endregion
private IEventAggregator _eventAggregator { get; set; } private IEventAggregator _eventAggregator { get; set; }
@ -90,7 +144,55 @@ namespace BOB.ViewModels.Dialogs
SetCCCurrentCommand = new AsyncDelegateCommand(OnSetCCCurrent); SetCCCurrentCommand = new AsyncDelegateCommand(OnSetCCCurrent);
SetOCPCurrentCommand = new AsyncDelegateCommand(OnSetOCPCurrent); SetOCPCurrentCommand = new AsyncDelegateCommand(OnSetOCPCurrent);
CloseCommand = new DelegateCommand(OnClose); CloseCommand = new DelegateCommand(OnClose);
ResetViewCommand = new DelegateCommand(ResetView);
_eventAggregator.GetEvent<CurveDataEvent>().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<string, double> 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 #region Command Handlers
private void OnClose() private void OnClose()

View File

@ -44,6 +44,7 @@ namespace BOB.ViewModels
{ {
_devices.Init(SystemConfig.Instance.DeviceList); _devices.Init(SystemConfig.Instance.DeviceList);
await _devices.ConnectAllDevicesAsync(); await _devices.ConnectAllDevicesAsync();
_devices.StartPollingCollectionAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -51,7 +52,7 @@ namespace BOB.ViewModels
} }
finally finally
{ {
_eventAggregator.GetEvent<WaitingEvent>().Publish(false); _eventAggregator.GetEvent<OverlayEvent>().Publish(false);
} }
} }
#region #region
@ -61,7 +62,7 @@ namespace BOB.ViewModels
{ {
if (!_isInitialized) if (!_isInitialized)
{ {
_eventAggregator.GetEvent<WaitingEvent>().Publish(true); _eventAggregator.GetEvent<OverlayEvent>().Publish(true);
_ = InitializeDevicesAsync(); _ = InitializeDevicesAsync();
_isInitialized = true; _isInitialized = true;
} }

View File

@ -1,8 +1,11 @@
using BOB.Singleton; using BOB.Services;
using BOB.Singleton;
using Common.PubEvent;
using Common.PubEvents; using Common.PubEvents;
using Logger; using Logger;
using Microsoft.Win32; using Microsoft.Win32;
using OxyPlot; using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends; using OxyPlot.Legends;
using OxyPlot.Series; using OxyPlot.Series;
using Prism.Dialogs; using Prism.Dialogs;
@ -10,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
@ -27,13 +31,6 @@ namespace BOB.ViewModels
get => _CurveModel; get => _CurveModel;
set => SetProperty(ref _CurveModel, value); set => SetProperty(ref _CurveModel, value);
} }
private List<LineSeries> _signalSeries = new List<LineSeries>();
public List<LineSeries> SignalSeries
{
get => _signalSeries;
set => SetProperty(ref _signalSeries, value);
}
private Dictionary<string, LineSeries> _SeriesDic = new (); private Dictionary<string, LineSeries> _SeriesDic = new ();
public Dictionary<string,LineSeries> SeriesDic public Dictionary<string,LineSeries> SeriesDic
@ -69,12 +66,19 @@ namespace BOB.ViewModels
get => _SelectedSaveDays; get => _SelectedSaveDays;
set => SetProperty(ref _SelectedSaveDays, value); set => SetProperty(ref _SelectedSaveDays, value);
} }
private ObservableCollection<string> _CurSingleList=new(); //private ObservableCollection<string> _CurSingleList=new();
public ObservableCollection<string> CurSingleList public ObservableCollection<string> CurSingleList
{ {
get => _CurSingleList; get => SystemConfig.Instance.CurSingleList;
set => SetProperty(ref _CurSingleList, value); set
{
if (SystemConfig.Instance.CurSingleList != value)
{
SystemConfig.Instance.CurSingleList = value;
RaisePropertyChanged();
}
}
} }
private ObservableCollection<string> _DeviceSingleList = new(); private ObservableCollection<string> _DeviceSingleList = new();
@ -83,8 +87,6 @@ namespace BOB.ViewModels
get => _DeviceSingleList; get => _DeviceSingleList;
set => SetProperty(ref _DeviceSingleList, value); set => SetProperty(ref _DeviceSingleList, value);
} }
#endregion #endregion
#region #region
public ICommand ResetViewCommand { get; set; } public ICommand ResetViewCommand { get; set; }
@ -97,135 +99,113 @@ namespace BOB.ViewModels
private IEventAggregator _eventAggregator; private IEventAggregator _eventAggregator;
private IDialogService _dialogService; private IDialogService _dialogService;
private static Random _random = new Random(); private static Random _random = new Random();
private DispatcherTimer _refreshTimer;
private Devices _devices { get; set; } private Devices _devices { get; set; }
private readonly IProgress<(string name, double value)> _progress;
public MonitorViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IContainerProvider containerProvider ) public MonitorViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IContainerProvider containerProvider )
{ {
_dialogService = dialogService; _dialogService = dialogService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_devices = containerProvider.Resolve<Devices>(); _devices = containerProvider.Resolve<Devices>();
ResetViewCommand = new DelegateCommand(ResetView); ResetViewCommand = new DelegateCommand(ResetView);
AddSignalCommand = new DelegateCommand(AddSignal); AddSignalCommand = new DelegateCommand<string>(AddSignal);
RemoveSignalCommand = new DelegateCommand(RemoveSignal); RemoveSignalCommand = new DelegateCommand(RemoveSignal);
ChanegPathCommand = new DelegateCommand(ChanegPath); ChanegPathCommand = new DelegateCommand(ChanegPath);
SaveCommand = new DelegateCommand(Save); SaveCommand = new DelegateCommand(Save);
_eventAggregator.GetEvent<CurveDataEvent>().Subscribe(OnCurveDataReceived);
_progress = new Progress<(string name, double value)>(UpdateCurveOnUI);
InitData(); 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<string, double> 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() private void InitData()
{ {
SelectedSaveDays=SystemConfig.Instance.SaveDay.ToString(); SelectedSaveDays=SystemConfig.Instance.SaveDay.ToString();
FilePath= SystemConfig.Instance.DBSavePath; FilePath= SystemConfig.Instance.DBSavePath;
} foreach (var _PollingRead in _devices.PollingReadDic)
#region
private DispatcherTimer _timer;
private double _time = 0;
private void InitializeSimulatedSignals()
{
CurveModel.Legends.Add(new Legend
{ {
LegendPosition= LegendPosition.TopLeft, string deviceName = _PollingRead.Key;
IsLegendVisible=true PollingRead pollingRead = _PollingRead.Value;
});
// 模拟四个信号的初始化
// 模拟量1正弦波 var props = pollingRead.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
var sineSignal = new LineSeries .Where(p => p.PropertyType == typeof(double));
{
Title = "正弦波信号",
MarkerType = MarkerType.Circle,
StrokeThickness = 2,
Color = OxyColors.Red
};
_signalSeries.Add(sineSignal);
SeriesDic.Add(sineSignal.Title, sineSignal);
// 模拟量2线性增长 foreach (var prop in props)
var linearSignal = new LineSeries {
{ DeviceSingleList.Add(deviceName + "." + prop.Name);
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 curve in CurSingleList)
{
AddSignal(curve);
}
InitCurveModel();
InitTimer();
} }
private void UpdateSimulatedData() private void InitCurveModel()
{ {
// 更新模拟量的值并添加新的数据点 CurveModel.Axes.Clear();
// 模拟量1正弦波 var timeAxis = new DateTimeAxis
var sineSignal = _signalSeries[0]; {
sineSignal.Points.Add(new DataPoint(_time, Math.Sin(_time))); Position = AxisPosition.Bottom,
if (sineSignal.Points.Count > 1000) sineSignal.Points.RemoveAt(0); // 保持最多100个点 StringFormat = "HH:mm:ss",
Title = "时间",
IntervalType = DateTimeIntervalType.Seconds
};
CurveModel.Axes.Add(timeAxis);
// 模拟量2线性增长 var valueAxis = new LinearAxis
var linearSignal = _signalSeries[1]; {
linearSignal.Points.Add(new DataPoint(_time, _time)); Position = AxisPosition.Left,
if (linearSignal.Points.Count > 1000) linearSignal.Points.RemoveAt(0); // 保持最多100个点 Title = "数值"
};
// 模拟量3随机波动 CurveModel.Axes.Add(valueAxis);
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秒
} }
#endregion
private void InitTimer()
{
_refreshTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_refreshTimer.Tick += (s, e) =>
{
CurveModel.InvalidatePlot(true);
};
_refreshTimer.Start();
}
private OxyColor GetRandomColor() private OxyColor GetRandomColor()
{ {
// 生成随机的红色、绿色、蓝色分量,范围是 0 到 255 // 生成随机的红色、绿色、蓝色分量,范围是 0 到 255
@ -237,28 +217,62 @@ namespace BOB.ViewModels
return OxyColor.FromRgb(r, g, b); return OxyColor.FromRgb(r, g, b);
} }
private void AddSignal() private void AddSignal(string Curve="")
{ {
try 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 else
{ {
// 开启资源
var sineSignal = new LineSeries var sineSignal = new LineSeries
{ {
Title = SelectCurve, Title = Curve,
MarkerType = MarkerType.Circle, MarkerType = MarkerType.Circle,
StrokeThickness = 2, StrokeThickness = 2,
Color = GetRandomColor() Color = GetRandomColor()
}; };
CurveModel.Series.Add(sineSignal); CurveModel.Series.Add(sineSignal);
SeriesDic.Add(SelectCurve, sineSignal); CurveModel.Legends.Add(new Legend
CurSingleList.Add(SelectCurve); {
LegendPosition = LegendPosition.TopCenter, // 图表顶部中间
LegendOrientation = LegendOrientation.Horizontal, // 横向排列
LegendPlacement = LegendPlacement.Inside, // 放在图上面
LegendBorderThickness = 0, // 去掉边框,可选
LegendBackground = OxyColors.WhiteSmoke // 背景色,可选
});
SeriesDic.Add(Curve, sineSignal);
CurveModel.InvalidatePlot(true); CurveModel.InvalidatePlot(true);
} }
} }
@ -277,9 +291,8 @@ namespace BOB.ViewModels
_SeriesDic.Remove(SelectDeleteCurve); _SeriesDic.Remove(SelectDeleteCurve);
CurveModel.Series.Remove(signalToRemove); CurveModel.Series.Remove(signalToRemove);
CurSingleList.Remove(SelectDeleteCurve); CurSingleList.Remove(SelectDeleteCurve);
SystemConfig.Instance.SaveToFile();
CurveModel.InvalidatePlot(true); CurveModel.InvalidatePlot(true);
// 释放资源
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -176,6 +176,7 @@ namespace BOB.ViewModels
} }
break; break;
} }
_eventAggregator.GetEvent<ConnectionChangeEvent>().Publish((DeviceList[i].Remark, isConnected));
} }
progress.Report((i, isConnected)); progress.Report((i, isConnected));

View File

@ -145,7 +145,7 @@ namespace BOB.ViewModels
break; break;
case "更新信息": case "更新信息":
_regionManager.RequestNavigate("ShellViewManager", "UpdateInfoView");
break; break;
default: default:

View File

@ -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<UpdateInfoModel> _updateList;
public List<UpdateInfoModel> 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<List<UpdateInfoModel>>(json)
?? new List<UpdateInfoModel>();
}
catch (Exception ex)
{
UpdateList = new List<UpdateInfoModel>();
Console.WriteLine("读取 UpdateInfo.json 出错: " + ex.Message);
}
}
else
{
UpdateList = new List<UpdateInfoModel>();
}
}
}
}

View File

@ -9,6 +9,7 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:ucs="clr-namespace:BOB.Views.UserControls"
Background="White" Background="White"
prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.AutoWireViewModel="True"
Height="850" Height="850"
@ -27,7 +28,6 @@
<!-- 实时监控 --> <!-- 实时监控 -->
<GroupBox x:Name="实时监控区" <GroupBox x:Name="实时监控区"
Header="实时监控" Header="实时监控"
Grid.Column="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<Grid Grid.Row="0"> <Grid Grid.Row="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -35,7 +35,12 @@
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition /> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ucs:NumericalDisplay DataContext="{Binding 电压MV,Mode=TwoWay}"
Grid.Column="0" />
<ucs:NumericalDisplay DataContext="{Binding 电流MV,Mode=TwoWay}"
Grid.Column="1" />
<ucs:NumericalDisplay DataContext="{Binding 功率MV,Mode=TwoWay}"
Grid.Column="2" />
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -43,7 +48,14 @@
<GroupBox Header="实时曲线" <GroupBox Header="实时曲线"
Grid.Row="1" Grid.Row="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<oxy:PlotView Model="{Binding 曲线Model}" /> <oxy:PlotView Model="{Binding 曲线Model}">
<oxy:PlotView.ContextMenu>
<ContextMenu>
<MenuItem Header="视图复位"
Command="{Binding ResetViewCommand}" />
</ContextMenu>
</oxy:PlotView.ContextMenu>
</oxy:PlotView>
</GroupBox> </GroupBox>
<!-- 调试选项 --> <!-- 调试选项 -->

View File

@ -50,7 +50,14 @@
<GroupBox Header="实时曲线" <GroupBox Header="实时曲线"
Grid.Row="1" Grid.Row="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<oxy:PlotView Model="{Binding 曲线Model}" /> <oxy:PlotView Model="{Binding 曲线Model}">
<oxy:PlotView.ContextMenu>
<ContextMenu>
<MenuItem Header="视图复位"
Command="{Binding ResetViewCommand}" />
</ContextMenu>
</oxy:PlotView.ContextMenu>
</oxy:PlotView>
</GroupBox> </GroupBox>
<!-- 调试选项 --> <!-- 调试选项 -->

View File

@ -15,8 +15,10 @@
<Style BasedOn="{StaticResource DialogUserManageStyle}" <Style BasedOn="{StaticResource DialogUserManageStyle}"
TargetType="Window" /> TargetType="Window" />
</prism:Dialog.WindowStyle> </prism:Dialog.WindowStyle>
<Grid MouseLeftButtonDown="MouseLeftButtonDown"> <GroupBox Header="IO板卡手动设置"
<TreeView Margin="10"> Padding="0,0,0,0">
<Grid MouseLeftButtonDown="MouseLeftButtonDown">
<TreeView Margin="10">
<TreeViewItem Header="连接状态设置"> <TreeViewItem Header="连接状态设置">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="连接" <Button Content="连接"
@ -74,13 +76,14 @@
</TreeViewItem> </TreeViewItem>
</TreeViewItem> </TreeViewItem>
</TreeView> </TreeView>
<StackPanel Grid.Row="2" <StackPanel Grid.Row="2"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
<Button Content="关闭" <Button Content="关闭"
Width="70" Width="70"
Margin="10" Margin="10"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CloseCommand}" /> Command="{Binding CloseCommand}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</GroupBox>
</UserControl> </UserControl>

View File

@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BOB.Views.Dialogs" xmlns:local="clr-namespace:BOB.Views.Dialogs"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ucs="clr-namespace:BOB.Views.UserControls"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
@ -26,15 +27,34 @@
<GroupBox x:Name="实时监控区" <GroupBox x:Name="实时监控区"
Header="实时监控" Header="实时监控"
Grid.Row="0"
Padding="0,0,0,0"> Padding="0,0,0,0">
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ucs:NumericalDisplay DataContext="{Binding 电压MV,Mode=TwoWay}"
Grid.Column="0" />
<ucs:NumericalDisplay DataContext="{Binding 电流MV,Mode=TwoWay}"
Grid.Column="1" />
<ucs:NumericalDisplay DataContext="{Binding 功率MV,Mode=TwoWay}"
Grid.Column="2" />
</Grid>
</GroupBox> </GroupBox>
<!-- 实时曲线 -->
<GroupBox Header="实时曲线" <GroupBox Header="实时曲线"
Grid.Row="1" Grid.Row="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<oxy:PlotView Model="{Binding 曲线Model}" /> <oxy:PlotView Model="{Binding 曲线Model}">
<oxy:PlotView.ContextMenu>
<ContextMenu>
<MenuItem Header="视图复位"
Command="{Binding ResetViewCommand}" />
</ContextMenu>
</oxy:PlotView.ContextMenu>
</oxy:PlotView>
</GroupBox> </GroupBox>
<GroupBox Header="调试选项" <GroupBox Header="调试选项"

View File

@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BOB.Views.Dialogs" xmlns:local="clr-namespace:BOB.Views.Dialogs"
xmlns:oxy="http://oxyplot.org/wpf" xmlns:oxy="http://oxyplot.org/wpf"
xmlns:ucs="clr-namespace:BOB.Views.UserControls"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d" mc:Ignorable="d"
@ -26,15 +27,33 @@
<GroupBox x:Name="实时监控区" <GroupBox x:Name="实时监控区"
Header="实时监控" Header="实时监控"
Grid.Column="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ucs:NumericalDisplay DataContext="{Binding 电压MV,Mode=TwoWay}"
Grid.Column="0" />
<ucs:NumericalDisplay DataContext="{Binding 电流MV,Mode=TwoWay}"
Grid.Column="1" />
<ucs:NumericalDisplay DataContext="{Binding 功率MV,Mode=TwoWay}"
Grid.Column="2" />
</Grid>
</GroupBox> </GroupBox>
<GroupBox Header="实时曲线" <GroupBox Header="实时曲线"
Grid.Row="1" Grid.Row="1"
Padding="0,0,0,0"> Padding="0,0,0,0">
<oxy:PlotView Model="{Binding 曲线Model}" /> <oxy:PlotView Model="{Binding 曲线Model}">
<oxy:PlotView.ContextMenu>
<ContextMenu>
<MenuItem Header="视图复位"
Command="{Binding ResetViewCommand}" />
</ContextMenu>
</oxy:PlotView.ContextMenu>
</oxy:PlotView>
</GroupBox> </GroupBox>
<GroupBox Header="调试选项" <GroupBox Header="调试选项"

View File

@ -16,52 +16,102 @@
<Style BasedOn="{StaticResource DialogUserManageStyle}" <Style BasedOn="{StaticResource DialogUserManageStyle}"
TargetType="Window" /> TargetType="Window" />
</prism:Dialog.WindowStyle> </prism:Dialog.WindowStyle>
<Grid MouseLeftButtonDown="MouseLeftButtonDown"> <GroupBox Header="AC源手动设置"
<TreeView Margin="10"> Padding="0,0,0,0">
<TreeViewItem Header="连接状态设置"> <Grid MouseLeftButtonDown="MouseLeftButtonDown">
<StackPanel Orientation="Horizontal"> <TreeView Margin="10">
<Button Content="连接" <TreeViewItem Header="连接状态设置">
Margin="5" <StackPanel Orientation="Horizontal">
Command="{Binding ConnectCommand}" /> <Button Content="连接"
<Button Content="断开" Margin="5"
Margin="5" Command="{Binding ConnectCommand}" />
Command="{Binding DisconnectCommand}" /> <Button Content="断开"
</StackPanel> Margin="5"
</TreeViewItem> Command="{Binding DisconnectCommand}" />
<TreeViewItem Header="电压开关设置"> </StackPanel>
<StackPanel Orientation="Horizontal"> </TreeViewItem>
<Button Content="开启" <TreeViewItem Header="电压开关设置">
Margin="5" <StackPanel Orientation="Horizontal">
Command="{Binding OpenPowerSupplyCommand}" /> <Button Content="开启"
<Button Content="关闭" Margin="5"
Margin="5" Command="{Binding OpenPowerSupplyCommand}" />
Command="{Binding ClosePowerSupplyCommand}" /> <Button Content="关闭"
</StackPanel> Margin="5"
</TreeViewItem> Command="{Binding ClosePowerSupplyCommand}" />
</StackPanel>
</TreeViewItem>
<TreeViewItem Header="设置"> <TreeViewItem Header="设置">
<TreeViewItem Header="设定三相电压输出"> <TreeViewItem Header="设定三相电压输出">
<TreeViewItem Header="设定三相电压档位"> <TreeViewItem Header="设定三相电压档位">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="低档" <Button Content="低档"
Margin="5" Margin="5"
Command="{Binding SetVoltageLevelLowCommand}" /> Command="{Binding SetVoltageLevelLowCommand}" />
<Button Content="高档" <Button Content="高档"
Margin="5" Margin="5"
Command="{Binding SetVoltageLevelHighCommand}" /> Command="{Binding SetVoltageLevelHighCommand}" />
</StackPanel> </StackPanel>
</TreeViewItem>
<TreeViewItem Header="输出">
<StackPanel Orientation="Vertical">
<Button Content="设定三相输出电压"
Margin="5"
Command="{Binding SetThreePhaseVoltageCommand}" />
<StackPanel Orientation="Horizontal">
<Label Content="U相输出电压"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding UPhaseVoltage}"
MinWidth="50"
materialDesign:HintAssist.Hint=""
Margin="5"
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="V相输出电压"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding VPhaseVoltage}"
MinWidth="50"
materialDesign:HintAssist.Hint=""
Margin="5"
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="W相输出电压"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding WPhaseVoltage}"
MinWidth="50"
materialDesign:HintAssist.Hint=""
Margin="5"
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
</StackPanel>
</TreeViewItem>
</TreeViewItem> </TreeViewItem>
<TreeViewItem Header="输出"> <TreeViewItem Header="设定通用功能输出">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<Button Content="设定三相输出电压"
Margin="5"
Command="{Binding SetThreePhaseVoltageCommand}" />
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Label Content="U相输出电压" <Button Content="设定通用功能输出电压"
Margin="5"
Command="{Binding SetCommonOutputVoltageCommand}" />
<Label Content="输出电压"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBox Text="{Binding UPhaseVoltage}" <TextBox Text="{Binding CommonOutputVoltage}"
MinWidth="50" MinWidth="50"
materialDesign:HintAssist.Hint="" materialDesign:HintAssist.Hint=""
Margin="5" Margin="5"
@ -71,134 +121,87 @@
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Label Content="V相输出电压" <Button Content="设定通用功能输出频率"
Margin="5"
Command="{Binding SetCommonOutputFrequencyCommand}" />
<Label Content="输出频率"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBox Text="{Binding VPhaseVoltage}" <TextBox Text="{Binding CommonOutputFrequency}"
MinWidth="50" MinWidth="50"
materialDesign:HintAssist.Hint=""
Margin="5" Margin="5"
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="W相输出电压"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding WPhaseVoltage}"
MinWidth="50"
materialDesign:HintAssist.Hint="" materialDesign:HintAssist.Hint=""
Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<Label Content="V" <Label Content="Hz"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</TreeViewItem> </TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="设定通用功能输出"> <TreeViewItem Header="设定步阶功能输出">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="设定通用功能输出电压" <Button Content="设定步阶功能输出电压"
Margin="5" Margin="5"
Command="{Binding SetCommonOutputVoltageCommand}" /> Command="{Binding SetStepOutputVoltageCommand}" />
<Label Content="输出电压" <Label Content="组号"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBox Text="{Binding CommonOutputVoltage}" <TextBox Text="{Binding StepOutputVoltageGroup}"
MinWidth="50" MinWidth="50"
materialDesign:HintAssist.Hint="" Margin="5"
Margin="5" materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<Label Content="V" <Label Content="输出电压"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputVoltage}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="设定步阶功能输出频率"
Margin="5"
Command="{Binding SetStepOutputFrequencyCommand}" />
<Label Content="组号"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputFrequencyGroup}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="输出频率"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputFrequency}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="Hz"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> </TreeViewItem>
<Button Content="设定通用功能输出频率"
Margin="5"
Command="{Binding SetCommonOutputFrequencyCommand}" />
<Label Content="输出频率"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding CommonOutputFrequency}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="Hz"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
</StackPanel>
</TreeViewItem>
<TreeViewItem Header="设定步阶功能输出">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Content="设定步阶功能输出电压"
Margin="5"
Command="{Binding SetStepOutputVoltageCommand}" />
<Label Content="组号"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputVoltageGroup}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="输出电压"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputVoltage}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="V"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="设定步阶功能输出频率"
Margin="5"
Command="{Binding SetStepOutputFrequencyCommand}" />
<Label Content="组号"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputFrequencyGroup}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="输出频率"
Margin="5"
VerticalContentAlignment="Center" />
<TextBox Text="{Binding StepOutputFrequency}"
MinWidth="50"
Margin="5"
materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" />
<Label Content="Hz"
Margin="5"
VerticalContentAlignment="Center" />
</StackPanel>
</StackPanel>
</TreeViewItem> </TreeViewItem>
</TreeView>
</TreeViewItem> <StackPanel Grid.Row="2"
</TreeView> VerticalAlignment="Bottom">
<StackPanel Grid.Row="2" <Button Content="关闭"
VerticalAlignment="Bottom"> Width="70"
<Button Content="关闭" Margin="10"
Width="70" HorizontalAlignment="Right"
Margin="10" Command="{Binding CloseCommand}" />
HorizontalAlignment="Right" </StackPanel>
Command="{Binding CloseCommand}" /> </Grid>
</StackPanel> </GroupBox>
</Grid>
</UserControl> </UserControl>

View File

@ -16,56 +16,59 @@
<Style BasedOn="{StaticResource DialogUserManageStyle}" <Style BasedOn="{StaticResource DialogUserManageStyle}"
TargetType="Window" /> TargetType="Window" />
</prism:Dialog.WindowStyle> </prism:Dialog.WindowStyle>
<Grid MouseLeftButtonDown="MouseLeftButtonDown"> <GroupBox Header="AC载手动设置"
<TreeView Margin="10"> Padding="0,0,0,0">
<TreeViewItem Header="连接状态设置"> <Grid MouseLeftButtonDown="MouseLeftButtonDown">
<StackPanel Orientation="Horizontal"> <TreeView Margin="10">
<Button Content="连接" <TreeViewItem Header="连接状态设置">
Margin="5" <StackPanel Orientation="Horizontal">
Command="{Binding ConnectCommand}" /> <Button Content="连接"
<Button Content="断开" Margin="5"
Margin="5" Command="{Binding ConnectCommand}" />
Command="{Binding DisconnectCommand}" /> <Button Content="断开"
</StackPanel> Margin="5"
</TreeViewItem> Command="{Binding DisconnectCommand}" />
</StackPanel>
</TreeViewItem>
<TreeViewItem Header="上下载设置"> <TreeViewItem Header="上下载设置">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="加载" <Button Content="加载"
Margin="5" Margin="5"
Command="{Binding LoadCommand}" /> Command="{Binding LoadCommand}" />
<Button Content="卸载" <Button Content="卸载"
Margin="5" Margin="5"
Command="{Binding UnloadCommand}" /> Command="{Binding UnloadCommand}" />
</StackPanel> </StackPanel>
</TreeViewItem> </TreeViewItem>
<TreeViewItem Header="功率设置"> <TreeViewItem Header="功率设置">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="读取功率" <Button Content="读取功率"
Margin="5" Margin="5"
Command="{Binding ReadPowerCommand}" /> Command="{Binding ReadPowerCommand}" />
<Button Content="设置功率" <Button Content="设置功率"
Margin="5" Margin="5"
Command="{Binding SetPowerCommand}" /> Command="{Binding SetPowerCommand}" />
<Label Content="功率" <Label Content="功率"
Margin="5" Margin="5"
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
<TextBox Text="{Binding CurrentPowerDisplay}" <TextBox Text="{Binding CurrentPowerDisplay}"
MinWidth="100" MinWidth="100"
Margin="5" Margin="5"
materialDesign:HintAssist.Hint="" materialDesign:HintAssist.Hint=""
VerticalContentAlignment="Center" /> VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TreeViewItem> </TreeViewItem>
</TreeView> </TreeView>
<StackPanel Grid.Row="2" <StackPanel Grid.Row="2"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
<Button Content="关闭" <Button Content="关闭"
Width="70" Width="70"
Margin="10" Margin="10"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CloseCommand}" /> Command="{Binding CloseCommand}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</GroupBox>
</UserControl> </UserControl>

View File

@ -1,23 +0,0 @@
<UserControl x:Class="BOB.Views.ProgressView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BOB.Views"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid Background="Transparent">
<StackPanel Width="150"
VerticalAlignment="Center"
Margin="0 0 0 100"
>
<ProgressBar Width="80"
Height="80"
Margin="20"
IsIndeterminate="True"
Style="{StaticResource MaterialDesignCircularProgressBar}" />
<TextBlock FontSize="30" Text="加载中......" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</UserControl>

View File

@ -252,20 +252,25 @@
Identifier="Root"> Identifier="Root">
<!-- 主内容区 --> <!-- 主内容区 -->
<Grid> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="29*" />
<ColumnDefinition Width="424*" />
<ColumnDefinition Width="91*" />
<ColumnDefinition Width="57*" />
<ColumnDefinition Width="1318*" />
</Grid.ColumnDefinitions>
<ContentControl prism:RegionManager.RegionName="ShellViewManager" <ContentControl prism:RegionManager.RegionName="ShellViewManager"
Grid.ColumnSpan="5" /> />
<Border x:Name="Overlay" <Border x:Name="Overlay"
Background="#40000000" Background="#40000000"
Visibility="Collapsed" Visibility="Collapsed"
Panel.ZIndex="10" Panel.ZIndex="1">
Grid.ColumnSpan="5" /> <StackPanel Width="150"
VerticalAlignment="Center"
Margin="0 0 0 100">
<ProgressBar Width="80"
Height="80"
Margin="20"
IsIndeterminate="True"
Style="{StaticResource MaterialDesignCircularProgressBar}" />
<TextBlock FontSize="30"
Text="加载中......"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Grid> </Grid>
</materialDesign:DialogHost> </materialDesign:DialogHost>
</Grid> </Grid>

View File

@ -26,8 +26,6 @@ namespace BOB.Views
public ShellView(IEventAggregator eventAggregator) public ShellView(IEventAggregator eventAggregator)
{ {
InitializeComponent(); InitializeComponent();
//注册等待消息窗口
eventAggregator.GetEvent<WaitingEvent>().Subscribe(ShowWaiting);
//注册灰度遮罩层 //注册灰度遮罩层
eventAggregator.GetEvent<OverlayEvent>().Subscribe(ShowOverlay); eventAggregator.GetEvent<OverlayEvent>().Subscribe(ShowOverlay);
} }
@ -36,13 +34,7 @@ namespace BOB.Views
{ {
Overlay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed; Overlay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed;
} }
private void ShowWaiting(bool arg)
{
DialogHost.IsOpen = arg;
if (DialogHost.IsOpen)
DialogHost.DialogContent = new ProgressView();
}
private void ColorZone_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) private void ColorZone_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ {

View File

@ -0,0 +1,30 @@
<UserControl x:Class="BOB.Views.UpdateInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Margin="20">
<ListView ItemsSource="{Binding UpdateList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5"
Orientation="Vertical">
<TextBlock Text="{Binding Time}"
FontWeight="Bold"
Foreground="#673ab7" />
<TextBlock Text="{Binding Message}"
TextWrapping="Wrap"
Margin="0,3,0,10" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>

View File

@ -10,16 +10,17 @@ using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
namespace BOB.Views namespace BOB.Views
{ {
/// <summary> /// <summary>
/// ProgressView.xaml 的交互逻辑 /// UpdateInfoView.xaml 的交互逻辑
/// </summary> /// </summary>
public partial class ProgressView : UserControl public partial class UpdateInfoView : UserControl
{ {
public ProgressView() public UpdateInfoView()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -38,7 +38,7 @@
<!-- 温度 --> <!-- 温度 -->
<Viewbox Grid.Row="1"> <Viewbox Grid.Row="1">
<TextBlock Text="{Binding Temperature, StringFormat='0.00'}" <TextBlock Text="{Binding MonitorValue, StringFormat='0.00'}"
FontSize="40" FontSize="40"
FontWeight="SemiBold" FontWeight="SemiBold"
Foreground="{Binding TextColor}" Foreground="{Binding TextColor}"

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class ChangeCurrentTagEvent:PubSubEvent<string>
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class ConnectionChangeEvent:PubSubEvent<(string,bool)>
{
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class CurveDataEvent:PubSubEvent<(string, Dictionary<string,double>)>
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class StartProcessEvent:PubSubEvent<(string,bool)>
{
}
}

View File

@ -174,6 +174,43 @@ namespace DeviceCommand.Device
else if (CurrentDevice == "IT6724CReverse") else if (CurrentDevice == "IT6724CReverse")
await _IT6724CReverse.(, ct); await _IT6724CReverse.(, ct);
} }
public virtual async Task<double> (CancellationToken ct = default)
{
double value = 0;
if (CurrentDevice == "E36233A")
value = await _E36233A.(ct);
else if (CurrentDevice == "IT6724CReverse")
value = await _IT6724CReverse.(ct);
return Math.Round(value, 1);
}
public virtual async Task<double> (CancellationToken ct = default)
{
double value = 0;
if (CurrentDevice == "E36233A")
value = await _E36233A.(ct);
else if (CurrentDevice == "IT6724CReverse")
value = await _IT6724CReverse.(ct);
return Math.Round(value, 1);
}
public virtual async Task<double> (CancellationToken ct = default)
{
double value = 0;
if (CurrentDevice == "E36233A")
value = await _E36233A.(ct);
else if (CurrentDevice == "IT6724CReverse")
value = await _IT6724CReverse.(ct);
return Math.Round(value, 1);
}
#endregion #endregion
} }

View File

@ -115,87 +115,146 @@ namespace DeviceCommand.Device
} }
} }
#endregion #endregion
#region #region
/// <summary>
/// 设置当前操作通道
/// </summary>
public async Task (int , CancellationToken ct = default)
{
await SendAsync($"INST:NSEL {通道}\n", ct);
}
/// <summary>
/// 查询当前通道
/// </summary>
public async Task<string> (CancellationToken ct = default)
{
await SendAsync("INST:NSEL?\n", ct);
return await ReadAsync(ct: ct);
}
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await SendAsync($"PROTection:CLEar\r\n", ct); await SendAsync("OUTP:PROT:CLE\n", ct);
} }
public async Task (bool , CancellationToken ct = default) public async Task (bool , CancellationToken ct = default)
{ {
await SendAsync($"OUTPut {(开关 ? 1 : 0)}\r\n", ct); await SendAsync($"OUTP {(开关 ? "ON" : "OFF")}\n", ct);
} }
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await SendAsync($"SYSTem:REMote\r\n", ct); await SendAsync("SYST:REM\n", ct);
} }
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await SendAsync($"SYSTem:BEEPer\r\n", ct); await SendAsync("SYST:BEEP\n", ct);
} }
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
await SendAsync($"CURRent {电流}\r\n", ct); await SendAsync($"CURR {电流}\n", ct);
} }
public async Task OCP电流(double , CancellationToken ct = default) public async Task OCP电流(double , CancellationToken ct = default)
{ {
await SendAsync($"CURRent:PROTection {电流}\r\n", ct); await SendAsync($"CURR:PROT {电流}\n", ct);
} }
public async Task OCP开关(bool , CancellationToken ct = default) public async Task OCP开关(bool , CancellationToken ct = default)
{ {
await SendAsync($"CURRent:PROTection:STATe {(开关 ? 1 : 0)}\r\n", ct); await SendAsync($"CURR:PROT:STAT {(开关 ? "ON" : "OFF")}\n", ct);
} }
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
await SendAsync($"VOLTage {电压}\r\n", ct); await SendAsync($"VOLT {电压}\n", ct);
} }
public async Task OVP电压(double , CancellationToken ct = default) public async Task OVP电压(double , CancellationToken ct = default)
{ {
await SendAsync($"VOLT:PROTection {电压}\r\n", ct); await SendAsync($"VOLT:PROT {电压}\n", ct);
} }
public async Task OVP开关(bool , CancellationToken ct = default) public async Task OVP开关(bool , CancellationToken ct = default)
{ {
await SendAsync($"VOLT:PROTection:STATe {(开关 ? 1 : 0)}\r\n", ct); await SendAsync($"VOLT:PROT:STAT {(开关 ? "ON" : "OFF")}\n", ct);
} }
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
await SendAsync($"POWer:PROTection {电压}\r\n", ct); await SendAsync($"POW:PROT {功率值}\n", ct);
} }
public async Task (bool , CancellationToken ct = default) public async Task (bool , CancellationToken ct = default)
{ {
await SendAsync($"POWer:PROTection:STATe {(开关 ? 1 : 0)}\r\n", ct); await SendAsync($"POW:PROT:STAT {(开关 ? "ON" : "OFF")}\n", ct);
} }
public async Task (double , double , CancellationToken ct = default) public async Task (double , double , CancellationToken ct = default)
{ {
await SendAsync($"CURRent:SLEW {上升斜率},{下降斜率}\r\n", ct); await SendAsync($"CURR:SLEW:UP {上升斜率}\n", ct);
await SendAsync($"CURR:SLEW:DOWN {下降斜率}\n", ct);
} }
public async Task (double , double , CancellationToken ct = default) public async Task (double , double , CancellationToken ct = default)
{ {
await SendAsync($"VOLTage:SLEW {上升斜率},{下降斜率}\r\n", ct); await SendAsync($"VOLT:SLEW:UP {上升斜率}\n", ct);
await SendAsync($"VOLT:SLEW:DOWN {下降斜率}\n", ct);
} }
public async Task (string , CancellationToken ct = default) public async Task (string , CancellationToken ct = default)
{ {
await SendAsync($"{指令}\r\n", ct); await SendAsync($"{指令}\n", ct);
} }
public virtual async Task<string> (CancellationToken ct = default)
/// <summary>
/// 查询设备基本信息
/// </summary>
public async Task<string> (CancellationToken ct = default)
{ {
await SendAsync("*IDN?\r\n", ct); await SendAsync("*IDN?\n", ct);
return await ReadAsync(ct: ct); return await ReadAsync(ct: ct);
} }
#endregion #endregion
#region
/// <summary>
/// 查询当前电压测量值
/// </summary>
public async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEAS:VOLT?\n", ct);
string result = await ReadAsync(ct: ct);
return double.Parse(result);
}
/// <summary>
/// 查询当前电流测量值
/// </summary>
public async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEAS:CURR?\n", ct);
string result = await ReadAsync(ct: ct);
return double.Parse(result);
}
/// <summary>
/// 查询当前功率测量值
/// </summary>
public async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEAS:POW?\n", ct);
string result = await ReadAsync(ct: ct);
return double.Parse(result);
}
#endregion
} }
} }

View File

@ -27,6 +27,7 @@ namespace DeviceCommand.Device
public override async Task<bool> ConnectAsync(CancellationToken ct = default) public override async Task<bool> ConnectAsync(CancellationToken ct = default)
{ {
await commLock.WaitAsync(ct);
try try
{ {
if (SerialPort.IsOpen) if (SerialPort.IsOpen)
@ -48,7 +49,7 @@ namespace DeviceCommand.Device
} }
finally finally
{ {
commLock.Release();
} }
} }
public override void Close() public override void Close()

View File

@ -25,6 +25,7 @@ namespace DeviceCommand.Device
public override async Task<bool> ConnectAsync(CancellationToken ct = default) public override async Task<bool> ConnectAsync(CancellationToken ct = default)
{ {
await commLock.WaitAsync(ct);
try try
{ {
if (SerialPort.IsOpen) if (SerialPort.IsOpen)
@ -46,7 +47,7 @@ namespace DeviceCommand.Device
} }
finally finally
{ {
commLock.Release();
} }
} }
public override void Close() public override void Close()
@ -171,7 +172,35 @@ namespace DeviceCommand.Device
await SendAsync("*IDN?\r\n", ct); await SendAsync("*IDN?\r\n", ct);
return await ReadAsync(ct: ct); return await ReadAsync(ct: ct);
} }
/// <summary>
/// 查询实际输出电流单位A
/// </summary>
public virtual async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEASure:CURRent?\r\n", ct);
var result = await ReadAsync(ct: ct);
return Convert.ToDouble(result);
}
/// <summary>
/// 查询实际输出电压单位V
/// </summary>
public virtual async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEASure:VOLTage?\r\n", ct);
var result = await ReadAsync(ct: ct);
return Convert.ToDouble(result);
}
/// <summary>
/// 查询实际输出功率单位W
/// </summary>
public virtual async Task<double> (CancellationToken ct = default)
{
await SendAsync("MEASure:POWer?\r\n", ct);
var result = await ReadAsync(ct: ct);
return Convert.ToDouble(result);
}
#endregion #endregion
} }

14
Model/UpdateInfoModel.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class UpdateInfoModel
{
public string Message { get; set; }
public string Time { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using BOB.Views;
using BOB.Views.Dialogs; using BOB.Views.Dialogs;
using Prism.Ioc; using Prism.Ioc;
using ProcessManager.ViewModels; using ProcessManager.ViewModels;
using ProcessManager.Views;
using System.Configuration; using System.Configuration;
using System.Data; using System.Data;
using System.Windows; using System.Windows;
@ -23,12 +24,18 @@ namespace ProcessManager
protected override void OnInitialized() protected override void OnInitialized()
{ {
base.OnInitialized(); base.OnInitialized();
var regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate("MainRegion", "Menu");
} }
protected override void RegisterTypes(IContainerRegistry containerRegistry) protected override void RegisterTypes(IContainerRegistry containerRegistry)
{ {
containerRegistry.RegisterForNavigation<MainWindow, PMainViewModel>(); containerRegistry.RegisterForNavigation<MainWindow, PMainViewModel>();
containerRegistry.RegisterForNavigation<MenuView, MenuViewModel>("Menu");
containerRegistry.RegisterForNavigation<DevicesView, DevicesViewModel>("DeviceOne");
containerRegistry.RegisterForNavigation<DeviceTwoView, DevicesViewModel>("DeviceTwo");
containerRegistry.RegisterForNavigation<DeviceThreeView, DevicesViewModel>("DeviceThree");
containerRegistry.RegisterForNavigation<DeviceOther, DevicesViewModel>("Other");
containerRegistry.RegisterDialog<Setting, SettingViewModel>("Setting"); containerRegistry.RegisterDialog<Setting, SettingViewModel>("Setting");
} }

View File

@ -8,8 +8,24 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<COMReference Include="{215d64d2-031c-33c7-96e3-61794cd1ee61}">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>2</VersionMajor>
<Guid>215d64d2-031c-33c7-96e3-61794cd1ee61</Guid>
</COMReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\BOB\BOB.csproj" /> <ProjectReference Include="..\BOB\BOB.csproj" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />
</ItemGroup>
<ItemGroup>
<Reference Include="WindowsFormsIntegration">
<HintPath>..\..\..\WindowsFormsIntegration.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,73 @@
using Common.PubEvent;
using OxyPlot;
using ProcessManager.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml.Linq;
namespace ProcessManager.ViewModels
{
public class DevicesViewModel:BindableBase,INavigationAware
{
public ICommand WindowLoadedCommand { get; set; }
public IEventAggregator _eventAggregator { get; set; }
public string Title { get; set; }
private UserControl? _currentView;
private System.Windows.Forms.Panel? _host;
public DevicesViewModel(IContainerProvider containerProvider)
{
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
_eventAggregator.GetEvent<StartProcessEvent>().Subscribe(GetProcess);
WindowLoadedCommand =new DelegateCommand(OnWindowLoaded);
}
private void GetProcess((string exeName, bool isStart) msg)
{
if(Title!= msg.exeName) return;
if (_currentView == null) return;
if (!msg.isStart) return;
WindowEmbedHelper.FindAndEmbedWindow(msg.exeName, _host);
}
private void OnWindowLoaded()
{
}
#region
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
Title= navigationContext.Parameters.GetValue<string>("Title");
string viewName = navigationContext.Parameters.GetValue<string>("viewName");
_currentView = navigationContext.NavigationService.Region .ActiveViews .FirstOrDefault() as UserControl;
if (_currentView != null)
{
// 反射拿 Host 属性
var hostProp = _currentView.GetType().GetProperty("Host");
_host = hostProp?.GetValue(_currentView) as System.Windows.Forms.Panel;
}
_eventAggregator.GetEvent<ChangeCurrentTagEvent>()
.Publish(!string.IsNullOrEmpty(viewName) ? viewName : Title);
}
#endregion
}
}

View File

@ -0,0 +1,163 @@
using Azure;
using BOB;
using Common.PubEvent;
using ProcessManager.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace ProcessManager.ViewModels
{
public class MenuViewModel : BindableBase, INavigationAware
{
private ObservableCollection<SystemConfig> _configList;
public ObservableCollection<SystemConfig> ConfigList
{
get => _configList;
set => SetProperty(ref _configList, value);
}
private readonly IDialogService _dialogService;
private readonly IRegionManager _regionManager;
private readonly IEventAggregator _eventAggregator;
private Dictionary<string, Process> ProcessDic { get; set; } = new Dictionary<string, Process>();
public ICommand StartCommand { get; set; }
public ICommand EditCommand { get; set; }
public ICommand WindowLoadedCommand { get; set; }
public MenuViewModel(IContainerProvider containerProvider)
{
_dialogService = containerProvider.Resolve<IDialogService>();
_regionManager = containerProvider.Resolve<IRegionManager>();
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
EditCommand = new DelegateCommand<string>(OnEdit);
StartCommand = new DelegateCommand<string>(OnStart);
WindowLoadedCommand = new DelegateCommand(OnWindowLoaded);
}
private void OnWindowLoaded()
{
if (ConfigList != null) ConfigList.Clear();
var JSONFiles = Directory.GetFiles(JsonHelper.SystemPath, "*.json");
foreach (var file in JSONFiles)
{
JsonHelper.ConfigFile = file;
var config = JsonHelper.LoadFromFile();
if (ConfigList == null)
{
ConfigList = new ObservableCollection<SystemConfig>();
}
ConfigList.Add(config);
}
}
private void OnStart(string deviceName)
{
if (ProcessDic.ContainsKey(deviceName))
{
MessageBox.Show("进程已启动");
return;
}
try
{
string currentDir = Environment.CurrentDirectory;
string exePath = Path.Combine(currentDir, "BOB.exe");
if (!File.Exists(exePath))
{
MessageBox.Show($"未找到文件: {exePath}");
return;
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = exePath,
Arguments = deviceName,
UseShellExecute = true
};
var configToSave = ConfigList.FirstOrDefault(x => x.Title == deviceName);
if (configToSave != null)
{
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
JsonHelper.SaveToFile(configToSave);
}
var process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += (s, args) =>
{
ProcessDic.Remove(deviceName);
Application.Current.Dispatcher.Invoke(() =>
{
OnWindowLoaded();
});
};
ProcessDic.Add(deviceName, process);
string viewName = deviceName switch
{
"设备1" => "DeviceOne",
"设备2" => "DeviceTwo",
"设备3" => "DeviceThree",
_ => "Other"
};
var parameters = new NavigationParameters
{
{ "Title", deviceName },
{ "viewName", viewName },
};
_regionManager.RequestNavigate("MainRegion", viewName, parameters);
_eventAggregator.GetEvent<StartProcessEvent>().Publish((deviceName, true));
}
catch (Exception ex)
{
MessageBox.Show($"启动失败: {ex.Message}");
}
}
private void OnEdit(string deviceName)
{
if (ProcessDic.ContainsKey(deviceName))
{
MessageBox.Show("请先关闭进程再进行编辑");
return;
}
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
var ConfigSystem = JsonHelper.LoadFromFile();
var parameters = new DialogParameters
{
{ "SystemConfig", ConfigSystem },
{ "Title", deviceName }
};
_dialogService.ShowDialog("Setting", parameters, r =>
{
if (r.Result == ButtonResult.OK)
{
// 用户点击 OK
}
});
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
_eventAggregator.GetEvent<ChangeCurrentTagEvent>().Publish("Menu");
}
}
}

View File

@ -1,5 +1,9 @@
using BOB; using Azure;
using BOB;
using Common.PubEvent;
using MahApps.Metro.Controls; using MahApps.Metro.Controls;
using Prism.Ioc;
using ProcessManager.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -15,111 +19,71 @@ namespace ProcessManager.ViewModels
{ {
public class PMainViewModel:BindableBase public class PMainViewModel:BindableBase
{ {
private ObservableCollection<SystemConfig> _configList; private string _currentTag;
public string CurrentTag
public ObservableCollection<SystemConfig> ConfigList
{ {
get => _configList; get => _currentTag;
set => SetProperty(ref _configList, value); set => SetProperty(ref _currentTag, value);
} }
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private Dictionary<string, Process> ProcessDic { get; set; } = new Dictionary<string, Process>(); private IRegionManager _regionManager;
public ICommand StartCommand { get; set; } private IEventAggregator _eventAggregator;
public ICommand EditCommand { get; set; } public ICommand MinimizeCommand { get; set; }
public ICommand WindowLoadedCommand { get; set; } public ICommand MaximizeCommand { get; set; }
public PMainViewModel(IDialogService dialogService) public ICommand CloseCommand { get; set; }
public ICommand NavigateToMenuCommand { get; set; }
public ICommand NavigateToDeviceCommand { get; set; }
public PMainViewModel(IContainerProvider containerProvider)
{ {
_dialogService = dialogService; containerProvider = containerProvider;
EditCommand=new DelegateCommand<string>(OnEdit); _eventAggregator=containerProvider.Resolve<IEventAggregator>();
StartCommand = new DelegateCommand<string>(OnStart); _dialogService = containerProvider.Resolve<IDialogService>();
WindowLoadedCommand = new DelegateCommand(OnWindowLoaded); _regionManager = containerProvider.Resolve<IRegionManager>();
NavigateToMenuCommand = new DelegateCommand(NavigateToMenu);
NavigateToDeviceCommand = new DelegateCommand<string>(NavigateToDevice);
MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow);
MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow);
CloseCommand = new DelegateCommand<Window>(CloseWindow);
_eventAggregator.GetEvent<ChangeCurrentTagEvent>().Subscribe(ChangeCurrentTag);
} }
private void OnWindowLoaded() private void ChangeCurrentTag(string obj)
{ {
if (ConfigList != null) ConfigList.Clear(); CurrentTag = obj;
var JSONFiles = Directory.GetFiles(JsonHelper.SystemPath, "*.json");
foreach(var file in JSONFiles)
{
JsonHelper.ConfigFile = file;
var config = JsonHelper.LoadFromFile();
if(ConfigList==null)
{
ConfigList = new ObservableCollection<SystemConfig>();
}
ConfigList.Add(config);
}
} }
private void OnStart(string deviceName) private void NavigateToDevice(string page)
{ {
if (ProcessDic.ContainsKey(deviceName)) var parameters = new NavigationParameters
{
MessageBox.Show("进程已启动");
return;
}
try
{
string currentDir = Environment.CurrentDirectory;
string exePath = Path.Combine(currentDir, "BOB.exe");
if (!File.Exists(exePath))
{ {
MessageBox.Show($"未找到文件: {exePath}"); { "Title", page },
return;
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = exePath,
Arguments = deviceName,
UseShellExecute = true
}; };
var configToSave = ConfigList.FirstOrDefault(x => x.Title == deviceName); _regionManager.RequestNavigate("MainRegion", page,parameters);
if (configToSave != null)
{
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
JsonHelper.SaveToFile(configToSave);
}
var process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += (s, args) =>
{
ProcessDic.Remove(deviceName);
Application.Current.Dispatcher.Invoke(() =>
{
OnWindowLoaded();
});
};
ProcessDic.Add(deviceName, process);
}
catch (Exception ex)
{
MessageBox.Show($"启动失败: {ex.Message}");
}
} }
private void OnEdit(string deviceName) private void NavigateToMenu()
{ {
if (ProcessDic.ContainsKey(deviceName)) _regionManager.RequestNavigate("MainRegion", "Menu");
{
MessageBox.Show("请先关闭进程再进行编辑");
return;
}
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
var ConfigSystem = JsonHelper.LoadFromFile();
var parameters = new DialogParameters
{
{ "SystemConfig", ConfigSystem },
{ "Title", deviceName }
};
_dialogService.ShowDialog("Setting", parameters, r =>
{
if (r.Result == ButtonResult.OK)
{
// 用户点击 OK
}
});
} }
private void MinimizeWindow(Window window)
{
if (window != null)
window.WindowState = WindowState.Minimized;
}
private void MaximizeWindow(Window window)
{
if (window != null)
{
window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
}
private void CloseWindow(Window window)
{
window?.Close();
}
} }
} }

View File

@ -0,0 +1,20 @@
<UserControl x:Class="ProcessManager.Views.DeviceOther"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Name="MainGrid">
<WindowsFormsHost>
<winform:Panel x:Name="PanelHost"></winform:Panel>
</WindowsFormsHost>
</Grid>
</UserControl>

View File

@ -0,0 +1,29 @@
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.Views
{
/// <summary>
/// DeviceOther.xaml 的交互逻辑
/// </summary>
public partial class DeviceOther : UserControl
{
public System.Windows.Forms.Panel Host => PanelHost;
public DeviceOther()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,20 @@
<UserControl x:Class="ProcessManager.DeviceThreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Name="MainGrid">
<WindowsFormsHost>
<winform:Panel x:Name="PanelHost"></winform:Panel>
</WindowsFormsHost>
</Grid>
</UserControl>

View File

@ -0,0 +1,29 @@
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
{
/// <summary>
/// DeviceThreeView.xaml 的交互逻辑
/// </summary>
public partial class DeviceThreeView : UserControl
{
public System.Windows.Forms.Panel Host => PanelHost;
public DeviceThreeView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,20 @@
<UserControl x:Class="ProcessManager.DeviceTwoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Name="MainGrid">
<WindowsFormsHost>
<winform:Panel x:Name="PanelHost"></winform:Panel>
</WindowsFormsHost>
</Grid>
</UserControl>

View File

@ -0,0 +1,29 @@
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
{
/// <summary>
/// DeviceTwoView.xaml 的交互逻辑
/// </summary>
public partial class DeviceTwoView : UserControl
{
public System.Windows.Forms.Panel Host => PanelHost;
public DeviceTwoView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,20 @@
<UserControl x:Class="ProcessManager.DevicesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
mc:Ignorable="d">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Name="MainGrid">
<WindowsFormsHost>
<winform:Panel x:Name="PanelHost"></winform:Panel>
</WindowsFormsHost>
</Grid>
</UserControl>

View File

@ -0,0 +1,29 @@
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
{
/// <summary>
/// DevicesView.xaml 的交互逻辑
/// </summary>
public partial class DevicesView : UserControl
{
public System.Windows.Forms.Panel Host => PanelHost;
public DevicesView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,213 @@
<Window x:Class="ProcessManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="hMetroWindowttp://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:local="clr-namespace:ProcessManager"
mc:Ignorable="d"
WindowStyle="None"
Title="MainWindow" Height="600" Width="1000">
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="-1" />
</WindowChrome.WindowChrome>
<materialDesign:DialogHost x:Name="MainDialogHost">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<materialDesign:ColorZone Mode="PrimaryMid"
Background="#FF6200EE">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Menu Grid.Column="0"
Background="Transparent"
Foreground="White"
VerticalAlignment="Center">
<MenuItem FontSize="14"
Height="50"
Header="菜单"
Foreground="White"
Command="{Binding DataContext.NavigateToMenuCommand, RelativeSource={RelativeSource AncestorType=Window}}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Menu"
Foreground="White" />
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.CurrentTag, RelativeSource={RelativeSource AncestorType=Window}}"
Value="Menu">
<Setter Property="Background"
Value="#FFBB86FC" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="设备一"
Foreground="White"
Command="{Binding DataContext.NavigateToDeviceCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="DeviceOne">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Menu"
Foreground="White" />
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.CurrentTag, RelativeSource={RelativeSource AncestorType=Window}}"
Value="DeviceOne">
<Setter Property="Background"
Value="#FFBB86FC" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="设备二"
Foreground="White"
Command="{Binding DataContext.NavigateToDeviceCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="DeviceTwo">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Menu"
Foreground="White" />
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.CurrentTag, RelativeSource={RelativeSource AncestorType=Window}}"
Value="DeviceTwo">
<Setter Property="Background"
Value="#FFBB86FC" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="设备三"
Foreground="White"
Command="{Binding DataContext.NavigateToDeviceCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="DeviceThree">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Menu"
Foreground="White" />
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.CurrentTag, RelativeSource={RelativeSource AncestorType=Window}}"
Value="DeviceThree">
<Setter Property="Background"
Value="#FFBB86FC" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="环境箱和水冷机"
Foreground="White"
Command="{Binding DataContext.NavigateToDeviceCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="Other">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Menu"
Foreground="White" />
</MenuItem.Icon>
<MenuItem.Style>
<Style TargetType="MenuItem"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.CurrentTag, RelativeSource={RelativeSource AncestorType=Window}}"
Value="Other">
<Setter Property="Background"
Value="#FFBB86FC" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</Menu>
<Border Grid.Column="1"/>
<Menu Grid.Column="2"
Margin="0 0 20 0">
<MenuItem FontSize="14"
Height="50"
Header="最小化"
Foreground="White"
Command="{Binding MinimizeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Minimize"
Foreground="White" />
</MenuItem.Icon>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="最大化"
Foreground="White"
Command="{Binding MaximizeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Maximize"
Foreground="White" />
</MenuItem.Icon>
</MenuItem>
<MenuItem FontSize="14"
Height="50"
Header="关闭"
Foreground="White"
Command="{Binding CloseCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="Close"
Foreground="White" />
</MenuItem.Icon>
</MenuItem>
</Menu>
</Grid>
</materialDesign:ColorZone>
<ContentControl Grid.Row="1"
prism:RegionManager.RegionName="MainRegion"/>
</Grid>
</materialDesign:DialogHost>
</Window>

View File

@ -12,7 +12,7 @@ namespace ProcessManager
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
public partial class MainWindow : MetroWindow public partial class MainWindow : Window
{ {
public MainWindow() public MainWindow()

View File

@ -1,24 +1,23 @@
<mah:MetroWindow x:Class="ProcessManager.MainWindow" <UserControl x:Class="ProcessManager.MenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="hMetroWindowttp://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:prism="http://prismlibrary.com/"
xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:local="clr-namespace:ProcessManager"
xmlns:local="clr-namespace:ProcessManager" mc:Ignorable="d"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
Title="MainWindow" Height="600" Width="1000">
<i:Interaction.Triggers> <i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded"> <i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" /> <i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger> </i:EventTrigger>
</i:Interaction.Triggers> </i:Interaction.Triggers>
<Grid>
<materialDesign:DialogHost x:Name="MainDialogHost"> <ItemsControl ItemsSource="{Binding ConfigList}"
<ItemsControl ItemsSource="{Binding ConfigList}"> Grid.Row="1">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" <WrapPanel IsItemsHost="True"
@ -27,7 +26,7 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<materialDesign:Card Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}" <materialDesign:Card Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"
Width="220" Width="220"
Margin="5" Margin="5"
Padding="8" Padding="8"
@ -63,11 +62,11 @@
<MenuItem Header="编辑" <MenuItem Header="编辑"
Command="{Binding EditCommand}" Command="{Binding EditCommand}"
CommandParameter="{Binding PlacementTarget.DataContext.Title, CommandParameter="{Binding PlacementTarget.DataContext.Title,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" /> RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
<MenuItem Header="启动" <MenuItem Header="启动"
Command="{Binding StartCommand}" Command="{Binding StartCommand}"
CommandParameter="{Binding PlacementTarget.DataContext.Title, CommandParameter="{Binding PlacementTarget.DataContext.Title,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" /> RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu> </ContextMenu>
</materialDesign:Card.ContextMenu> </materialDesign:Card.ContextMenu>
@ -75,5 +74,5 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</materialDesign:DialogHost> </Grid>
</mah:MetroWindow> </UserControl>

View File

@ -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
{
/// <summary>
/// MenuView.xaml 的交互逻辑
/// </summary>
public partial class MenuView : UserControl
{
public MenuView()
{
InitializeComponent();
}
}
}

View File

@ -1,6 +1,7 @@
<UserControl x:Class="ProcessManager.Setting" <UserControl x:Class="ProcessManager.Setting"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:ProcessManager" xmlns:local="clr-namespace:ProcessManager"
MinHeight="250" MinHeight="250"

View File

@ -0,0 +1,93 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
namespace ProcessManager.Helpers
{
public static class WindowEmbedHelper
{
private const int GWL_STYLE = -16;
private const long WS_CAPTION = 0x00C00000;
private const long WS_THICKFRAME = 0x00040000;
private const long WS_CHILD = 0x40000000;
public static void FindAndEmbedWindow(string windowTitle, System.Windows.Forms.Panel panel, int timeoutMs = 10000)
{
Task.Run(async () =>
{
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);
}
}