alielie
7 天以前 e57938fbda9bff702004038f76d009422968bff5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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);
        }
    }
}