From c6cfe35124b2752ab52ecc6b4ec839bc43a0ebc3 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 5 Nov 2022 21:06:59 +0100 Subject: [PATCH 1/5] Added heartbeat functionality to device update trigger --- .../Devices/AbstractRGBDeviceProvider.cs | 3 +-- .../Extensions/CustomUpdateDataExtension.cs | 12 +++++++++++ RGB.NET.Core/Update/CustomUpdateData.cs | 5 +++++ .../Update/Devices/DeviceUpdateTrigger.cs | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs b/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs index 7843edd..ae66ca0 100644 --- a/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs +++ b/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs @@ -42,7 +42,7 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider #endregion #region Constructors - + /// /// Initializes a new instance of the class. /// @@ -157,7 +157,6 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// The newly created update trigger. protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit); - /// /// Resets the device provider and disposes all devices and update triggers. /// diff --git a/RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs b/RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs index 4c73e84..ff014a9 100644 --- a/RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs +++ b/RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs @@ -40,4 +40,16 @@ public static class CustomUpdateDataExtension customUpdateData[CustomUpdateDataIndex.UPDATE_DEVICES] = value; return customUpdateData; } + + /// + /// Sets the -Parameter to the given value. + /// + /// The update-data to modify. + /// The value to set. + /// The modified update-data. + public static CustomUpdateData Heartbeat(this CustomUpdateData customUpdateData, bool value = true) + { + customUpdateData[CustomUpdateDataIndex.HEARTBEAT] = value; + return customUpdateData; + } } \ No newline at end of file diff --git a/RGB.NET.Core/Update/CustomUpdateData.cs b/RGB.NET.Core/Update/CustomUpdateData.cs index a16011f..88f734a 100644 --- a/RGB.NET.Core/Update/CustomUpdateData.cs +++ b/RGB.NET.Core/Update/CustomUpdateData.cs @@ -24,6 +24,11 @@ public static class CustomUpdateDataIndex /// default: true /// public const string UPDATE_DEVICES = "updateDevices"; + + /// + /// Used by to indicate heatbeat updates. + /// + public const string HEARTBEAT = "heartbeat"; } /// diff --git a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs index 6868a87..02321f7 100644 --- a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs +++ b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs @@ -1,5 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -52,9 +53,20 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger } } + /// + /// Gets or sets the time in ms after which a refresh-request is sent even if no changes are made in the meantime to prevent the target from timing out or similar problems. + /// To disable heartbeats leave it at 0. + /// + public int HeartbeatTimer { get; set; } + /// public override double LastUpdateTime { get; protected set; } + /// + /// Gets or sets the timestamp of the last update. + /// + protected long LastUpdateTimestamp { get; set; } + /// /// Gets or sets the event to trigger when new data is available (). /// @@ -145,6 +157,14 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger while (!UpdateToken.IsCancellationRequested) if (HasDataEvent.WaitOne(Timeout)) LastUpdateTime = TimerHelper.Execute(() => OnUpdate(), UpdateFrequency * 1000); + else if ((HeartbeatTimer > 0) && (LastUpdateTimestamp > 0) && ((Stopwatch.GetTimestamp() - LastUpdateTimestamp) > HeartbeatTimer)) + OnUpdate(new CustomUpdateData().Heartbeat()); + } + + protected override void OnUpdate(CustomUpdateData? updateData = null) + { + base.OnUpdate(updateData); + LastUpdateTimestamp = Stopwatch.GetTimestamp(); } /// From 15c056f11b0bfb386a722674ed213416f5697a68 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 5 Nov 2022 21:07:11 +0100 Subject: [PATCH 2/5] Addded heartbeats to dmx devices --- RGB.NET.Devices.DMX/DMXDeviceProvider.cs | 14 ++++++++++++-- .../E131/E131DMXDeviceDefinition.cs | 6 ++++++ RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs | 8 ++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs index ee12196..55e8e3f 100644 --- a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs +++ b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs @@ -57,14 +57,15 @@ public class DMXDeviceProvider : AbstractRGBDeviceProvider /// protected override IEnumerable LoadDevices() { - foreach (IDMXDeviceDefinition dmxDeviceDefinition in DeviceDefinitions) + for (int i = 0; i < DeviceDefinitions.Count; i++) { + IDMXDeviceDefinition dmxDeviceDefinition = DeviceDefinitions[i]; IRGBDevice? device = null; try { if (dmxDeviceDefinition is E131DMXDeviceDefinition e131DMXDeviceDefinition) if (e131DMXDeviceDefinition.Leds.Count > 0) - device = new E131Device(new E131DeviceInfo(e131DMXDeviceDefinition), e131DMXDeviceDefinition.Leds, GetUpdateTrigger(0)); + device = new E131Device(new E131DeviceInfo(e131DMXDeviceDefinition), e131DMXDeviceDefinition.Leds, GetUpdateTrigger(i)); } catch (Exception ex) { @@ -76,5 +77,14 @@ public class DMXDeviceProvider : AbstractRGBDeviceProvider } } + protected override IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) + { + DeviceUpdateTrigger updateTrigger = new(updateRateHardLimit); + if ((DeviceDefinitions[id] is E131DMXDeviceDefinition e131DMXDeviceDefinition)) + updateTrigger.HeartbeatTimer = e131DMXDeviceDefinition.HeartbeatTimer; + + return updateTrigger; + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.DMX/E131/E131DMXDeviceDefinition.cs b/RGB.NET.Devices.DMX/E131/E131DMXDeviceDefinition.cs index 12f0cb1..37db5ea 100644 --- a/RGB.NET.Devices.DMX/E131/E131DMXDeviceDefinition.cs +++ b/RGB.NET.Devices.DMX/E131/E131DMXDeviceDefinition.cs @@ -57,6 +57,12 @@ public class E131DMXDeviceDefinition : IDMXDeviceDefinition /// public Dictionary getValueFunc)>> Leds { get; } = new(); + /// + /// The time in ms after which the device is updated even if no changes are made in the meantime to prevent the target from timing out or similar problems. + /// To disable heartbeats leave it at 0. + /// + public int HeartbeatTimer { get; set; } = 0; + #endregion #region Constructors diff --git a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs index e6c108f..850e4d2 100644 --- a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs +++ b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs @@ -50,6 +50,14 @@ public class E131UpdateQueue : UpdateQueue #region Methods + protected override void OnUpdate(object? sender, CustomUpdateData customData) + { + if (customData[CustomUpdateDataIndex.HEARTBEAT] as bool? == true) + Update(Array.Empty<(object key, Color color)>()); + else + base.OnUpdate(sender, customData); + } + /// protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet) { From 2c71196fce4aeb821d0c610f1993d51f2625c54b Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 5 Nov 2022 21:11:01 +0100 Subject: [PATCH 3/5] Fixed heartbeat timer check --- RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs index 02321f7..5d357b6 100644 --- a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs +++ b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs @@ -157,7 +157,7 @@ public class DeviceUpdateTrigger : AbstractUpdateTrigger, IDeviceUpdateTrigger while (!UpdateToken.IsCancellationRequested) if (HasDataEvent.WaitOne(Timeout)) LastUpdateTime = TimerHelper.Execute(() => OnUpdate(), UpdateFrequency * 1000); - else if ((HeartbeatTimer > 0) && (LastUpdateTimestamp > 0) && ((Stopwatch.GetTimestamp() - LastUpdateTimestamp) > HeartbeatTimer)) + else if ((HeartbeatTimer > 0) && (LastUpdateTimestamp > 0) && (TimerHelper.GetElapsedTime(LastUpdateTimestamp) > HeartbeatTimer)) OnUpdate(new CustomUpdateData().Heartbeat()); } From ca8bc67f03a8cbdc1c035659d3070f5873ab8328 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 5 Nov 2022 21:11:17 +0100 Subject: [PATCH 4/5] Small refactoring --- RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs index 850e4d2..b1bffdb 100644 --- a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs +++ b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs @@ -52,7 +52,7 @@ public class E131UpdateQueue : UpdateQueue protected override void OnUpdate(object? sender, CustomUpdateData customData) { - if (customData[CustomUpdateDataIndex.HEARTBEAT] as bool? == true) + if (customData[CustomUpdateDataIndex.HEARTBEAT] as bool? ?? false) Update(Array.Empty<(object key, Color color)>()); else base.OnUpdate(sender, customData); From b12d0dc6314df2e46ed1d37b7c7cce3b6cbbd16d Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 5 Nov 2022 21:11:41 +0100 Subject: [PATCH 5/5] Changed SteelSeries devices to use the core heartbeat functionality --- .../Generic/SteelSeriesDeviceUpdateQueue.cs | 4 +- .../Generic/SteelSeriesDeviceUpdateTrigger.cs | 80 ------------------- .../SteelSeriesDeviceProvider.cs | 8 +- 3 files changed, 9 insertions(+), 83 deletions(-) delete mode 100644 RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs index 3ccc664..67c39a4 100644 --- a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs @@ -14,7 +14,7 @@ internal class SteelSeriesDeviceUpdateQueue : UpdateQueue { #region Properties & Fields - private string _deviceType; + private readonly string _deviceType; #endregion @@ -37,7 +37,7 @@ internal class SteelSeriesDeviceUpdateQueue : UpdateQueue protected override void OnUpdate(object? sender, CustomUpdateData customData) { - if (customData["refresh"] as bool? ?? false) + if (customData[CustomUpdateDataIndex.HEARTBEAT] as bool? ?? false) SteelSeriesSDK.SendHeartbeat(); else base.OnUpdate(sender, customData); diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs deleted file mode 100644 index ddc644b..0000000 --- a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs +++ /dev/null @@ -1,80 +0,0 @@ -// ReSharper disable MemberCanBePrivate.Global - -using System.Diagnostics; -using System.Threading; -using RGB.NET.Core; - -namespace RGB.NET.Devices.SteelSeries; - -/// -/// Represents an update-trigger used to update SteelSeries devices -/// -public class SteelSeriesDeviceUpdateTrigger : DeviceUpdateTrigger -{ - #region Constants - - private static readonly long FLUSH_TIMER = 5 * 1000 * (long)(Stopwatch.Frequency / 1000.0); // flush the device every 5 seconds to prevent timeouts - - #endregion - - #region Properties & Fields - - private long _lastUpdateTimestamp; - - #endregion - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - public SteelSeriesDeviceUpdateTrigger() - { } - - /// - /// Initializes a new instance of the class. - /// - /// The hard limit of the update rate of this trigger. - public SteelSeriesDeviceUpdateTrigger(double updateRateHardLimit) - : base(updateRateHardLimit) - { } - - #endregion - - #region Methods - - /// - protected override void UpdateLoop() - { - OnStartup(); - - while (!UpdateToken.IsCancellationRequested) - { - if (HasDataEvent.WaitOne(Timeout)) - { - long preUpdateTicks = Stopwatch.GetTimestamp(); - - OnUpdate(); - - if (UpdateFrequency > 0) - { - double lastUpdateTime = ((_lastUpdateTimestamp - preUpdateTicks) / (Stopwatch.Frequency / 1000.0)); - int sleep = (int)((UpdateFrequency * 1000.0) - lastUpdateTime); - if (sleep > 0) - Thread.Sleep(sleep); - } - } - else if ((_lastUpdateTimestamp > 0) && ((Stopwatch.GetTimestamp() - _lastUpdateTimestamp) > FLUSH_TIMER)) - OnUpdate(new CustomUpdateData(("refresh", true))); - } - } - - /// - protected override void OnUpdate(CustomUpdateData? updateData = null) - { - base.OnUpdate(updateData); - _lastUpdateTimestamp = Stopwatch.GetTimestamp(); - } - - #endregion -} \ No newline at end of file diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs index 3deb842..8b04c34 100644 --- a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -12,6 +12,12 @@ namespace RGB.NET.Devices.SteelSeries; /// public class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider { + #region Constants + + private static readonly int HEARTBEAT_TIMER = 5000; // flush the device every 5 seconds to prevent timeouts + + #endregion + #region Properties & Fields private static SteelSeriesDeviceProvider? _instance; @@ -121,7 +127,7 @@ public class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider } /// - protected override IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new SteelSeriesDeviceUpdateTrigger(updateRateHardLimit); + protected override IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit) { HeartbeatTimer = HEARTBEAT_TIMER }; /// public override void Dispose()