Files
2026-04-17 14:55:32 -04:00

136 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 portbased 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<int> 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<int>`).
- `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).