Arrange code structure. Introduce dynamic scheduler and multi leader.

This commit is contained in:
Youmu Liu
2025-10-22 14:00:13 +08:00
parent 91a0add1d1
commit 3788db4cd0
6 changed files with 163 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for command types that can be built into bytes by an ICommandBuilder.
/// Implement this on record/struct command shapes.
/// </summary>
public interface ICommand { }
}
+30
View File
@@ -0,0 +1,30 @@
namespace EM.LowLevel
{
/// <summary>
/// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.).
/// </summary>
public interface ICommandBuilder
{
/// <summary>
/// Build a command payload (already framed or raw depending on implementation contract) from a
/// strongly-typed command description.
/// </summary>
/// <typeparam name="TCommand">
/// Command type (e.g., an enum or a record/struct)
/// </typeparam>
/// <param name="command">
/// Command value to build
/// </param>
/// <returns>
/// Command bytes
/// </returns>
ReadOnlyMemory<byte> Build<TCommand>(TCommand command) where TCommand: notnull;
/// <summary>
/// Try-build variant to avoid throwing and allow buffer reuse strategies.
/// </summary>
bool TryBuild<TCommand>(TCommand command, out ReadOnlyMemory<byte> bytes) where TCommand: notnull;
}
}
+29
View File
@@ -0,0 +1,29 @@
namespace EM.LowLevel
{
/// <summary>
/// Decodes device-specific binary responses into C#-friendly models.
/// </summary>
public interface IDataDecoder
{
/// <summary>
/// Decode a response of a known type into a model.
/// </summary>
/// <typeparam name="TModel">
/// Target model type
/// </typeparam>
/// <param name="payload">
/// Raw or deframed payload depending on transport contract
/// </param>
/// <returns>
/// Decoded model
/// </returns>
TModel Decode<TModel>(ReadOnlySpan<byte> payload);
/// <summary>
/// Try-decode variant to avoid throwing on partial/bad frames.
/// </summary>
bool TryDecode<TModel>(ReadOnlySpan<byte> payload, out TModel model);
}
}
+54
View File
@@ -0,0 +1,54 @@
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// Generic high-level controller that composes a PortStream, an ICommandBuilder and an
/// IDataDecoder to provide a complete peripheral control workflow.
/// </summary>
/// <typeparam name="TPort">
/// Concrete transport deriving from PortStream
/// </typeparam>
/// <typeparam name="TBuilder">
/// Concrete command builder
/// </typeparam>
/// <typeparam name="TDecoder">
/// Concrete data decoder
/// </typeparam>
public interface IPortController<TPort, TBuilder, TDecoder> where TPort: PortStream where TBuilder: ICommandBuilder
where TDecoder: IDataDecoder
{
TBuilder CommandBuilder { get; }
TDecoder Decoder { get; }
TPort Port { get; }
/// <summary>
/// Close the underlying port.
/// </summary>
void Close();
/// <summary>
/// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync).
/// </summary>
ValueTask OpenAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Build a command, send it via the port and decode the response into a model.
/// </summary>
ValueTask<TResponse> RoundTripAsync<TCommand, TResponse>(
TCommand command,
CancellationToken cancellationToken = default)
where TCommand: notnull;
/// <summary>
/// Build a command and send it without expecting a typed response.
/// </summary>
ValueTask WriteAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand: notnull;
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for decoded response models returned by IDataDecoder.
/// </summary>
public interface IResponse { }
}
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// 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.
/// </summary>
public abstract class PortStream : Stream
{
/// <summary>
/// Optional bytes available hint if the transport can provide it.
/// </summary>
public virtual long? BytesAvailable => null;
/// <summary>
/// Close the underlying transport and release resources.
/// </summary>
public abstract override void Close();
/// <summary>
/// Ensure the port is opened and ready. Idempotent. May perform device-specific
/// handshakes.
/// </summary>
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.
}
}