using System.Net; using DotNext; using Tapper; namespace Peripherals.WS2812Client; class WS2812Addr { public const UInt32 BASE = 0xB0_00_01_00; public const int LED_COUNT = 128; } /// /// RGB颜色结构体,包含红、绿、蓝三个颜色分量 /// [TranspilationSource] public class RGBColor { public byte Red { get; set; } public byte Green { get; set; } public byte Blue { get; set; } public RGBColor(byte red, byte green, byte blue) { Red = red; Green = green; Blue = blue; } /// /// 从32位数据的低24位提取RGB颜色 /// /// 32位数据 /// RGB颜色 public static RGBColor FromUInt32(UInt32 data) { return new RGBColor( (byte)((data >> 16) & 0xFF), // Red (byte)((data >> 8) & 0xFF), // Green (byte)(data & 0xFF) // Blue ); } /// /// 转换为32位数据格式 /// /// 32位数据 public UInt32 ToUInt32() { return ((UInt32)Red << 16) | ((UInt32)Green << 8) | (UInt32)Blue; } public override string ToString() { return $"RGB({Red}, {Green}, {Blue})"; } } public class WS2812Client { private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); readonly int timeout = 500; readonly int taskID; readonly int port; readonly string address; private IPEndPoint ep; public WS2812Client(string address, int port, int taskID, int timeout = 500) { if (timeout < 0) throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); this.address = address; this.port = port; this.ep = new IPEndPoint(IPAddress.Parse(address), port); this.taskID = taskID; this.timeout = timeout; } /// /// 获取指定灯珠的RGB颜色 /// /// 灯珠索引,范围0-127 /// RGB颜色结果 public async ValueTask> GetLedColor(int ledIndex) { if (ledIndex < 0 || ledIndex >= WS2812Addr.LED_COUNT) { return new(new ArgumentOutOfRangeException(nameof(ledIndex), $"LED index must be between 0 and {WS2812Addr.LED_COUNT - 1}")); } if (MsgBus.IsRunning) MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); else return new(new Exception("Message Bus not work!")); var addr = WS2812Addr.BASE + (UInt32)(ledIndex * 4); // 每个地址32位,步长为4字节 var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, addr, this.timeout); if (!ret.IsSuccessful) { logger.Error($"Get LED {ledIndex} color failed: {ret.Error}"); return new(ret.Error); } var retData = ret.Value.Options.Data; if (retData is null) return new(new Exception($"Device {address} receive none")); if (retData.Length < 4) { var error = new Exception($"Invalid data length: expected 4 bytes, got {retData.Length}"); logger.Error($"Get LED {ledIndex} color failed: {error}"); return new(error); } var colorData = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value); var color = RGBColor.FromUInt32(colorData); return new(color); } /// /// 获取所有灯珠的RGB颜色 /// /// 包含所有灯珠颜色的数组 public async ValueTask> GetAllLedColors() { if (MsgBus.IsRunning) MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); else return new(new Exception("Message Bus not work!")); try { // 一次性读取所有LED数据,每个LED占用4字节,总共128*4=512字节 var ret = await UDPClientPool.ReadAddr4Bytes(this.ep, this.taskID, WS2812Addr.BASE, WS2812Addr.LED_COUNT, this.timeout); if (!ret.IsSuccessful) { logger.Error($"Get all LED colors failed: {ret.Error}"); return new(ret.Error); } var data = ret.Value; var expectedLength = WS2812Addr.LED_COUNT * 4; // 128 * 4 = 512 bytes if (data.Length < expectedLength) { var error = new Exception($"Invalid data length: expected {expectedLength} bytes, got {data.Length}"); logger.Error(error.Message); return new(error); } var colors = new RGBColor[WS2812Addr.LED_COUNT]; for (int i = 0; i < WS2812Addr.LED_COUNT; i++) { var offset = i * 4; // 将4字节数据转换为UInt32 var colorData = BitConverter.ToUInt32(data, offset); colors[i] = RGBColor.FromUInt32(colorData); } return new(colors); } catch (Exception ex) { logger.Error($"Get all LED colors failed: {ex}"); return new(ex); } } }