Files
ADP/UIShare/GlobalVariable/DeviceManager.cs
2026-06-11 17:25:59 +08:00

225 lines
8.7 KiB
C#
Raw 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
{
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);
}
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);
}
#region
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
}
}