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 UnAdviseAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { MxCommandReply reply = await UnAdviseRawAsync(serverHandle, itemHandle, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); } public Task UnAdviseRawAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.UnAdvise, UnAdvise = new UnAdviseCommand { ServerHandle = serverHandle, ItemHandle = itemHandle, }, }, cancellationToken); } public async Task RemoveItemAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { MxCommandReply reply = await RemoveItemRawAsync(serverHandle, itemHandle, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); } public Task RemoveItemRawAsync( int serverHandle, int itemHandle, CancellationToken cancellationToken = default) { return InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.RemoveItem, RemoveItem = new RemoveItemCommand { ServerHandle = serverHandle, ItemHandle = itemHandle, }, }, cancellationToken); } public async Task> AddItemBulkAsync( int serverHandle, IReadOnlyList tagAddresses, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(tagAddresses); AddItemBulkCommand command = new() { ServerHandle = serverHandle }; command.TagAddresses.Add(tagAddresses); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.AddItemBulk, AddItemBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.AddItemBulk?.Results.ToArray() ?? []; } public async Task> AdviseItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(itemHandles); AdviseItemBulkCommand command = new() { ServerHandle = serverHandle }; command.ItemHandles.Add(itemHandles); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.AdviseItemBulk, AdviseItemBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.AdviseItemBulk?.Results.ToArray() ?? []; } public async Task> RemoveItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(itemHandles); RemoveItemBulkCommand command = new() { ServerHandle = serverHandle }; command.ItemHandles.Add(itemHandles); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.RemoveItemBulk, RemoveItemBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.RemoveItemBulk?.Results.ToArray() ?? []; } public async Task> UnAdviseItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(itemHandles); UnAdviseItemBulkCommand command = new() { ServerHandle = serverHandle }; command.ItemHandles.Add(itemHandles); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.UnAdviseItemBulk, UnAdviseItemBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.UnAdviseItemBulk?.Results.ToArray() ?? []; } public async Task> SubscribeBulkAsync( int serverHandle, IReadOnlyList tagAddresses, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(tagAddresses); SubscribeBulkCommand command = new() { ServerHandle = serverHandle }; command.TagAddresses.Add(tagAddresses); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.SubscribeBulk, SubscribeBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.SubscribeBulk?.Results.ToArray() ?? []; } public async Task> UnsubscribeBulkAsync( int serverHandle, IReadOnlyList itemHandles, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(itemHandles); UnsubscribeBulkCommand command = new() { ServerHandle = serverHandle }; command.ItemHandles.Add(itemHandles); MxCommandReply reply = await InvokeCommandAsync( new MxCommand { Kind = MxCommandKind.UnsubscribeBulk, UnsubscribeBulk = command, }, cancellationToken) .ConfigureAwait(false); reply.EnsureProtocolSuccess().EnsureMxAccessSuccess(); return reply.UnsubscribeBulk?.Results.ToArray() ?? []; } 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); } }