| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using TeamAAS_VP.Core.Lights;
- using TeamAAS_VP.Interfaces;
- using TeamAAS_VP.Models;
- using TeamAAS_VP.Models.Lights;
- namespace TeamAAS_VP.Services
- {
- /// <summary>
- /// 管理多个灯光控制器及其全局通道的服务。
- /// 提供控制器注册/注销、全局通道的亮度与开关控制以及批量连接/断开功能。
- /// </summary>
- public class LightManagerService : ILightManagerService
- {
- /// <summary>
- /// 本地按控制器ID索引的控制器集合。
- /// 键:控制器ID,值:实现 <see cref="ILightController"/> 的实例。
- /// </summary>
- private readonly Dictionary<int, ILightController> _controllers;
- /// <summary>
- /// 全局通道映射。键为全局通道ID,值为具体的通道实例。
- /// </summary>
- private readonly Dictionary<int, ILightChannel> _globalChannels;
- /// <summary>
- /// 不同灯具型号对应的工厂方法字典。
- /// 键:灯具型号,值:创建控制器的工厂函数 (id, config) => ILightController
- /// </summary>
- private readonly Dictionary<LightModel, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController>> _controllerFactories;
- /// <summary>
- /// 下一个可分配的全局通道ID(用于冲突时自动分配)。
- /// </summary>
- private int _nextGlobalChannelId = 0;
- /// <summary>
- /// 只读访问已注册的控制器集合。
- /// </summary>
- public IReadOnlyDictionary<int, ILightController> Controllers => _controllers;
- /// <summary>
- /// 只读访问全局通道映射。
- /// </summary>
- public IReadOnlyDictionary<int, ILightChannel> GlobalChannels => _globalChannels;
- /// <summary>
- /// 当控制器被添加时触发的事件。
- /// </summary>
- public event EventHandler<LightControllerEventArgs> ControllerAdded;
- /// <summary>
- /// 当控制器被移除时触发的事件。
- /// </summary>
- public event EventHandler<LightControllerEventArgs> ControllerRemoved;
- /// <summary>
- /// 当某个全局通道状态(开/关/亮度)变化时触发的事件。
- /// </summary>
- public event EventHandler<LightChannelEventArgs> ChannelStatusChanged;
- /// <summary>
- /// 构造函数,初始化内部字典并注册默认的控制器工厂。
- /// </summary>
- public LightManagerService()
- {
- _controllers = new Dictionary<int, ILightController>();
- _globalChannels = new Dictionary<int, ILightChannel>();
- _controllerFactories = new Dictionary<LightModel, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController>>();
- RegisterDefaultFactories();
- }
- /// <summary>
- /// 注册默认的控制器工厂(当前含 KCS 型号)。
- /// </summary>
- private void RegisterDefaultFactories()
- {
- // 注册KCS控制器工厂
- RegisterControllerFactory(LightModel.KCS_KDC_12V60W_4T, (id, config) =>
- {
- if (config == null) throw new ArgumentNullException(nameof(config));
- // 使用配置里的串口配置创建协议实现,再创建控制器实例
- var protocol = new SerialPortProtocol(config.SerialPortConfig);
- return new KCSLightController(id, protocol, config.ChannelCount,config);
- });
- // 注册KCS控制器工厂
- RegisterControllerFactory(LightModel.KCS_KDC3_24V300W_8T, (id, config) =>
- {
- if (config == null) throw new ArgumentNullException(nameof(config));
- // 使用配置里的串口配置创建协议实现,再创建控制器实例
- var protocol = new SerialPortProtocol(config.SerialPortConfig);
- return new KCSLightController(id, protocol, config.ChannelCount, config);
- });
- }
- /// <summary>
- /// 手动注册一个控制器工厂,用于扩展支持更多型号。
- /// </summary>
- /// <param name="model">灯具模型</param>
- /// <param name="factory">工厂函数:根据 id 和配置返回 <see cref="ILightController"/> 实例</param>
- public void RegisterControllerFactory(LightModel model, Func<int, TeamAAS_VP.Models.Lights.LightControllerConfig, ILightController> factory)
- {
- _controllerFactories[model] = factory;
- }
- /// <summary>
- /// 异步注册控制器并建立其通道到全局通道ID的映射。
- /// </summary>
- /// <param name="id">控制器ID(局部)</param>
- /// <param name="configuration">控制器配置</param>
- /// <returns>已创建的控制器实例</returns>
- /// <exception cref="ArgumentNullException">当配置为 null 时抛出</exception>
- /// <exception cref="ArgumentException">当同ID已存在或未注册工厂时抛出</exception>
- /// <exception cref="InvalidOperationException">当工厂返回 null 时抛出</exception>
- public async Task<ILightController> RegisterControllerAsync(int id, TeamAAS_VP.Models.Lights.LightControllerConfig configuration)
- {
- if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- if (_controllers.ContainsKey(id))
- throw new ArgumentException($"Controller with id '{id}' already exists");
- if (!_controllerFactories.TryGetValue(configuration.LightModel, out var factory))
- throw new ArgumentException($"No factory registered for model '{configuration.LightModel}'");
- var controller = factory(id, configuration);
- if (controller == null)
- throw new InvalidOperationException("Factory returned null controller");
- // 将控制器加入管理集合
- _controllers[id] = controller;
- // 映射控制器内部通道到全局通道ID
- for (int i = 0; i < controller.Channels.Count; i++)
- {
- var channelConfig = configuration.ChannelConfigs[i];
- int globalIndex = channelConfig.GlobalIndex;
- // 若指定的全局ID已被占用,则自动分配下一个可用ID
- if (_globalChannels.ContainsKey(globalIndex))
- {
- // 自动重新分配全局ID
- globalIndex = _nextGlobalChannelId++;
- }
- else if (globalIndex >= _nextGlobalChannelId)
- {
- // 若指定ID大于等于当前_nextGlobalChannelId,则推进_nextGlobalChannelId以避免重复
- _nextGlobalChannelId = globalIndex + 1;
- }
- // 如果配置中的GlobalIndex与最终分配不一致,则更新配置(保持配置与运行时一致)
- if (channelConfig.GlobalIndex != globalIndex)
- {
- channelConfig.GlobalIndex = globalIndex;
- }
- // 在全局映射表中建立映射:全局ID -> 控制器通道实例
- _globalChannels[globalIndex] = controller.Channels[i];
- }
- // 通知订阅方有新控制器添加
- ControllerAdded?.Invoke(this, new LightControllerEventArgs(controller));
- return controller;
- }
- /// <summary>
- /// 注销指定ID的控制器,断开并释放资源,同时移除其全局通道映射。
- /// </summary>
- /// <param name="controllerId">要注销的控制器ID</param>
- /// <returns>如果存在并成功注销返回 true,否则 false</returns>
- public async Task<bool> UnregisterControllerAsync(int controllerId)
- {
- if (!_controllers.TryGetValue(controllerId, out var controller))
- return false;
- // 找到属于该控制器的全局通道键并移除
- var channelsToRemove = _globalChannels
- .Where(kvp => controller.Channels.Contains(kvp.Value))
- .Select(kvp => kvp.Key)
- .ToList();
- foreach (var channelId in channelsToRemove)
- {
- _globalChannels.Remove(channelId);
- }
- // 从管理集合中移除控制器并断开连接、释放资源
- _controllers.Remove(controllerId);
- await controller.DisconnectAsync();
- controller.Dispose();
- // 通知订阅方控制器已移除
- ControllerRemoved?.Invoke(this, new LightControllerEventArgs(controller));
- return true;
- }
- /// <summary>
- /// 设置指定全局通道的亮度值。
- /// </summary>
- /// <param name="globalChannelId">全局通道ID</param>
- /// <param name="brightness">亮度(由具体实现定义的范围)</param>
- /// <returns>操作是否成功</returns>
- public async Task<bool> SetGlobalChannelBrightnessAsync(int globalChannelId, int brightness)
- {
- if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
- return false;
- var result = await channel.SetBrightnessAsync(brightness);
- // 触发状态变化事件,通知外部当前通道的开关与亮度状态
- ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
- return result;
- }
- /// <summary>
- /// 设置多个通道的亮度值。
- /// </summary>
- /// <param name="channelBrightnessMap">通道,亮度</param>
- /// <returns>操作是否成功</returns>
- public async Task<bool> SetGlobalChannelBrightnessAsync2(IDictionary<int, int> channelBrightnessMap)
- {
- var validChannelsMap = new Dictionary<int, int>();
- foreach (var item in channelBrightnessMap)
- {
- if (_globalChannels.TryGetValue(item.Key, out var channel))
- {
- validChannelsMap[item.Key] = item.Value;
- }
- }
- if (validChannelsMap.Count == 0)
- return false;
- var firstChannel = _globalChannels[validChannelsMap.Keys.First()];
- bool result = await firstChannel.SetChannelsAsync(validChannelsMap);
- // 触发所有通道的状态变化事件
- foreach (var key in validChannelsMap.Keys)
- {
- var channel = _globalChannels[key];
- ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
- }
- return result;
- }
- /// <summary>
- /// 打开指定的全局通道。
- /// </summary>
- /// <param name="globalChannelId">全局通道ID</param>
- /// <returns>操作是否成功</returns>
- public async Task<bool> TurnOnGlobalChannelAsync(int globalChannelId)
- {
- if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
- return false;
- var result = await channel.TurnOnAsync();
- ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
- return result;
- }
- /// <summary>
- /// 关闭指定的全局通道。
- /// </summary>
- /// <param name="globalChannelId">全局通道ID</param>
- /// <returns>操作是否成功</returns>
- public async Task<bool> TurnOffGlobalChannelAsync(int globalChannelId)
- {
- if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
- return false;
- var result = await channel.TurnOffAsync();
- ChannelStatusChanged?.Invoke(this, new LightChannelEventArgs(channel, channel.IsOn, channel.Brightness));
- return result;
- }
- /// <summary>
- /// 异步连接所有已注册的控制器。
- /// </summary>
- /// <returns>当所有控制器连接成功返回 true;任一失败返回 false。</returns>
- public async Task<bool> ConnectAllAsync()
- {
- var tasks = _controllers.Values.Select(c => c.ConnectAsync());
- var results = await Task.WhenAll(tasks);
- return results.All(r => r);
- }
- /// <summary>
- /// 异步断开所有已注册的控制器(不会销毁控制器实例)。
- /// </summary>
- /// <returns>操作完成后返回 true。</returns>
- public async Task<bool> DisconnectAllAsync()
- {
- foreach (var controller in _controllers.Values)
- {
- await controller.DisconnectAsync();
- }
- return true;
- }
- /// <summary>
- /// 将当前配置保存到指定文件(未实现)。
- /// </summary>
- /// <param name="filePath">目标文件路径</param>
- /// <returns>完成任务</returns>
- public Task SaveConfigurationAsync(string filePath)
- {
- // TODO: 实现配置保存逻辑(序列化控制器与通道映射)
- return Task.CompletedTask;
- }
- /// <summary>
- /// 从指定文件加载配置(未实现)。
- /// </summary>
- /// <param name="filePath">配置文件路径</param>
- /// <returns>完成任务</returns>
- public Task LoadConfigurationAsync(string filePath)
- {
- // TODO: 实现配置加载逻辑(反序列化并调用 RegisterControllerAsync)
- return Task.CompletedTask;
- }
- }
- }
|