优化多程序启动界面

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.Models;
using BOB.Singleton;
using BOB.ViewModels;
using BOB.ViewModels.Dialogs;
using BOB.Views;
@ -7,6 +8,7 @@ using BOB.Views.Dialogs;
using Castle.DynamicProxy;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using static System.Runtime.InteropServices.JavaScript.JSType;
@ -22,6 +24,21 @@ namespace BOB
{
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()
{
base.OnInitialized();
@ -40,6 +57,7 @@ namespace BOB
//注册全局变量
containerRegistry.RegisterSingleton<GlobalVariables>();
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]
public string SystemPath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BOB");
public string Title { get; set; } = "";
public int PerformanceLevel { get; set; } = 50;
@ -51,7 +52,7 @@ namespace BOB
if (!Directory.Exists(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,
@ -73,11 +74,13 @@ namespace BOB
public void LoadFromFile()
{
string configPath = Path.Combine(SystemPath, "system.json");
string configPath = Path.Combine(SystemPath, $"{Title}.json");
if(Title == "")
{
return;
}
if (!File.Exists(configPath))
{
// 文件不存在则保存当前配置
SaveToFile();
return;
}

View File

@ -1,6 +1,9 @@
using Logger;
using BOB.Singleton;
using DeviceCommand.Device;
using Logger;
using Prism.Mvvm;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
@ -18,8 +21,10 @@ namespace BOB.ViewModels
set => SetProperty(ref _logs, value);
}
public ICommand ClearLogCommand { get; set; }
public LogAreaViewModel()
public Devices devices { get; set; }
public LogAreaViewModel(IContainerProvider containerProvider)
{
devices=containerProvider.Resolve<Devices>();
ClearLogCommand = new DelegateCommand(ClearLog);
LoggerHelper.Progress = new System.Progress<(string message, string color,int depth)>(
log =>
@ -27,8 +32,63 @@ namespace BOB.ViewModels
var brush = (Brush)new BrushConverter().ConvertFromString(log.color);
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()
{
Logs.Clear();

View File

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

View File

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

View File

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

View File

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

View File

@ -11,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand]
public class EAEL9080:ModbusTcp
{
public EAEL9080(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
{
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
ConnectAsync();
}
public EAEL9080()
{
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync();
}
#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]
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()
{
ConfigureDevice("COM1", 9600, 8, StopBits.One, Parity.None, 3000, 3000);
ConnectAsync();
}

View File

@ -1,6 +1,7 @@
using Common.Attributes;
using DeviceCommand.Base;
using System;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
@ -10,9 +11,13 @@ namespace DeviceCommand.Device
[BOBCommand]
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()
{
ConfigureDevice("COM1", 9600);
ConnectAsync();
}
public byte SlaveAddress { get; set; } = 1; // default slave address

View File

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

View File

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

View File

@ -9,9 +9,13 @@ namespace DeviceCommand.Device
[BOBCommand]
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()
{
ConfigureDevice("127.0.0.1", 502, 3000, 3000);
ConnectAsync();
}
private const byte SlaveAddress = 1;

View File

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

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Castle.Core" Version="5.2.1" />
<PackageReference Include="NLog" Version="6.0.5" />
</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; } =
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()
{
@ -41,10 +41,7 @@ namespace ProcessManager
{
if (!File.Exists(ConfigFile))
{
// 如果不存在自动创建一个默认的 SystemConfig
var defaultConfig = new SystemConfig();
SaveToFile(defaultConfig);
return defaultConfig;
throw new Exception("没找到系统配置文件");
}
try

View File

@ -1,188 +1,79 @@
<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: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: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"
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">
<UniformGrid Columns="3"
Margin="16"
HorizontalAlignment="Center"
Cursor="">
<materialDesign:Card Width="220"
Margin="5"
Padding="8"
Height="230"
Background="DarkSlateGray"
Foreground="white"
Cursor="">
<StackPanel>
<TextBlock Margin="16,16,12,8"
FontSize="16"
Text="设备1" />
<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}" />
<ItemsControl ItemsSource="{Binding ConfigList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<materialDesign:Card Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}"
Width="220"
Margin="5"
Padding="8"
Height="230"
Background="DarkSlateGray"
Foreground="white">
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu>
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="设备1" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="设备1" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
<materialDesign:Card Width="220"
Margin="5"
Height="230"
Padding="8"
Background="#1976D2"
Foreground="white">
<StackPanel>
<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>
<!-- 标题 -->
<TextBlock Margin="16,16,12,8"
FontSize="16"
Text="{Binding Title}" />
</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}" />
<!-- 设备列表自动生成对应 CheckBox -->
<ItemsControl ItemsSource="{Binding DeviceList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Margin="16,4,16,0"
Content="{Binding DeviceName}"
IsChecked="{Binding IsEnabled,Mode=TwoWay}"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}" />
</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}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu>
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="水冷机和环境箱" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="水冷机和环境箱" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
<Separator Style="{StaticResource MaterialDesignLightSeparator}" />
</UniformGrid>
</StackPanel>
<materialDesign:Card.ContextMenu>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Tag}">
<MenuItem Header="编辑"
Command="{Binding EditCommand}"
CommandParameter="{Binding PlacementTarget.DataContext.Title,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
<MenuItem Header="启动"
Command="{Binding StartCommand}"
CommandParameter="{Binding PlacementTarget.DataContext.Title,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</materialDesign:Card.ContextMenu>
</materialDesign:Card>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</materialDesign:DialogHost>
</mah:MetroWindow>

View File

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:local="clr-namespace:ProcessManager"
Height="500"
Width="800">
<Grid>
<Grid.ColumnDefinitions>
@ -64,65 +64,91 @@
Width="200" />
</StackPanel>
<!-- IPAddress -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="IP地址:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.IPAddress, Mode=TwoWay}"
Width="200" />
<!-- TCP相关 -->
<StackPanel Orientation="Vertical">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility"
Value="Collapsed" />
<Style.Triggers>
<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>
<!-- Port -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="端口:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.Port, Mode=TwoWay}"
Width="200" />
</StackPanel>
<!-- IPAddress -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="串口:"
Width="120"
VerticalAlignment="Center" />
<TextBox Text="{Binding CommunicationConfig.COMPort, Mode=TwoWay}"
Width="200" />
<!-- 串口相关 -->
<StackPanel Orientation="Vertical">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility"
Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.SelectedDeviceType, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="True">
<Setter Property="Visibility"
Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<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>
<!-- 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"
Margin="0,2">
<TextBlock Text="读取超时:"
@ -131,8 +157,6 @@
<TextBox Text="{Binding CommunicationConfig.ReadTimeout, Mode=TwoWay}"
Width="200" />
</StackPanel>
<!-- WriteTimeout -->
<StackPanel Orientation="Horizontal"
Margin="0,2">
<TextBlock Text="写入超时:"
@ -145,6 +169,7 @@
<StackPanel Orientation="Horizontal"
Grid.ColumnSpan="2"
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.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Reflection.Metadata;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace ProcessManager.ViewModels
{
public class PMainViewModel:BindableBase
{
private ObservableCollection<SystemConfig> _configList;
public ObservableCollection<SystemConfig> ConfigList
{
get => _configList;
set => SetProperty(ref _configList, value);
}
private readonly IDialogService _dialogService;
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 PMainViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
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)
@ -41,13 +71,24 @@ namespace ProcessManager.ViewModels
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);
}
@ -64,6 +105,7 @@ namespace ProcessManager.ViewModels
MessageBox.Show("请先关闭进程再进行编辑");
return;
}
JsonHelper.ConfigFile = Path.Combine(JsonHelper.SystemPath, $"{deviceName}.json");
var ConfigSystem = JsonHelper.LoadFromFile();
var parameters = new DialogParameters
{

View File

@ -3,6 +3,7 @@ using BOB.Models;
using Model;
using Prism.Mvvm;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows.Input;
namespace ProcessManager.ViewModels
@ -16,12 +17,32 @@ namespace ProcessManager.ViewModels
get => _Title;
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;
public DeviceConfigModel 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;
@ -44,7 +65,19 @@ namespace ProcessManager.ViewModels
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()