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
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,33 @@ This is important if certain actions should happen at the same time (i.e. contro

###

### Dependency Injection
### Autofac
> [!IMPORTANT]
> While this library works without dependency injection, DI is still recommend as it makes usage of this library much easier.
[Z21.DependencyInjection](https://www.nuget.org/packages/Z21.DependencyInjection/) provides extension methods to register all required classes directly in the container.
> While this library works without Autofac, Autofac is still recommend as it makes usage of this library much easier.
[Z21.Autofac](https://www.nuget.org/packages/Z21.Autofac/) provides extension methods to register all required classes directly in the container.

```csharp
var builder = new ContainerBuilder();
builder.AddZ21();
var container = builder.Build();
```

### Dependency Injection
Dependency Injection is supported natively via [Z21.DependencyInjection](https://www.nuget.org/packages/Z21.DependencyInjection/) and requires the use of hosted services.
Z21 registers background components that must run inside the .NET Generic Host lifecycle.

A minimal setup looks like this:
```csharp
services.ConfigureZ21Client(Z21Configuration.Defaults.IpEndPoint);
services.AddZ21Client();
services.AddZ21Transport();
services.AddZ21ResponseParser();
services.AddZ21ResponseHandler();
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddZ21();
})
.Build();

await host.RunAsync();
```
The host is responsible for starting all Z21‑related hosted services and managing their lifetime.

## Z21 Commands

Expand Down
16 changes: 5 additions & 11 deletions src/Z21.Autofac.UnitTests/Z21AutofacExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ private IContainer BuildContainer(Action<ContainerBuilder> configure)
[Test]
public void AddZ21ResponseHandler_RegistersTypesCorrectly()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 21105);
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21(endpoint));
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21());

var handler = container.Resolve<IHardwareInfoResponseHandler>();
Assert.That(handler, Is.InstanceOf<HardwareInfoResponseHandler>());
Expand All @@ -35,8 +34,7 @@ public void AddZ21ResponseHandler_RegistersTypesCorrectly()
[Test]
public void AddZ21ResponseParser_Registers_All_Parser_Types()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 21105);
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21(endpoint));
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21());

var baseInterface = typeof(IZ21ResponseParser);
var parserTypes = baseInterface.Assembly
Expand Down Expand Up @@ -64,8 +62,7 @@ public void AddZ21ResponseParser_Registers_All_Parser_Types()
[Test]
public void AddZ21Transport_Registers_Transport_As_Singleton()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 21105);
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21(endpoint));
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21());

var t1 = container.Resolve<IZ21Transport>();
var t2 = container.Resolve<IZ21Transport>();
Expand All @@ -78,8 +75,7 @@ public void AddZ21Transport_Registers_Transport_As_Singleton()
[Test]
public void AddZ21Client_Registers_Client_As_Singleton()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 21105);
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21(endpoint));
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21());

var c1 = container.Resolve<IZ21Client>();
var c2 = container.Resolve<IZ21Client>();
Expand All @@ -92,13 +88,11 @@ public void AddZ21Client_Registers_Client_As_Singleton()
[Test]
public void ConfigureZ21Client_Registers_Configuration_Instance()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 21105);
using var container = BuildContainer(containerBuilder => containerBuilder.ConfigureZ21Client(endpoint, cfg => cfg.ResponseTime = TimeSpan.FromSeconds(5)));
using var container = BuildContainer(containerBuilder => containerBuilder.AddZ21(cfg => cfg.ResponseTime = TimeSpan.FromSeconds(5)));

var config = container.Resolve<Z21Configuration>();

