using System.Collections.Generic; using System.Linq; using RGB.NET.Core; namespace RGB.NET.Devices.WS281X.Arduino; // ReSharper disable once InconsistentNaming /// /// /// Represents the update-queue performing updates for arduino WS2812 devices. /// public sealed class ArduinoWS2812USBUpdateQueue : SerialConnectionUpdateQueue { #region Constants private static readonly byte[] COUNT_COMMAND = [0x01]; private static readonly byte[] UPDATE_COMMAND = [0x02]; private static readonly byte[] ASK_PROMPT_COMMAND = [0x0F]; #endregion #region Properties & Fields private readonly Dictionary _dataBuffer = []; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The update trigger used by this queue. /// The serial connection used to access the device. public ArduinoWS2812USBUpdateQueue(IDeviceUpdateTrigger updateTrigger, ISerialConnection serialConnection) : base(updateTrigger, serialConnection) { } #endregion #region Methods /// protected override void OnStartup(object? sender, CustomUpdateData customData) { base.OnStartup(sender, customData); SendCommand(ASK_PROMPT_COMMAND); // Get initial prompt } /// protected override IEnumerable GetCommands(IList<(object key, Color color)> dataSet) { foreach (IGrouping channelData in dataSet.Select(x => (((int channel, int key))x.key, x.color)) .GroupBy(x => x.Item1.channel)) { int channel = channelData.Key; if (!_dataBuffer.TryGetValue(channel, out byte[]? dataBuffer) || (dataBuffer.Length != ((dataSet.Count * 3) + 1))) _dataBuffer[channel] = dataBuffer = new byte[(dataSet.Count * 3) + 1]; dataBuffer[0] = (byte)((channel << 4) | UPDATE_COMMAND[0]); int i = 1; foreach ((byte _, byte r, byte g, byte b) in channelData.OrderBy(x => x.Item1.key) .Select(x => x.Value.GetRGBBytes())) { dataBuffer[i++] = r; dataBuffer[i++] = g; dataBuffer[i++] = b; } yield return dataBuffer; } yield return UPDATE_COMMAND; } /// protected override void SendCommand(byte[] command) => SerialConnection.Write(command, 0, command.Length); internal IEnumerable<(int channel, int ledCount)> GetChannels() { if (!SerialConnection.IsOpen) SerialConnection.Open(); SerialConnection.DiscardInBuffer(); SendCommand(ASK_PROMPT_COMMAND); SerialConnection.ReadTo(Prompt); SendCommand(COUNT_COMMAND); int channelCount = SerialConnection.ReadByte(); for (int i = 1; i <= channelCount; i++) { SerialConnection.ReadTo(Prompt); byte[] channelLedCountCommand = [(byte)((i << 4) | COUNT_COMMAND[0])]; SendCommand(channelLedCountCommand); int ledCount = SerialConnection.ReadByte(); if (ledCount > 0) yield return (i, ledCount); } } #endregion }