136 lines
9.1 KiB
Markdown
136 lines
9.1 KiB
Markdown
|
|
---
|
|||
|
|
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<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).
|