LightManagerService.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using TeamAAS_VP.Core.Lights;
  7. using TeamAAS_VP.Interfaces;
  8. using TeamAAS_VP.Models;
  9. using TeamAAS_VP.Models.Lights;
  10. namespace TeamAAS_VP.Services
  11. {
  12. /// <summary>
  13. /// 管理多个灯光控制器及其全局通道的服务。
  14. /// 提供控制器注册/注销、全局通道的亮度与开关控制以及批量连接/断开功能。
  15. /// </summary>
  16. public class LightManagerService : ILightManagerService
  17. {
  18. /// <summary>
  19. /// 本地按控制器ID索引的控制器集合。
  20. /// 键:控制器ID,值:实现 <see cref="ILightController"/> 的实例。
  21. /// </summary>
  22. private readonly Dictionary<int, ILightController> _controllers;
  23. /// <summary>
  24. /// 全局通道映射。键为全局通道ID,值为具体的通道实例。
  25. /// </summary>
  26. private readonly Dictionary<int, ILightChannel> _globalChannels;
  27. /// <summary>
  28. /// 不同灯具型号对应的工厂方法字典。
  29. /// 键:灯具型号,值:创建控制器的工厂函数 (id, config) => ILightController
  30. /// </summary>
  31. private readonly Dictionary<LightModel, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController>> _controllerFactories;
  32. /// <summary>
  33. /// 下一个可分配的全局通道ID(用于冲突时自动分配)。
  34. /// </summary>
  35. private int _nextGlobalChannelId = 0;
  36. /// <summary>
  37. /// 只读访问已注册的控制器集合。
  38. /// </summary>
  39. public IReadOnlyDictionary<int, ILightController> Controllers => _controllers;
  40. /// <summary>
  41. /// 只读访问全局通道映射。
  42. /// </summary>
  43. public IReadOnlyDictionary<int, ILightChannel> GlobalChannels => _globalChannels;
  44. /// <summary>
  45. /// 当控制器被添加时触发的事件。
  46. /// </summary>
  47. public event EventHandler<LightControllerEventArgs> ControllerAdded;
  48. /// <summary>
  49. /// 当控制器被移除时触发的事件。
  50. /// </summary>
  51. public event EventHandler<LightControllerEventArgs> ControllerRemoved;
  52. /// <summary>
  53. /// 当某个全局通道状态(开/关/亮度)变化时触发的事件。
  54. /// </summary>
  55. public event EventHandler<LightChannelEventArgs> ChannelStatusChanged;
  56. /// <summary>
  57. /// 构造函数,初始化内部字典并注册默认的控制器工厂。
  58. /// </summary>
  59. public LightManagerService()
  60. {
  61. _controllers = new Dictionary<int, ILightController>();
  62. _globalChannels = new Dictionary<int, ILightChannel>();
  63. _controllerFactories = new Dictionary<LightModel, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController>>();
  64. RegisterDefaultFactories();
  65. }
  66. /// <summary>
  67. /// 注册默认的控制器工厂(当前含 KCS 型号)。
  68. /// </summary>
  69. private void RegisterDefaultFactories()
  70. {
  71. // 注册KCS控制器工厂
  72. RegisterControllerFactory(LightModel.KCS_KDC_12V60W_4T, (id, config) =>
  73. {
  74. if (config == null) throw new ArgumentNullException(nameof(config));
  75. // 使用配置里的串口配置创建协议实现,再创建控制器实例
  76. var protocol = new SerialPortProtocol(config.SerialPortConfig);
  77. return new KCSLightController(id, protocol, config.ChannelCount,config);
  78. });
  79. // 注册KCS控制器工厂
  80. RegisterControllerFactory(LightModel.KCS_KDC3_24V300W_8T, (id, config) =>
  81. {
  82. if (config == null) throw new ArgumentNullException(nameof(config));
  83. // 使用配置里的串口配置创建协议实现,再创建控制器实例
  84. var protocol = new SerialPortProtocol(config.SerialPortConfig);
  85. return new KCSLightController(id, protocol, config.ChannelCount, config);
  86. });
  87. }
  88. /// <summary>
  89. /// 手动注册一个控制器工厂,用于扩展支持更多型号。
  90. /// </summary>
  91. /// <param name="model">灯具模型</param>
  92. /// <param name="factory">工厂函数:根据 id 和配置返回 <see cref="ILightController"/> 实例</param>
  93. public void RegisterControllerFactory(LightModel model, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController> factory)
  94. {
  95. _controllerFactories[model] = factory;
  96. }
  97. /// <summary>
  98. /// 异步注册控制器并建立其通道到全局通道ID的映射。
  99. /// </summary>
  100. /// <param name="id">控制器ID(局部)</param>
  101. /// <param name="configuration">控制器配置</param>
  102. /// <returns>已创建的控制器实例</returns>
  103. /// <exception cref="ArgumentNullException">当配置为 null 时抛出</exception>
  104. /// <exception cref="ArgumentException">当同ID已存在或未注册工厂时抛出</exception>
  105. /// <exception cref="InvalidOperationException">当工厂返回 null 时抛出</exception>
  106. public async Task<ILightController> RegisterControllerAsync(int id, TeamAAS_VP.Models.Lights.LightControllerConfig configuration)
  107. {
  108. if (configuration == null) throw new ArgumentNullException(nameof(configuration));
  109. if (_controllers.ContainsKey(id))
  110. throw new ArgumentException($"Controller with id '{id}' already exists");
  111. if (!_controllerFactories.TryGetValue(configuration.LightModel, out var factory))
  112. throw new ArgumentException($"No factory registered for model '{configuration.LightModel}'");
  113. var controller = factory(id, configuration);
  114. if (controller == null)
  115. throw new InvalidOperationException("Factory returned null controller");
  116. // 将控制器加入管理集合
  117. _controllers[id] = controller;
  118. // 映射控制器内部通道到全局通道ID
  119. for (int i = 0; i < controller.Channels.Count; i++)
  120. {
  121. var channelConfig = configuration.ChannelConfigs[i];
  122. int globalIndex = channelConfig.GlobalIndex;
  123. // 若指定的全局ID已被占用,则自动分配下一个可用ID
  124. if (_globalChannels.ContainsKey(globalIndex))
  125. {
  126. // 自动重新分配全局ID
  127. globalIndex = _nextGlobalChannelId++;
  128. }
  129. else if (globalIndex >= _nextGlobalChannelId)
  130. {
  131. // 若指定ID大于等于当前_nextGlobalChannelId,则推进_nextGlobalChannelId以避免重复
  132. _nextGlobalChannelId = globalIndex + 1;
  133. }
  134. // 如果配置中的GlobalIndex与最终分配不一致,则更新配置(保持配置与运行时一致)
  135. if (channelConfig.GlobalIndex != globalIndex)
  136. {
  137. channelConfig.GlobalIndex = globalIndex;
  138. }
  139. // 在全局映射表中建立映射:全局ID -> 控制器通道实例
  140. _globalChannels[globalIndex] = controller.Channels[i];
  141. }
  142. // 通知订阅方有新控制器添加
  143. ControllerAdded?.Invoke(this, new LightControllerEventArgs(controller));
  144. return controller;
  145. }
  146. /// <summary>
  147. /// 注销指定ID的控制器,断开并释放资源,同时移除其全局通道映射。
  148. /// </summary>
  149. /// <param name="controllerId">要注销的控制器ID</param>
  150. /// <returns>如果存在并成功注销返回 true,否则 false</returns>
  151. public async Task<bool> UnregisterControllerAsync(int controllerId)
  152. {
  153. if (!_controllers.TryGetValue(controllerId, out var controller))
  154. return false;
  155. // 找到属于该控制器的全局通道键并移除
  156. var channelsToRemove = _globalChannels
  157. .Where(kvp => controller.Channels.Contains(kvp.Value))
  158. .Select(kvp => kvp.Key)
  159. .ToList();
  160. foreach (var channelId in channelsToRemove)
  161. {
  162. _globalChannels.Remove(channelId);
  163. }
  164. // 从管理集合中移除控制器并断开连接、释放资源
  165. _controllers.Remove(controllerId);
  166. await controller.DisconnectAsync();
  167. controller.Dispose();
  168. // 通知订阅方控制器已移除
  169. ControllerRemoved?.Invoke(this, new LightControllerEventArgs(controller));
  170. return true;
  171. }
  172. /// <summary>
  173. /// 设置指定全局通道的亮度值。
  174. /// </summary>
  175. /// <param name="globalChannelId">全局通道ID</param>
  176. /// <param name="brightness">亮度(由具体实现定义的范围)</param>
  177. /// <returns>操作是否成功</returns>
  178. public async Task<bool> SetGlobalChannelBrightnessAsync(int globalChannelId, int brightness)
  179. {
  180. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  181. return false;
  182. var result = await channel.SetBrightnessAsync(brightness);
  183. // 触发状态变化事件,通知外部当前通道的开关与亮度状态
  184. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  185. return result;
  186. }
  187. /// <summary>
  188. /// 获取指定全局通道的亮度值。
  189. /// </summary>
  190. /// <param name="globalChannelId">全局通道ID</param>
  191. /// <returns>操作是否成功</returns>
  192. public async Task<int> GetGlobalChannelBrightnessAsync(int globalChannelId)
  193. {
  194. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  195. return -1;
  196. var result = await channel.GetBrightnessAsync();
  197. // 触发状态变化事件,通知外部当前通道的开关与亮度状态
  198. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  199. return result;
  200. }
  201. /// <summary>
  202. /// 打开指定的全局通道。
  203. /// </summary>
  204. /// <param name="globalChannelId">全局通道ID</param>
  205. /// <returns>操作是否成功</returns>
  206. public async Task<bool> TurnOnGlobalChannelAsync(int globalChannelId)
  207. {
  208. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  209. return false;
  210. var result = await channel.TurnOnAsync();
  211. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  212. return result;
  213. }
  214. /// <summary>
  215. /// 关闭指定的全局通道。
  216. /// </summary>
  217. /// <param name="globalChannelId">全局通道ID</param>
  218. /// <returns>操作是否成功</returns>
  219. public async Task<bool> TurnOffGlobalChannelAsync(int globalChannelId)
  220. {
  221. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  222. return false;
  223. var result = await channel.TurnOffAsync();
  224. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  225. return result;
  226. }
  227. /// <summary>
  228. /// 异步连接所有已注册的控制器。
  229. /// </summary>
  230. /// <returns>当所有控制器连接成功返回 true;任一失败返回 false。</returns>
  231. public async Task<bool> ConnectAllAsync()
  232. {
  233. var tasks = _controllers.Values.Select(c => c.ConnectAsync());
  234. var results = await Task.WhenAll(tasks);
  235. return results.All(r => r);
  236. }
  237. /// <summary>
  238. /// 异步断开所有已注册的控制器(不会销毁控制器实例)。
  239. /// </summary>
  240. /// <returns>操作完成后返回 true。</returns>
  241. public async Task<bool> DisconnectAllAsync()
  242. {
  243. foreach (var controller in _controllers.Values)
  244. {
  245. await controller.DisconnectAsync();
  246. }
  247. return true;
  248. }
  249. /// <summary>
  250. /// 将当前配置保存到指定文件(未实现)。
  251. /// </summary>
  252. /// <param name="filePath">目标文件路径</param>
  253. /// <returns>完成任务</returns>
  254. public Task SaveConfigurationAsync(string filePath)
  255. {
  256. // TODO: 实现配置保存逻辑(序列化控制器与通道映射)
  257. return Task.CompletedTask;
  258. }
  259. /// <summary>
  260. /// 从指定文件加载配置(未实现)。
  261. /// </summary>
  262. /// <param name="filePath">配置文件路径</param>
  263. /// <returns>完成任务</returns>
  264. public Task LoadConfigurationAsync(string filePath)
  265. {
  266. // TODO: 实现配置加载逻辑(反序列化并调用 RegisterControllerAsync)
  267. return Task.CompletedTask;
  268. }
  269. }
  270. }