using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TeamAAS_VP.Models.Lights; namespace TeamAAS_VP.Core.Lights { /// /// 基于 KCS 协议的光源控制器实现。 /// 该类通过注入的 与物理设备通信, /// 并提供通道创建、连接/断开、初始化、全部开/关及通道亮度的内部读写实现。 /// public class KCSLightController : LightControllerBase { // 通信协议抽象,用于发送/接收字节数据 private readonly ICommunicationProtocol _protocol; // 使用 ASCII 编码将字符串转换为字节流 private readonly Encoding _encoding; //光源配置 private readonly LightControllerConfig _config; /// /// 创建一个新的 实例。 /// /// 控制器的唯一标识符。 /// 用于与设备通信的协议实现(必须已实现连接/发送/接收等)。 /// 该控制器管理的通道数量。 /// 控制器的配置对象。 public KCSLightController(int id, ICommunicationProtocol protocol,int channelCount, LightControllerConfig config) : base(id, channelCount,config) { _protocol = protocol; _encoding = Encoding.ASCII; _config = config; } /// /// 为指定索引创建通道实例。 /// /// 通道索引(从 0 开始)。 /// 通道名称。 /// 返回对应的 实例。 protected override ILightChannel CreateChannel(int index,string channelName) { return new KCSLightChannel(index, this, channelName); } /// /// 异步建立与设备的连接并在成功后执行初始化。 /// /// 若连接成功返回 true,否则返回 false。 public override async Task ConnectAsync() { var connected = await _protocol.ConnectAsync(); if (connected) { IsConnected = true; await InitializeAsync(); } return connected; } /// /// 异步断开与设备的连接并更新连接状态。 /// public override async Task DisconnectAsync() { await _protocol.DisconnectAsync(); IsConnected = false; } /// /// 异步初始化控制器,通常用于测试通信并同步初始状态。 /// 目前实现通过读取第 0 通道亮度来验证通信链路是否正常。 /// /// 若初始化成功返回 true;若发生异常或通信失败返回 false。 public override async Task InitializeAsync() { try { // 初始化操作:读取第 0 通道亮度以测试通信是否正常 await GetChannelBrightnessInternal(0); // 测试通信 return true; } catch { // 初始化失败(通信异常等) return false; } } /// /// 将所有通道设置为打开状态。命令格式参考控制器手册。 /// 构造格式示例:S{CH1:DDD}T{CH2:DDD}T...C# /// 其中每个通道使用 3 位亮度值(D3),后跟动作字符 'T' 表示开。 /// /// 若设备返回确认字符 '!' 则认为操作成功。 public override async Task TurnOnAllAsync() { StringBuilder commandBuilder = new StringBuilder("S"); for (int i = 0; i < ChannelCount; i++) { var channel = (KCSLightChannel)ChannelsInternal[i]; // 使用三位数字格式表示亮度(例如 005、120、255) commandBuilder.Append(channel.CurrentBrightness.ToString("D3")); // 'T' 表示打开当前通道 commandBuilder.Append("T"); } // 以 C# 结尾表示执行命令(协议特定) commandBuilder.Append("C#"); var response = await SendCommandAsync(commandBuilder.ToString()); // 期望设备返回 "!" 表示成功(根据协议) return response?.Trim() == "!"; } /// /// 将所有通道设置为关闭状态。 /// 构造格式示例:S000F000F...C# /// 其中 '000' 表示亮度 0,'F' 表示关闭通道。 /// /// 若设备返回确认字符 '!' 则认为操作成功。 public override async Task TurnOffAllAsync() { StringBuilder commandBuilder = new StringBuilder("S"); for (int i = 0; i < ChannelCount; i++) { // 将每个通道设置为 000(亮度 0)并附带 'F' 动作表示关闭 commandBuilder.Append("000"); commandBuilder.Append("F"); } commandBuilder.Append("C#"); var response = await SendCommandAsync(commandBuilder.ToString()); return response?.Trim() == "!"; } /// /// 将字符串命令编码为字节并通过协议发送,接收响应后以字符串返回。 /// /// 要发送的命令字符串(协议约定的格式)。 /// 设备响应的字符串表示(使用 ASCII 解码)。 internal async Task SendCommandAsync(string command) { var data = _encoding.GetBytes(command); // 使用协议的 SendAndReceiveAsync 发送数据并等待响应(超时时间以协议或调用方为准) var response = await _protocol.SendAndReceiveAsync(data, 1000); return _encoding.GetString(response); } /// /// 读取指定通道的亮度(内部方法)。 /// 命令格式示例:S{ChannelLetter}#,例如读取第 0 通道为 S A #。 /// 响应预期长度为 5 字符,亮度位于索引 2..4(3 个字符)。 /// /// 通道索引(从 0 开始)。 /// 解析得到的亮度值(0-999);若解析失败或响应格式不符则返回 0。 internal async Task GetChannelBrightnessInternal(int channelIndex) { // 通道字母从 'A' 开始递增 string command = $"S{(char)('A' + channelIndex)}#"; if (_config.LightModel == LightModel.KCS_KDC3_24V300W_8T) { command = $"SR{(char)('A' + channelIndex)}#"; } var response = await SendCommandAsync(command); // 响应示例(假设):?XDDD(总长度 5),亮度在索引 2 开始的 3 个字符 if (response.Length == 5) { string valueStr = response.Substring(2, 3); if (int.TryParse(valueStr, out int brightness)) { return brightness; } } else if (response.Length == 7) { string valueStr = response.Substring(3, 3); if (int.TryParse(valueStr, out int brightness)) { return brightness; } } // 返回默认亮度 0(表示读取失败或设备返回异常) return 0; } /// /// 设置指定通道的亮度(内部方法)。 /// 命令格式示例:S{ChannelLetter}0{DDD}#,其中 '0' 可能为协议占位符,具体请参考设备手册。 /// 成功时设备返回与通道字母相同的字符作为确认。 /// /// 通道索引(从 0 开始)。 /// 目标亮度(0-999)。方法内部会格式化为三位数字。 /// 若设备返回与通道字母相同的字符则认为设置成功。 internal async Task SetChannelBrightnessInternal(int channelIndex, int brightness) { string command = $"S{(char)('A' + channelIndex)}0{brightness.ToString("D3")}#"; var response = await SendCommandAsync(command); if (response=="!") { return true; } // 成功时返回通道字母(例如 'A'),去除空白后比较 return response?.Trim() == ((char)('A' + channelIndex)).ToString(); } } }