LightManagerService.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. }
  80. /// <summary>
  81. /// 手动注册一个控制器工厂,用于扩展支持更多型号。
  82. /// </summary>
  83. /// <param name="model">灯具模型</param>
  84. /// <param name="factory">工厂函数:根据 id 和配置返回 <see cref="ILightController"/> 实例</param>
  85. public void RegisterControllerFactory(LightModel model, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController> factory)
  86. {
  87. _controllerFactories[model] = factory;
  88. }
  89. /// <summary>
  90. /// 异步注册控制器并建立其通道到全局通道ID的映射。
  91. /// </summary>
  92. /// <param name="id">控制器ID(局部)</param>
  93. /// <param name="configuration">控制器配置</param>
  94. /// <returns>已创建的控制器实例</returns>
  95. /// <exception cref="ArgumentNullException">当配置为 null 时抛出</exception>
  96. /// <exception cref="ArgumentException">当同ID已存在或未注册工厂时抛出</exception>
  97. /// <exception cref="InvalidOperationException">当工厂返回 null 时抛出</exception>
  98. public async Task<ILightController> RegisterControllerAsync(int id, TeamAAS_VP.Models.Lights.LightControllerConfig configuration)
  99. {
  100. if (configuration == null) throw new ArgumentNullException(nameof(configuration));
  101. if (_controllers.ContainsKey(id))
  102. throw new ArgumentException($"Controller with id '{id}' already exists");
  103. if (!_controllerFactories.TryGetValue(configuration.LightModel, out var factory))
  104. throw new ArgumentException($"No factory registered for model '{configuration.LightModel}'");
  105. var controller = factory(id, configuration);
  106. if (controller == null)
  107. throw new InvalidOperationException("Factory returned null controller");
  108. // 将控制器加入管理集合
  109. _controllers[id] = controller;
  110. // 映射控制器内部通道到全局通道ID
  111. for (int i = 0; i < controller.Channels.Count; i++)
  112. {
  113. var channelConfig = configuration.ChannelConfigs[i];
  114. int globalIndex = channelConfig.GlobalIndex;
  115. // 若指定的全局ID已被占用,则自动分配下一个可用ID
  116. if (_globalChannels.ContainsKey(globalIndex))
  117. {
  118. // 自动重新分配全局ID
  119. globalIndex = _nextGlobalChannelId++;
  120. }
  121. else if (globalIndex >= _nextGlobalChannelId)
  122. {
  123. // 若指定ID大于等于当前_nextGlobalChannelId,则推进_nextGlobalChannelId以避免重复
  124. _nextGlobalChannelId = globalIndex + 1;
  125. }
  126. // 如果配置中的GlobalIndex与最终分配不一致,则更新配置(保持配置与运行时一致)
  127. if (channelConfig.GlobalIndex != globalIndex)
  128. {
  129. channelConfig.GlobalIndex = globalIndex;
  130. }
  131. // 在全局映射表中建立映射:全局ID -> 控制器通道实例
  132. _globalChannels[globalIndex] = controller.Channels[i];
  133. }
  134. // 通知订阅方有新控制器添加
  135. ControllerAdded?.Invoke(this, new LightControllerEventArgs(controller));
  136. return controller;
  137. }
  138. /// <summary>
  139. /// 注销指定ID的控制器,断开并释放资源,同时移除其全局通道映射。
  140. /// </summary>
  141. /// <param name="controllerId">要注销的控制器ID</param>
  142. /// <returns>如果存在并成功注销返回 true,否则 false</returns>
  143. public async Task<bool> UnregisterControllerAsync(int controllerId)
  144. {
  145. if (!_controllers.TryGetValue(controllerId, out var controller))
  146. return false;
  147. // 找到属于该控制器的全局通道键并移除
  148. var channelsToRemove = _globalChannels
  149. .Where(kvp => controller.Channels.Contains(kvp.Value))
  150. .Select(kvp => kvp.Key)
  151. .ToList();
  152. foreach (var channelId in channelsToRemove)
  153. {
  154. _globalChannels.Remove(channelId);
  155. }
  156. // 从管理集合中移除控制器并断开连接、释放资源
  157. _controllers.Remove(controllerId);
  158. await controller.DisconnectAsync();
  159. controller.Dispose();
  160. // 通知订阅方控制器已移除
  161. ControllerRemoved?.Invoke(this, new LightControllerEventArgs(controller));
  162. return true;
  163. }
  164. /// <summary>
  165. /// 设置指定全局通道的亮度值。
  166. /// </summary>
  167. /// <param name="globalChannelId">全局通道ID</param>
  168. /// <param name="brightness">亮度(由具体实现定义的范围)</param>
  169. /// <returns>操作是否成功</returns>
  170. public async Task<bool> SetGlobalChannelBrightnessAsync(int globalChannelId, int brightness)
  171. {
  172. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  173. return false;
  174. var result = await channel.SetBrightnessAsync(brightness);
  175. // 触发状态变化事件,通知外部当前通道的开关与亮度状态
  176. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  177. return result;
  178. }
  179. /// <summary>
  180. /// 打开指定的全局通道。
  181. /// </summary>
  182. /// <param name="globalChannelId">全局通道ID</param>
  183. /// <returns>操作是否成功</returns>
  184. public async Task<bool> TurnOnGlobalChannelAsync(int globalChannelId)
  185. {
  186. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  187. return false;
  188. var result = await channel.TurnOnAsync();
  189. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  190. return result;
  191. }
  192. /// <summary>
  193. /// 关闭指定的全局通道。
  194. /// </summary>
  195. /// <param name="globalChannelId">全局通道ID</param>
  196. /// <returns>操作是否成功</returns>
  197. public async Task<bool> TurnOffGlobalChannelAsync(int globalChannelId)
  198. {
  199. if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
  200. return false;
  201. var result = await channel.TurnOffAsync();
  202. ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
  203. return result;
  204. }
  205. /// <summary>
  206. /// 异步连接所有已注册的控制器。
  207. /// </summary>
  208. /// <returns>当所有控制器连接成功返回 true;任一失败返回 false。</returns>
  209. public async Task<bool> ConnectAllAsync()
  210. {
  211. var tasks = _controllers.Values.Select(c => c.ConnectAsync());
  212. var results = await Task.WhenAll(tasks);
  213. return results.All(r => r);
  214. }
  215. /// <summary>
  216. /// 异步断开所有已注册的控制器(不会销毁控制器实例)。
  217. /// </summary>
  218. /// <returns>操作完成后返回 true。</returns>
  219. public async Task<bool> DisconnectAllAsync()
  220. {
  221. foreach (var controller in _controllers.Values)
  222. {
  223. await controller.DisconnectAsync();
  224. }
  225. return true;
  226. }
  227. /// <summary>
  228. /// 将当前配置保存到指定文件(未实现)。
  229. /// </summary>
  230. /// <param name="filePath">目标文件路径</param>
  231. /// <returns>完成任务</returns>
  232. public Task SaveConfigurationAsync(string filePath)
  233. {
  234. // TODO: 实现配置保存逻辑(序列化控制器与通道映射)
  235. return Task.CompletedTask;
  236. }
  237. /// <summary>
  238. /// 从指定文件加载配置(未实现)。
  239. /// </summary>
  240. /// <param name="filePath">配置文件路径</param>
  241. /// <returns>完成任务</returns>
  242. public Task LoadConfigurationAsync(string filePath)
  243. {
  244. // TODO: 实现配置加载逻辑(反序列化并调用 RegisterControllerAsync)
  245. return Task.CompletedTask;
  246. }
  247. }
  248. }