225 lines
8.7 KiB
C#
225 lines
8.7 KiB
C#
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
|
||
}
|
||
}
|