From 3788db4cd00076f9cdbe16aa13a9c0e589711c35 Mon Sep 17 00:00:00 2001
From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com>
Date: Wed, 22 Oct 2025 14:00:13 +0800
Subject: [PATCH] Arrange code structure. Introduce dynamic scheduler and multi
leader.
---
StgSharp.Device/ICommand.cs | 8 +++++
StgSharp.Device/ICommandBuilder.cs | 30 +++++++++++++++++
StgSharp.Device/IDataDecoder.cs | 29 ++++++++++++++++
StgSharp.Device/IPortController.cs | 54 ++++++++++++++++++++++++++++++
StgSharp.Device/IResponse.cs | 7 ++++
StgSharp.Device/PortStream.cs | 35 +++++++++++++++++++
6 files changed, 163 insertions(+)
create mode 100644 StgSharp.Device/ICommand.cs
create mode 100644 StgSharp.Device/ICommandBuilder.cs
create mode 100644 StgSharp.Device/IDataDecoder.cs
create mode 100644 StgSharp.Device/IPortController.cs
create mode 100644 StgSharp.Device/IResponse.cs
create mode 100644 StgSharp.Device/PortStream.cs
diff --git a/StgSharp.Device/ICommand.cs b/StgSharp.Device/ICommand.cs
new file mode 100644
index 00000000..bae62457
--- /dev/null
+++ b/StgSharp.Device/ICommand.cs
@@ -0,0 +1,8 @@
+namespace EM.LowLevel
+{
+ ///
+ /// Marker interface for command types that can be built into bytes by an ICommandBuilder.
+ /// Implement this on record/struct command shapes.
+ ///
+ public interface ICommand { }
+}
diff --git a/StgSharp.Device/ICommandBuilder.cs b/StgSharp.Device/ICommandBuilder.cs
new file mode 100644
index 00000000..396f7ea1
--- /dev/null
+++ b/StgSharp.Device/ICommandBuilder.cs
@@ -0,0 +1,30 @@
+namespace EM.LowLevel
+{
+ ///
+ /// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.).
+ ///
+ public interface ICommandBuilder
+ {
+
+ ///
+ /// Build a command payload (already framed or raw depending on implementation contract) from a
+ /// strongly-typed command description.
+ ///
+ ///
+ /// Command type (e.g., an enum or a record/struct)
+ ///
+ ///
+ /// Command value to build
+ ///
+ ///
+ /// Command bytes
+ ///
+ ReadOnlyMemory Build(TCommand command) where TCommand: notnull;
+
+ ///
+ /// Try-build variant to avoid throwing and allow buffer reuse strategies.
+ ///
+ bool TryBuild(TCommand command, out ReadOnlyMemory bytes) where TCommand: notnull;
+
+ }
+}
diff --git a/StgSharp.Device/IDataDecoder.cs b/StgSharp.Device/IDataDecoder.cs
new file mode 100644
index 00000000..ae834d8a
--- /dev/null
+++ b/StgSharp.Device/IDataDecoder.cs
@@ -0,0 +1,29 @@
+namespace EM.LowLevel
+{
+ ///
+ /// Decodes device-specific binary responses into C#-friendly models.
+ ///
+ public interface IDataDecoder
+ {
+
+ ///
+ /// Decode a response of a known type into a model.
+ ///
+ ///
+ /// Target model type
+ ///
+ ///
+ /// Raw or deframed payload depending on transport contract
+ ///
+ ///
+ /// Decoded model
+ ///
+ TModel Decode(ReadOnlySpan payload);
+
+ ///
+ /// Try-decode variant to avoid throwing on partial/bad frames.
+ ///
+ bool TryDecode(ReadOnlySpan payload, out TModel model);
+
+ }
+}
diff --git a/StgSharp.Device/IPortController.cs b/StgSharp.Device/IPortController.cs
new file mode 100644
index 00000000..53e47ff3
--- /dev/null
+++ b/StgSharp.Device/IPortController.cs
@@ -0,0 +1,54 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EM.LowLevel
+{
+ ///
+ /// Generic high-level controller that composes a PortStream, an ICommandBuilder and an
+ /// IDataDecoder to provide a complete peripheral control workflow.
+ ///
+ ///
+ /// Concrete transport deriving from PortStream
+ ///
+ ///
+ /// Concrete command builder
+ ///
+ ///
+ /// Concrete data decoder
+ ///
+ public interface IPortController where TPort: PortStream where TBuilder: ICommandBuilder
+ where TDecoder: IDataDecoder
+ {
+
+ TBuilder CommandBuilder { get; }
+
+ TDecoder Decoder { get; }
+
+ TPort Port { get; }
+
+ ///
+ /// Close the underlying port.
+ ///
+ void Close();
+
+ ///
+ /// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync).
+ ///
+ ValueTask OpenAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// Build a command, send it via the port and decode the response into a model.
+ ///
+ ValueTask RoundTripAsync(
+ TCommand command,
+ CancellationToken cancellationToken = default)
+ where TCommand: notnull;
+
+ ///
+ /// Build a command and send it without expecting a typed response.
+ ///
+ ValueTask WriteAsync(TCommand command, CancellationToken cancellationToken = default)
+ where TCommand: notnull;
+
+ }
+}
diff --git a/StgSharp.Device/IResponse.cs b/StgSharp.Device/IResponse.cs
new file mode 100644
index 00000000..5a9cc3a2
--- /dev/null
+++ b/StgSharp.Device/IResponse.cs
@@ -0,0 +1,7 @@
+namespace EM.LowLevel
+{
+ ///
+ /// Marker interface for decoded response models returned by IDataDecoder.
+ ///
+ public interface IResponse { }
+}
diff --git a/StgSharp.Device/PortStream.cs b/StgSharp.Device/PortStream.cs
new file mode 100644
index 00000000..ecdc31c0
--- /dev/null
+++ b/StgSharp.Device/PortStream.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EM.LowLevel
+{
+ ///
+ /// High-level transport abstraction for device communication. Derives from Stream and
+ /// encapsulates initialization/handshake and frame (header/tail/checksum) handling as needed
+ /// by the device. Implementations may wrap SerialPort, Sockets, USB, etc.
+ ///
+ public abstract class PortStream : Stream
+ {
+
+ ///
+ /// Optional bytes available hint if the transport can provide it.
+ ///
+ public virtual long? BytesAvailable => null;
+
+ ///
+ /// Close the underlying transport and release resources.
+ ///
+ public abstract override void Close();
+
+ ///
+ /// Ensure the port is opened and ready. Idempotent. May perform device-specific
+ /// handshakes.
+ ///
+ public abstract ValueTask OpenAsync(CancellationToken cancellationToken = default);
+
+ // Concrete transports must implement Stream's abstract members.
+ // Seek-related members are typically not supported and should throw NotSupportedException.
+ }
+}