Skip to main content

Host

The host is the main switch for a Zeus application. It creates channels, opens them, starts acquisition, and releases resources on shutdown.

Minimal Pattern​

await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("meter");
});

await app.StartAsync();
var meter = app.Channels.Get("meter");
await meter.WriteAsync(new byte[] { 0x01 });
await app.StopAsync();

Read it as:

  1. ZeusHost.Create: declare channels, devices, and services.
  2. StartAsync: open channels and start background services.
  3. Channels.Get: access a running channel.
  4. StopAsync: close channels and pause services.

Create Does Not Open Ports​

This only registers the channel:

await using var app = ZeusHost.Create(builder =>
{
builder.AddSerialPort("meter", "COM3", 9600);
});

The port opens only after:

await app.StartAsync();

Calling WriteAsync before StartAsync fails because the channel is still Created.

Registration Order​

Use this order in most apps:

await using var app = ZeusHost.Create(builder =>
{
builder.AddAcquisition(TimeSpan.FromMilliseconds(500));
builder.AddSerialPort("bus", "COM3", 9600);
builder.AddModbusRtu("oven", "bus", unitId: 1, points: map =>
{
map.HoldingRegister("temperature", 0, 0.1)
.HoldingRegister("setpoint", 1, 0.1).Writable("setpoint");
});
});
OrderReason
Configure acquisitionPolling interval is host-level behavior
Register channelsDevices bind to channel names
Register devicesDevices expose protocol APIs and points
Start hostOpen everything after all declarations are complete

Runtime Access​

var channel = app.Channels.Get("bus");
var device = app.Devices.Get<ModbusDevice>("oven");
var temperature = app.Points.Get<double>("temperature");

Missing names produce exceptions that include currently registered names.

Restart After Stop​

StopAsync closes channels and pauses acquisition, but the host can be started again:

await app.StopAsync();
await app.StartAsync();

After DisposeAsync, create a new host.

Runtime Topology Changes​

await app.AddVirtualChannelAsync("bus");
app.AddModbusRtu("oven", "bus", points: map => map.HoldingRegister("pv", 0));

await app.RemoveDeviceAsync("oven");
await app.RemoveChannelAsync("bus");

When the host is running, new channels open immediately. Removing a channel removes bound devices by default.

If the supplied CancellationToken is already canceled, runtime add/remove APIs return cancellation before mutating registries; they do not leave a half-registered channel/device or half-remove existing topology. When the host is running and a new required channel fails while opening, it is rolled back from the channel registry.

Reconnects​

After a channel becomes Faulted, Zeus waits 1 second, retries, then backs off up to 30 seconds by default. Explicit CloseAsync does not reconnect.

Caller cancellation is not considered a channel failure. If OpenAsync, WriteAsync, StartAsync, or runtime channel registration observes the caller's token cancellation, Zeus preserves OperationCanceledException, does not mark the channel Faulted, and does not schedule reconnect. Reconnect only takes over after real open, receive, or write failures put the channel in Faulted.

builder.AddReconnect(options =>
{
options.Enabled = true;
options.InitialDelay = TimeSpan.FromSeconds(1);
options.MaxDelay = TimeSpan.FromSeconds(30);
});

Disable reconnects if you want to drive recovery manually through StateChanged and OpenAsync.

Register Your Services​

Zeus uses .NET dependency injection:

await using var app = ZeusHost.Create(builder =>
{
builder.Services.AddSingleton<MyBusinessService>();
builder.AddVirtualChannel("meter");
});

This is a good place for acquisition processing, alarm logic, or database writers that should not live inside a form class.

Custom devices that need a logger from the container can use the factory overload that receives IServiceProvider:

builder.AddDevice("meter", "bus", (services, name, channel) =>
new TemperatureMeter(name, channel, services.GetRequiredService<ILogger<TemperatureMeter>>()));

Configure Logging​

builder.Logging is a standard ILoggingBuilder. The default is the Generic Host console logger. Attach JSON, files, or Serilog here:

await using var app = ZeusHost.Create(builder =>
{
builder.Logging.AddJsonConsole();
builder.Logging.SetMinimumLevel(LogLevel.Information);
builder.AddVirtualChannel("meter");
});

Channel open/close, reconnect, acquisition failures, and point write-back failures go to ILogger with stable ids from ZeusLogEvents. Filter by EventId in the field instead of matching Chinese message text.

To log TX/RX for every channel while troubleshooting:

builder.AddCommunicationLogging(); // Debug by default

The default level is Debug so hex payloads do not drown business logs. The category defaults to Zeus.Communication and can be changed in the options callback.

Next: Virtual Channel, Serial Port, or TCP / UDP.