整合设备

This commit is contained in:
hsc 2025-11-20 13:42:13 +08:00
parent a25a4bc6ed
commit d2bf7ab4c0
15 changed files with 237 additions and 256 deletions

View File

@ -59,7 +59,12 @@ namespace BOB
containerRegistry.RegisterSingleton<StepRunning>(); containerRegistry.RegisterSingleton<StepRunning>();
containerRegistry.RegisterSingleton<Devices>(); containerRegistry.RegisterSingleton<Devices>();
} }
protected override void OnExit(ExitEventArgs e)
{
var devices = Container.Resolve<Devices>();
devices.Dispose();
base.OnExit(e);
}
} }
} }

View File

@ -13,6 +13,9 @@ namespace BOB.Singleton
{ {
public class Devices public class Devices
{ {
private CancellationTokenSource _appCancellationTokenSource = new();
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>();
private readonly ProxyGenerator _proxyGen = new ProxyGenerator(); private readonly ProxyGenerator _proxyGen = new ProxyGenerator();
@ -230,5 +233,11 @@ namespace BOB.Singleton
} }
} }
public void Dispose()
{
_appCancellationTokenSource.Cancel();
_appCancellationTokenSource.Dispose();
}
} }
} }

View File

@ -1,5 +1,6 @@
using BOB.Models; using BOB.Models;
using Common.PubEvent; using Common.PubEvent;
using Model;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -21,156 +22,30 @@ namespace BOB.ViewModels.Dialogs
get => _title; get => _title;
set => SetProperty(ref _title, value); set => SetProperty(ref _title, value);
} }
private ObservableCollection<string> _types = ["串口", "Tcp", "ModbusRtu", "ModbusTcp", "CAN", "Udp"]; private DeviceInfoModel _Device;
public ObservableCollection<string> Types public DeviceInfoModel Device
{
get => _types;
set => SetProperty(ref _types, value);
}
private string _Mode;
public string Mode
{
get => _Mode;
set => SetProperty(ref _Mode, value);
}
private bool _IsAdd;
public bool IsAdd
{
get => _IsAdd;
set => SetProperty(ref _IsAdd, value);
}
private ProgramModel _program;
public ProgramModel Program
{
get => _program;
set => SetProperty(ref _program, value);
}
private DeviceModel _Device;
public DeviceModel Device
{ {
get => _Device; get => _Device;
set => SetProperty(ref _Device, value); set => SetProperty(ref _Device, value);
} }
private ObservableCollection<DeviceConnectSettingModel> _DeviceConnectSettings=new();
public ObservableCollection<DeviceConnectSettingModel> DeviceConnectSettings
{
get => _DeviceConnectSettings;
set => SetProperty(ref _DeviceConnectSettings, value);
}
#endregion #endregion
public DialogCloseListener RequestClose { get; set; } public DialogCloseListener RequestClose { get; set; }
private GlobalVariables _globalVariables; private GlobalVariables _globalVariables;
private IEventAggregator _eventAggregator; private IEventAggregator _eventAggregator;
public ICommand CancelCommand { get; set; } public ICommand CancelCommand { get; set; }
public ICommand SaveCommand { get; set; } public ICommand SaveCommand { get; set; }
public ICommand SelectionChangedCommand { get; set; }
public DeviceSettingViewModel(GlobalVariables globalVariables, IEventAggregator eventAggregator) public DeviceSettingViewModel(GlobalVariables globalVariables, IEventAggregator eventAggregator)
{ {
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_globalVariables = globalVariables; _globalVariables = globalVariables;
CancelCommand = new DelegateCommand(Cancel); CancelCommand = new DelegateCommand(Cancel);
SaveCommand = new DelegateCommand(Save); SaveCommand = new DelegateCommand(Save);
SelectionChangedCommand = new DelegateCommand(SelectionChanged);
} }
private void SelectionChanged()
{
if (Device == null) return;
DeviceConnectSettings.Clear();
switch (Device!.Type)
{
case "Tcp":
case "ModbusTcp":
DeviceConnectSettings.Add(new()
{
Name = "IP地址",
Value = "127.0.0.1"
});
DeviceConnectSettings.Add(new()
{
Name = "端口号",
Value = "502"
});
break;
case "串口":
case "ModbusRtu":
DeviceConnectSettings.Add(new()
{
Name = "COM口",
Value = "COM1"
});
DeviceConnectSettings.Add(new()
{
Name = "波特率",
Value = "9600"
});
DeviceConnectSettings.Add(new()
{
Name = "数据位",
Value = "8"
});
DeviceConnectSettings.Add(new()
{
Name = "停止位",
Value = "1"
});
DeviceConnectSettings.Add(new()
{
Name = "奇偶",
Value = "无"
});
break;
case "Udp":
DeviceConnectSettings.Add(new()
{
Name = "远程IP地址",
Value = "127.0.0.1"
});
DeviceConnectSettings.Add(new()
{
Name = "远程端口号",
Value = "8080"
});
DeviceConnectSettings.Add(new()
{
Name = "本地端口号",
Value = "0"
});
break;
}
DeviceConnectSettings.Add(new()
{
Name = "读超时",
Value = "3000"
});
DeviceConnectSettings.Add(new()
{
Name = "写超时",
Value = "3000"
});
}
private void Save() private void Save()
{ {
if (Mode == "ADD")
{
Program.Devices.Add(Device);
_globalVariables.SelectedDevice = Device;
}
else
{
var index = Program.Devices
.Select((x, i) => new { x, i })
.FirstOrDefault(p => p.x.ID == _globalVariables.SelectedDevice.ID)?.i;
if (index.HasValue)
{
Program.Devices[index.Value] = Device;
}
}
RequestClose.Invoke(ButtonResult.OK); RequestClose.Invoke(ButtonResult.OK);
} }
@ -192,20 +67,7 @@ namespace BOB.ViewModels.Dialogs
public void OnDialogOpened(IDialogParameters parameters) public void OnDialogOpened(IDialogParameters parameters)
{ {
_eventAggregator.GetEvent<OverlayEvent>().Publish(true); _eventAggregator.GetEvent<OverlayEvent>().Publish(true);
Program = _globalVariables.Program;
Mode = parameters.GetValue<string>("Mode");
if(Mode == "ADD")
{
Device = new();
IsAdd = true;
Device.Type = "Tcp";
SelectionChanged();
}
else
{
Device = new DeviceModel(_globalVariables.SelectedDevice);
IsAdd = false;
}
} }
#endregion #endregion
} }

