using DeviceService.Core;
|
using DeviceService.Logging;
|
using System.Reflection;
|
|
namespace DeviceService.Device
|
{
|
/// <summary>
|
/// 设备信息持久化辅助:负责设备信息与 Config\Devices.db 之间的读写。
|
/// DeviceManager 只负责服务正式启动后的设备管理,数据库读写一律经由本类。
|
/// </summary>
|
public static class DeviceHelper
|
{
|
|
private static readonly object _scanSync = new object();
|
private static readonly Dictionary<string, Type> _deviceTypeMap = new Dictionary<string, Type>(StringComparer.Ordinal);
|
|
/// <summary>
|
/// 扫描到的设备类型列表(取自 DeviceDescAttribute.Type)。
|
/// 程序启动后调用 <see cref="ScanDeviceTypes"/> 填充。
|
/// </summary>
|
public static List<string> AllDeviceType { get; } = new List<string>();
|
public static List<IDevice> AllDevices { get; } = new List<IDevice>();
|
|
/// <summary>
|
/// 扫描本程序集中所有标注了 <see cref="DeviceDescAttribute"/> 且继承 <see cref="IDevice"/> 的类,
|
/// 将注解中的 Type 存入 <see cref="AllDeviceType"/>,并记录 Type 到设备类的映射。
|
/// </summary>
|
public static void ScanDeviceTypes()
|
{
|
lock (_scanSync)
|
{
|
AllDeviceType.Clear();
|
_deviceTypeMap.Clear();
|
|
IEnumerable<Type?> types;
|
try
|
{
|
types = Assembly.GetExecutingAssembly().GetTypes();
|
}
|
catch (ReflectionTypeLoadException ex)
|
{
|
types = ex.Types;
|
}
|
|
foreach (Type? t in types)
|
{
|
if (t == null || !typeof(IDevice).IsAssignableFrom(t) || t.IsAbstract)
|
{
|
continue;
|
}
|
|
DeviceDescAttribute? attr = t.GetCustomAttribute<DeviceDescAttribute>(false);
|
if (attr == null || string.IsNullOrEmpty(attr.Type))
|
{
|
continue;
|
}
|
|
_deviceTypeMap[attr.Type] = t;
|
AllDeviceType.Add(attr.Type);
|
}
|
}
|
}
|
|
/// <summary>按注解 Type 创建对应的设备实例。</summary>
|
public static bool TryCreateDevice(string type, out IDevice? device)
|
{
|
device = null;
|
lock (_scanSync)
|
{
|
if (type == null || !_deviceTypeMap.TryGetValue(type, out Type? deviceType))
|
{
|
return false;
|
}
|
|
device = (IDevice?)Activator.CreateInstance(deviceType);
|
return device != null;
|
}
|
}
|
|
/// <summary>
|
/// 保存设备信息到 Config\Devices.db(仅持久化,不调用 Init、不影响 DeviceManager 的内存集合)。
|
/// 返回是否保存成功;新增记录会回填自增 Id。
|
/// </summary>
|
public static bool SaveDevice(IDevice device)
|
{
|
if (device == null) throw new ArgumentNullException(nameof(device));
|
|
// 写入 Config\Devices.db,下次启动时据此恢复设备列表;新增记录回填自增 Id
|
long id = DeviceDatabase.Instance.Save(ToRecord(device));
|
if (id > 0)
|
{
|
TrySet(device, nameof(IDevice.Id), id);
|
}
|
|
return id > 0;
|
}
|
|
/// <summary>
|
/// 从 Config\Devices.db 读取全部设备并创建实例(不调用 Init、不加入任何集合)。
|
/// 调用方按需加入 DeviceManager,服务正式启动后再逐一 <see cref="DeviceManager.Register"/>。
|
/// </summary>
|
public static IReadOnlyList<IDevice> LoadDevices()
|
{
|
var devices = new List<IDevice>();
|
AllDevices.Clear();
|
|
foreach (DeviceRecord record in DeviceDatabase.Instance.LoadAll())
|
{
|
if (string.IsNullOrWhiteSpace(record.DeviceId) || string.IsNullOrWhiteSpace(record.Type))
|
{
|
continue;
|
}
|
|
if (!DeviceHelper.TryCreateDevice(record.Type, out IDevice? device) || device == null)
|
{
|
GlobalLogger.Logger.Warn("DeviceHelper", $"[设备库] 未知设备类型 {record.Type},已跳过 {record.DeviceId}");
|
continue;
|
}
|
|
ApplyRecord(device, record);
|
devices.Add(device);
|
AllDevices.Add(device);
|
}
|
|
return devices;
|
}
|
|
/// <summary>从 Config\Devices.db 删除设备记录。</summary>
|
public static void RemoveDevice(string deviceId)
|
{
|
DeviceDatabase.Instance.Remove(deviceId);
|
}
|
|
private static DeviceRecord ToRecord(IDevice device)
|
{
|
return new DeviceRecord
|
{
|
Id = device.Id,
|
DeviceId = device.DeviceId ?? string.Empty,
|
Type = device.Type ?? string.Empty,
|
Brand = device.Brand ?? string.Empty,
|
ConnectStr = device.ConnectStr ?? string.Empty,
|
AccessType = device.AccessType
|
};
|
}
|
|
/// <summary>IDevice 接口只暴露 getter,实现类一般有 setter,通过反射回填。</summary>
|
private static void ApplyRecord(IDevice device, DeviceRecord record)
|
{
|
TrySet(device, nameof(IDevice.Id), record.Id);
|
TrySet(device, nameof(IDevice.DeviceId), record.DeviceId);
|
TrySet(device, nameof(IDevice.Brand), record.Brand);
|
TrySet(device, nameof(IDevice.ConnectStr), record.ConnectStr);
|
TrySet(device, nameof(IDevice.AccessType), record.AccessType);
|
}
|
|
private static void TrySet(object target, string propertyName, object value)
|
{
|
PropertyInfo? property = target.GetType().GetProperty(propertyName);
|
if (property == null || !property.CanWrite || !property.PropertyType.IsInstanceOfType(value))
|
{
|
return;
|
}
|
|
property.SetValue(target, value);
|
}
|
}
|
}
|