Add XML documentation across gateway, worker, and .NET client
This commit is contained in:
@@ -23,6 +23,11 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
private readonly ResiliencePipeline _safeUnaryRetryPipeline;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a Galaxy Repository client with custom transport and options.
|
||||
/// </summary>
|
||||
/// <param name="options">Client options.</param>
|
||||
/// <param name="transport">The underlying gRPC transport.</param>
|
||||
internal GalaxyRepositoryClient(
|
||||
MxGatewayClientOptions options,
|
||||
IGalaxyRepositoryClientTransport transport)
|
||||
@@ -50,12 +55,23 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
Options.LoggerFactory?.CreateLogger<GalaxyRepositoryClient>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client options used to configure timeouts, authentication, and retry policy.
|
||||
/// </summary>
|
||||
public MxGatewayClientOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The underlying generated gRPC client for advanced operations.
|
||||
/// </summary>
|
||||
public GalaxyRepository.GalaxyRepositoryClient RawClient =>
|
||||
_transport.RawClient
|
||||
?? throw new InvalidOperationException("The raw generated gRPC client is not available for this client instance.");
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Galaxy Repository client with the given options, establishing a new gRPC channel.
|
||||
/// </summary>
|
||||
/// <param name="options">Client options.</param>
|
||||
/// <returns>A new client instance.</returns>
|
||||
public static GalaxyRepositoryClient Create(MxGatewayClientOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
@@ -81,6 +97,8 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
/// Probes the Galaxy Repository database connection. Returns true when the
|
||||
/// gateway can reach the configured ZB SQL Server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>True if connection is successful, false otherwise.</returns>
|
||||
public async Task<bool> TestConnectionAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
TestConnectionReply reply = await TestConnectionRawAsync(
|
||||
@@ -91,6 +109,12 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
return reply.Ok;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Probes the Galaxy Repository database connection without result wrapping.
|
||||
/// </summary>
|
||||
/// <param name="request">The test connection request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<TestConnectionReply> TestConnectionRawAsync(
|
||||
TestConnectionRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -107,6 +131,8 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
/// Returns the timestamp of the most recent Galaxy deployment, or
|
||||
/// <see langword="null"/> when no deployment has been recorded.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The deployment timestamp, or null if not recorded.</returns>
|
||||
public async Task<DateTime?> GetLastDeployTimeAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
GetLastDeployTimeReply reply = await GetLastDeployTimeRawAsync(
|
||||
@@ -122,6 +148,12 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
return reply.TimeOfLastDeploy.ToDateTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most recent Galaxy deployment timestamp without result wrapping.
|
||||
/// </summary>
|
||||
/// <param name="request">The last deploy-time request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<GetLastDeployTimeReply> GetLastDeployTimeRawAsync(
|
||||
GetLastDeployTimeRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -139,6 +171,8 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
/// includes its dynamic attributes so callers can determine which tag references
|
||||
/// they may subscribe to via the MxAccessGateway service.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The collection of Galaxy objects in the hierarchy.</returns>
|
||||
public async Task<IReadOnlyList<GalaxyObject>> DiscoverHierarchyAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
DiscoverHierarchyReply reply = await DiscoverHierarchyRawAsync(
|
||||
@@ -149,6 +183,12 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
return reply.Objects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the Galaxy object hierarchy without result wrapping.
|
||||
/// </summary>
|
||||
/// <param name="request">The discover-hierarchy request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<DiscoverHierarchyReply> DiscoverHierarchyRawAsync(
|
||||
DiscoverHierarchyRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -173,6 +213,9 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
/// at-least-once delivery beyond the per-subscriber buffer (gaps in
|
||||
/// <see cref="DeployEvent.Sequence"/> indicate dropped events).
|
||||
/// </remarks>
|
||||
/// <param name="lastSeenDeployTime">Optional timestamp to suppress the bootstrap event.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>An async enumerable of deploy events.</returns>
|
||||
public IAsyncEnumerable<DeployEvent> WatchDeployEventsAsync(
|
||||
DateTimeOffset? lastSeenDeployTime = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -188,6 +231,12 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
return WatchDeployEventsRawAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to Galaxy deploy events without result wrapping.
|
||||
/// </summary>
|
||||
/// <param name="request">The watch-deploy-events request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>An async enumerable of raw deploy events.</returns>
|
||||
public IAsyncEnumerable<DeployEvent> WatchDeployEventsRawAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -211,6 +260,9 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the gRPC channel and releases resources.
|
||||
/// </summary>
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -223,16 +275,32 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options with the client's default timeout and API-key authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The call options.</returns>
|
||||
internal CallOptions CreateCallOptions(CancellationToken cancellationToken)
|
||||
{
|
||||
return CreateCallOptions(cancellationToken, Options.DefaultCallTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options for streaming RPCs with the stream timeout and API-key authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The stream call options.</returns>
|
||||
internal CallOptions CreateStreamCallOptions(CancellationToken cancellationToken)
|
||||
{
|
||||
return CreateCallOptions(cancellationToken, Options.StreamTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options with the specified timeout and API-key authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <param name="timeout">Optional timeout duration.</param>
|
||||
/// <returns>The call options.</returns>
|
||||
internal CallOptions CreateCallOptions(
|
||||
CancellationToken cancellationToken,
|
||||
TimeSpan? timeout)
|
||||
|
||||
@@ -3,16 +3,27 @@ using MxGateway.Contracts.Proto.Galaxy;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>
|
||||
/// gRPC implementation of IGalaxyRepositoryClientTransport.
|
||||
/// </summary>
|
||||
internal sealed class GrpcGalaxyRepositoryClientTransport(
|
||||
MxGatewayClientOptions options,
|
||||
GalaxyRepository.GalaxyRepositoryClient rawClient) : IGalaxyRepositoryClientTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the gateway client options.
|
||||
/// </summary>
|
||||
public MxGatewayClientOptions Options { get; } = options;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying gRPC client.
|
||||
/// </summary>
|
||||
public GalaxyRepository.GalaxyRepositoryClient RawClient { get; } = rawClient;
|
||||
|
||||
/// <inheritdoc />
|
||||
GalaxyRepository.GalaxyRepositoryClient? IGalaxyRepositoryClientTransport.RawClient => RawClient;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TestConnectionReply> TestConnectionAsync(
|
||||
TestConnectionRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -29,6 +40,7 @@ internal sealed class GrpcGalaxyRepositoryClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<GetLastDeployTimeReply> GetLastDeployTimeAsync(
|
||||
GetLastDeployTimeRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -45,6 +57,7 @@ internal sealed class GrpcGalaxyRepositoryClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DiscoverHierarchyReply> DiscoverHierarchyAsync(
|
||||
DiscoverHierarchyRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -61,6 +74,7 @@ internal sealed class GrpcGalaxyRepositoryClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<DeployEvent> WatchDeployEventsAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CallOptions callOptions,
|
||||
@@ -94,6 +108,7 @@ internal sealed class GrpcGalaxyRepositoryClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IAsyncEnumerable<DeployEvent> IGalaxyRepositoryClientTransport.WatchDeployEventsAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CallOptions callOptions)
|
||||
|
||||
@@ -3,16 +3,27 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>
|
||||
/// gRPC implementation of IMxGatewayClientTransport.
|
||||
/// </summary>
|
||||
internal sealed class GrpcMxGatewayClientTransport(
|
||||
MxGatewayClientOptions options,
|
||||
MxAccessGateway.MxAccessGatewayClient rawClient) : IMxGatewayClientTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the gateway client options.
|
||||
/// </summary>
|
||||
public MxGatewayClientOptions Options { get; } = options;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying gRPC client.
|
||||
/// </summary>
|
||||
public MxAccessGateway.MxAccessGatewayClient RawClient { get; } = rawClient;
|
||||
|
||||
/// <inheritdoc />
|
||||
MxAccessGateway.MxAccessGatewayClient? IMxGatewayClientTransport.RawClient => RawClient;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<OpenSessionReply> OpenSessionAsync(
|
||||
OpenSessionRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -29,6 +40,7 @@ internal sealed class GrpcMxGatewayClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<CloseSessionReply> CloseSessionAsync(
|
||||
CloseSessionRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -45,6 +57,7 @@ internal sealed class GrpcMxGatewayClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CallOptions callOptions)
|
||||
@@ -61,6 +74,7 @@ internal sealed class GrpcMxGatewayClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CallOptions callOptions,
|
||||
@@ -94,6 +108,7 @@ internal sealed class GrpcMxGatewayClientTransport(
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IAsyncEnumerable<MxEvent> IMxGatewayClientTransport.StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CallOptions callOptions)
|
||||
|
||||
@@ -3,24 +3,39 @@ using MxGateway.Contracts.Proto.Galaxy;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Transport layer for Galaxy Repository gRPC operations.</summary>
|
||||
internal interface IGalaxyRepositoryClientTransport
|
||||
{
|
||||
/// <summary>Gets the client options used to configure this transport.</summary>
|
||||
MxGatewayClientOptions Options { get; }
|
||||
|
||||
/// <summary>Gets the underlying gRPC client, or <c>null</c> if not yet initialized.</summary>
|
||||
GalaxyRepository.GalaxyRepositoryClient? RawClient { get; }
|
||||
|
||||
/// <summary>Tests the connection to the Galaxy Repository server.</summary>
|
||||
/// <param name="request">The test connection request.</param>
|
||||
/// <param name="callOptions">gRPC call options (timeout, cancellation, etc.).</param>
|
||||
Task<TestConnectionReply> TestConnectionAsync(
|
||||
TestConnectionRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>Gets the last deploy time from the Galaxy Repository server.</summary>
|
||||
/// <param name="request">The get last deploy time request.</param>
|
||||
/// <param name="callOptions">gRPC call options (timeout, cancellation, etc.).</param>
|
||||
Task<GetLastDeployTimeReply> GetLastDeployTimeAsync(
|
||||
GetLastDeployTimeRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>Discovers the object hierarchy in the Galaxy Repository.</summary>
|
||||
/// <param name="request">The discover hierarchy request.</param>
|
||||
/// <param name="callOptions">gRPC call options (timeout, cancellation, etc.).</param>
|
||||
Task<DiscoverHierarchyReply> DiscoverHierarchyAsync(
|
||||
DiscoverHierarchyRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>Watches for deployment events from the Galaxy Repository server.</summary>
|
||||
/// <param name="request">The watch deploy events request.</param>
|
||||
/// <param name="callOptions">gRPC call options (timeout, cancellation, etc.).</param>
|
||||
IAsyncEnumerable<DeployEvent> WatchDeployEventsAsync(
|
||||
WatchDeployEventsRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
@@ -5,22 +5,52 @@ namespace MxGateway.Client;
|
||||
|
||||
internal interface IMxGatewayClientTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the client configuration options.
|
||||
/// </summary>
|
||||
MxGatewayClientOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying gRPC client, if available.
|
||||
/// </summary>
|
||||
MxAccessGateway.MxAccessGatewayClient? RawClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens a new gateway session.
|
||||
/// </summary>
|
||||
/// <param name="request">Session open request.</param>
|
||||
/// <param name="callOptions">gRPC call options.</param>
|
||||
/// <returns>The session open reply.</returns>
|
||||
Task<OpenSessionReply> OpenSessionAsync(
|
||||
OpenSessionRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Closes an open gateway session.
|
||||
/// </summary>
|
||||
/// <param name="request">Session close request.</param>
|
||||
/// <param name="callOptions">gRPC call options.</param>
|
||||
/// <returns>The session close reply.</returns>
|
||||
Task<CloseSessionReply> CloseSessionAsync(
|
||||
CloseSessionRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an MXAccess command on the session.
|
||||
/// </summary>
|
||||
/// <param name="request">The command request.</param>
|
||||
/// <param name="callOptions">gRPC call options.</param>
|
||||
/// <returns>The command reply.</returns>
|
||||
Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Streams events from the session.
|
||||
/// </summary>
|
||||
/// <param name="request">The stream events request.</param>
|
||||
/// <param name="callOptions">gRPC call options.</param>
|
||||
/// <returns>An async enumerable of events.</returns>
|
||||
IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CallOptions callOptions);
|
||||
|
||||
@@ -2,8 +2,13 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when an MXAccess command fails with a non-zero HResult or failing status.</summary>
|
||||
public sealed class MxAccessException : MxGatewayCommandException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given message, reply, and optional inner exception.</summary>
|
||||
/// <param name="message">The error message describing the MXAccess failure.</param>
|
||||
/// <param name="reply">The MxCommandReply containing the failure details (statuses, HResult, etc.).</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxAccessException(
|
||||
string message,
|
||||
MxCommandReply reply,
|
||||
@@ -20,5 +25,6 @@ public sealed class MxAccessException : MxGatewayCommandException
|
||||
Reply = reply;
|
||||
}
|
||||
|
||||
/// <summary>Gets the underlying MxCommandReply containing full failure details.</summary>
|
||||
public MxCommandReply Reply { get; }
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Extension methods for checking MxCommandReply success conditions.</summary>
|
||||
public static class MxCommandReplyExtensions
|
||||
{
|
||||
/// <summary>Validates that the reply has a successful protocol status (Ok or MxAccessFailure), throwing a gateway exception if not.</summary>
|
||||
/// <param name="reply">The command reply to check.</param>
|
||||
public static MxCommandReply EnsureProtocolSuccess(this MxCommandReply reply)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(reply);
|
||||
@@ -19,6 +22,8 @@ public static class MxCommandReplyExtensions
|
||||
throw CreateProtocolException(reply, code);
|
||||
}
|
||||
|
||||
/// <summary>Validates that the reply indicates MXAccess success (no HResult or status failures), throwing MxAccessException if not.</summary>
|
||||
/// <param name="reply">The command reply to check.</param>
|
||||
public static MxCommandReply EnsureMxAccessSuccess(this MxCommandReply reply)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(reply);
|
||||
|
||||
@@ -2,8 +2,17 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when an API key is invalid, expired, or malformed.</summary>
|
||||
public sealed class MxGatewayAuthenticationException : MxGatewayException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given details.</summary>
|
||||
/// <param name="message">The error message describing the authentication failure.</param>
|
||||
/// <param name="sessionId">The session ID, if available.</param>
|
||||
/// <param name="correlationId">The correlation ID for tracing, if available.</param>
|
||||
/// <param name="protocolStatus">The protocol status details, if available.</param>
|
||||
/// <param name="hResult">The HResult code, if available.</param>
|
||||
/// <param name="statuses">The MXAccess statuses, if available.</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxGatewayAuthenticationException(
|
||||
string message,
|
||||
string? sessionId = null,
|
||||
|
||||
@@ -2,8 +2,17 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when the API key lacks required scopes for an operation.</summary>
|
||||
public sealed class MxGatewayAuthorizationException : MxGatewayException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given details.</summary>
|
||||
/// <param name="message">The error message describing the authorization failure.</param>
|
||||
/// <param name="sessionId">The session ID, if available.</param>
|
||||
/// <param name="correlationId">The correlation ID for tracing, if available.</param>
|
||||
/// <param name="protocolStatus">The protocol status details, if available.</param>
|
||||
/// <param name="hResult">The HResult code, if available.</param>
|
||||
/// <param name="statuses">The MXAccess statuses, if available.</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxGatewayAuthorizationException(
|
||||
string message,
|
||||
string? sessionId = null,
|
||||
|
||||
@@ -19,6 +19,11 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
private readonly ResiliencePipeline _safeUnaryRetryPipeline;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MxGatewayClient"/> with given options and transport.
|
||||
/// </summary>
|
||||
/// <param name="options">Client configuration options.</param>
|
||||
/// <param name="transport">Transport implementation for gateway communication.</param>
|
||||
internal MxGatewayClient(
|
||||
MxGatewayClientOptions options,
|
||||
IMxGatewayClientTransport transport)
|
||||
@@ -46,12 +51,23 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
Options.LoggerFactory?.CreateLogger<MxGatewayClient>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client configuration options.
|
||||
/// </summary>
|
||||
public MxGatewayClientOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying generated gRPC client.
|
||||
/// </summary>
|
||||
public MxAccessGateway.MxAccessGatewayClient RawClient =>
|
||||
_transport.RawClient
|
||||
?? throw new InvalidOperationException("The raw generated gRPC client is not available for this client instance.");
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new gateway client with the given options.
|
||||
/// </summary>
|
||||
/// <param name="options">Client configuration options.</param>
|
||||
/// <returns>A new gateway client instance.</returns>
|
||||
public static MxGatewayClient Create(MxGatewayClientOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
@@ -73,6 +89,12 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
new MxAccessGateway.MxAccessGatewayClient(channel)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a new gateway session.
|
||||
/// </summary>
|
||||
/// <param name="request">Session open request; defaults to empty request if null.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A wrapped gateway session.</returns>
|
||||
public async Task<MxGatewaySession> OpenSessionAsync(
|
||||
OpenSessionRequest? request = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -85,6 +107,12 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
return new MxGatewaySession(this, reply);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a new gateway session and returns the raw protobuf reply.
|
||||
/// </summary>
|
||||
/// <param name="request">Session open request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>The raw gateway session open reply.</returns>
|
||||
public Task<OpenSessionReply> OpenSessionRawAsync(
|
||||
OpenSessionRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -95,6 +123,12 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
return _transport.OpenSessionAsync(request, CreateCallOptions(cancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes an open gateway session.
|
||||
/// </summary>
|
||||
/// <param name="request">Session close request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>The session close reply.</returns>
|
||||
public Task<CloseSessionReply> CloseSessionRawAsync(
|
||||
CloseSessionRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -107,6 +141,12 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an MXAccess command on the open session.
|
||||
/// </summary>
|
||||
/// <param name="request">The command request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>The command reply.</returns>
|
||||
public Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -124,6 +164,12 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
return _transport.InvokeAsync(request, CreateCallOptions(cancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streams events from the gateway session.
|
||||
/// </summary>
|
||||
/// <param name="request">The stream events request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>An async enumerable of events.</returns>
|
||||
public IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
StreamEventsRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -134,6 +180,9 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
return _transport.StreamEventsAsync(request, CreateStreamCallOptions(cancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the client and releases all resources.
|
||||
/// </summary>
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -146,16 +195,32 @@ public sealed class MxGatewayClient : IAsyncDisposable
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options with default timeout and authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the call.</param>
|
||||
/// <returns>Configured call options.</returns>
|
||||
internal CallOptions CreateCallOptions(CancellationToken cancellationToken)
|
||||
{
|
||||
return CreateCallOptions(cancellationToken, Options.DefaultCallTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options for streaming with stream timeout and authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the call.</param>
|
||||
/// <returns>Configured call options.</returns>
|
||||
internal CallOptions CreateStreamCallOptions(CancellationToken cancellationToken)
|
||||
{
|
||||
return CreateCallOptions(cancellationToken, Options.StreamTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates gRPC call options with specified timeout and authorization.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the call.</param>
|
||||
/// <param name="timeout">Optional timeout duration; null means no timeout.</param>
|
||||
/// <returns>Configured call options.</returns>
|
||||
internal CallOptions CreateCallOptions(
|
||||
CancellationToken cancellationToken,
|
||||
TimeSpan? timeout)
|
||||
|
||||
@@ -7,26 +7,62 @@ namespace MxGateway.Client;
|
||||
/// </summary>
|
||||
public sealed class MxGatewayClientOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the gateway endpoint URI (required).
|
||||
/// </summary>
|
||||
public required Uri Endpoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the API key for gateway authentication (required).
|
||||
/// </summary>
|
||||
public required string ApiKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to use TLS for the gateway connection.
|
||||
/// </summary>
|
||||
public bool UseTls { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to a CA certificate file for custom certificate validation.
|
||||
/// </summary>
|
||||
public string? CaCertificatePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the server name override for SNI during TLS handshake.
|
||||
/// </summary>
|
||||
public string? ServerNameOverride { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timeout for establishing connection to the gateway.
|
||||
/// </summary>
|
||||
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default timeout for unary gRPC calls.
|
||||
/// </summary>
|
||||
public TimeSpan DefaultCallTimeout { get; init; } = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional timeout for streaming gRPC calls.
|
||||
/// </summary>
|
||||
public TimeSpan? StreamTimeout { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the retry configuration for safe unary calls.
|
||||
/// </summary>
|
||||
public MxGatewayClientRetryOptions Retry { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger factory for diagnostic logging.
|
||||
/// </summary>
|
||||
public ILoggerFactory? LoggerFactory { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Validates the client options for consistency and correctness.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">Endpoint is null.</exception>
|
||||
/// <exception cref="ArgumentException">Options are invalid or inconsistent.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Timeout values are not greater than zero.</exception>
|
||||
public void Validate()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(Endpoint);
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Configuration for automatic retry behavior on transient gRPC call failures.</summary>
|
||||
public sealed class MxGatewayClientRetryOptions
|
||||
{
|
||||
/// <summary>Gets the maximum number of attempts (initial + retries); default is 2.</summary>
|
||||
public int MaxAttempts { get; init; } = 2;
|
||||
|
||||
/// <summary>Gets the initial delay between retry attempts; default is 200 milliseconds.</summary>
|
||||
public TimeSpan Delay { get; init; } = TimeSpan.FromMilliseconds(200);
|
||||
|
||||
/// <summary>Gets the maximum delay between retry attempts; default is 2 seconds.</summary>
|
||||
public TimeSpan MaxDelay { get; init; } = TimeSpan.FromSeconds(2);
|
||||
|
||||
/// <summary>Gets a value indicating whether to add randomness to retry delays; default is true.</summary>
|
||||
public bool UseJitter { get; init; } = true;
|
||||
|
||||
/// <summary>Validates the retry options and throws if any constraint is violated.</summary>
|
||||
public void Validate()
|
||||
{
|
||||
if (MaxAttempts <= 0)
|
||||
|
||||
@@ -6,8 +6,12 @@ using Polly.Retry;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Factory and helpers for exponential-backoff retry policies on transient gRPC failures.</summary>
|
||||
internal static class MxGatewayClientRetryPolicy
|
||||
{
|
||||
/// <summary>Creates a Polly ResiliencePipeline that retries transient gRPC failures with exponential backoff.</summary>
|
||||
/// <param name="options">Retry configuration (max attempts, delay bounds, jitter).</param>
|
||||
/// <param name="logger">Optional logger for retry diagnostics.</param>
|
||||
public static ResiliencePipeline Create(
|
||||
MxGatewayClientRetryOptions options,
|
||||
ILogger? logger)
|
||||
@@ -36,6 +40,8 @@ internal static class MxGatewayClientRetryPolicy
|
||||
.Build();
|
||||
}
|
||||
|
||||
/// <summary>Returns whether a command kind is eligible for automatic retry on transient failures.</summary>
|
||||
/// <param name="kind">The command kind to check.</param>
|
||||
public static bool IsRetryableCommand(MxCommandKind kind)
|
||||
{
|
||||
return kind is MxCommandKind.Ping
|
||||
|
||||
@@ -2,8 +2,17 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when a gateway command fails due to an unclassified protocol error.</summary>
|
||||
public class MxGatewayCommandException : MxGatewayException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given details.</summary>
|
||||
/// <param name="message">The error message describing the command failure.</param>
|
||||
/// <param name="sessionId">The session ID, if available.</param>
|
||||
/// <param name="correlationId">The correlation ID for tracing, if available.</param>
|
||||
/// <param name="protocolStatus">The protocol status details, if available.</param>
|
||||
/// <param name="hResult">The HResult code, if available.</param>
|
||||
/// <param name="statuses">The MXAccess statuses, if available.</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxGatewayCommandException(
|
||||
string message,
|
||||
string? sessionId = null,
|
||||
|
||||
@@ -2,20 +2,42 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when a gateway RPC call fails or returns an error status.
|
||||
/// </summary>
|
||||
public class MxGatewayException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MxGatewayException class with the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">Diagnostic message describing the failure.</param>
|
||||
public MxGatewayException(string message)
|
||||
: base(message)
|
||||
{
|
||||
Statuses = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MxGatewayException class with the specified message and inner exception.
|
||||
/// </summary>
|
||||
/// <param name="message">Diagnostic message describing the failure.</param>
|
||||
/// <param name="innerException">Underlying exception that caused this failure.</param>
|
||||
public MxGatewayException(string message, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
Statuses = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MxGatewayException class with full diagnostic information.
|
||||
/// </summary>
|
||||
/// <param name="message">Diagnostic message describing the failure.</param>
|
||||
/// <param name="sessionId">Session ID associated with the exception, if available.</param>
|
||||
/// <param name="correlationId">Correlation ID associated with the exception, if available.</param>
|
||||
/// <param name="protocolStatus">Protocol-level status returned by the gateway, if available.</param>
|
||||
/// <param name="hResult">HRESULT code returned by the worker or MXAccess, if available.</param>
|
||||
/// <param name="statuses">List of MXAccess status codes returned by the operation.</param>
|
||||
/// <param name="innerException">Underlying exception that caused this failure.</param>
|
||||
public MxGatewayException(
|
||||
string message,
|
||||
string? sessionId,
|
||||
@@ -33,13 +55,28 @@ public class MxGatewayException : Exception
|
||||
Statuses = statuses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session ID associated with the exception, if available.
|
||||
/// </summary>
|
||||
public string? SessionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the correlation ID associated with the exception, if available.
|
||||
/// </summary>
|
||||
public string? CorrelationId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the protocol-level status returned by the gateway, if available.
|
||||
/// </summary>
|
||||
public ProtocolStatus? ProtocolStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HRESULT code returned by the worker or MXAccess, if available.
|
||||
/// </summary>
|
||||
public int? HResultCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of MXAccess status codes returned by the operation.
|
||||
/// </summary>
|
||||
public IReadOnlyList<MxStatusProxy> Statuses { get; }
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
private readonly SemaphoreSlim _closeLock = new(1, 1);
|
||||
private CloseSessionReply? _closeReply;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new session backed by the given MXAccess gateway client.
|
||||
/// </summary>
|
||||
/// <param name="client">The gateway client used for commands and events.</param>
|
||||
/// <param name="openSessionReply">The server's session creation response.</param>
|
||||
internal MxGatewaySession(
|
||||
MxGatewayClient client,
|
||||
OpenSessionReply openSessionReply)
|
||||
@@ -19,10 +24,21 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
OpenSessionReply = openSessionReply ?? throw new ArgumentNullException(nameof(openSessionReply));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The session ID assigned by the gateway.
|
||||
/// </summary>
|
||||
public string SessionId => OpenSessionReply.SessionId;
|
||||
|
||||
/// <summary>
|
||||
/// The server's session creation response containing metadata.
|
||||
/// </summary>
|
||||
public OpenSessionReply OpenSessionReply { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the session on the gateway. Idempotent.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The server's close-session reply.</returns>
|
||||
public async Task<CloseSessionReply> CloseAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_closeReply is not null)
|
||||
@@ -50,6 +66,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a client with the MXAccess session, returning a ServerHandle.
|
||||
/// </summary>
|
||||
/// <param name="clientName">Name to register.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The server handle assigned to the registered client.</returns>
|
||||
public async Task<int> RegisterAsync(
|
||||
string clientName,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -60,6 +82,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.Register?.ServerHandle ?? reply.ReturnValue.Int32Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a client with the MXAccess session without error checking.
|
||||
/// </summary>
|
||||
/// <param name="clientName">Name to register.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> RegisterRawAsync(
|
||||
string clientName,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -75,6 +103,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the MXAccess session, returning an ItemHandle.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemDefinition">The item tag address.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The item handle assigned to the new item.</returns>
|
||||
public async Task<int> AddItemAsync(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
@@ -89,6 +124,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.AddItem?.ItemHandle ?? reply.ReturnValue.Int32Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the MXAccess session without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemDefinition">The item tag address.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> AddItemRawAsync(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
@@ -109,6 +151,14 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item with context to the MXAccess session, returning an ItemHandle.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemDefinition">The item tag address.</param>
|
||||
/// <param name="itemContext">Additional context for the item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The item handle assigned to the new item.</returns>
|
||||
public async Task<int> AddItem2Async(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
@@ -125,6 +175,14 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.AddItem2?.ItemHandle ?? reply.ReturnValue.Int32Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item with context to the MXAccess session without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemDefinition">The item tag address.</param>
|
||||
/// <param name="itemContext">Additional context for the item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> AddItem2RawAsync(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
@@ -147,6 +205,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events for an item (advises in MXAccess terminology).
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public async Task AdviseAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -157,6 +221,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events for an item without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> AdviseRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -175,6 +246,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events for an item (unadvises in MXAccess terminology).
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public async Task UnAdviseAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -185,6 +262,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events for an item without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> UnAdviseRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -203,6 +287,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the MXAccess session.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public async Task RemoveItemAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -213,6 +303,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the MXAccess session without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> RemoveItemRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -231,6 +328,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple items to the MXAccess session in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="tagAddresses">The item tag addresses to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> AddItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<string> tagAddresses,
|
||||
@@ -253,6 +357,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.AddItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advises multiple items in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandles">The ItemHandles to advise.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> AdviseItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
@@ -275,6 +386,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.AdviseItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes multiple items in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandles">The ItemHandles to remove.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> RemoveItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
@@ -297,6 +415,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.RemoveItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unadvises multiple items in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandles">The ItemHandles to unadvise.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> UnAdviseItemBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
@@ -319,6 +444,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.UnAdviseItemBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds and advises multiple items in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="tagAddresses">The item tag addresses to add and advise.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> SubscribeBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<string> tagAddresses,
|
||||
@@ -341,6 +473,13 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.SubscribeBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unadvises and removes multiple items in a single command.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandles">The ItemHandles to unsubscribe.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Per-item subscription results.</returns>
|
||||
public async Task<IReadOnlyList<SubscribeResult>> UnsubscribeBulkAsync(
|
||||
int serverHandle,
|
||||
IReadOnlyList<int> itemHandles,
|
||||
@@ -363,6 +502,14 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return reply.UnsubscribeBulk?.Results.ToArray() ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to an item on the MXAccess server.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="userId">User ID context for the write.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public async Task WriteAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -375,6 +522,15 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to an item on the MXAccess server without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="userId">User ID context for the write.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> WriteRawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -399,6 +555,15 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value and timestamp to an item on the MXAccess server.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestampValue">The timestamp to write with the value.</param>
|
||||
/// <param name="userId">User ID context for the write.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
public async Task Write2Async(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -418,6 +583,16 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value and timestamp to an item on the MXAccess server without error checking.
|
||||
/// </summary>
|
||||
/// <param name="serverHandle">The ServerHandle from register.</param>
|
||||
/// <param name="itemHandle">The ItemHandle from add-item.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestampValue">The timestamp to write with the value.</param>
|
||||
/// <param name="userId">User ID context for the write.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> Write2RawAsync(
|
||||
int serverHandle,
|
||||
int itemHandle,
|
||||
@@ -445,6 +620,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an MXAccess command on this session.
|
||||
/// </summary>
|
||||
/// <param name="request">The command request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The raw server reply.</returns>
|
||||
public Task<MxCommandReply> InvokeAsync(
|
||||
MxCommandRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -453,6 +634,12 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
return _client.InvokeAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streams events from the worker for this session, optionally starting after a given sequence number.
|
||||
/// </summary>
|
||||
/// <param name="afterWorkerSequence">The sequence number to stream from. Defaults to 0.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>An async enumerable of events.</returns>
|
||||
public IAsyncEnumerable<MxEvent> StreamEventsAsync(
|
||||
ulong afterWorkerSequence = 0,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -466,6 +653,9 @@ public sealed class MxGatewaySession : IAsyncDisposable
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the session and releases resources.
|
||||
/// </summary>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await CloseAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -2,8 +2,17 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when a session is not found, not ready, or invalid.</summary>
|
||||
public sealed class MxGatewaySessionException : MxGatewayException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given details.</summary>
|
||||
/// <param name="message">The error message describing the session failure.</param>
|
||||
/// <param name="sessionId">The session ID, if available.</param>
|
||||
/// <param name="correlationId">The correlation ID for tracing, if available.</param>
|
||||
/// <param name="protocolStatus">The protocol status details, if available.</param>
|
||||
/// <param name="hResult">The HResult code, if available.</param>
|
||||
/// <param name="statuses">The MXAccess statuses, if available.</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxGatewaySessionException(
|
||||
string message,
|
||||
string? sessionId = null,
|
||||
|
||||
@@ -2,8 +2,17 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Exception thrown when the worker process is unavailable or fails to process a command.</summary>
|
||||
public sealed class MxGatewayWorkerException : MxGatewayException
|
||||
{
|
||||
/// <summary>Initializes a new instance with the given details.</summary>
|
||||
/// <param name="message">The error message describing the worker failure.</param>
|
||||
/// <param name="sessionId">The session ID, if available.</param>
|
||||
/// <param name="correlationId">The correlation ID for tracing, if available.</param>
|
||||
/// <param name="protocolStatus">The protocol status details, if available.</param>
|
||||
/// <param name="hResult">The HResult code, if available.</param>
|
||||
/// <param name="statuses">The MXAccess statuses, if available.</param>
|
||||
/// <param name="innerException">The underlying exception, if any.</param>
|
||||
public MxGatewayWorkerException(
|
||||
string message,
|
||||
string? sessionId = null,
|
||||
|
||||
@@ -2,8 +2,11 @@ using MxGateway.Contracts.Proto;
|
||||
|
||||
namespace MxGateway.Client;
|
||||
|
||||
/// <summary>Extension methods for MxStatusProxy values.</summary>
|
||||
public static class MxStatusProxyExtensions
|
||||
{
|
||||
/// <summary>Returns whether the status indicates success (success flag set and category is Ok).</summary>
|
||||
/// <param name="status">The status to check.</param>
|
||||
public static bool IsSuccess(this MxStatusProxy status)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(status);
|
||||
@@ -12,6 +15,8 @@ public static class MxStatusProxyExtensions
|
||||
&& status.Category is MxStatusCategory.Ok;
|
||||
}
|
||||
|
||||
/// <summary>Returns a formatted summary of the status for diagnostic output.</summary>
|
||||
/// <param name="status">The status to summarize.</param>
|
||||
public static string ToDiagnosticSummary(this MxStatusProxy status)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(status);
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace MxGateway.Client;
|
||||
/// </summary>
|
||||
public static class MxValueExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a boolean value to an MxValue with MxDataType.Boolean.
|
||||
/// </summary>
|
||||
/// <param name="value">Scalar boolean value to wrap.</param>
|
||||
public static MxValue ToMxValue(this bool value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -20,6 +24,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 32-bit integer value to an MxValue with MxDataType.Integer.
|
||||
/// </summary>
|
||||
/// <param name="value">32-bit integer value to wrap.</param>
|
||||
public static MxValue ToMxValue(this int value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -30,6 +38,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 64-bit integer value to an MxValue with MxDataType.Integer.
|
||||
/// </summary>
|
||||
/// <param name="value">64-bit integer value to wrap.</param>
|
||||
public static MxValue ToMxValue(this long value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -40,6 +52,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a single-precision floating-point value to an MxValue with MxDataType.Float.
|
||||
/// </summary>
|
||||
/// <param name="value">Single-precision floating-point value to wrap.</param>
|
||||
public static MxValue ToMxValue(this float value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -50,6 +66,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a double-precision floating-point value to an MxValue with MxDataType.Double.
|
||||
/// </summary>
|
||||
/// <param name="value">Double-precision floating-point value to wrap.</param>
|
||||
public static MxValue ToMxValue(this double value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -60,6 +80,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string value to an MxValue with MxDataType.String.
|
||||
/// </summary>
|
||||
/// <param name="value">String value to wrap.</param>
|
||||
public static MxValue ToMxValue(this string value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
@@ -72,6 +96,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTimeOffset value to an MxValue with MxDataType.Time.
|
||||
/// </summary>
|
||||
/// <param name="value">DateTimeOffset value to wrap.</param>
|
||||
public static MxValue ToMxValue(this DateTimeOffset value)
|
||||
{
|
||||
return new MxValue
|
||||
@@ -82,6 +110,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTime value to an MxValue with MxDataType.Time.
|
||||
/// </summary>
|
||||
/// <param name="value">DateTime value to wrap.</param>
|
||||
public static MxValue ToMxValue(this DateTime value)
|
||||
{
|
||||
return new DateTimeOffset(
|
||||
@@ -91,6 +123,10 @@ public static class MxValueExtensions
|
||||
.ToMxValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a boolean array to an MxValue with MxDataType.Boolean.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of boolean values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<bool> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -105,6 +141,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 32-bit integer array to an MxValue with MxDataType.Integer.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of 32-bit integer values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<int> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -119,6 +159,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 64-bit integer array to an MxValue with MxDataType.Integer.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of 64-bit integer values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<long> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -133,6 +177,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a single-precision floating-point array to an MxValue with MxDataType.Float.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of single-precision floating-point values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<float> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -147,6 +195,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a double-precision floating-point array to an MxValue with MxDataType.Double.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of double-precision floating-point values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<double> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -161,6 +213,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string array to an MxValue with MxDataType.String.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of string values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<string> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -175,6 +231,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTimeOffset array to an MxValue with MxDataType.Time.
|
||||
/// </summary>
|
||||
/// <param name="values">Array of DateTimeOffset values to wrap.</param>
|
||||
public static MxValue ToMxValue(this IReadOnlyList<DateTimeOffset> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
@@ -189,6 +249,10 @@ public static class MxValueExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the projection kind (field name) of the given MxValue's current oneof value.
|
||||
/// </summary>
|
||||
/// <param name="value">The MxValue whose oneof projection kind is returned.</param>
|
||||
public static string GetProjectionKind(this MxValue value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
@@ -208,6 +272,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an MxValue to a CLR object; returns the boxed value or null for null MxValues.
|
||||
/// </summary>
|
||||
/// <param name="value">The MxValue to convert.</param>
|
||||
public static object? ToClrValue(this MxValue value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
@@ -227,6 +295,10 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an MxArray to a CLR array; returns null if the array does not have a known element type.
|
||||
/// </summary>
|
||||
/// <param name="array">The MxArray to convert.</param>
|
||||
public static object? ToClrArrayValue(this MxArray array)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(array);
|
||||
@@ -249,6 +321,13 @@ public static class MxValueExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an MxValue with MxDataType.Unknown from raw byte data, variant type, and diagnostic info.
|
||||
/// </summary>
|
||||
/// <param name="value">Raw byte data representing the value.</param>
|
||||
/// <param name="variantType">Variant type string (e.g., "VT_BSTR").</param>
|
||||
/// <param name="rawDiagnostic">Diagnostic string describing the raw value.</param>
|
||||
/// <param name="rawDataType">Optional MXAccess data type override.</param>
|
||||
public static MxValue ToRawMxValue(
|
||||
byte[] value,
|
||||
string variantType,
|
||||
|
||||
Reference in New Issue
Block a user