View File

@ -1,6 +1,11 @@
using BOB.Models; using BOB.Models;
using BOB.Singleton;
using Common.PubEvent; using Common.PubEvent;
using DeviceCommand.Base;
using DeviceCommand.Device;
using Logger;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using Model;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -16,6 +21,21 @@ namespace BOB.ViewModels
public class ParametersManagerViewModel:BindableBase public class ParametersManagerViewModel:BindableBase
{ {
#region #region
private ObservableCollection<DeviceConfigModel> _DeviceList;
public ObservableCollection<DeviceConfigModel> DeviceList
{
get { return _DeviceList; }
set { SetProperty(ref _DeviceList,value); }
}
private ObservableCollection<DeviceInfoModel> _DeviceInfoModel;
public ObservableCollection<DeviceInfoModel> DeviceInfoModel
{
get { return _DeviceInfoModel; }
set { SetProperty(ref _DeviceInfoModel, value); }
}
public ProgramModel Program public ProgramModel Program
{ {
get => _globalVariables.Program; get => _globalVariables.Program;
@ -40,84 +60,148 @@ namespace BOB.ViewModels
} }
} }
} }
private DeviceModel _SelectedDevice; private DeviceInfoModel _SelectedDevice;
public DeviceModel SelectedDevice public DeviceInfoModel SelectedDevice
{ {
get => _SelectedDevice; get { return _SelectedDevice; }
set set { SetProperty(ref _SelectedDevice, value); }
{
if (SetProperty(ref _SelectedDevice, value))
{
_globalVariables.SelectedDevice = value;
}
}
} }
#endregion #endregion
private GlobalVariables _globalVariables { get; set; } private GlobalVariables _globalVariables { get; set; }
private IDialogService _dialogService { get; set; } private IDialogService _dialogService { get; set; }
private Devices _devices { get; set; }
private IEventAggregator _eventAggregator { get; set; } private IEventAggregator _eventAggregator { get; set; }
public ICommand ParameterAddCommand { get; set; } public ICommand ParameterAddCommand { get; set; }
public ICommand ParameterEditCommand { get; set; } public ICommand ParameterEditCommand { get; set; }
public ICommand ParameterDeleteCommand { get; set; } public ICommand ParameterDeleteCommand { get; set; }
public ICommand DeviceAddCommand { get; set; }
public ICommand DeviceEditCommand { get; set; } public ICommand DeviceEditCommand { get; set; }
public ICommand DeviceDeleteCommand { get; set; } public ICommand ReconnnectCommand { get; set; }
public ParametersManagerViewModel(GlobalVariables GlobalVariables,IDialogService dialogService,IEventAggregator eventAggregator) public ParametersManagerViewModel(GlobalVariables GlobalVariables,IDialogService dialogService,IEventAggregator eventAggregator,IContainerProvider containerProvider)
{ {
_globalVariables = GlobalVariables; _globalVariables = GlobalVariables;
_eventAggregator= eventAggregator; _eventAggregator= eventAggregator;
_devices=containerProvider.Resolve<Devices>();
_dialogService = dialogService; _dialogService = dialogService;
Program = _globalVariables.Program; Program = _globalVariables.Program;
ParameterAddCommand = new DelegateCommand(ParameterAdd); ParameterAddCommand = new DelegateCommand(ParameterAdd);
ParameterEditCommand = new DelegateCommand(ParameterEdit); ParameterEditCommand = new DelegateCommand(ParameterEdit);
ParameterDeleteCommand = new DelegateCommand(ParameterDelete); ParameterDeleteCommand = new DelegateCommand(ParameterDelete);
DeviceAddCommand = new DelegateCommand(DeviceAdd);
DeviceEditCommand = new DelegateCommand(DeviceEdit); DeviceEditCommand = new DelegateCommand(DeviceEdit);
DeviceDeleteCommand = new DelegateCommand(DeviceDelete); ReconnnectCommand = new DelegateCommand(Reconnnect);
DeviceList = new ObservableCollection<DeviceConfigModel>(SystemConfig.Instance.DeviceList);
InitData();
} }
private void Reconnnect()
{
if(SelectedDevice!=null)
{
if (_devices.DeviceDic.TryGetValue(SelectedDevice.Remark, out var device) && device != null)
{
Task.Run(async () =>
{
switch (device)
{
case ITcp tcpDevice:
await tcpDevice.ConnectAsync();
break;
case ISerialPort serialDevice:
await serialDevice.ConnectAsync();
break;
case IModbusDevice modbusDevice:
await modbusDevice.ConnectAsync();
break;
}
});
}
}
}
private void InitData()
{
DeviceInfoModel = new ObservableCollection<DeviceInfoModel>();
for (int i = 0; i < DeviceList.Count; i++)
{
DeviceInfoModel.Add(new DeviceInfoModel
{
DeviceName = DeviceList[i].DeviceName,
Remark = DeviceList[i].Remark,
IsConnected = false,
IsEnabled= DeviceList[i].IsEnabled,
DeviceType= DeviceList[i].DeviceType,
CommunicationConfig= DeviceList[i].CommunicationConfig
});
}
var progress = new Progress<(int index, bool isConnected)>();
progress.ProgressChanged += (s, e) =>
{
DeviceInfoModel[e.index].IsConnected = e.isConnected;
};
var token = _devices.AppCancellationToken;
Task.Run(() => PollingConnection(progress, token));
}
private async Task PollingConnection(IProgress<(int index, bool isConnected)> progress, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
for (int i = 0; i < DeviceList.Count; i++)
{
bool isConnected = false;
if (_devices.DeviceDic.TryGetValue(DeviceList[i].Remark, out var device) && device != null)
{
switch (device)
{
case ITcp tcpDevice:
isConnected = tcpDevice.TcpClient?.Connected ?? false;
break;
case ISerialPort serialDevice:
isConnected = serialDevice.SerialPort?.IsOpen ?? false;
break;
case IModbusDevice modbusDevice:
isConnected = (modbusDevice.TcpClient?.Connected ?? false) || (modbusDevice.SerialPort?.IsOpen ?? false);
break;
}
}
progress.Report((i, isConnected));
}
await Task.Delay(200, token); // 支持取消
}
}
#region #region
private void DeviceDelete()
{
Program.Devices.Remove(SelectedDevice);
}
private void DeviceEdit() private void DeviceEdit()
{ {
if(SelectedDevice==null)
{
return;
}
var param = new DialogParameters var param = new DialogParameters
{ {
{ "Mode", SelectedDevice==null?"ADD":"Edit" } { "Devices", SelectedDevice }
}; };
_dialogService.ShowDialog("DeviceSetting", param, (r) => _dialogService.ShowDialog("DeviceSetting", param, (r) =>
{ {
if (r.Result == ButtonResult.OK) if (r.Result == ButtonResult.OK)
{ {
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
}
else
{
}
});
}
private void DeviceAdd()
{
var param = new DialogParameters
{
{ "Mode", "ADD" }
};
_dialogService.ShowDialog("DeviceSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
} }
else else
{ {

View File

@ -56,31 +56,12 @@
<StackPanel Height="30" <StackPanel Height="30"
Orientation="Horizontal" Orientation="Horizontal"
Margin="7"> Margin="7">
<Label Content="通讯协议类型" <Label Content="是否启用"
VerticalAlignment="Bottom"
Width="85" />
<ComboBox ItemsSource="{Binding Types}"
SelectedItem="{Binding Device.Type,UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Bottom"
Width="120"
materialDesign:HintAssist.Hint="通讯协议类型"
IsEnabled="{Binding IsAdd}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding SelectionChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
<StackPanel Height="30"
Orientation="Horizontal"
Margin="7">
<Label Content="设备描述"
Width="85" Width="85"
VerticalAlignment="Bottom" /> VerticalAlignment="Bottom" />
<TextBox Text="{Binding Device.Description}" <CheckBox IsChecked="{Binding IsEnable}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
materialDesign:HintAssist.Hint="设备描述" materialDesign:HintAssist.Hint="是否启用"
Width="120" /> Width="120" />
</StackPanel> </StackPanel>
<Label Content="连接参数" <Label Content="连接参数"

View File

@ -70,7 +70,7 @@
<TabItem Header="设备"> <TabItem Header="设备">
<DataGrid Padding="10" <DataGrid Padding="10"
Background="Transparent" Background="Transparent"
ItemsSource="{Binding Program.Devices}" ItemsSource="{Binding DeviceInfoModel}"
AutoGenerateColumns="False" AutoGenerateColumns="False"
CanUserAddRows="False" CanUserAddRows="False"
IsReadOnly="True" IsReadOnly="True"
@ -90,12 +90,12 @@
<Setter Property="Fill" <Setter Property="Fill"
Value="Transparent" /> Value="Transparent" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding Connected}" <DataTrigger Binding="{Binding IsConnected}"
Value="True"> Value="True">
<Setter Property="Fill" <Setter Property="Fill"
Value="LimeGreen" /> Value="LimeGreen" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding Connected}" <DataTrigger Binding="{Binding IsConnected}"
Value="False"> Value="False">
<Setter Property="Fill" <Setter Property="Fill"
Value="Red" /> Value="Red" />
@ -109,25 +109,24 @@
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTextColumn Header="设备名称" <DataGridTextColumn Header="设备名称"
Binding="{Binding Name}" /> Binding="{Binding DeviceName}" />
<DataGridTextColumn Header="备注" <DataGridTemplateColumn Header="是否启用">
Binding="{Binding Description}" /> <DataGridTemplateColumn.CellTemplate>
</DataGrid.Columns> <DataTemplate>
<CheckBox IsChecked="{Binding IsEnabled}" IsEnabled="False" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="新增" <!--<MenuItem Header="编辑"
Command="{Binding DeviceAddCommand}" /> Command="{Binding DeviceEditCommand}" />-->
<MenuItem Header="编辑" <MenuItem Header="重新连接"
Command="{Binding DeviceEditCommand}" Command="{Binding ReconnnectCommand}" />
/>
<MenuItem Header="删除"
Foreground="Red"
Command="{Binding DeviceDeleteCommand}"
/>
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
</DataGrid> </DataGrid>
</TabItem> </TabItem>
</TabControl> </TabControl>

View File

@ -1,6 +1,9 @@
using System; using NModbus;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Ports;
using System.Linq; using System.Linq;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,6 +11,9 @@ namespace DeviceCommand.Base
{ {
public interface IModbusDevice public interface IModbusDevice
{ {
public TcpClient TcpClient { get; set; }
public SerialPort SerialPort { get; set; }
public IModbusMaster Modbus { get; set; }
Task<bool> ConnectAsync(CancellationToken ct = default); Task<bool> ConnectAsync(CancellationToken ct = default);
void Close(); void Close();

View File

@ -7,6 +7,7 @@ namespace DeviceCommand.Base
{ {
public interface ISerialPort public interface ISerialPort
{ {
public SerialPort SerialPort { get; set; }
Task<bool> ConnectAsync(CancellationToken ct = default); Task<bool> ConnectAsync(CancellationToken ct = default);
void Close(); void Close();

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -6,6 +7,7 @@ namespace DeviceCommand.Base
{ {
public interface ITcp public interface ITcp
{ {
public TcpClient TcpClient { get; set; }
Task<bool> ConnectAsync(CancellationToken ct = default); Task<bool> ConnectAsync(CancellationToken ct = default);
void Close(); void Close();

View File

@ -2,6 +2,7 @@
using NModbus.Serial; using NModbus.Serial;
using System; using System;
using System.IO.Ports; using System.IO.Ports;
using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,8 +18,9 @@ namespace DeviceCommand.Base
public int ReadTimeout { get; private set; } = 3000; public int ReadTimeout { get; private set; } = 3000;
public int WriteTimeout { get; private set; } = 3000; public int WriteTimeout { get; private set; } = 3000;
public SerialPort SerialPort { get; private set; } = new SerialPort(); public SerialPort SerialPort { get; set; } = new SerialPort();
public IModbusMaster Modbus { get; private set; } public TcpClient TcpClient { get; set; }
public IModbusMaster Modbus { get; set; }
private readonly SemaphoreSlim _commLock = new(1, 1); private readonly SemaphoreSlim _commLock = new(1, 1);

View File

@ -1,5 +1,6 @@
using NModbus; using NModbus;
using System; using System;
using System.IO.Ports;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
@ -13,8 +14,9 @@ namespace DeviceCommand.Base
public int Port { get; private set; } = 502; public int Port { get; private set; } = 502;
public int SendTimeout { get; private set; } = 3000; public int SendTimeout { get; private set; } = 3000;
public int ReceiveTimeout { get; private set; } = 3000; public int ReceiveTimeout { get; private set; } = 3000;
public TcpClient TcpClient { get; private set; } = new TcpClient(); public SerialPort SerialPort { get; set; }
public IModbusMaster Modbus { get; private set; } public TcpClient TcpClient { get; set; } = new TcpClient();
public IModbusMaster Modbus { get; set; }
private readonly SemaphoreSlim _commLock = new(1, 1); private readonly SemaphoreSlim _commLock = new(1, 1);

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace DeviceCommand.Base namespace DeviceCommand.Base
{ {
public class Serial_Port: ISerialPort public class Serial_Port : ISerialPort
{ {
public string PortName { get; set; } = "COM1"; public string PortName { get; set; } = "COM1";
public int BaudRate { get; set; } = 9600; public int BaudRate { get; set; } = 9600;
@ -15,8 +15,8 @@ namespace DeviceCommand.Base
public Parity Parity { get; set; } = Parity.None; public Parity Parity { get; set; } = Parity.None;
public int ReadTimeout { get; set; } = 3000; public int ReadTimeout { get; set; } = 3000;
public int WriteTimeout { get; set; } = 3000; public int WriteTimeout { get; set; } = 3000;
public SerialPort _SerialPort { get; private set; } = new SerialPort(); public SerialPort SerialPort { get; set; } = new SerialPort();
private readonly SemaphoreSlim _commLock = new(1, 1); private readonly SemaphoreSlim commLock = new(1, 1);
public void ConfigureDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000) public void ConfigureDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000)
{ {
@ -30,43 +30,43 @@ namespace DeviceCommand.Base
} }
public async Task<bool> ConnectAsync(CancellationToken ct = default) public async Task<bool> ConnectAsync(CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await commLock.WaitAsync(ct);
try try
{ {
if (_SerialPort.IsOpen) _SerialPort.Close(); if (SerialPort.IsOpen) SerialPort.Close();
_SerialPort.PortName = PortName; SerialPort.PortName = PortName;
_SerialPort.BaudRate = BaudRate; SerialPort.BaudRate = BaudRate;
_SerialPort.DataBits = DataBits; SerialPort.DataBits = DataBits;
_SerialPort.StopBits = StopBits; SerialPort.StopBits = StopBits;
_SerialPort.Parity = Parity; SerialPort.Parity = Parity;
_SerialPort.ReadTimeout = ReadTimeout; SerialPort.ReadTimeout = ReadTimeout;
_SerialPort.WriteTimeout = WriteTimeout; SerialPort.WriteTimeout = WriteTimeout;
_SerialPort.Open(); SerialPort.Open();
return true; return true;
} }
finally finally
{ {
_commLock.Release(); commLock.Release();
} }
} }
public void Close() public void Close()
{ {
if (_SerialPort.IsOpen) _SerialPort.Close(); if (SerialPort.IsOpen) SerialPort.Close();
} }
public async Task SendAsync(string data, CancellationToken ct = default) public async Task SendAsync(string data, CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await commLock.WaitAsync(ct);
try try
{ {
if (!_SerialPort.IsOpen) return; if (!SerialPort.IsOpen) return;
byte[] bytes = Encoding.UTF8.GetBytes(data); byte[] bytes = Encoding.UTF8.GetBytes(data);
var timeoutTask = Task.Delay(WriteTimeout > 0 ? WriteTimeout : Timeout.Infinite, ct); var timeoutTask = Task.Delay(WriteTimeout > 0 ? WriteTimeout : Timeout.Infinite, ct);
var sendTask = Task.Run(() => _SerialPort.Write(bytes, 0, bytes.Length), ct); var sendTask = Task.Run(() => SerialPort.Write(bytes, 0, bytes.Length), ct);
var completedTask = await Task.WhenAny(sendTask, timeoutTask); var completedTask = await Task.WhenAny(sendTask, timeoutTask);
if (completedTask == timeoutTask) throw new TimeoutException($"写入操作在 {WriteTimeout} ms内未完成"); if (completedTask == timeoutTask) throw new TimeoutException($"写入操作在 {WriteTimeout} ms内未完成");
@ -74,16 +74,16 @@ namespace DeviceCommand.Base
} }
finally finally
{ {
_commLock.Release(); commLock.Release();
} }
} }
public async Task<string> ReadAsync(string delimiter = "\n", CancellationToken ct = default) public async Task<string> ReadAsync(string delimiter = "\n", CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await commLock.WaitAsync(ct);
try try
{ {
if (!_SerialPort.IsOpen) return null; if (!SerialPort.IsOpen) return null;
delimiter ??= "\n"; delimiter ??= "\n";
var sb = new StringBuilder(); var sb = new StringBuilder();
@ -91,9 +91,9 @@ namespace DeviceCommand.Base
while (!ct.IsCancellationRequested) while (!ct.IsCancellationRequested)
{ {
if (_SerialPort.BytesToRead > 0) if (SerialPort.BytesToRead > 0)
{ {
int bytesRead = _SerialPort.Read(buffer, 0, buffer.Length); int bytesRead = SerialPort.Read(buffer, 0, buffer.Length);
sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal); int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal);
@ -112,7 +112,7 @@ namespace DeviceCommand.Base
} }
finally finally
{ {
_commLock.Release(); commLock.Release();
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace DeviceCommand.Base
public int Port { get; private set; } = 502; public int Port { get; private set; } = 502;
public int SendTimeout { get; private set; } = 3000; public int SendTimeout { get; private set; } = 3000;
public int ReceiveTimeout { get; private set; } = 3000; public int ReceiveTimeout { get; private set; } = 3000;
public TcpClient TcpClient { get; private set; } = new TcpClient(); public TcpClient TcpClient { get; set; } = new TcpClient();
private readonly SemaphoreSlim _commLock = new(1, 1); private readonly SemaphoreSlim _commLock = new(1, 1);
public void ConfigureDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) public void ConfigureDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)

View File

@ -8,6 +8,14 @@ public class LoggingInterceptor : IInterceptor
{ {
string className = invocation.TargetType.Name; string className = invocation.TargetType.Name;
string methodName = invocation.Method.Name; string methodName = invocation.Method.Name;
// 排除 TcpClient, SerialPort, Modbus 的 getter
if (methodName.Contains("get_TcpClient") ||
methodName.Contains("get_SerialPort") ||
methodName.Contains("get_Modbus"))
{
invocation.Proceed(); // 不拦截
return;
}
LoggerHelper.InfoWithNotify($"调用 {className}.{methodName}() 开始"); LoggerHelper.InfoWithNotify($"调用 {className}.{methodName}() 开始");

20
Model/DeviceInfoModel.cs Normal file
View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class DeviceInfoModel
{
public string DeviceName { get; set; }
public string DeviceType { get; set; }
public string Remark { get; set; }
public bool IsEnabled { get; set; }
public bool IsConnected { get; set; }
public ICommunicationConfig CommunicationConfig { get; set; }
}
}