优化多程序启动界面

This commit is contained in:
hsc 2025-11-18 10:22:32 +08:00
parent e007188418
commit cd5683dae9
23 changed files with 648 additions and 260 deletions

View File

@ -1,5 +1,6 @@
using BOB.Converters; using BOB.Converters;
using BOB.Models; using BOB.Models;
using BOB.Singleton;
using BOB.ViewModels; using BOB.ViewModels;
using BOB.ViewModels.Dialogs; using BOB.ViewModels.Dialogs;
using BOB.Views; using BOB.Views;
@ -7,6 +8,7 @@ using BOB.Views.Dialogs;
using Castle.DynamicProxy; using Castle.DynamicProxy;
using System.Configuration; using System.Configuration;
using System.Data; using System.Data;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
using static System.Runtime.InteropServices.JavaScript.JSType; using static System.Runtime.InteropServices.JavaScript.JSType;
@ -22,6 +24,21 @@ namespace BOB
{ {
return Container.Resolve<ShellView>(); return Container.Resolve<ShellView>();
} }
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Length > 0)
{
string deviceName = e.Args[0];
SystemConfig.Instance.Title = deviceName;
//Debugger.Launch();
}
else
{
SystemConfig.Instance.Title = "设备2";//模拟打开的设备
}
base.OnStartup(e);
}
protected override void OnInitialized() protected override void OnInitialized()
{ {
base.OnInitialized(); base.OnInitialized();
@ -40,6 +57,7 @@ namespace BOB
//注册全局变量 //注册全局变量
containerRegistry.RegisterSingleton<GlobalVariables>(); containerRegistry.RegisterSingleton<GlobalVariables>();
containerRegistry.RegisterSingleton<StepRunning>(); containerRegistry.RegisterSingleton<StepRunning>();
containerRegistry.RegisterSingleton<Devices>();
} }
} }

175
BOB/Singleton/Devices.cs Normal file
View File

