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