Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion ArtNetSharp/ApplicationLogging.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using static ArtNetSharp.ApplicationLogging;

Expand All @@ -11,7 +12,34 @@ namespace ArtNetSharp
/// </summary>
internal static class ApplicationLogging
{
internal static readonly ILoggerFactory LoggerFactory = new LoggerFactory();
private static ILoggerFactory loggerFactory;
internal static ILoggerFactory LoggerFactory
{
get
{
if (loggerFactory == null)
{
bool isTest = AppDomain.CurrentDomain.GetAssemblies()
.Any(a => a.FullName.StartsWith("NUnit", StringComparison.OrdinalIgnoreCase));
loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create((builder) =>
{
FileProvider fp = isTest ? new FileProvider() : null;
#if Debug
fp ?= new FileProvider();
#endif
if (isTest)
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Trace);
}
if (fp != null)
builder.AddProvider(fp);
});
}
return loggerFactory;
}
}

internal static ILogger<T> CreateLogger<T>() => LoggerFactory.CreateLogger<T>();
internal static ILogger CreateLogger(Type type) => LoggerFactory.CreateLogger(type);
internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
Expand Down
79 changes: 46 additions & 33 deletions ArtNetSharp/ArtNet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace ArtNetSharp
public class ArtNet : IDisposable
{
private static readonly Random _random = new Random();
private static ILogger<ArtNet> Logger = null;
private static ILogger<ArtNet> Logger = ApplicationLogging.CreateLogger<ArtNet>();
private static ArtNet instance;
public static ArtNet Instance
{
Expand Down Expand Up @@ -107,6 +107,9 @@ public bool Enabled
internal NetworkClientBag(IPAddress broadcastIpAddress, UnicastIPAddressInformation unicastIPAddressInformation)
{
UnicastIPAddressInfo = unicastIPAddressInformation;
if (unicastIPAddressInformation.Address.Equals(IPAddress.Loopback))
broadcastIpAddress = IPAddress.Loopback;

BroadcastIpAddress = broadcastIpAddress;
broadcastEndpoint = new IPEndPoint(broadcastIpAddress, Constants.ARTNET_PORT);
Logger?.LogTrace($"Create Client ({LocalIpAddress})");
Expand Down Expand Up @@ -152,8 +155,9 @@ private async Task openClient()
_client.ExclusiveAddressUse = false;
_client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_client.EnableBroadcast = true;
var endpointIp = Tools.IsLinux() ? IPAddress.Any : LocalIpAddress;
IPEndPoint localEp = new IPEndPoint(IPAddress.Any, Constants.ARTNET_PORT);

IPAddress endpointIp = getEndointIP();
IPEndPoint localEp = new IPEndPoint(endpointIp, Constants.ARTNET_PORT);
_client.Client.Bind(localEp);
_clientAlive = true;
_ = StartListening();
Expand All @@ -162,6 +166,18 @@ private async Task openClient()
catch (Exception e) { Logger?.LogError(e, $"Client ({LocalIpAddress}): Error on initialize"); }
finally { semaphoreSlim?.Release(); }
}
private IPAddress getEndointIP()
{
IPAddress endpointIp = IPAddress.Any;
if (Tools.IsWindows())
endpointIp = LocalIpAddress;
if (Tools.IsMac())
endpointIp = LocalIpAddress;
if (Tools.IsLinux())
endpointIp = LocalIpAddress;

return endpointIp;
}

private async Task StartListening()
{
Expand Down Expand Up @@ -194,6 +210,8 @@ private async Task StartListening()
private readonly List<IPAddress> matchingIpAdddresses = new List<IPAddress>();
public async Task<bool> MatchIP(IPAddress ip)
{
if (ip == IPAddress.Loopback)
return true;
if (ip == null)
return false;
if (ip.ToString().Equals("0.0.0.0"))
Expand Down Expand Up @@ -315,12 +333,6 @@ public void Dispose()

internal ArtNet([CallerFilePath] string caller = "", [CallerLineNumber] int line = -1)
{
if (Logger == null)
{
ApplicationLogging.LoggerFactory.AddProvider(new FileProvider());
Logger = ApplicationLogging.CreateLogger<ArtNet>();
}

Logger?.LogTrace($"Initialize {caller} (Line: {line})");
_updateNetworkClientsTimer = new System.Timers.Timer();
_updateNetworkClientsTimer.Interval = 1000;
Expand Down Expand Up @@ -385,48 +397,49 @@ private void UpdateNetworkClientsTimer_Elapsed(object sender, System.Timers.Elap
}

private void ReceivedData(object sender, Tuple<IPv4Address, UdpReceiveResult> e)
{
ProcessReceivedData(e.Item2);
}
private void ProcessReceivedData(UdpReceiveResult result)
{
if (IsDisposed || IsDisposing)
return;

IPAddress localIpAddress = e.Item1;
UdpReceiveResult result = e.Item2;
IPEndPoint RemoteIpEndPoint = result.RemoteEndPoint;
IPv4Address sourceIp = RemoteIpEndPoint.Address;
byte[] received = result.Buffer;
try
{
IPv4Address sourceIp = RemoteIpEndPoint.Address;
if (Tools.TryDeserializePacket(received, out var packet))
{
var nic = networkClients.Values.FirstOrDefault(n => Tools.IsInSubnet(n.LocalIpAddress, n.IPv4Mask, sourceIp));
if (nic != null)
{
//Logger?.LogTrace($"Process Network Packet:{packet} {Environment.NewLine} Local:{nic.LocalIpAddress}, Mask: {nic.IPv4Mask}, Remote: {sourceIp}");
processPacket(packet, nic.LocalIpAddress, sourceIp);
}
//var nic = networkClients.Values.FirstOrDefault(n => Tools.IsInSubnet(n.LocalIpAddress, n.IPv4Mask, sourceIp));
//if (nic != null)
//{
// //Logger?.LogTrace($"Process Network Packet:{packet} {Environment.NewLine} Local:{nic.LocalIpAddress}, Mask: {nic.IPv4Mask}, Remote: {sourceIp}");
// processPacket(packet, nic.LocalIpAddress, sourceIp);
//}
processPacket(packet, localIpAddress, sourceIp);
return;
}
Logger.LogWarning($"Can't deserialize Data to ArtNet-Packet from {sourceIp}");
}
catch (ObjectDisposedException ed) { Logger.LogTrace(ed); }
catch (SocketException se) { Logger.LogTrace(se); }
catch (Exception e) { Logger.LogError(e); }
catch (Exception ex) { Logger.LogError(ex); }
}
private void processPacket(AbstractArtPacketCore packet, IPv4Address localIp, IPv4Address sourceIp)
{
#if DEBUG
Logger.LogTrace($"Received Packet from {sourceIp} -> {packet}");
#endif
foreach (var inst in instances) try
{
((IInstance)inst.Value).PacketReceived(packet, localIp, sourceIp);
}
catch (Exception e)

foreach (var inst in instances)
Task.Run(() =>
{
Logger.LogError(e);
}
try
{
((IInstance)inst.Value).PacketReceived(packet, localIp, sourceIp);
}
catch (Exception e)
{
Logger.LogError(e);
}
});
}

public static bool IsNetworkAvailable(long? minimumSpeed = null)
Expand Down Expand Up @@ -482,7 +495,7 @@ public MACAddress GetMacAdress(IPv4Address ip)
IPAddress _ipToTest = ip;

var nicWithThisIP = nics.FirstOrDefault(nic => nic.GetIPProperties().UnicastAddresses.Any(_ip => IPAddress.Equals(_ip.Address, _ipToTest)));
if (nicWithThisIP != null)
if (nicWithThisIP != null && nicWithThisIP.NetworkInterfaceType != NetworkInterfaceType.Loopback)
mac = new MACAddress(nicWithThisIP.GetPhysicalAddress().GetAddressBytes());
else
mac = new MACAddress();
Expand Down Expand Up @@ -514,7 +527,7 @@ private async void updateNetworkClients()
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface @interface in interfaces)
{
if (@interface.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
//if (@interface.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
if (@interface.OperationalStatus != OperationalStatus.Up) continue;
UnicastIPAddressInformationCollection unicastIpInfoCol = @interface.GetIPProperties().UnicastAddresses;
foreach (UnicastIPAddressInformation ipInfo in unicastIpInfoCol)
Expand Down
3 changes: 2 additions & 1 deletion ArtNetSharp/ArtNetSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DMXControlProjects.WellKnownDataTypes" Version="0.0.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
<PackageReference Include="RDMSharp" Version="0.0.13" />
</ItemGroup>
</Project>
54 changes: 34 additions & 20 deletions ArtNetSharp/Communication/AbstractInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

[assembly: InternalsVisibleTo("ArtNetTests")]
namespace ArtNetSharp.Communication
Expand All @@ -25,7 +24,7 @@ public abstract class AbstractInstance : IInstance
bool IDisposableExtended.IsDisposed { get => IsDisposed; }
bool IDisposableExtended.IsDisposing { get => IsDisposing; }

public bool IsDeactivated { get { return !ArtNetInstance.Instances.Contains(this); } }
public bool IsDeactivated { get { return !ArtNetInstance?.Instances.Contains(this) ?? true; } }

private readonly Random _random;
internal ArtNet ArtNetInstance;
Expand Down Expand Up @@ -258,6 +257,8 @@ public void Dispose()

internal class SendPollThreadBag
{
private static readonly TimeSpan PollPeriod = TimeSpan.FromSeconds(2.7); // Spec 1.4dd page 13

private readonly Thread sendPollThread;
public EventHandler SendArtPollEvent;
public SendPollThreadBag()
Expand All @@ -269,8 +270,9 @@ public SendPollThreadBag()
{
try
{
if ((DateTime.UtcNow - lastSendPollTime).TotalSeconds < 2.7)// Spec 1.4dd page 13
continue;
TimeSpan elapsed = DateTime.UtcNow - lastSendPollTime;
if (elapsed < PollPeriod)
await Task.Delay(PollPeriod - elapsed);

SendArtPollEvent?.InvokeFailSafe(null,EventArgs.Empty);
}
Expand Down Expand Up @@ -328,7 +330,7 @@ void IInstance.PacketReceived(AbstractArtPacketCore packet, IPv4Address localIp,
{
if (this.IsDisposing || this.IsDisposed || this.IsDeactivated)
return;

try
{
switch (packet)
Expand Down Expand Up @@ -600,32 +602,38 @@ private async Task sendAllArtDMX()
{
const double dmxRefreshTime = 1000 / 44.0; // Spec 1.4dh page 56
const double dmxKeepAliveTime = 800; // Spec 1.4dh page 53
const int interval = (int)(dmxRefreshTime / 3);
List<Task> sendTasks = new List<Task>();
while (!(this.IsDisposing || this.IsDisposed))
{
// Prevent CPU loop
await Task.Delay(interval);

if (!this.EnableDmxOutput)
continue;
await Task.Delay(300);
if (this.IsDeactivated)
continue;
await Task.Delay(300);

try
{
var ports = RemoteClientsPorts?.Where(port => port.OutputPortAddress.HasValue && !port.Timouted())?.ToList();

int sended = 0;
var utcNow = DateTime.UtcNow;
foreach (var port in ports)
try
{
if (sendDMXBuffer.TryGetValue(port.OutputPortAddress.Value, out DMXSendBag bag))
if ((bag.Updated && (DateTime.UtcNow - bag.LastSended).TotalMilliseconds >= dmxRefreshTime) || (DateTime.UtcNow - bag.LastSended).TotalMilliseconds >= dmxKeepAliveTime)
if ((bag.Updated && (utcNow - bag.LastSended).TotalMilliseconds >= dmxRefreshTime) || (utcNow - bag.LastSended).TotalMilliseconds >= dmxKeepAliveTime)
{
PortConfig config = null;
byte sourcePort = 0;
try
{
bag.LastSended = DateTime.UtcNow;
bag.LastSended = utcNow;
config = portConfigs?.FirstOrDefault(pc => PortAddress.Equals(pc.PortAddress, port.OutputPortAddress));
sourcePort = config?.PortNumber ?? 0;
await sendArtDMX(port, sourcePort, bag.Data, bag.GetSequence(), config?.ForceBroadcast ?? false);
sendTasks.Add(sendArtDMX(port, sourcePort, bag.Data, bag.GetSequence(), config?.ForceBroadcast ?? false));
sended++;
if (config == null)
return;
Expand All @@ -637,13 +645,15 @@ private async Task sendAllArtDMX()
}
foreach (IPv4Address ip in config?.AdditionalIPEndpoints)
{
await sendArtDMX(ip, config.PortAddress, sourcePort, bag.Data, bag.GetSequence());
sendTasks.Add(sendArtDMX(ip, config.PortAddress, sourcePort, bag.Data, bag.GetSequence()));
sended++;
}
bag.LastSended = DateTime.UtcNow;
}
}
catch (Exception e) { Logger.LogError(e, "Outer Block"); }
await Task.WhenAll(sendTasks);
sendTasks.Clear();
if (EnableSync && sended != 0)
await sendArtSync();

Expand Down Expand Up @@ -787,14 +797,16 @@ private async Task processArtPollReply(ArtPollReply artPollReply, IPv4Address lo
if (this.IsDisposing || this.IsDisposed || this.IsDeactivated)
return;

if (MajorVersion == artPollReply.MajorVersion
&& MinorVersion == artPollReply.MinorVersion
&& OEMProductCode == artPollReply.OemCode
&& ESTAManufacturerCode == artPollReply.ManufacturerCode
&& IPv4Address.Equals(artPollReply.OwnIp, localIp)
&& string.Equals(artPollReply.ShortName, ShortName)
&& string.Equals(artPollReply.LongName, Name))
return; //break loopback
if (localIp == sourceIp)
{
if (MajorVersion == artPollReply.MajorVersion
&& MinorVersion == artPollReply.MinorVersion
&& OEMProductCode == artPollReply.OemCode
&& ESTAManufacturerCode == artPollReply.ManufacturerCode
&& IPv4Address.Equals(artPollReply.OwnIp, localIp)
&& EstCodes == artPollReply.Style)
return; //break loopback
}

string id = RemoteClient.getIDOf(artPollReply);
RemoteClient remoteClient = null;
Expand Down Expand Up @@ -1227,6 +1239,8 @@ private async Task triggerSendArtPoll()
{
if (this.IsDisposed || this.IsDisposing || this.IsDeactivated)
return;
//if (EstCodes != EStCodes.StController)// As Spec. only Controler are allowed to send ArtPoll
// return;
if (SendArtPollBroadcast)
await sendArtPoll();
else if (SendArtPollTargeted)
Expand Down Expand Up @@ -1267,7 +1281,7 @@ private async void TimerSendPoll_Elapsed(object sender, EventArgs e)
return;

await triggerSendArtPoll();
await Task.Delay(3000);
await Task.Delay(4000);
await pollReplyProcessSemaphoreSlim.WaitAsync();
try
{
Expand Down
Loading
Loading