using System; using System.Collections.Generic; using MxGateway.Contracts.Proto; using MxGateway.Worker.Conversion; using MxGateway.Worker.Sta; namespace MxGateway.Worker.MxAccess; /// /// Executes MXAccess commands on an STA session. /// public sealed class MxAccessCommandExecutor : IStaCommandExecutor { private readonly MxAccessSession session; private readonly VariantConverter variantConverter; /// /// Initializes a command executor with an MXAccess session. /// /// MXAccess session on the STA thread. public MxAccessCommandExecutor(MxAccessSession session) : this(session, new VariantConverter()) { } /// /// Initializes a command executor with an MXAccess session and a variant converter. /// /// MXAccess session on the STA thread. /// Converter for MXAccess variant values to MxValue protobuf messages. public MxAccessCommandExecutor( MxAccessSession session, VariantConverter variantConverter) { this.session = session ?? throw new ArgumentNullException(nameof(session)); this.variantConverter = variantConverter ?? throw new ArgumentNullException(nameof(variantConverter)); } /// /// Executes an MXAccess command and returns the reply. /// /// STA command to execute. /// Command reply with result or error details. public MxCommandReply Execute(StaCommand command) { if (command is null) { throw new ArgumentNullException(nameof(command)); } return command.Kind switch { MxCommandKind.Register => ExecuteRegister(command), MxCommandKind.Unregister => ExecuteUnregister(command), MxCommandKind.AddItem => ExecuteAddItem(command), MxCommandKind.AddItem2 => ExecuteAddItem2(command), MxCommandKind.RemoveItem => ExecuteRemoveItem(command), MxCommandKind.Advise => ExecuteAdvise(command), MxCommandKind.UnAdvise => ExecuteUnAdvise(command), MxCommandKind.AdviseSupervisory => ExecuteAdviseSupervisory(command), MxCommandKind.AddItemBulk => ExecuteAddItemBulk(command), MxCommandKind.AdviseItemBulk => ExecuteAdviseItemBulk(command), MxCommandKind.RemoveItemBulk => ExecuteRemoveItemBulk(command), MxCommandKind.UnAdviseItemBulk => ExecuteUnAdviseItemBulk(command), MxCommandKind.SubscribeBulk => ExecuteSubscribeBulk(command), MxCommandKind.UnsubscribeBulk => ExecuteUnsubscribeBulk(command), _ => CreateInvalidRequestReply(command, $"Unsupported MXAccess command kind {command.Kind}."), }; } private MxCommandReply ExecuteRegister(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Register) { return CreateInvalidRequestReply(command, "Register command payload is required."); } int serverHandle = session.Register(command.Command.Register.ClientName); MxCommandReply reply = CreateOkReply(command); reply.ReturnValue = variantConverter.Convert(serverHandle); reply.Register = new RegisterReply { ServerHandle = serverHandle, }; return reply; } private MxCommandReply ExecuteUnregister(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Unregister) { return CreateInvalidRequestReply(command, "Unregister command payload is required."); } session.Unregister(command.Command.Unregister.ServerHandle); return CreateOkReply(command); } private MxCommandReply ExecuteAddItem(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddItem) { return CreateInvalidRequestReply(command, "AddItem command payload is required."); } AddItemCommand addItemCommand = command.Command.AddItem; int itemHandle = session.AddItem( addItemCommand.ServerHandle, addItemCommand.ItemDefinition); MxCommandReply reply = CreateOkReply(command); reply.ReturnValue = variantConverter.Convert(itemHandle); reply.AddItem = new AddItemReply { ItemHandle = itemHandle, }; return reply; } private MxCommandReply ExecuteAddItem2(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddItem2) { return CreateInvalidRequestReply(command, "AddItem2 command payload is required."); } AddItem2Command addItem2Command = command.Command.AddItem2; int itemHandle = session.AddItem2( addItem2Command.ServerHandle, addItem2Command.ItemDefinition, addItem2Command.ItemContext); MxCommandReply reply = CreateOkReply(command); reply.ReturnValue = variantConverter.Convert(itemHandle); reply.AddItem2 = new AddItem2Reply { ItemHandle = itemHandle, }; return reply; } private MxCommandReply ExecuteRemoveItem(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.RemoveItem) { return CreateInvalidRequestReply(command, "RemoveItem command payload is required."); } RemoveItemCommand removeItemCommand = command.Command.RemoveItem; session.RemoveItem( removeItemCommand.ServerHandle, removeItemCommand.ItemHandle); return CreateOkReply(command); } private MxCommandReply ExecuteAdvise(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Advise) { return CreateInvalidRequestReply(command, "Advise command payload is required."); } AdviseCommand adviseCommand = command.Command.Advise; session.Advise( adviseCommand.ServerHandle, adviseCommand.ItemHandle); return CreateOkReply(command); } private MxCommandReply ExecuteUnAdvise(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.UnAdvise) { return CreateInvalidRequestReply(command, "UnAdvise command payload is required."); } UnAdviseCommand unAdviseCommand = command.Command.UnAdvise; session.UnAdvise( unAdviseCommand.ServerHandle, unAdviseCommand.ItemHandle); return CreateOkReply(command); } private MxCommandReply ExecuteAdviseSupervisory(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AdviseSupervisory) { return CreateInvalidRequestReply(command, "AdviseSupervisory command payload is required."); } AdviseSupervisoryCommand adviseSupervisoryCommand = command.Command.AdviseSupervisory; session.AdviseSupervisory( adviseSupervisoryCommand.ServerHandle, adviseSupervisoryCommand.ItemHandle); return CreateOkReply(command); } private MxCommandReply ExecuteAddItemBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddItemBulk) { return CreateInvalidRequestReply(command, "AddItemBulk command payload is required."); } AddItemBulkCommand addItemBulkCommand = command.Command.AddItemBulk; return CreateBulkReply( command, session.AddItemBulk(addItemBulkCommand.ServerHandle, addItemBulkCommand.TagAddresses)); } private MxCommandReply ExecuteAdviseItemBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AdviseItemBulk) { return CreateInvalidRequestReply(command, "AdviseItemBulk command payload is required."); } AdviseItemBulkCommand adviseItemBulkCommand = command.Command.AdviseItemBulk; return CreateBulkReply( command, session.AdviseItemBulk(adviseItemBulkCommand.ServerHandle, adviseItemBulkCommand.ItemHandles)); } private MxCommandReply ExecuteRemoveItemBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.RemoveItemBulk) { return CreateInvalidRequestReply(command, "RemoveItemBulk command payload is required."); } RemoveItemBulkCommand removeItemBulkCommand = command.Command.RemoveItemBulk; return CreateBulkReply( command, session.RemoveItemBulk(removeItemBulkCommand.ServerHandle, removeItemBulkCommand.ItemHandles)); } private MxCommandReply ExecuteUnAdviseItemBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.UnAdviseItemBulk) { return CreateInvalidRequestReply(command, "UnAdviseItemBulk command payload is required."); } UnAdviseItemBulkCommand unAdviseItemBulkCommand = command.Command.UnAdviseItemBulk; return CreateBulkReply( command, session.UnAdviseItemBulk(unAdviseItemBulkCommand.ServerHandle, unAdviseItemBulkCommand.ItemHandles)); } private MxCommandReply ExecuteSubscribeBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.SubscribeBulk) { return CreateInvalidRequestReply(command, "SubscribeBulk command payload is required."); } SubscribeBulkCommand subscribeBulkCommand = command.Command.SubscribeBulk; return CreateBulkReply( command, session.SubscribeBulk(subscribeBulkCommand.ServerHandle, subscribeBulkCommand.TagAddresses)); } private MxCommandReply ExecuteUnsubscribeBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.UnsubscribeBulk) { return CreateInvalidRequestReply(command, "UnsubscribeBulk command payload is required."); } UnsubscribeBulkCommand unsubscribeBulkCommand = command.Command.UnsubscribeBulk; return CreateBulkReply( command, session.UnsubscribeBulk(unsubscribeBulkCommand.ServerHandle, unsubscribeBulkCommand.ItemHandles)); } private static MxCommandReply CreateOkReply(StaCommand command) { return new MxCommandReply { SessionId = command.SessionId, CorrelationId = command.CorrelationId, Kind = command.Kind, Hresult = 0, ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.Ok, Message = "OK", }, }; } private static MxCommandReply CreateBulkReply( StaCommand command, IEnumerable results) { MxCommandReply reply = CreateOkReply(command); BulkSubscribeReply bulkReply = new(); bulkReply.Results.Add(results); switch (command.Kind) { case MxCommandKind.AddItemBulk: reply.AddItemBulk = bulkReply; break; case MxCommandKind.AdviseItemBulk: reply.AdviseItemBulk = bulkReply; break; case MxCommandKind.RemoveItemBulk: reply.RemoveItemBulk = bulkReply; break; case MxCommandKind.UnAdviseItemBulk: reply.UnAdviseItemBulk = bulkReply; break; case MxCommandKind.SubscribeBulk: reply.SubscribeBulk = bulkReply; break; case MxCommandKind.UnsubscribeBulk: reply.UnsubscribeBulk = bulkReply; break; default: throw new InvalidOperationException($"Unsupported bulk command kind {command.Kind}."); } return reply; } private static MxCommandReply CreateInvalidRequestReply( StaCommand command, string message) { return new MxCommandReply { SessionId = command.SessionId, CorrelationId = command.CorrelationId, Kind = command.Kind, ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.InvalidRequest, Message = message, }, DiagnosticMessage = message, }; } }