diff --git a/ArtNetSharp/ArtNet.cs b/ArtNetSharp/ArtNet.cs index 8f90f43..1f5e2b5 100644 --- a/ArtNetSharp/ArtNet.cs +++ b/ArtNetSharp/ArtNet.cs @@ -307,7 +307,7 @@ private ArtNet() ApplicationLogging.LoggerFactory.AddProvider(new FileProvider()); Logger = ApplicationLogging.CreateLogger(); - Logger.LogTrace("Initialized!"); + Logger?.LogTrace("Initialized!"); _updateNetworkClientsTimer = new System.Timers.Timer(); _updateNetworkClientsTimer.Interval = 1000; @@ -322,6 +322,8 @@ private ArtNet() internal static void Clear() { + Logger?.LogDebug($"Clear"); + if (instance == null) return; @@ -644,7 +646,6 @@ void IDisposable.Dispose() ipTomacAddressCache.Clear(); OnInstanceAdded = null; OnInstanceRemoved = null; - Logger = null; IsDisposed = true; IsDisposing = false; diff --git a/ArtNetSharp/Communication/AbstractInstance.cs b/ArtNetSharp/Communication/AbstractInstance.cs index 98d1d83..a54f2ea 100644 --- a/ArtNetSharp/Communication/AbstractInstance.cs +++ b/ArtNetSharp/Communication/AbstractInstance.cs @@ -9,6 +9,7 @@ using System.Data; using System.Diagnostics; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using System.Timers; @@ -54,7 +55,7 @@ public abstract class AbstractInstance : IInstance private readonly List portConfigs = new List(); public ReadOnlyCollection PortConfigs { get => portConfigs.AsReadOnly(); } - private readonly ConcurrentDictionary> receivedDMXBuffer = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary, DMXReceiveBag> receivedDMXBuffer = new ConcurrentDictionary, DMXReceiveBag>(); private readonly ConcurrentDictionary artRDMdeBumbReceive = new(); private readonly struct RDM_TransactionID : IEquatable @@ -91,13 +92,16 @@ public override int GetHashCode() return Transaction.GetHashCode() + Controller.GetHashCode() + Responder.GetHashCode(); } } - private class DMXReceiveBag + private class DMXReceiveBag: IDisposable { public byte[] Data { get; private set; } = new byte[512]; public byte Sequence { get; private set; } = byte.MaxValue; - public PortAddress PortAddress { get; private set; } - public IPv4Address Source { get; private set; } + public readonly PortAddress PortAddress; + public readonly IPv4Address Source; public DateTime LastUpdate { get; private set; } + + public bool IsDisposed; + public bool IsDisposing; public DMXReceiveBag(ArtDMX artDMX, IPv4Address source) { PortAddress = new PortAddress(artDMX.Net, artDMX.Address); @@ -106,6 +110,9 @@ public DMXReceiveBag(ArtDMX artDMX, IPv4Address source) } internal bool Update(ArtDMX artDMX, IPv4Address source) { + if (IsDisposed || IsDisposing) + return false; + if (Source != source) return false; @@ -123,6 +130,9 @@ internal bool Update(ArtDMX artDMX, IPv4Address source) private bool? checkSequence(byte _old, byte _new) { + if (IsDisposed || IsDisposing) + return null; + if (_new == 0) return null; //SpezialCase @@ -134,26 +144,44 @@ internal bool Update(ArtDMX artDMX, IPv4Address source) return false; } + + public void Dispose() + { + if (IsDisposed || IsDisposing) + return; + + Data = null; + Sequence = 0; + GC.SuppressFinalize(this); + } } - private class DMXSendBag + private class DMXSendBag: IDisposable { public byte[] Data { get; private set; } = new byte[512]; public bool Updated => LastUpdated > LastSended; public byte Sequence { get; private set; } + public readonly PortAddress PortAddress; public DateTime LastUpdated { get; private set; } public DateTime LastSended { get; internal set; } - private readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1); + private SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1); - public DMXSendBag(byte[] data) + public bool IsDisposed; + public bool IsDisposing; + + public DMXSendBag(byte[] data, PortAddress portAddress) { Update(data); + PortAddress = portAddress; } internal async void Update(byte[] data, ushort? startindex = null, ushort? count = null) { - await SemaphoreSlim.WaitAsync(); + if (IsDisposed || IsDisposing) + return; + + await SemaphoreSlim?.WaitAsync(); try { if ((startindex + count) <= Data.Length) @@ -167,7 +195,7 @@ internal async void Update(byte[] data, ushort? startindex = null, ushort? count { Logger.LogError(e); } - SemaphoreSlim.Release(); + SemaphoreSlim?.Release(); } internal byte GetSequence() @@ -175,6 +203,20 @@ internal byte GetSequence() Sequence++; return Sequence; } + + public void Dispose() + { + if (IsDisposed || IsDisposing) + return; + + Data = null; + Sequence = 0; + LastUpdated = DateTime.MaxValue; + LastSended = DateTime.MaxValue; + SemaphoreSlim?.Dispose(); + SemaphoreSlim = null; + GC.SuppressFinalize(this); + } } private readonly ConcurrentDictionary sendDMXBuffer = new ConcurrentDictionary(); private readonly SemaphoreSlim semaphoreSlimAddRemoteClient = new SemaphoreSlim(1, 1); @@ -725,7 +767,7 @@ async Task add() } catch (Exception ex) { Logger.LogError(ex); } - var deadline = 7500; // Spec 1.4dd page 12, doubled to allow one lost reply (6s is allowad, for some delay i add 1500 ms) + var deadline = 9500; // Spec 1.4dd page 12, doubled to allow one lost reply (6s is allowad, for some delay i add 2500 ms) var timoutedClients = remoteClients.Where(p => (DateTime.UtcNow - p.Value.LastSeen).TotalMilliseconds > deadline); if (timoutedClients.Count() != 0) { @@ -754,15 +796,11 @@ private void processArtDMX(ArtDMX artDMX, IPv4Address sourceIp) return; bool success = false; - if (!receivedDMXBuffer.TryGetValue(port.PortAddress, out ConcurrentDictionary cdb)) - { - cdb = new ConcurrentDictionary(); - receivedDMXBuffer.TryAdd(port.PortAddress, cdb); - } - if (!cdb.TryGetValue(sourceIp, out DMXReceiveBag bag)) + var key = new Tuple(port.PortAddress, sourceIp); + if (!receivedDMXBuffer.TryGetValue(key, out DMXReceiveBag bag)) { bag = new DMXReceiveBag(artDMX, sourceIp); - success = cdb.TryAdd(sourceIp, bag); + success = receivedDMXBuffer.TryAdd(key, bag); } else success = bag.Update(artDMX, sourceIp); @@ -965,22 +1003,19 @@ public void WriteDMXValues(PortAddress portAddress, byte[] data, ushort? startin bag.Update(data, startindex, count); else { - var newBag = new DMXSendBag(data); + var newBag = new DMXSendBag(data, portAddress); sendDMXBuffer.TryAdd(portAddress, newBag); } } catch (Exception e) { Logger.LogError(e); } } - public byte[] GetReceivedDMX(in PortAddress portAddress, EMergeMode mergeMode = EMergeMode.HTP) + public byte[] GetReceivedDMX(PortAddress portAddress, EMergeMode mergeMode = EMergeMode.HTP) { - if (this.IsDisposing || this.IsDisposed) - return null; - - if (!receivedDMXBuffer.TryGetValue(portAddress, out ConcurrentDictionary cdb)) + if (this.IsDisposing || this.IsDisposed || this.receivedDMXBuffer.IsEmpty) return null; - var bags = cdb.Select(b => b.Value).ToList(); + var bags = receivedDMXBuffer.Values.Where(v => v.PortAddress == portAddress).ToList(); if (bags.Count == 0) return null; @@ -1162,11 +1197,20 @@ void IDisposable.Dispose() _timerSendPoll.Elapsed -= TimerSendPoll_Elapsed; _timerSendPoll.Enabled = false; + foreach (var rBuffer in receivedDMXBuffer.Values) + rBuffer.Dispose(); receivedDMXBuffer.Clear(); + + foreach (var sBuffer in sendDMXBuffer.Values) + sBuffer.Dispose(); sendDMXBuffer.Clear(); + RemovePortConfig(portConfigs.ToArray()); + portConfigs.Clear(); remoteClients.Clear(); + knownControllerRDMUIDs.Clear(); + knownRDMUIDs.Clear(); RemoteClients = null; Dispose(); diff --git a/ArtNetTests/LoopTests/ControllerToControllerTests.cs b/ArtNetTests/LoopTests/ControllerToControllerTests.cs index b800238..b7e8df0 100644 --- a/ArtNetTests/LoopTests/ControllerToControllerTests.cs +++ b/ArtNetTests/LoopTests/ControllerToControllerTests.cs @@ -1,6 +1,7 @@ using ArtNetSharp; using ArtNetSharp.Communication; using ArtNetTests.Mocks; +using Microsoft.Extensions.Logging; using RDMSharp; using System.Diagnostics; @@ -9,6 +10,7 @@ namespace ArtNetTests.LoopTests [Order(20)] public class ControllerToControllerTests { + private static readonly ILogger Logger = ApplicationLogging.CreateLogger(); private ArtNet artNet; private ControllerInstanceMock instanceTX; private OutputPortConfig outputPort; @@ -23,6 +25,7 @@ public class ControllerToControllerTests [OneTimeSetUp] public void OneTimeSetUp() { + Logger.LogDebug($"Test Setup: {nameof(ControllerToControllerTests)}"); ArtNet.Clear(); artNet = ArtNet.Instance;