# ObjectDetectionClient

<Badge tone="type">class · sealed</Badge> <Badge>IAsyncDisposable</Badge> <Badge>thread-safe</Badge>

```csharp
namespace AR51.Net;

public sealed class ObjectDetectionClient : IObjectDetectionClient, IAsyncDisposable
```

**Implements:** [`IObjectDetectionClient`](/docs/sdk-api/csharp-api/classes/iobjectdetectionclient/) · [`IAsyncDisposable`](https://learn.microsoft.com/en-us/dotnet/api/system.iasyncdisposable)

Dispose it with `await using` (or `DisposeAsync`): this stops every stream and closes every connection. Calling it again does nothing.

Receives live tracked objects from the AR 51 object detection service. Get one from [`IAR51Client.ObjectDetection`](/docs/sdk-api/csharp-api/classes/iar51client/) (recommended: the server is found for you), or create one on its own with the service's address.

:::tip[How-to]
This page is **reference** (facts only). For a walkthrough with a complete sample, see the [Object detection guide](/docs/csharp-api/objects/).
:::

<Example>

```csharp
await using var objects = new ObjectDetectionClient("192.168.1.20", 1503);
objects.ObjectReceived += (_, e) => Console.WriteLine($"{e.Object.Name} at {e.Object.Position}");
await objects.StartAsync();
```

</Example>

## Properties

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `IsAvailable` | `bool` | `get` | `true` when the object detection service's address is known (always, for a standalone `ObjectDetectionClient`). Implements [`IObjectDetectionClient.IsAvailable`](/docs/sdk-api/csharp-api/classes/iobjectdetectionclient/). |

</div>

## Method summary

<div className="apiTable">

| Method | Returns | |
|---|---|---|
| [`Constructor`](#constructor) | — | <Badge>constructor</Badge> |
| [`StartAsync`](#startasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) | <Badge>implements</Badge> |
| [`StopAsync`](#stopasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) | <Badge>implements</Badge> |
| [`StreamAsync`](#streamasync) | [`IAsyncEnumerable<TrackedObject>`](/docs/sdk-api/csharp-api/classes/trackedobject/) | <Badge>implements</Badge> |
| [`DisposeAsync`](#disposeasync) | `ValueTask` |  |

</div>

→ Full descriptions in [Method details](#method-details) below.

## Events

Standard .NET events (`EventHandler<T>`): subscribe with `+=`, unsubscribe with `-=`. They are raised on a background thread.

<div className="apiTable">

| Event | Args | Raised when |
|---|---|---|
| `ObjectReceived` | [`TrackedObjectEventArgs`](/docs/sdk-api/csharp-api/classes/iobjectdetectionclient/#trackedobjecteventargs) | Raised for every object received while started, one event per object per frame, on a background thread. Keep handlers short: they run on the receiving thread. An exception thrown by a handler is logged and does not stop the others or the stream. Implements [`IObjectDetectionClient.ObjectReceived`](/docs/sdk-api/csharp-api/classes/iobjectdetectionclient/). |

</div>

## Method details

### Constructor

<ApiBody kind="method">

```csharp
public ObjectDetectionClient(string host, int port, ILoggerFactory? loggerFactory = null);
```

Creates a standalone client for the object detection service at `host`:`port`. Call [`StartAsync`](#startasync) to begin receiving.

<Params>
<Param name="host" type="string">Host name or IP address of the AR 51 server running the object detection service.</Param>
<Param name="port" type="int">Port of the service: the same as the skeleton service's.</Param>
<Param name="loggerFactory" type="ILoggerFactory?" typeHref="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.iloggerfactory">Receives the client's diagnostic log. `null` disables logging. Optional.</Param>
</Params>

<Throws>
<Throw type="ArgumentException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception">`host` is empty.</Throw>
<Throw type="ArgumentOutOfRangeException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception">`port` is not between 1 and 65535.</Throw>
</Throws>

:::note
AR 51 service ports are not fixed. Prefer [`IAR51Client.ObjectDetection`](/docs/sdk-api/csharp-api/classes/iar51client/), which finds the service for you.
:::

</ApiBody>

### StartAsync

<ApiBody kind="method" tags="implements IObjectDetectionClient">

```csharp
public Task StartAsync(CancellationToken cancellationToken = default);
```

Starts receiving objects into `ObjectReceived`. Returns at once; calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Ignored. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A completed task.</Returns>

<Throws>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Receiving begins as soon as the service is reachable, and resumes by itself after the service restarts or the network drops, until [`StopAsync`](#stopasync) or dispose.
:::

</ApiBody>

### StopAsync

<ApiBody kind="method" tags="implements IObjectDetectionClient">

```csharp
public Task StopAsync(CancellationToken cancellationToken = default);
```

Stops receiving objects: stops `ObjectReceived`, ends every [`StreamAsync`](#streamasync) loop running now, and waits until the event stream is closed. Calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops waiting for the stream to close; the stream still closes. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A task that completes when the event stream is closed.</Returns>

:::note
[`StreamAsync`](#streamasync) loops end normally, without an exception, after the object they are handling. Loops started after this call are not affected.
:::

</ApiBody>

### StreamAsync

<ApiBody kind="method" tags="implements IObjectDetectionClient">

```csharp
public IAsyncEnumerable<TrackedObject> StreamAsync([EnumeratorCancellation] CancellationToken cancellationToken = default);
```

Streams objects until [`StopAsync`](#stopasync) is called (the loop ends normally), `cancellationToken` is cancelled, or the connection drops. Works without [`StartAsync`](#startasync).

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops the stream. Optional.</Param>
</Params>

<Returns type="IAsyncEnumerable<TrackedObject>" typeHref="/docs/sdk-api/csharp-api/classes/trackedobject/">Objects in the order they arrive, one per object per frame.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The object detection service is not available, or the stream failed.</Throw>
<Throw type="OperationCanceledException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.operationcanceledexception">`cancellationToken` was cancelled.</Throw>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Each call opens its own stream, which closes when the loop ends. Unlike `ObjectReceived`, it does not reconnect: start a new loop after an [`AR51Exception`](/docs/sdk-api/csharp-api/classes/ar51exception/).
:::

</ApiBody>

### DisposeAsync

<ApiBody kind="method">

```csharp
public ValueTask DisposeAsync();
```

Stops receiving, closes every stream and, for a standalone client, its connection. Calling it again does nothing.

<Returns type="ValueTask">A task that completes when everything is closed.</Returns>

</ApiBody>

## See also

- [Object detection guide](/docs/csharp-api/objects/) — walkthrough with a complete, compiled sample
- [Class index](/docs/sdk-api/csharp-api/classes/) — every AR51.Net type
- [Data conventions, threading and errors](/docs/csharp-api/conventions/)
