224 lines
11 KiB
Markdown
224 lines
11 KiB
Markdown
---
|
||
source_files:
|
||
- Common/DTS.Common.IConnection/EthernetConnection/RESTConnection.cs
|
||
- Common/DTS.Common.IConnection/EthernetConnection/EthernetConnection.cs
|
||
generated_at: "2026-04-16T02:09:04.702798+00:00"
|
||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||
schema_version: 1
|
||
sha256: "21dfcca726b35e5d"
|
||
---
|
||
|
||
# Documentation: `RESTConnection` and `EthernetConnection` Classes
|
||
|
||
## 1. Purpose
|
||
|
||
This module provides two concrete implementations of the `IConnection` interface for network communication: `RESTConnection`, a stub implementation that simulates connection operations without actual network activity (intended for REST-based or mock scenarios), and `EthernetConnection`, a full-featured implementation using `System.Net.Sockets.Socket` for real-time Ethernet communication with hardware devices. `RESTConnection` exists to satisfy interface contracts in environments where actual socket communication is unnecessary or undesirable (e.g., testing, REST API interactions), while `EthernetConnection` handles real socket operations including connection establishment, data transfer, keep-alive configuration, and soft disconnect/reconnect workflows for embedded hardware.
|
||
|
||
## 2. Public Interface
|
||
|
||
### `RESTConnection` (namespace `DTS.DASLib.Connection`)
|
||
|
||
- **`bool IConnection.IsSoftDisconnected => false`**
|
||
Always returns `false`; does not support soft disconnect semantics.
|
||
|
||
- **`SocketFlags IConnection.Flags { get; set; }`**
|
||
Gets/sets socket flags (unused; always `SocketFlags.None`).
|
||
|
||
- **`string IConnection.ConnectString => _ConnectString`**
|
||
Returns the stored connection string (e.g., `"host:port"`), set via `Create`.
|
||
|
||
- **`bool IConnection.Connected => _bConnected`**
|
||
Returns internal `_bConnected` state (set to `true` on `BeginConnect`, `false` on `BeginDisconnect`).
|
||
|
||
- **`event EventHandler OnDisconnected`**
|
||
Declared but never raised (no invocation in source).
|
||
|
||
- **`void IConnection.Create(string connectString)`**
|
||
Stores `connectString` in `_ConnectString`.
|
||
|
||
- **`void IConnection.Create(string connectString, string hostIPAddress)`**
|
||
Stores `connectString` and `hostIPAddress` in respective fields.
|
||
|
||
- **`IAsyncResult IConnection.BeginConnect(AsyncCallback callback, object callbackObject)`**
|
||
Sets `_bConnected = true` and returns a *synchronously completed* `IAsyncResult` via `GetAlreadyCompleted`.
|
||
|
||
- **`IAsyncResult IConnection.BeginDisconnect(bool reuseSocket, AsyncCallback callback, object state)`**
|
||
Sets `_bConnected = false` and returns a *synchronously completed* `IAsyncResult`.
|
||
|
||
- **`IAsyncResult IConnection.BeginSend(...)` / `BeginReceive(...)` / `BeginAccept(...)`**
|
||
All return *synchronously completed* `IAsyncResult` instances; no actual I/O.
|
||
|
||
- **`void IConnection.EndConnect(...)` / `EndDisconnect(...)` / `EndSend(...)` / `EndReceive(...)` / `EndAccept(...)`**
|
||
No-op implementations. `EndSend`/`EndReceive` return `0`. `EndAccept` returns `this`.
|
||
|
||
- **`void IConnection.Bind(int port)`**
|
||
Stub; no-op.
|
||
|
||
- **`void IConnection.Listen(int backlog)`**
|
||
Stub; no-op.
|
||
|
||
- **`void IConnection.KeepAliveErrorReceived()`**
|
||
Stub; no-op.
|
||
|
||
- **`string IConnection.GetConnectionData()`**
|
||
Returns `"{_ConnectString} - {_hostIPAddress}"`.
|
||
|
||
- **`Task<int> IConnection.SendAsync(...)`**
|
||
Wraps `BeginSend`/`EndSend` using `Task.Factory.FromAsync`; returns a completed task with result `0`.
|
||
|
||
- **`void IConnection.SoftConnect()` / `SoftDisconnect()`**
|
||
Stubs; no-op.
|
||
|
||
- **`void Dispose()` / `~RESTConnection()`**
|
||
Implements `IDisposable`; marks `_disposed = true` but performs no socket cleanup.
|
||
|
||
### `EthernetConnection` (namespace `DTS.Common`)
|
||
|
||
- **`bool IsSoftDisconnected { get; private set; }`**
|
||
Tracks soft-disconnect state; `true` after successful `SoftDisconnect()`.
|
||
|
||
- **`bool Connected => Sock != null && Sock.Connected`**
|
||
Reflects underlying socket state.
|
||
|
||
- **`string ConnectString => Connect_String`**
|
||
Returns stored connection string.
|
||
|
||
- **`SocketFlags Flags { get; set; }`**
|
||
Gets/sets socket flags used in `BeginSend`/`BeginReceive`.
|
||
|
||
- **`Socket Sock`**
|
||
Public field holding the underlying `Socket` instance.
|
||
|
||
- **`event EventHandler OnDisconnected`**
|
||
Raised in `KeepAliveErrorReceived()`.
|
||
|
||
- **`void Create(string connectString, string hostIPAddress)`**
|
||
Stores `connectString` and `hostIPAddress`, then calls `CreateSock` to initialize `Sock`.
|
||
|
||
- **`Socket CreateSock(string connectString, string hostIPAddress)`**
|
||
Creates and configures a `Socket` with:
|
||
- `NoDelay = true` (Nagle disabled)
|
||
- `KeepAlive = true`
|
||
- Buffer sizes from `DFConstantsAndEnums.SendBufferSizeBytes` / `ReceiveBufferSizeBytes`
|
||
- Custom keep-alive parameters from `DFConstantsAndEnums.LocalKeepAliveTimeOutMS` / `LocalKeepAliveRetryIntervalMS`
|
||
- Optional binding to `hostIPAddress` if provided.
|
||
|
||
- **`void SoftDisconnect()`**
|
||
If `HardwareConstants.AllowSoftDisconnects` is `true` and socket is connected:
|
||
- Disconnects and disposes `Sock`
|
||
- Sets `IsSoftDisconnected = true`
|
||
- Logs action.
|
||
|
||
- **`void SoftConnect()`**
|
||
If `AllowSoftDisconnects` is `true` and socket is not connected:
|
||
- Parses `Connect_String` as `"host:port"`.
|
||
- If `RequiresKeepAliveReset` is `true`, connects to port `8200`, sends `<60,5,4>`, receives response, then disconnects.
|
||
- Retries up to 3 times to connect to `host:port` (1s delay between attempts).
|
||
- Sets `IsSoftDisconnected = false`, logs success.
|
||
|
||
- **`void KeepAliveErrorReceived()`**
|
||
Shuts down, closes, and disposes `Sock`, sets `Sock = null`, and raises `OnDisconnected`.
|
||
|
||
- **`string GetConnectionData()`**
|
||
Returns `"local: {LocalEndPoint}, Remote: {RemoteEndPoint}"` or `""` on error.
|
||
|
||
- **`IAsyncResult BeginConnect(AsyncCallback, object)`**
|
||
Validates `Sock`, `Connect_String`, and port format; delegates to `Sock.BeginConnect`.
|
||
|
||
- **`void EndConnect(IAsyncResult)`**
|
||
Calls `Sock.EndConnect`, logs connection details.
|
||
|
||
- **`IAsyncResult BeginDisconnect(bool, AsyncCallback, object)`**
|
||
Validates `Sock`; throws if socket is `null` or *not* connected (`SocketException WSAEISCONN`); delegates to `Sock.BeginDisconnect`.
|
||
|
||
- **`void EndDisconnect(IAsyncResult)`**
|
||
Calls `Sock.EndDisconnect`.
|
||
|
||
- **`IAsyncResult BeginAccept(AsyncCallback, object)`**
|
||
Validates `Sock`; delegates to `Sock.BeginAccept`.
|
||
|
||
- **`IConnection EndAccept(IAsyncResult)`**
|
||
Calls `Sock.EndAccept`, wraps result in a *new* `EthernetConnection` instance.
|
||
|
||
- **`void Bind(int port)`**
|
||
Validates `Sock`; binds to first DNS-resolved IP address on `port`.
|
||
|
||
- **`void Listen(int backlog)`**
|
||
Validates `Sock`; calls `Sock.Listen`.
|
||
|
||
- **`IAsyncResult BeginSend(byte[], int, int, AsyncCallback, object)`**
|
||
Validates `Sock`, callback, and connection state; throws if not connected; delegates to `Sock.BeginSend`.
|
||
|
||
- **`int EndSend(IAsyncResult)`**
|
||
Calls `Sock.EndSend`; sets `Sock = null` on exception.
|
||
|
||
- **`Task<int> SendAsync(...)`**
|
||
Wraps `BeginSend`/`EndSend` via `Task.Factory.FromAsync`.
|
||
|
||
- **`IAsyncResult BeginReceive(byte[], int, int, AsyncCallback, object)`**
|
||
Validates `Sock` and callback; delegates to `Sock.BeginReceive`.
|
||
|
||
- **`int EndReceive(IAsyncResult)`**
|
||
Calls `Sock.EndReceive`.
|
||
|
||
- **`void Dispose()` / `~EthernetConnection()`**
|
||
Implements `IDisposable`; safely shuts down/disposes `Sock` in `Dispose(bool)`.
|
||
|
||
## 3. Invariants
|
||
|
||
- **`RESTConnection`**:
|
||
- `_bConnected` is the sole source of truth for connection state; never updated by external I/O.
|
||
- All `Begin*`/`End*` operations are synchronous and return immediately; no async state is maintained.
|
||
- `IsSoftDisconnected` is always `false`; soft disconnect logic is absent.
|
||
- `Connected` reflects `_bConnected`, which is manually toggled by `BeginConnect`/`BeginDisconnect`.
|
||
|
||
- **`EthernetConnection`**:
|
||
- `Connected` depends on `Sock != null && Sock.Connected`; `Sock` may be `null` after errors or disposal.
|
||
- `SoftDisconnect()` and `SoftConnect()` only execute if `HardwareConstants.AllowSoftDisconnects` is `true`.
|
||
- `SoftConnect()` requires `Connect_String` to be in `"host:port"` format; port must be parseable as `int`.
|
||
- `BeginDisconnect` throws `SocketException` if socket is not connected (per `WSAEISCONN`).
|
||
- `BeginSend`/`BeginReceive` throw if socket is not connected or callback is `null`.
|
||
- `KeepAliveErrorReceived()` guarantees `Sock` is `null` after execution (disposal path).
|
||
|
||
## 4. Dependencies
|
||
|
||
### `RESTConnection`:
|
||
- **Imports**:
|
||
- `DTS.Common.Interface.Connection` (for `IConnection`)
|
||
- `DTS.Common.Utilities.Logging` (for `APILogger`, but *not used* in this file)
|
||
- Standard .NET libraries (`System.Net.Sockets`, `System.Threading`, etc.)
|
||
- **Depended on by**: Any code requiring an `IConnection` instance where network activity is unnecessary (e.g., unit tests, REST-based workflows).
|
||
|
||
### `EthernetConnection`:
|
||
- **Imports**:
|
||
- `DTS.Common.Interface.Connection` (`IConnection`)
|
||
- `DTS.Common.DASResource` (for `Strings.EthernetConnection_*_Err*` exception messages)
|
||
- `DTS.Common.Enums.DASFactory` (for `HardwareConstants.AllowSoftDisconnects`)
|
||
- `DTS.Common.Utilities.Logging` (for `APILogger`)
|
||
- `DFConstantsAndEnums` (for buffer sizes, keep-alive timeouts, `WSAEISCONN`)
|
||
- **Depended on by**: Higher-level connection management logic (e.g., device initialization, data acquisition pipelines) requiring real Ethernet communication.
|
||
|
||
## 5. Gotchas
|
||
|
||
- **`RESTConnection`**:
|
||
- `IsSoftDisconnected` is hardcoded to `false`; callers expecting soft-disconnect behavior will be misled.
|
||
- `OnDisconnected` is declared but never raised; event subscriptions have no effect.
|
||
- `Begin*`/`End*` methods are *synchronous stubs*; using them in async contexts may cause deadlocks or incorrect assumptions about I/O latency.
|
||
- `EndSend`/`EndReceive` always return `0`—no actual bytes sent/received.
|
||
- `SoftConnect`/`SoftDisconnect` are no-ops despite being part of `IConnection`.
|
||
|
||
- **`EthernetConnection`**:
|
||
- `SoftConnect()` uses `Thread.Sleep` (50ms/100ms/1000ms) for timing; this is blocking and may cause issues in high-throughput or UI contexts.
|
||
- `SoftConnect()` sends `<60,5,4>` to port `8200` for keep-alive setup; this is a hardcoded protocol-specific command (device-dependent).
|
||
- `BeginDisconnect` throws `SocketException` if socket is not connected—*not* an error condition per se, but callers must handle this exception to avoid crashes.
|
||
- `EndSend` sets `Sock = null` on exception; this may cause subsequent operations to fail unexpectedly.
|
||
- `Bind` uses the *first* DNS-resolved IP address (`AddressList[0]`), which may be non-deterministic or IPv6 in some environments.
|
||
- `CreateSock` uses `IOControlCode.KeepAliveValues` with hardcoded `KeepAliveOn = 1`; this may conflict with system-wide settings.
|
||
- `IsSoftDisconnected` is not reset to `false` in `SoftConnect` if `AllowSoftDisconnects` is `false`—only if soft disconnect logic runs.
|
||
|
||
- **Both**:
|
||
- Neither class validates `connectString` format in `Create(string)` alone (only in `Create(string, string)` or `BeginConnect`).
|
||
- `RESTConnection` and `EthernetConnection` share the same `IConnection` interface but have wildly different semantics; callers must know which implementation they hold.
|
||
- `RESTConnection`’s `GetConnectionData()` returns a string with `_hostIPAddress`, but `_hostIPAddress` is never used in `RESTConnection` beyond storage—no binding or resolution occurs.
|
||
|
||
None identified beyond those above. |