--- source_files: - Common/DTS.Common.IConnection/SerialConnection/SerialConnection.cs generated_at: "2026-04-16T02:09:02.781823+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "4992256eaf51da73" --- # SerialConnection ## Documentation: `SerialConnection` Class --- ### 1. Purpose The `SerialConnection` class provides a serial port–based implementation of the `IConnection` interface, enabling asynchronous I/O operations over a system serial port (`System.IO.Ports.SerialPort`). It wraps the .NET `SerialPort` class to expose standard connection semantics (connect, disconnect, send, receive) using the asynchronous programming model (APM) and Task-based wrappers. Despite implementing methods like `SoftConnect`, `SoftDisconnect`, `Bind`, `Listen`, and `Accept`, the class does **not** support soft disconnection or server-side listening—those methods are stubs or throw exceptions when misused. Its primary role is to allow serial communication to be used interchangeably with other connection types (e.g., TCP) via the `IConnection` abstraction. --- ### 2. Public Interface All methods and properties are part of the public API unless explicitly marked `protected`. | Member | Signature | Description | |--------|-----------|-------------| | **`IsSoftDisconnected`** | `public bool IsSoftDisconnected { get; private set; }` | Always `false`. Not used for serial connections. | | **`SoftConnect()`** | `public void SoftConnect()` | No-op. Comment notes: *"does nothing for serial as we don't have a soft disconnect option yet."* | | **`SoftDisconnect()`** | `public void SoftDisconnect()` | No-op. Comment notes: *"does nothing for serial as we don't have a soft disconnect option yet."* | | **`KeepAliveErrorReceived()`** | `void IConnection.KeepAliveErrorReceived()` | Explicit interface implementation; no-op. | | **`Create(string connectString, string hostIPAddress)`** | `public void Create(string connectString, string hostIPAddress)` | No-op. Does not assign `connectString`. | | **`GetConnectionData()`** | `public string GetConnectionData()` | Returns `""`. | | **`GetCurrentDownloadRate()`** | `public double GetCurrentDownloadRate()` | Returns `0D`. | | **`GetCurrentUploadRate()`** | `public double GetCurrentUploadRate()` | Returns `0D`. | | **`OnDisconnected`** | `public event EventHandler OnDisconnected` | Event declared but never raised. | | **`Connected`** | `public bool Connected { get; }` | Always `false`. Not updated on connect. | | **`ConnectString`** | `public string ConnectString => _PortName` | Returns the last-assigned port name (e.g., `"COM3"`). | | **`Flags`** | `public System.Net.Sockets.SocketFlags Flags { get; set; }` | Property inherited from `IConnection`; unused (no socket semantics). | | **`Create(string PortName)`** | `public void Create(string PortName)` | Initializes `Port` with `_PortName` (note: bug—see *Gotchas*). Sets `_PortName = PortName`. | | **`BeginConnect(...)`** | `public IAsyncResult BeginConnect(AsyncCallback cb, object state)` | Opens the `SerialPort`. Throws if `Port == null` or `_PortName` is null/empty. Returns `null`. | | **`EndConnect(...)`** | `public void EndConnect(IAsyncResult ar)` | No-op. Throws if `Port == null`. | | **`BeginDisconnect(...)`** | `public IAsyncResult BeginDisconnect(bool reuseSocket, AsyncCallback cb, object state)` | No-op. Throws if `Port == null`. Returns `null`. | | **`EndDisconnect(...)`** | `public void EndDisconnect(IAsyncResult asyncResult)` | No-op. Throws if `Port == null`. | | **`BeginAccept(...)`** | `public IAsyncResult BeginAccept(AsyncCallback callback, Object state)` | Throws if `Port == null`. Returns `null`. | | **`EndAccept(...)`** | `public IConnection EndAccept(IAsyncResult asyncResult)` | Returns a **new** `SerialConnection` instance (uninitialized—no `Port` created). Throws if `Port == null`. | | **`Bind(int port)`** | `public void Bind(int port)` | No-op. Throws if `Port == null`. | | **`Listen(int backlog)`** | `public void Listen(int backlog)` | No-op. Throws if `Port == null`. | | **`BeginSend(...)`** | `public IAsyncResult BeginSend(byte[] buffer, int offset, int size, AsyncCallback cb, object state)` | Delegates to `SerialPort.BaseStream.BeginWrite(...)`. Throws if `Port == null` or `cb == null`. | | **`EndSend(...)`** | `public int EndSend(IAsyncResult ar)` | Calls `SerialPort.BaseStream.EndWrite(ar)`. Returns `Port.BytesToWrite`. Throws if `Port == null`. | | **`SendAsync(...)`** | `public Task SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend)` | Wraps `BeginSend`/`EndSend` using `Task.Factory.FromAsync`. | | **`BeginReceive(...)`** | `public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, AsyncCallback cb, object state)` | Delegates to `SerialPort.BaseStream.BeginRead(...)`. Throws if `Port == null` or `cb == null`. | | **`EndReceive(...)`** | `public int EndReceive(IAsyncResult ar)` | Calls `SerialPort.BaseStream.EndRead(ar)`. Throws if `Port == null`. | | **`Dispose()`** | `public void Dispose()` | Calls `Dispose(true)` and suppresses finalization. | | **`~SerialConnection()`** | `~SerialConnection()` | Finalizer calls `Dispose(false)`. | | **`Dispose(bool)`** | `protected virtual void Dispose(bool disposing)` | Closes and nulls `Port` if `disposing` is `true`. Sets `disposed = true`. | > **Note**: The class implements `IDisposable`. `Connected` is never set to `true`, and `OnDisconnected` is never invoked. --- ### 3. Invariants - `Connected` is **always** `false`. It is a read-only property initialized to `false` and never modified. - `_PortName` is set only via `Create(string PortName)`. `ConnectString` returns `_PortName`. - `Port` is only initialized in `Create(string PortName)` and `EndAccept(...)`. In the latter case, the new `SerialConnection` instance has `Port == null`. - `BeginConnect` is the only method that actually opens the serial port (`Port.Open()`). No other method affects port state. - `EndSend` returns `Port.BytesToWrite`, which reflects the number of bytes *still pending in the transmit buffer*—not bytes sent. - `SoftConnect`, `SoftDisconnect`, `Bind`, `Listen`, and `BeginAccept`/`EndAccept` are **not functional** for serial connections and may throw if `Port == null`. - `IsSoftDisconnected` is always `false` and unused. --- ### 4. Dependencies - **Depends on**: - `DTS.Common.Interface.Connection` (via `IConnection` interface). - `System.IO.Ports` (for `SerialPort`). - `System.Threading.Tasks` (for `Task`). - `System` (for `EventHandler`, `AsyncCallback`, `IAsyncResult`, `Exception`). - **Depended on by**: - Unknown from source alone. Presumably used by higher-level connection-handling logic that consumes `IConnection`. --- ### 5. Gotchas - **Critical Bug in `Create(string PortName)`**: ```csharp Port = new SerialPort(_PortName); // ❌ Uses uninitialized _PortName _PortName = PortName; // ✅ Sets _PortName *after* use ``` The `SerialPort` constructor is called with `_PortName`, which is `null` at that point. Should be `new SerialPort(PortName)`. - **`Connected` is never updated**: Even after `BeginConnect` succeeds, `Connected` remains `false`. Consumers cannot rely on this property. - **`OnDisconnected` is never raised**: The event is declared but never invoked—even during `Dispose` or `Port.Close()`. - **`EndAccept` returns an uninitialized `SerialConnection`**: The new instance has `Port == null`, so calling `BeginConnect`/`Send`/`Receive` on it will throw. - **`SoftConnect`/`SoftDisconnect` are no-ops**: Despite being part of `IConnection`, they do nothing. Do not expect disconnection behavior from them. - **`BeginDisconnect`/`EndDisconnect` do not close the port**: Only `Dispose` closes the `SerialPort`. `EndDisconnect` is a no-op. - **`GetCurrentDownloadRate()` / `GetCurrentUploadRate()` always return `0D`**: No actual rate tracking is implemented. - **`Flags` property is unused**: It is a `SocketFlags` property (from `IConnection`) but serial ports do not use socket flags. - **`BeginSend`/`BeginReceive` use `SerialPort.BaseStream`**: This is correct for async I/O, but note that `SerialPort.BaseStream` is synchronous under the hood—performance may be suboptimal for high-throughput scenarios. - **No validation of `PortName` in `Create`**: Invalid port names (e.g., `"COM999"`) will only fail at `Port.Open()` in `BeginConnect`. - **`ConnectString` is misleading**: It holds only the port name (e.g., `"COM3"`), not a full connection string. Also, the overload `Create(string connectString, string hostIPAddress)` ignores `connectString`. - **`disposed` flag prevents double-dispose**: Safe, but `Port` may be `null` after first dispose—subsequent `Dispose` calls are no-ops. - **No exception handling in `BeginConnect`/`EndConnect`**: Exceptions from `Port.Open()` (e.g., `UnauthorizedAccessException`, `IOException`) propagate directly. - **No cancellation support**: All async methods lack `CancellationToken` support. - **`EndSend` returns `BytesToWrite`**: This is the number of bytes *remaining* in the output buffer—not bytes sent. Likely a mistake; should be bytes *written* (which is implicit in `EndWrite`’s return value, but `EndSend` discards it and returns `BytesToWrite` instead).