diff --git a/Project-Aurora/Project-Aurora/Devices/UDP/UDPDevice.cs b/Project-Aurora/Project-Aurora/Devices/UDP/UDPDevice.cs new file mode 100644 index 000000000..fd73ca160 --- /dev/null +++ b/Project-Aurora/Project-Aurora/Devices/UDP/UDPDevice.cs @@ -0,0 +1,137 @@ +using Aurora.Settings; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Net; +using System.Net.Sockets; + +namespace Aurora.Devices.UDP +{ + public class UdpDevice : DefaultDevice + { + public override string DeviceName => "UDP"; + + private Stopwatch redrawWatch = new Stopwatch(); + private UdpClient udpClient; + + private string[] ips = new string[0]; + private List<IPEndPoint> endpoints; + private DeviceKeys deviceKey; + private int ledCount; + + private Color lastColor; + + private const int defaultPort = 19446; + + protected override string DeviceInfo => string.Join(", ", endpoints.Select(endpoint => endpoint.ToString())); + + protected override void RegisterVariables(VariableRegistry variableRegistry) + { + var devKeysEnumAsEnumerable = Enum.GetValues(typeof(DeviceKeys)).Cast<DeviceKeys>(); + + variableRegistry.Register($"{DeviceName}_devicekey", DeviceKeys.Peripheral, "Key Color to Use", + devKeysEnumAsEnumerable.Max(), devKeysEnumAsEnumerable.Min()); + variableRegistry.Register($"{DeviceName}_led_count", 300, "LED Count"); + variableRegistry.Register($"{DeviceName}_ip", "", "Addresses (IP:port - Comma separated)"); + } + + + public override bool Initialize() + { + if (IsInitialized) return IsInitialized = true; + + deviceKey = Global.Configuration.VarRegistry.GetVariable<DeviceKeys>($"{DeviceName}_devicekey"); + ledCount = Global.Configuration.VarRegistry.GetVariable<int>($"{DeviceName}_led_count"); + ips = Global.Configuration.VarRegistry.GetVariable<string>($"{DeviceName}_ip").Split(','); + + if (ips.Length < 2 && ips[0] == "") + { + LogError("UDP has no IP"); + return IsInitialized = false; + } + else + { + LogInfo($"{ips.Length} |{string.Join("|", ips)}|"); + } + + endpoints = new List<IPEndPoint>(); + foreach (var ip in ips) + { + try + { + int udpPort = defaultPort; + var parts = ip.Split(':'); + if (parts.Length > 1) + { + if (!int.TryParse(parts[1], out udpPort)) + { + udpPort = defaultPort; + } + } + endpoints.Add(new IPEndPoint(IPAddress.Parse(parts[0]), udpPort)); + } + catch (FormatException) + { + } // Don't crash on malformed IPs + } + + udpClient = new UdpClient(); + lastColor = Color.Black; + redrawWatch.Start(); + + return IsInitialized = true; + } + + public override void Shutdown() + { + endpoints = null; + udpClient.Dispose(); // udpClient is IDisposable + udpClient = null; + redrawWatch.Stop(); + + IsInitialized = false; + } + + public override bool UpdateDevice(Dictionary<DeviceKeys, Color> keyColors, DoWorkEventArgs e, + bool forced = false) + { + if (!IsInitialized) return false; + + if (keyColors.ContainsKey(deviceKey)) + { + var c = keyColors[deviceKey]; + if (redrawWatch.ElapsedMilliseconds < 1000 + ) // Only send the color when it changes or a full second has passed + { + if (c == lastColor && !forced) return true; + } + else + { + redrawWatch.Restart(); + } + + lastColor = c; + + // Build a payload by repeating the color ledCount times + var payload = new byte[3 * ledCount]; + for (var i = 0; i < ledCount * 3; i += 3) + { + payload[i + 0] = c.R; + payload[i + 1] = c.G; + payload[i + 2] = c.B; + } + + // Actually send the payload to each endpoint + foreach (var endpoint in endpoints) + { + udpClient.Send(payload, payload.Length, endpoint); + } + } + + return true; + } + } +} \ No newline at end of file