|
|
@@ -10,20 +10,62 @@ 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>();
|
|
|
@@ -33,22 +75,40 @@ namespace TeamAAS_VP.Services
|
|
|
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);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /// <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));
|
|
|
@@ -63,15 +123,16 @@ namespace TeamAAS_VP.Services
|
|
|
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已被占用,则自动分配下一个可用ID
|
|
|
if (_globalChannels.ContainsKey(globalIndex))
|
|
|
{
|
|
|
// 自动重新分配全局ID
|
|
|
@@ -79,30 +140,36 @@ namespace TeamAAS_VP.Services
|
|
|
}
|
|
|
else if (globalIndex >= _nextGlobalChannelId)
|
|
|
{
|
|
|
- // 更新下一个可用ID
|
|
|
+ // 若指定ID大于等于当前_nextGlobalChannelId,则推进_nextGlobalChannelId以避免重复
|
|
|
_nextGlobalChannelId = globalIndex + 1;
|
|
|
}
|
|
|
|
|
|
- // 更新配置中的GlobalIndex(如果需要)
|
|
|
+ // 如果配置中的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)
|
|
|
@@ -113,24 +180,38 @@ namespace TeamAAS_VP.Services
|
|
|
_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="globalChannelId">全局通道ID</param>
|
|
|
+ /// <returns>操作是否成功</returns>
|
|
|
public async Task<bool> TurnOnGlobalChannelAsync(int globalChannelId)
|
|
|
{
|
|
|
if (!_globalChannels.TryGetValue(globalChannelId, out var channel))
|
|
|
@@ -141,6 +222,11 @@ namespace TeamAAS_VP.Services
|
|
|
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))
|
|
|
@@ -151,6 +237,10 @@ namespace TeamAAS_VP.Services
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 异步连接所有已注册的控制器。
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>当所有控制器连接成功返回 true;任一失败返回 false。</returns>
|
|
|
public async Task<bool> ConnectAllAsync()
|
|
|
{
|
|
|
var tasks = _controllers.Values.Select(c => c.ConnectAsync());
|
|
|
@@ -158,6 +248,10 @@ namespace TeamAAS_VP.Services
|
|
|
return results.All(r => r);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 异步断开所有已注册的控制器(不会销毁控制器实例)。
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>操作完成后返回 true。</returns>
|
|
|
public async Task<bool> DisconnectAllAsync()
|
|
|
{
|
|
|
foreach (var controller in _controllers.Values)
|
|
|
@@ -167,15 +261,25 @@ namespace TeamAAS_VP.Services
|
|
|
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;
|
|
|
}
|
|
|
}
|