using MxGateway.Contracts.Proto; namespace MxGateway.Client; /// /// Represents one gateway-backed MXAccess session. /// public sealed class MxGatewaySession : IAsyncDisposable { private readonly MxGatewayClient _client; private readonly SemaphoreSlim _closeLock = new(1, 1); private CloseSessionReply? _closeReply; internal MxGatewaySession( MxGatewayClient client, OpenSessionReply openSessionReply) { _client = client ?? throw new ArgumentNullException(nameof(client)); OpenSessionReply = openSessionReply ?? throw new ArgumentNullException(nameof(openSessionReply)); } public string SessionId => OpenSessionReply.SessionId; public OpenSessionReply OpenSessionReply { get; } public async Task CloseAsync(CancellationToken cancellationToken = default) { if (_closeReply is not null) { return _closeReply; } await _closeLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (_closeReply is not null) { return _closeReply; } _closeReply = await _client.CloseSessionRawAsync( new CloseSessionRequest { SessionId = SessionId }, cancellationToken) .ConfigureAwait(false); return _closeReply; } finally { _closeLock.Release(); } } public async Task RegisterAsync( string clientName, CancellationToken cancellationToken = default) { MxCommandReply reply = await RegisterRawAsync(clientName, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.Register?.ServerHandle ?? reply.ReturnValue.Int32Value; } public Task RegisterRawAsync( string clientName, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(clientName); return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.Register, Register = new RegisterCommand { ClientName = clientName }, }, cancellationToken); } public async Task AddItemAsync( int serverHandle, string itemDefinition, CancellationToken cancellationToken = default) { MxCommandReply reply = await AddItemRawAsync( serverHandle, itemDefinition, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.AddItem?.ItemHandle ?? reply.ReturnValue.Int32Value; } public Task AddItemRawAsync( int serverHandle, string itemDefinition, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(itemDefinition); return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.AddItem, AddItem = new AddItemCommand { ServerHandle = serverHandle, ItemDefinition = itemDefinition, }, }, cancellationToken); } public async Task AddItem2Async( int serverHandle, string itemDefinition, string itemContext, CancellationToken cancellationToken = default) { MxCommandReply reply = await AddItem2RawAsync( serverHandle, itemDefinition, itemContext, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.AddItem2?.ItemHandle ?? reply.ReturnValue.Int32Value; } public Task AddItem2RawAsync( int serverHandle, string itemDefinition, string itemContext, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(itemDefinition); return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.AddItem2, AddItem2 = new AddItem2Command { ServerHandle = serverHandle, ItemDefinition = itemDefinition, ItemContext = itemContext ?? string.Empty, }, }, cancellationToken); } public async Task AdviseAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { MxCommandReply reply = await AdviseRawAsync(serverHandle, itemHandle, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); } public Task AdviseRawAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.Advise, Advise = new AdviseCommand { ServerHandle = serverHandle, ItemHandle = itemHandle, }, }, cancellationToken); } public async Task WriteAsync( int serverHandle, int itemHandle, MxValue value, int userId, CancellationToken cancellationToken = default) { MxCommandReply reply = await WriteRawAsync(serverHandle, itemHandle, value, userId, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); } public Task WriteRawAsync( int serverHandle, int itemHandle, MxValue value, int userId, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(value); return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.Write, Write = new WriteCommand { ServerHandle = serverHandle, ItemHandle = itemHandle, Value = value, UserId = userId, }, }, cancellationToken); } public async Task Write2Async( int serverHandle, int itemHandle, MxValue value, MxValue timestampValue, int userId, CancellationToken cancellationToken = default) { MxCommandReply reply = await Write2RawAsync( serverHandle, itemHandle, value, timestampValue, userId, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); } public Task Write2RawAsync( int serverHandle, int itemHandle, MxValue value, MxValue timestampValue, int userId, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(value); ArgumentNullException.ThrowIfNull(timestampValue); return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.Write2, Write2 = new Write2Command { ServerHandle = serverHandle, ItemHandle = itemHandle, Value = value, TimestampValue = timestampValue, UserId = userId, }, }, cancellationToken); } public Task InvokeAsync( MxCommandRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); return _client.InvokeAsync(request, cancellationToken); } public IAsyncEnumerable StreamEventsAsync( ulong afterWorkerSequence = 0, CancellationToken cancellationToken = default) { return _client.StreamEventsAsync( new StreamEventsRequest { SessionId = SessionId, AfterWorkerSequence = afterWorkerSequence, }, cancellationToken); } public async ValueTask DisposeAsync() { await CloseAsync().ConfigureAwait(false); _closeLock.Dispose(); } private Task InvokeCommandAsync( MxCommand command, CancellationToken cancellationToken) { return _client.InvokeAsync( new MxCommandRequest { SessionId = SessionId, ClientCorrelationId = Guid.NewGuid().ToString("N"), Command = command, }, cancellationToken); } }