Files
ADP/UIShare/GlobalVariable/DeviceManager.cs
2026-06-12 09:25:40 +08:00

304 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DeviceCommand.Base;
using Logger;
using Model.Models;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using UIShare.UIViewModel;
namespace UIShare.GlobalVariable
{
/// <summary>
/// 设备管理器:根据 <see cref="SystemConfig.DeviceList"/> 反射实例化所有启用的设备,
/// 通过 <see cref="IBaseInterface"/> 多态统一管理,避免为每种设备单独硬编码字段。
/// </summary>
public class DeviceManager
{
private object _lockObj = new object();
public SystemConfig _systemConfig { get; set; }
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
= new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
public DeviceManager(SystemConfig systemConfig)
{
_systemConfig = systemConfig;
InitDevices();
}
private void InitDevices()
{
DeviceMap = new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
if (_systemConfig?.DeviceList == null) return;
foreach (var config in _systemConfig.DeviceList)
{
if (config == null || !config.IsEnabled) continue;
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
{
LoggerHelper.Warn($"未识别的设备类型 [{config.DeviceType}],已跳过 [{config.DeviceName}]。");
continue;
}
try
{
IBaseInterface? instance = config.ConnectionType switch
{
"Tcp" => CreateTcpDevice(deviceType, config.TcpConfig),
"Serial" => CreateSerialDevice(deviceType, config.SerialPortConfig),
_ => null
};
if (instance == null)
{
LoggerHelper.Warn($"设备 [{config.DeviceName}] 连接方式 [{config.ConnectionType}] 不支持,已跳过。");
continue;
}
if (!string.IsNullOrWhiteSpace(config.DeviceName))
{
DeviceMap[config.DeviceName] = instance;
}
LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}]");
}
catch (Exception ex)
{
var inner = ex.InnerException?.Message ?? ex.Message;
LoggerHelper.ErrorWithNotify($"设备 [{config.DeviceName}] 实例化失败:{inner}");
}
}
}
public async Task ConnectAllDevices(CancellationToken ct = default)
{
if (_systemConfig?.DeviceList == null || DeviceMap.Count == 0) return;
var tasks = new List<Task>();
foreach (var info in _systemConfig.DeviceList)
{
if (info == null || !info.IsEnabled) continue;
if (string.IsNullOrWhiteSpace(info.DeviceName)) continue;
if (!DeviceMap.TryGetValue(info.DeviceName, out var device)) continue;
tasks.Add(ConnectInternalAsync(info, device, ct));
}
await Task.WhenAll(tasks);
}
public async Task ConnectSpecifiedDevice(string deviceName, CancellationToken ct = default)
{
if (string.IsNullOrWhiteSpace(deviceName))
{
LoggerHelper.Warn("ConnectSpecifiedDevice设备名为空。");
return;
}
if (!DeviceMap.TryGetValue(deviceName, out var device))
{
LoggerHelper.Warn($"ConnectSpecifiedDevice未找到设备 [{deviceName}]。");
return;
}
var info = _systemConfig?.DeviceList?
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
await ConnectInternalAsync(info, device, ct);
}
/// <summary>
/// 异步关闭指定设备,释放底层连接并更新 UI 状态
/// </summary>
public async Task CloseDeviceAsync(string deviceName)
{
if (string.IsNullOrWhiteSpace(deviceName)) return;
IBaseInterface? device;
DeviceInfoVM? info;
lock (_lockObj)
{
if (!DeviceMap.TryGetValue(deviceName, out device)) return;
info = _systemConfig?.DeviceList?
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
}
await CloseInternalAsync(info, device);
}
/// <summary>
/// 异步关闭所有设备
/// </summary>
public async Task CloseAllDevicesAsync()
{
List<Task> tasks = new List<Task>();
lock (_lockObj)
{
if (DeviceMap.Count == 0) return;
foreach (var kvp in DeviceMap)
{
string deviceName = kvp.Key;
var device = kvp.Value;
var info = _systemConfig?.DeviceList?
.FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
tasks.Add(CloseInternalAsync(info, device));
}
}
await Task.WhenAll(tasks);
LoggerHelper.Info("所有设备已执行关闭操作。");
}
#region
private async Task CloseInternalAsync(DeviceInfoVM? info, IBaseInterface device)
{
string name = info?.DeviceName ?? device.GetType().Name;
string conn = info?.ConnectionType ?? "?";
try
{
// 如果设备本身已经是断开状态,直接更新 UI 并返回
if (!device.IsConnected)
{
if (info != null) info.IsConnected = false;
LoggerHelper.Info($"设备 [{name}] 本就处于断开状态。");
return;
}
await Task.Run(() => device.Close());
LoggerHelper.Info($"设备 [{name}/{conn}] 已成功关闭连接。");
}
catch (Exception ex)
{
var inner = ex.InnerException?.Message ?? ex.Message;
LoggerHelper.Error($"设备 [{name}/{conn}] 关闭连接时出现异常: {inner}");
}
finally
{
// 无论关闭时是否抛出异常,均强制同步 UI 状态为未连接
if (info != null)
{
info.IsConnected = false;
}
}
}
private async Task ConnectInternalAsync(DeviceInfoVM? info, IBaseInterface device, CancellationToken ct)
{
string name = info?.DeviceName ?? device.GetType().Name;
string conn = info?.ConnectionType ?? "?";
try
{
if (device.IsConnected)
{
if (info != null) info.IsConnected = true;
LoggerHelper.Info($"设备 [{name}] 已连接,跳过。");
return;
}
bool ok = conn switch
{
"Tcp" => await ConnectTcpAsync(name, device, ct),
"Serial" => await ConnectSerialAsync(name, device, ct),
_ => false
};
if (info != null) info.IsConnected = ok;
if (ok)
LoggerHelper.Info($"设备 [{name}/{conn}] 连接成功。");
else
LoggerHelper.Warn($"设备 [{name}/{conn}] 连接失败。");
}
catch (OperationCanceledException)
{
if (info != null) info.IsConnected = false;
LoggerHelper.Warn($"设备 [{name}/{conn}] 连接已取消。");
}
catch (Exception ex)
{
if (info != null) info.IsConnected = false;
var inner = ex.InnerException?.Message ?? ex.Message;
LoggerHelper.ErrorWithNotify($"设备 [{name}/{conn}] 连接异常:{inner}");
}
}
private static async Task<bool> ConnectTcpAsync(string name, IBaseInterface device, CancellationToken ct)
{
if (device is not ITcp tcp)
{
LoggerHelper.Warn($"设备 [{name}] 配置为 Tcp 但未实现 ITcp实际类型为 {device.GetType().Name}。");
return false;
}
return await tcp.ConnectAsync(ct);
}
private static async Task<bool> ConnectSerialAsync(string name, IBaseInterface device, CancellationToken ct)
{
if (device is not ISerialPort sp)
{
LoggerHelper.Warn($"设备 [{name}] 配置为 Serial 但未实现 ISerialPort实际类型为 {device.GetType().Name}。");
return false;
}
return await sp.ConnectAsync(ct);
}
private static IReadOnlyDictionary<string, Type> BuildDeviceTypeMap()
{
try
{
return typeof(IBaseInterface).Assembly
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && typeof(IBaseInterface).IsAssignableFrom(t))
.ToDictionary(t => t.Name, t => t, StringComparer.OrdinalIgnoreCase);
}
catch (ReflectionTypeLoadException ex)
{
LoggerHelper.Error($"扫描设备类型失败:{ex.Message}");
return new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
}
}
private static IBaseInterface? CreateTcpDevice(Type type, TcpConfigVM? vm)
{
vm ??= new TcpConfigVM();
var cfg = new TcpConfig
{
IPAddress = vm.IPAddress,
Port = vm.Port,
SendTimeout = vm.SendTimeout,
ReceiveTimeout = vm.ReceiveTimeout
};
return Activator.CreateInstance(type, cfg) as IBaseInterface;
}
private static IBaseInterface? CreateSerialDevice(Type type, SerialPortConfigVM? vm)
{
vm ??= new SerialPortConfigVM();
var cfg = new SerialPortConfig
{
PortName = vm.PortName,
BaudRate = vm.BaudRate,
DataBits = vm.DataBits,
StopBits = Enum.TryParse<StopBits>(vm.StopBits, true, out var sb) ? sb : StopBits.One,
Parity = Enum.TryParse<Parity>(vm.Parity, true, out var pa) ? pa : Parity.None,
ReadTimeout = vm.ReadTimeout,
WriteTimeout = vm.WriteTimeout
};
return Activator.CreateInstance(type, cfg) as IBaseInterface;
}
#endregion
}
}