@ -0,0 +1,175 @@
using DeviceCommand.Device;
using Model;
using Castle.DynamicProxy;
using Logger;
using System;
using System.Collections.Generic;
namespace BOB.Singleton
{
public class Devices
{
public Dictionary<string, object> DeviceDic { get; private set; } = new Dictionary<string, object>();
private readonly ProxyGenerator _proxyGen = new ProxyGenerator();
private readonly IInterceptor _loggingInterceptor = new LoggingInterceptor();
// 保留对单个实例引用(可选)
private E36233A E36233ADevice_1 { get; set; }
private E36233A E36233ADevice_2 { get; set; }
private IT6724C IT6724CDevice_1 { get; set; }
private IT6724C IT6724CDevice_2 { get; set; }
private EAEL9080 EAEL9080Device { get; set; }
private IOBoard IOBoardevice { get; set; }
private LQ7500_D LQ7500_DDevice { get; set; }
private PSB11000 PSB11000Device { get; set; }
private WS_68030_380T WS_68030_380TDevice { get; set; }
private SQ0030G1D SQ0030G1DTDevice { get; set; }
private ZXKS ZXKSTDevice { get; set; }
public Devices(List<DeviceConfigModel> deviceList)
{
foreach (var device in deviceList)
{
if (!device.IsEnabled) continue;
switch (device.DeviceType)
{
case "DeviceCommand.Device.E36233A":
if (device.CommunicationConfig is TcpConfig tcp1)
{
if (E36233ADevice_1 == null)
{
E36233ADevice_1 = _proxyGen.CreateClassProxy<E36233A>(
new object[] { tcp1.IPAddress, tcp1.Port, tcp1.ReadTimeout, tcp1.WriteTimeout },
_loggingInterceptor
);
DeviceDic["E36233A_1"] = E36233ADevice_1;
}
else
{
E36233ADevice_2 = _proxyGen.CreateClassProxy<E36233A>(
new object[] { tcp1.IPAddress, tcp1.Port, tcp1.ReadTimeout, tcp1.WriteTimeout },
_loggingInterceptor
);
DeviceDic["E36233A_2"] = E36233ADevice_2;
}
}
else throw new InvalidOperationException("E36233A 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.IT6724C":
if (device.CommunicationConfig is SerialPortConfig sp1)
{
if (IT6724CDevice_1 == null)
{
IT6724CDevice_1 = _proxyGen.CreateClassProxy<IT6724C>(
new object[] { sp1.COMPort, sp1.BaudRate, sp1.DataBit, sp1.StopBit, sp1.ParityBit, sp1.ReadTimeout, sp1.WriteTimeout },
_loggingInterceptor
);
DeviceDic["IT6724C_1"] = IT6724CDevice_1;
}
else
{
IT6724CDevice_2 = _proxyGen.CreateClassProxy<IT6724C>(
new object[] { sp1.COMPort, sp1.BaudRate, sp1.DataBit, sp1.StopBit, sp1.ParityBit, sp1.ReadTimeout, sp1.WriteTimeout },
_loggingInterceptor
);
DeviceDic["IT6724C_2"] = IT6724CDevice_2;
}
}
else throw new InvalidOperationException("IT6724C 必须使用 SerialPortConfig");
break;
case "DeviceCommand.Device.LQ7500_D":
if (device.CommunicationConfig is SerialPortConfig sp2)
{
LQ7500_DDevice = _proxyGen.CreateClassProxy<LQ7500_D>(
new object[] { sp2.COMPort, sp2.BaudRate, sp2.DataBit, sp2.StopBit, sp2.ParityBit, sp2.ReadTimeout, sp2.WriteTimeout },
_loggingInterceptor
);
DeviceDic["LQ7500_D"] = LQ7500_DDevice;
}
else throw new InvalidOperationException("LQ7500D 必须使用 SerialPortConfig");
break;
case "DeviceCommand.Device.EAEL9080":
if (device.CommunicationConfig is TcpConfig tcp2)
{
EAEL9080Device = _proxyGen.CreateClassProxy<EAEL9080>(
new object[] { tcp2.IPAddress, tcp2.Port, tcp2.ReadTimeout, tcp2.WriteTimeout },
_loggingInterceptor
);
DeviceDic["EAEL9080"] = EAEL9080Device;
}
else throw new InvalidOperationException("EAEL9080 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.IOBoard":
if (device.CommunicationConfig is TcpConfig tcp3)
{
IOBoardevice = _proxyGen.CreateClassProxy<IOBoard>(
new object[] { tcp3.IPAddress, tcp3.Port, tcp3.ReadTimeout, tcp3.WriteTimeout },
_loggingInterceptor
);
DeviceDic["IOBoard"] = IOBoardevice;
}
else throw new InvalidOperationException("IOBoard 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.PSB11000":
if (device.CommunicationConfig is TcpConfig tcp4)
{
PSB11000Device = _proxyGen.CreateClassProxy<PSB11000>(
new object[] { tcp4.IPAddress, tcp4.Port, tcp4.ReadTimeout, tcp4.WriteTimeout },
_loggingInterceptor
);
DeviceDic["PSB11000"] = PSB11000Device;
}
else throw new InvalidOperationException("PSB11000 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.WS_68030_380T":
if (device.CommunicationConfig is TcpConfig tcp5)
{
WS_68030_380TDevice = _proxyGen.CreateClassProxy<WS_68030_380T>(
new object[] { tcp5.IPAddress, tcp5.Port, tcp5.ReadTimeout, tcp5.WriteTimeout },
_loggingInterceptor
);
DeviceDic["WS_68030_380T"] = WS_68030_380TDevice;
}
else throw new InvalidOperationException("WS_68030_380T 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.SQ0030G1D":
if (device.CommunicationConfig is TcpConfig tcp7)
{
SQ0030G1DTDevice = _proxyGen.CreateClassProxy<SQ0030G1D>(
new object[] { tcp7.IPAddress, tcp7.Port, tcp7.ReadTimeout, tcp7.WriteTimeout },
_loggingInterceptor
);
DeviceDic["SQ0030G1D"] = SQ0030G1DTDevice;
}
else throw new InvalidOperationException("SQ0030G1D 必须使用 TcpConfig");
break;
case "DeviceCommand.Device.ZXKS":
if (device.CommunicationConfig is TcpConfig tcp6)
{
ZXKSTDevice = _proxyGen.CreateClassProxy<ZXKS>(
new object[] { tcp6.IPAddress, tcp6.Port, tcp6.ReadTimeout, tcp6.WriteTimeout },
_loggingInterceptor
);
DeviceDic["ZXKS"] = ZXKSTDevice;
}
else throw new InvalidOperationException("ZXKS 必须使用 TcpConfig");
break;
default:
throw new NotSupportedException($"未知设备类型:{device.DeviceType}");
}
}
}
}
}

View File

@ -31,6 +31,7 @@ namespace BOB
[JsonIgnore] [JsonIgnore]
public string SystemPath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB"); public string SystemPath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB");
public string Title { get; set; } = "";
public int PerformanceLevel { get; set; } = 50; public int PerformanceLevel { get; set; } = 50;
@ -51,7 +52,7 @@ namespace BOB
if (!Directory.Exists(SystemPath)) if (!Directory.Exists(SystemPath))
Directory.CreateDirectory(SystemPath); Directory.CreateDirectory(SystemPath);
string configPath = Path.Combine(SystemPath, "system.json"); string configPath = Path.Combine(SystemPath, $"{Title}.json");
// 支持接口多态序列化 // 支持接口多态序列化
string json = JsonConvert.SerializeObject(this, Formatting.Indented, string json = JsonConvert.SerializeObject(this, Formatting.Indented,
@ -73,11 +74,13 @@ namespace BOB
public void LoadFromFile() public void LoadFromFile()
{ {
string configPath = Path.Combine(SystemPath, "system.json"); string configPath = Path.Combine(SystemPath, $"{Title}.json");
if(Title == "")
{
return;
}
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
// 文件不存在则保存当前配置
SaveToFile(); SaveToFile();
return; return;
} }

View File

@ -1,6 +1,9 @@
using Logger; using BOB.Singleton;
using DeviceCommand.Device;
using Logger;
using Prism.Mvvm; using Prism.Mvvm;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
@ -18,8 +21,10 @@ namespace BOB.ViewModels
set => SetProperty(ref _logs, value); set => SetProperty(ref _logs, value);
} }
public ICommand ClearLogCommand { get; set; } public ICommand ClearLogCommand { get; set; }
public LogAreaViewModel() public Devices devices { get; set; }
public LogAreaViewModel(IContainerProvider containerProvider)
{ {
devices=containerProvider.Resolve<Devices>();
ClearLogCommand = new DelegateCommand(ClearLog); ClearLogCommand = new DelegateCommand(ClearLog);
LoggerHelper.Progress = new System.Progress<(string message, string color,int depth)>( LoggerHelper.Progress = new System.Progress<(string message, string color,int depth)>(
log => log =>
@ -27,8 +32,63 @@ namespace BOB.ViewModels
var brush = (Brush)new BrushConverter().ConvertFromString(log.color); var brush = (Brush)new BrushConverter().ConvertFromString(log.color);
Logs.Add(new LogItem(log.message, brush, log.depth)); Logs.Add(new LogItem(log.message, brush, log.depth));
}); });
ShowDeviesInfo();
} }
private void ShowDeviesInfo()
{
foreach (var kv in devices.DeviceDic)
{
string name = kv.Key;
object dev = kv.Value;
if (dev == null)
{
LoggerHelper.InfoWithNotify($"{name} 实例为空", 0);
continue;
}
string typeName = dev.GetType().Name;
string status = GetDeviceConnectionStatus(dev);
LoggerHelper.InfoWithNotify(
$"设备:{name} | 类型:{typeName} | {status}", 0);
}
}
private string GetDeviceConnectionStatus(object device)
{
// 串口设备
var serialField = device.GetType().GetProperty("_SerialPort",
BindingFlags.Public | BindingFlags.Instance);
if (serialField != null)
{
var serial = serialField.GetValue(device) as System.IO.Ports.SerialPort;
if (serial == null)
return "串口未初始化";
return serial.IsOpen ? "串口已连接" : "串口未连接";
}
// TCP 设备
var tcpProperty = device.GetType().GetProperty(
"TcpClient",
BindingFlags.Public | BindingFlags.Instance
);
if (tcpProperty != null)
{
var client = tcpProperty.GetValue(device) as System.Net.Sockets.TcpClient;
if (client == null)
return "TCP 未初始化";
return client.Connected ? "TCP 已连接" : "TCP 未连接";
}
return "无法识别设备类型";
}
private void ClearLog() private void ClearLog()
{ {
Logs.Clear(); Logs.Clear();

View File

@ -1,9 +1,11 @@
using BOB.Models; using BOB.Models;
using BOB.Singleton;
using BOB.Views; using BOB.Views;
using Common.PubEvent; using Common.PubEvent;
using Logger; using Logger;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using Microsoft.Win32; using Microsoft.Win32;
using Model;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
@ -15,6 +17,13 @@ namespace BOB.ViewModels
public class ShellViewModel : BindableBase public class ShellViewModel : BindableBase
{ {
#region #region
private string _Title="";
public string Title
{
get => _Title;
set => SetProperty(ref _Title, value);
}
private bool _IsLeftDrawerOpen; private bool _IsLeftDrawerOpen;
public bool IsLeftDrawerOpen public bool IsLeftDrawerOpen
@ -90,11 +99,14 @@ namespace BOB.ViewModels
private IEventAggregator _eventAggregator; private IEventAggregator _eventAggregator;
private GlobalVariables _globalVariables; private GlobalVariables _globalVariables;
private IContainerProvider _containerProvider;
private StepRunning _stepRunning; private StepRunning _stepRunning;
private Devices _devices;
private Task? currentExecutionTask; private Task? currentExecutionTask;
public ShellViewModel(IEventAggregator eventAggregator, IContainerProvider containerProvider,GlobalVariables globalVariables, StepRunning stepRunning) public ShellViewModel(IEventAggregator eventAggregator, IContainerProvider containerProvider,GlobalVariables globalVariables, StepRunning stepRunning)
{ {
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_containerProvider = containerProvider;
_globalVariables = globalVariables; _globalVariables = globalVariables;
_stepRunning =stepRunning; _stepRunning =stepRunning;
LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen); LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
@ -124,9 +136,10 @@ namespace BOB.ViewModels
_ => RunIcon _ => RunIcon
}; };
} }
#region ToolBar命令
private void Load() private void Load()
{ {
SystemConfig.Instance.LoadFromFile();
_containerProvider.Resolve<Devices>((typeof(List<DeviceConfigModel>), SystemConfig.Instance.DeviceList));
if (SystemConfig.Instance.DefaultSubProgramFilePath != null) if (SystemConfig.Instance.DefaultSubProgramFilePath != null)
{ {
if (File.Exists(SystemConfig.Instance.DefaultSubProgramFilePath)) if (File.Exists(SystemConfig.Instance.DefaultSubProgramFilePath))
@ -134,7 +147,10 @@ namespace BOB.ViewModels
Open(SystemConfig.Instance.DefaultSubProgramFilePath); Open(SystemConfig.Instance.DefaultSubProgramFilePath);
} }
} }
} Title = SystemConfig.Instance.Title;
}
#region ToolBar命令
private void SetDefault() private void SetDefault()
{ {
if(_globalVariables.CurrentFilePath!=null) if(_globalVariables.CurrentFilePath!=null)

View File

@ -11,7 +11,7 @@
mc:Ignorable="d" mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.AutoWireViewModel="True"
WindowStyle="None" WindowStyle="None"
Title="ShellView" Title="{Binding Title}"
d:DesignHeight="1080" d:DesignHeight="1080"
d:DesignWidth="1920"> d:DesignWidth="1920">
<WindowChrome.WindowChrome> <WindowChrome.WindowChrome>

View File

@ -41,7 +41,6 @@ namespace DeviceCommand.Base
TcpClient.Dispose(); TcpClient.Dispose();
TcpClient = new TcpClient(); TcpClient = new TcpClient();
} }
await TcpClient.ConnectAsync(IPAddress, Port, ct); await TcpClient.ConnectAsync(IPAddress, Port, ct);
Modbus = new ModbusFactory().CreateMaster(TcpClient); Modbus = new ModbusFactory().CreateMaster(TcpClient);
return true; return true;

View File

@ -12,9 +12,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class E36233A:Tcp public class E36233A:Tcp
{ {
public E36233A(string IpAddress,int port,int SendTimeout,int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public E36233A() public E36233A()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
#region SCPI #region SCPI

View File

@ -11,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class EAEL9080:ModbusTcp public class EAEL9080:ModbusTcp
{ {
public EAEL9080(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public EAEL9080() public EAEL9080()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
#region #region

View File

@ -0,0 +1,32 @@
using DeviceCommand.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Device
{
public class IOBoard:ModbusTcp
{
public IOBoard(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public IOBoard()
{
ConnectAsync();
}
public async Task WriteOutput(byte slaveId, ushort startAddress, ushort value)
{
await WriteSingleRegisterAsync(slaveId, startAddress, value);
}
public async Task WriteOutputsBatch(byte slaveId, ushort startAddress, ushort[] values)
{
await WriteMultipleRegistersAsync(slaveId, startAddress, values);
}
}
}

View File

@ -10,9 +10,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class IT6724C : Serial_Port public class IT6724C : Serial_Port
{ {
public IT6724C(string COMPort,int BaudRate,int DataBits, StopBits stopBits, Parity parity,int ReadTimeout,int ReceiveTimeout)
{
ConfigureDevice(COMPort,BaudRate,DataBits,stopBits,parity, ReadTimeout, ReceiveTimeout);
ConnectAsync();
}
public IT6724C() public IT6724C()
{ {
ConfigureDevice("COM1", 9600, 8, StopBits.One, Parity.None, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }

View File

@ -1,6 +1,7 @@
using Common.Attributes; using Common.Attributes;
using DeviceCommand.Base; using DeviceCommand.Base;
using System; using System;
using System.IO.Ports;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -10,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class LQ7500_D : ModbusRtu public class LQ7500_D : ModbusRtu
{ {
public LQ7500_D(string COMPort, int BaudRate, int DataBits, StopBits stopBits, Parity parity, int ReadTimeout, int ReceiveTimeout)
{
ConfigureDevice(COMPort, BaudRate, DataBits, stopBits, parity, ReadTimeout, ReceiveTimeout);
ConnectAsync();
}
public LQ7500_D() public LQ7500_D()
{ {
ConfigureDevice("COM1", 9600);
ConnectAsync(); ConnectAsync();
} }
public byte SlaveAddress { get; set; } = 1; // default slave address public byte SlaveAddress { get; set; } = 1; // default slave address

View File

@ -11,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class PSB11000: ModbusTcp public class PSB11000: ModbusTcp
{ {
public PSB11000(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public PSB11000() public PSB11000()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
#region #region

View File

@ -13,9 +13,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class SQ0030G1D : Tcp public class SQ0030G1D : Tcp
{ {
public SQ0030G1D(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public SQ0030G1D() public SQ0030G1D()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
#region ========== SCPI ========== #region ========== SCPI ==========

View File

@ -9,9 +9,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class WS_68030_380T : ModbusTcp public class WS_68030_380T : ModbusTcp
{ {
public WS_68030_380T(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public WS_68030_380T() public WS_68030_380T()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
private const byte SlaveAddress = 1; private const byte SlaveAddress = 1;

View File

@ -11,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand] [BOBCommand]
public class ZXKS:ModbusTcp public class ZXKS:ModbusTcp
{ {
public ZXKS(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public ZXKS() public ZXKS()
{ {
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync(); ConnectAsync();
} }
} }

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Castle.Core" Version="5.2.1" />
<PackageReference Include="NLog" Version="6.0.5" /> <PackageReference Include="NLog" Version="6.0.5" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,63 @@
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Logger
{
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
string className = invocation.TargetType.Name;
string methodName = invocation.Method.Name;
LoggerHelper.InfoWithNotify($"调用 {className}.{methodName}() 开始");
var sw = Stopwatch.StartNew();
try
{
// 执行原方法
invocation.Proceed();
// 如果是异步方法
if (invocation.Method.ReturnType == typeof(Task) ||
(invocation.Method.ReturnType.IsGenericType &&
invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)))
{
invocation.ReturnValue = InterceptAsync(invocation, sw);
return;
}
sw.Stop();
LoggerHelper.SuccessWithNotify($"调用 {className}.{methodName}() 完成,耗时 {sw.ElapsedMilliseconds} ms");
}
catch (Exception ex)
{
sw.Stop();
LoggerHelper.ErrorWithNotify($"调用 {className}.{methodName}() 异常:{ex.Message}", ex.StackTrace);
throw;
}
}
private async Task InterceptAsync(IInvocation invocation, Stopwatch sw)
{
try
{
await (Task)invocation.ReturnValue;
sw.Stop();
LoggerHelper.SuccessWithNotify($"调用 {invocation.TargetType.Name}.{invocation.Method.Name}() 完成,耗时 {sw.ElapsedMilliseconds} ms");
}
catch (Exception ex)
{
sw.Stop();
LoggerHelper.ErrorWithNotify($"调用 {invocation.TargetType.Name}.{invocation.Method.Name}() 异常:{ex.Message}", ex.StackTrace);
throw;
}
}
}
}

View File

@ -11,7 +11,7 @@ namespace ProcessManager
public static string SystemPath { get; set; } = public static string SystemPath { get; set; } =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB"); Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB");
private static string ConfigFile => Path.Combine(SystemPath, "system.json"); public static string ConfigFile;
private static readonly JsonSerializerSettings jsonSettings = new() private static readonly JsonSerializerSettings jsonSettings = new()
{ {
@ -41,10 +41,7 @@ namespace ProcessManager
{ {
if (!File.Exists(ConfigFile)) if (!File.Exists(ConfigFile))
{ {
// 如果不存在自动创建一个默认的 SystemConfig throw new Exception("没找到系统配置文件");
var defaultConfig = new SystemConfig();
SaveToFile(defaultConfig);
return defaultConfig;
} }
try try

View File

@ -1,188 +1,79 @@
<mah:MetroWindow x:Class="ProcessManager.MainWindow" <mah:MetroWindow x:Class="ProcessManager.MainWindow"
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="http://schemas.microsoft.com/expression/blend/2008" 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:mah="http://metro.mahapps.com/winfx/xaml/controls" 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"
Title="MainWindow" Height="600" Width="1000"> Title="MainWindow" Height="600" Width="1000">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<materialDesign:DialogHost x:Name="MainDialogHost"> <materialDesign:DialogHost x:Name="MainDialogHost">
<UniformGrid Columns="3" <ItemsControl ItemsSource="{Binding ConfigList}">
Margin="16" <ItemsControl.ItemsPanel>
HorizontalAlignment="Center" <ItemsPanelTemplate>
Cursor=""> <WrapPanel IsItemsHost="True"
<materialDesign:Card Width="220" Orientation="Horizontal" />
Margin="5" </ItemsPanelTemplate>
Padding="8" </ItemsControl.ItemsPanel>
Height="230" <ItemsControl.ItemTemplate>
Background="DarkSlateGray" <DataTemplate>
Foreground="white" <materialDesign:Card Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}"
Cursor=""> Width="220"
<StackPanel> Margin="5"
<TextBlock Margin="16,16,12,8" Padding="8"
FontSize="16" Height="230"
Text="设备1" /> Background="DarkSlateGray"
<CheckBox Margin="16,4,16,0" Foreground="white">
Content="交流负载WS-68030-380T"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> <StackPanel>
<CheckBox Margin="16,4,16,0" <!-- 标题 -->
Content="交流电源SQ0030G1D" <TextBlock Margin="16,16,12,8"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> FontSize="16"
<CheckBox Margin="16,4,16,0" Text="{Binding Title}" />
Content="高压电流双向源PSB11000"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> <!-- 设备列表自动生成对应 CheckBox -->
<CheckBox Margin="16,4,16,0" <ItemsControl ItemsSource="{Binding DeviceList}">
Content="低压直流电源IT6724C" <ItemsControl.ItemTemplate>
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> <DataTemplate>
<CheckBox Margin="16,4,16,0" <CheckBox Margin="16,4,16,0"
Content="低压直流电源IT6724C" Content="{Binding DeviceName}"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> IsChecked="{Binding IsEnabled,Mode=TwoWay}"
<CheckBox Margin="16,4,16,0" Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
Content="低压直流负载EAEL9080"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" /> </DataTemplate>
<RadioButton Margin="16,4,16,0" </ItemsControl.ItemTemplate>
Content="自动启动" </ItemsControl>
Style="{StaticResource MaterialDesignUserForegroundRadioButton}" />
<Separator Style="{StaticResource MaterialDesignLightSeparator}" /> <Separator Style="{StaticResource MaterialDesignLightSeparator}" />
</StackPanel> </StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu> <materialDesign:Card.ContextMenu>
<MenuItem Header="编辑" <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Tag}">
Command="{Binding EditCommand}" <MenuItem Header="编辑"
CommandParameter="设备1" /> Command="{Binding EditCommand}"
<MenuItem Header="启动" CommandParameter="{Binding PlacementTarget.DataContext.Title,
Command="{Binding StartCommand}" RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
CommandParameter="设备1" /> <MenuItem Header="启动"
</ContextMenu> Command="{Binding StartCommand}"
</materialDesign:Card.ContextMenu> CommandParameter="{Binding PlacementTarget.DataContext.Title,
</materialDesign:Card> RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
<materialDesign:Card Width="220"
Margin="5" </ContextMenu>
Height="230" </materialDesign:Card.ContextMenu>
Padding="8" </materialDesign:Card>
Background="#1976D2" </DataTemplate>
Foreground="white"> </ItemsControl.ItemTemplate>
<StackPanel> </ItemsControl>
<TextBlock Margin="16,16,12,8"
FontSize="16"
Text="设备2" />
<CheckBox Margin="16,4,16,0"
Content="交流负载WS-68030-380T"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="交流电源SQ0030G1D"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="高压电流双向源PSB11000"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流电源IT6724C"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流电源IT6724C"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流负载EAEL9080"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<RadioButton Margin="16,4,16,0"
Content="自动启动"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}" />
<Separator Style="{StaticResource MaterialDesignLightSeparator}" />
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu>
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="设备2" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="设备2" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
<materialDesign:Card Margin="5"
Width="220"
Height="230"
Padding="8"
Background="#FF9800"
Foreground="White">
<StackPanel Cursor="">
<TextBlock Margin="16,16,12,8"
FontSize="16"
Text="设备3" />
<CheckBox Margin="16,4,16,0"
Content="交流负载WS-68030-380T"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="交流电源SQ0030G1D"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="高压电流双向源PSB11000"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流电源E36233A"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流电源E36233A"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="低压直流负载EAEL9080"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<RadioButton Margin="16,4,16,0"
Content="自动启动"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}" />
<Separator Style="{StaticResource MaterialDesignLightSeparator}" />
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu>
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="设备3" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="设备3" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
<materialDesign:Card Margin="5" Width="220"
Padding="8"
Height="140"
Background="#00BCD4"
Foreground="White">
<StackPanel Cursor="">
<TextBlock Margin="16,16,12,8"
FontSize="16"
Text="水冷机和环境箱" />
<CheckBox Margin="16,4,16,0"
Content="水冷机LQ7500-D"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<CheckBox Margin="16,4,16,0"
Content="环境箱ZXKS"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
<RadioButton Margin="16,4,16,0"
Content="自动启动"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}" />
<Separator Style="{StaticResource MaterialDesignLightSeparator}" />
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu>
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="水冷机和环境箱" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="水冷机和环境箱" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
</UniformGrid>
</materialDesign:DialogHost> </materialDesign:DialogHost>
</mah:MetroWindow> </mah:MetroWindow>

View File

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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"
Height="500"
Width="800"> Width="800">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -64,65 +64,91 @@
Width="200" /> Width="200" />
</StackPanel> </StackPanel>
<!-- IPAddress --> <!-- TCP相关 -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Vertical">
Margin="0,2"> <StackPanel.Style>
<TextBlock Text="IP地址:" <Style TargetType="StackPanel">
Width="120" <Setter Property="Visibility"
VerticalAlignment="Center" /> Value="Collapsed" />
<TextBox Text="{Binding CommunicationConfig.IPAddress, Mode=TwoWay}" <Style.Triggers>
Width="200" /> <DataTrigger Binding="{Binding DataContext.SelectedDeviceType, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="False">
<Setter Property="Visibility"
Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="IP地址:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.IPAddress, Mode=TwoWay}"
Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="端口:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.Port, Mode=TwoWay}"
Width="200" />
</StackPanel>
</StackPanel> </StackPanel>
<!-- Port --> <!-- 串口相关 -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Vertical">
Margin="0,2"> <StackPanel.Style>
<TextBlock Text="端口:" <Style TargetType="StackPanel">
Width="120" <Setter Property="Visibility"
VerticalAlignment="Center" /> Value="Collapsed" />
<TextBox Text="{Binding CommunicationConfig.Port, Mode=TwoWay}" <Style.Triggers>
Width="200" /> <DataTrigger Binding="{Binding DataContext.SelectedDeviceType, RelativeSource={RelativeSource AncestorType=UserControl}}"
</StackPanel> Value="True">
<!-- IPAddress --> <Setter Property="Visibility"
<StackPanel Orientation="Horizontal" Value="Visible" />
Margin="0,2"> </DataTrigger>
<TextBlock Text="串口:" </Style.Triggers>
Width="120" </Style>
VerticalAlignment="Center" /> </StackPanel.Style>
<TextBox Text="{Binding CommunicationConfig.COMPort, Mode=TwoWay}"
Width="200" /> <StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="串口:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.COMPort, Mode=TwoWay}"
Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="波特率:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.BaudRate, Mode=TwoWay}"
Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="数据位:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.DataBit, Mode=TwoWay}"
Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="停止位:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.StopBit, Mode=TwoWay}"
Width="200" />
</StackPanel>
</StackPanel> </StackPanel>
<!-- Port --> <!-- 公共超时 -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="波特率:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.BaudRate, Mode=TwoWay}"
Width="200" />
</StackPanel>
<!-- IPAddress -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="数据位:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.DataBit, Mode=TwoWay}"
Width="200" />
</StackPanel>
<!-- Port -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="停止位:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.StopBit, Mode=TwoWay}"
Width="200" />
</StackPanel>
<!-- ReadTimeout -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,2"> Margin="0,2">
<TextBlock Text="读取超时:" <TextBlock Text="读取超时:"
@ -131,8 +157,6 @@
<TextBox Text="{Binding CommunicationConfig.ReadTimeout, Mode=TwoWay}" <TextBox Text="{Binding CommunicationConfig.ReadTimeout, Mode=TwoWay}"
Width="200" /> Width="200" />
</StackPanel> </StackPanel>
<!-- WriteTimeout -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,2"> Margin="0,2">
<TextBlock Text="写入超时:" <TextBlock Text="写入超时:"
@ -145,6 +169,7 @@
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"

View File

@ -1,25 +1,55 @@
using System; using BOB;
using MahApps.Metro.Controls;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
namespace ProcessManager.ViewModels namespace ProcessManager.ViewModels
{ {
public class PMainViewModel:BindableBase public class PMainViewModel:BindableBase
{ {
private ObservableCollection<SystemConfig> _configList;
public ObservableCollection<SystemConfig> ConfigList
{
get => _configList;
set => SetProperty(ref _configList, value);
}
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private Dictionary<string, Process> ProcessDic { get; set; } = new Dictionary<string, Process>(); private Dictionary<string, Process> ProcessDic { get; set; } = new Dictionary<string, Process>();
public ICommand StartCommand { get; set; } public ICommand StartCommand { get; set; }
public ICommand EditCommand { get; set; } public ICommand EditCommand { get; set; }
public ICommand WindowLoadedCommand { get; set; }
public PMainViewModel(IDialogService dialogService) public PMainViewModel(IDialogService dialogService)
{ {
_dialogService = dialogService; _dialogService = dialogService;
EditCommand=new DelegateCommand<string>(OnEdit); EditCommand=new DelegateCommand<string>(OnEdit);
StartCommand = new DelegateCommand<string>(OnStart); 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) private void OnStart(string deviceName)
@ -41,13 +71,24 @@ namespace ProcessManager.ViewModels
ProcessStartInfo startInfo = new ProcessStartInfo ProcessStartInfo startInfo = new ProcessStartInfo
{ {
FileName = exePath, FileName = exePath,
Arguments = deviceName,
UseShellExecute = true 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); var process = Process.Start(startInfo);
process.EnableRaisingEvents = true; process.EnableRaisingEvents = true;
process.Exited += (s, args) => process.Exited += (s, args) =>
{ {
ProcessDic.Remove(deviceName); ProcessDic.Remove(deviceName);
Application.Current.Dispatcher.Invoke(() =>
{
OnWindowLoaded();
});
}; };
ProcessDic.Add(deviceName, process); ProcessDic.Add(deviceName, process);
} }
@ -64,6 +105,7 @@ namespace ProcessManager.ViewModels
MessageBox.Show("请先关闭进程再进行编辑"); MessageBox.Show("请先关闭进程再进行编辑");
return; return;
} }
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
var ConfigSystem = JsonHelper.LoadFromFile(); var ConfigSystem = JsonHelper.LoadFromFile();
var parameters = new DialogParameters var parameters = new DialogParameters
{ {

View File

@ -3,6 +3,7 @@ using BOB.Models;
using Model; using Model;
using Prism.Mvvm; using Prism.Mvvm;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Windows.Input; using System.Windows.Input;
namespace ProcessManager.ViewModels namespace ProcessManager.ViewModels
@ -16,12 +17,32 @@ namespace ProcessManager.ViewModels
get => _Title; get => _Title;
set => SetProperty(ref _Title, value); set => SetProperty(ref _Title, value);
} }
private bool _SelectedDeviceType;
//true为serialport ,false为tcp
public bool SelectedDeviceType
{
get => _SelectedDeviceType;
set => SetProperty(ref _SelectedDeviceType, value);
}
private DeviceConfigModel _SelectedDevice; private DeviceConfigModel _SelectedDevice;
public DeviceConfigModel SelectedDevice public DeviceConfigModel SelectedDevice
{ {
get => _SelectedDevice; get => _SelectedDevice;
set => SetProperty(ref _SelectedDevice, value); set
{
if(SetProperty(ref _SelectedDevice, value))
{
if(value.CommunicationConfig is SerialPortConfig)
{
SelectedDeviceType = true;
}
else
{
SelectedDeviceType = false;
}
}
}
} }
private SystemConfig _config; private SystemConfig _config;
@ -44,7 +65,19 @@ namespace ProcessManager.ViewModels
private void Save() private void Save()
{ {
RequestClose.Invoke(); try
{
//foreach(var d in Config.DeviceList)
//{
// d.Id=Guid.NewGuid();
//}
JsonHelper.SaveToFile(Config);
RequestClose.Invoke();
}
catch (Exception)
{
throw;
}
} }
private void Cancel() private void Cancel()