Assert.NotNull(config);
Assert.That(config.ClientIPEndPoint, Is.EqualTo(endpoint));
Assert.That(config.ResponseTime, Is.EqualTo(TimeSpan.FromSeconds(5)));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Z21.Autofac/Z21.Autofac.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<Authors>Jakob Eichberger</Authors>
<RepositoryUrl>https://github.com/Jakob-Eichberger/z21Client</RepositoryUrl>
<Version>5.0.0</Version>
<Version>6.0.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
Expand Down
46 changes: 11 additions & 35 deletions src/Z21.Autofac/Z21AutofacExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Net;
using Autofac;
using Autofac;
using Z21.Core;
using Z21.Core.Model;
using Z21.Core.ResponseHandler;
Expand All @@ -11,20 +10,22 @@ namespace Z21.Autofac
public static class Z21AutofacExtensions
{

public static ContainerBuilder AddZ21(this ContainerBuilder builder, IPEndPoint z21EndPoint, Action<Z21Configuration>? configurationAction = null)
public static ContainerBuilder AddZ21(this ContainerBuilder builder, Action<Z21Configuration>? configurationAction = null)
{
builder.ConfigureZ21Client(z21EndPoint, configurationAction);
builder.RegisterType<Z21Transport>().As<IZ21Transport>().SingleInstance();
builder.RegisterType<Z21Client>().As<IZ21Client>().SingleInstance();
builder.RegisterType<Z21ResponseHandler>().AsSelf().SingleInstance().AutoActivate();

builder.ConfigureZ21Client(configurationAction);
builder.AddZ21ResponseParser();
builder.AddZ21ResponseHandler();
builder.AddZ21Transport();
builder.AddZ21Client();
return builder;
}

/// <summary>
/// Discovers all Z21 response handlers and registers them in the <paramref name="builder"/> container.
/// </summary>
public static ContainerBuilder AddZ21ResponseHandler(this ContainerBuilder builder)
private static ContainerBuilder AddZ21ResponseHandler(this ContainerBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

Expand Down Expand Up @@ -57,7 +58,7 @@ public static ContainerBuilder AddZ21ResponseHandler(this ContainerBuilder build
return builder;
}

public static ContainerBuilder AddZ21ResponseParser(this ContainerBuilder builder)
private static ContainerBuilder AddZ21ResponseParser(this ContainerBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

Expand Down Expand Up @@ -90,36 +91,11 @@ public static ContainerBuilder AddZ21ResponseParser(this ContainerBuilder builde
return builder;
}

public static ContainerBuilder AddZ21Transport(this ContainerBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

builder.RegisterType<Z21Transport>()
.As<IZ21Transport>()
.SingleInstance();

return builder;
}

public static ContainerBuilder AddZ21Client(this ContainerBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

builder.RegisterType<Z21Client>()
.As<IZ21Client>()
.SingleInstance();

return builder;
}

public static ContainerBuilder ConfigureZ21Client(this ContainerBuilder builder,
IPEndPoint z21EndPoint,
Action<Z21Configuration>? configurationAction = null)
private static ContainerBuilder ConfigureZ21Client(this ContainerBuilder builder, Action<Z21Configuration>? configurationAction = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(z21EndPoint);

var config = new Z21Configuration(z21EndPoint);
var config = new Z21Configuration();
configurationAction?.Invoke(config);

builder.RegisterInstance(config)
Expand Down
28 changes: 28 additions & 0 deletions src/Z21.Client/Core/Helper/DelayedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Threading.Tasks;
using System.Timers;

namespace Z21.Core.Helper
{
public class DelayedAction
{
private readonly Timer _connectionKeepAlive;

public DelayedAction(TimeSpan delayTime, Func<Task> action)
{
_connectionKeepAlive = new(delayTime)
{
AutoReset = true,
Enabled = false
};

_connectionKeepAlive.Elapsed += async (_, _) => await action();
}

public void Delay()
{
_connectionKeepAlive.Stop();
_connectionKeepAlive.Start();
}
}
}
57 changes: 36 additions & 21 deletions src/Z21.Client/Core/Model/Z21Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,62 @@ namespace Z21.Core.Model
{
public class Z21Configuration
{

public Z21Configuration(IPEndPoint clientIpEndPoint)
{
ArgumentNullException.ThrowIfNull(clientIpEndPoint, nameof(clientIpEndPoint));

ClientIPEndPoint = clientIpEndPoint;
}
private IPEndPoint _clientIpEndPoint = Defaults.IpEndPoint;
private bool _allowNatTraversal = true;

/// <summary>
/// IPEndPoint of the Z21.
/// </summary>
public IPEndPoint ClientIPEndPoint { get; }
public IPEndPoint ClientIPEndPoint
{
get => _clientIpEndPoint;
set
{
ArgumentNullException.ThrowIfNull(value);
if (_clientIpEndPoint.Equals(value))
return;

_clientIpEndPoint = value;
ConfigurationUpdated?.Invoke(this, System.EventArgs.Empty);
}
}

/// <summary>
/// Enables or disables Network Address Translation (NAT) traversal on a UdpClient instance.
/// </summary>
public bool AllowNatTraversal { get; set; } = true;

/// <summary>
/// Configures the interval in witch the client will send a keep alive command to the z21. This Setting should not need changing!
/// </summary>
/// <remarks>The specification states that the client must communicate at least once per minute with the z21 or else the z21 assumes that the client has disconnected.</remarks>
public TimeSpan ConnectionKeepAliveCommandInterval { get; set; } = TimeSpan.FromSeconds(20);
public bool AllowNatTraversal
{
get => _allowNatTraversal;
set
{
if (_allowNatTraversal.Equals(value))
return;
_allowNatTraversal = value;
ConfigurationUpdated?.Invoke(this, System.EventArgs.Empty);
}
}

/// <summary>
/// Time it takes between a command being sent and a response being received. This Setting should not need changing!
/// </summary>
public TimeSpan ResponseTime { get; set; } = TimeSpan.FromSeconds(2);

/// <summary>
/// Configures the default broadcast flags that should be sent to the Z21
/// </summary>
public uint[] BroadcastFlags { get; set; } =
[
Z21BroadcastFlags.DriveAndSwitchingMessages,
Z21BroadcastFlags.LocoInfoChangedMessages
];
public uint[] BroadcastFlags { get; set; } = Defaults.BroadcastFlags;

public event EventHandler<System.EventArgs>? ConfigurationUpdated;

public static class Defaults
{
public readonly static IPEndPoint IpEndPoint = new(IPAddress.Parse("192.168.0.111"), 21105);

public readonly static uint[] BroadcastFlags =
[
Z21BroadcastFlags.DriveAndSwitchingMessages,
Z21BroadcastFlags.LocoInfoChangedMessages
];
}
}
}
Loading
Loading