using System; using System.Collections.Generic; using ZB.MOM.WW.MxGateway.Contracts.Proto; using ZB.MOM.WW.MxGateway.Worker.Conversion; using ZB.MOM.WW.MxGateway.Worker.Sta; namespace ZB.MOM.WW.MxGateway.Worker.MxAccess; /// /// Executes MXAccess commands on an STA session. /// public sealed class MxAccessCommandExecutor : IStaCommandExecutor { /// Default per-tag timeout used when ReadBulkCommand.timeout_ms is zero. internal static readonly TimeSpan DefaultReadBulkTimeout = TimeSpan.FromMilliseconds(1000); private readonly MxAccessSession session; private readonly VariantConverter variantConverter; private readonly IAlarmCommandHandler? alarmCommandHandler; private readonly Action pumpStep; /// /// Initializes a command executor with an MXAccess session. /// /// MXAccess session on the STA thread. public MxAccessCommandExecutor(MxAccessSession session) : this(session, new VariantConverter(), alarmCommandHandler: null, pumpStep: null) { } /// /// 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, variantConverter, alarmCommandHandler: null, pumpStep: null) { } /// /// Initializes a command executor with an MXAccess session, variant /// converter, and an alarm command handler. The alarm handler is /// optional — when null, alarm-side commands return an /// "alarm consumer not configured" diagnostic. /// public MxAccessCommandExecutor( MxAccessSession session, VariantConverter variantConverter, IAlarmCommandHandler? alarmCommandHandler) : this(session, variantConverter, alarmCommandHandler, pumpStep: null) { } /// /// Initializes a command executor with an MXAccess session, variant /// converter, alarm command handler, and a Windows-message pump action. /// The pump action is invoked from inside ReadBulk's wait loop so /// MXAccess COM events queued for this STA can be dispatched while the /// executor is still holding the thread. Pass null in tests where /// ReadBulk is exercised against a fake worker that pre-populates the /// value cache — the executor falls back to a no-op pump step. /// public MxAccessCommandExecutor( MxAccessSession session, VariantConverter variantConverter, IAlarmCommandHandler? alarmCommandHandler, Action? pumpStep) { this.session = session ?? throw new ArgumentNullException(nameof(session)); this.variantConverter = variantConverter ?? throw new ArgumentNullException(nameof(variantConverter)); this.alarmCommandHandler = alarmCommandHandler; this.pumpStep = pumpStep ?? (static () => { }); } /// /// 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.Write => ExecuteWrite(command), MxCommandKind.Write2 => ExecuteWrite2(command), MxCommandKind.WriteSecured => ExecuteWriteSecured(command), MxCommandKind.WriteSecured2 => ExecuteWriteSecured2(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), MxCommandKind.WriteBulk => ExecuteWriteBulk(command), MxCommandKind.Write2Bulk => ExecuteWrite2Bulk(command), MxCommandKind.WriteSecuredBulk => ExecuteWriteSecuredBulk(command), MxCommandKind.WriteSecured2Bulk => ExecuteWriteSecured2Bulk(command), MxCommandKind.ReadBulk => ExecuteReadBulk(command), MxCommandKind.SubscribeAlarms => ExecuteSubscribeAlarms(command), MxCommandKind.UnsubscribeAlarms => ExecuteUnsubscribeAlarms(command), MxCommandKind.AcknowledgeAlarm => ExecuteAcknowledgeAlarm(command), MxCommandKind.AcknowledgeAlarmByName => ExecuteAcknowledgeAlarmByName(command), MxCommandKind.QueryActiveAlarms => ExecuteQueryActiveAlarms(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 ExecuteWrite(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Write) { return CreateInvalidRequestReply(command, "Write command payload is required."); } WriteCommand writeCommand = command.Command.Write; if (writeCommand.Value is null) { return CreateInvalidRequestReply(command, "Write command value is required."); } session.Write( writeCommand.ServerHandle, writeCommand.ItemHandle, variantConverter.ConvertToComValue(writeCommand.Value), writeCommand.UserId); return CreateOkReply(command); } private MxCommandReply ExecuteWrite2(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Write2) { return CreateInvalidRequestReply(command, "Write2 command payload is required."); } Write2Command write2Command = command.Command.Write2; if (write2Command.Value is null) { return CreateInvalidRequestReply(command, "Write2 command value is required."); } if (write2Command.TimestampValue is null) { return CreateInvalidRequestReply(command, "Write2 command timestamp value is required."); } session.Write2( write2Command.ServerHandle, write2Command.ItemHandle, variantConverter.ConvertToComValue(write2Command.Value), variantConverter.ConvertToComValue(write2Command.TimestampValue), write2Command.UserId); return CreateOkReply(command); } private MxCommandReply ExecuteWriteSecured(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.WriteSecured) { return CreateInvalidRequestReply(command, "WriteSecured command payload is required."); } WriteSecuredCommand writeSecuredCommand = command.Command.WriteSecured; if (writeSecuredCommand.Value is null) { return CreateInvalidRequestReply(command, "WriteSecured command value is required."); } session.WriteSecured( writeSecuredCommand.ServerHandle, writeSecuredCommand.ItemHandle, writeSecuredCommand.CurrentUserId, writeSecuredCommand.VerifierUserId, variantConverter.ConvertToComValue(writeSecuredCommand.Value)); return CreateOkReply(command); } private MxCommandReply ExecuteWriteSecured2(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.WriteSecured2) { return CreateInvalidRequestReply(command, "WriteSecured2 command payload is required."); } WriteSecured2Command writeSecured2Command = command.Command.WriteSecured2; if (writeSecured2Command.Value is null) { return CreateInvalidRequestReply(command, "WriteSecured2 command value is required."); } if (writeSecured2Command.TimestampValue is null) { return CreateInvalidRequestReply(command, "WriteSecured2 command timestamp value is required."); } session.WriteSecured2( writeSecured2Command.ServerHandle, writeSecured2Command.ItemHandle, writeSecured2Command.CurrentUserId, writeSecured2Command.VerifierUserId, variantConverter.ConvertToComValue(writeSecured2Command.Value), variantConverter.ConvertToComValue(writeSecured2Command.TimestampValue)); 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 MxCommandReply ExecuteWriteBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.WriteBulk) { return CreateInvalidRequestReply(command, "WriteBulk command payload is required."); } WriteBulkCommand writeBulkCommand = command.Command.WriteBulk; foreach (WriteBulkEntry entry in writeBulkCommand.Entries) { if (entry.Value is null) { return CreateInvalidRequestReply( command, $"WriteBulk entry for item handle {entry.ItemHandle} is missing its value."); } } return CreateBulkWriteReply( command, session.WriteBulk( writeBulkCommand.ServerHandle, writeBulkCommand.Entries, value => variantConverter.ConvertToComValue(value))); } private MxCommandReply ExecuteWrite2Bulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Write2Bulk) { return CreateInvalidRequestReply(command, "Write2Bulk command payload is required."); } Write2BulkCommand write2BulkCommand = command.Command.Write2Bulk; foreach (Write2BulkEntry entry in write2BulkCommand.Entries) { if (entry.Value is null) { return CreateInvalidRequestReply( command, $"Write2Bulk entry for item handle {entry.ItemHandle} is missing its value."); } if (entry.TimestampValue is null) { return CreateInvalidRequestReply( command, $"Write2Bulk entry for item handle {entry.ItemHandle} is missing its timestamp value."); } } return CreateBulkWriteReply( command, session.Write2Bulk( write2BulkCommand.ServerHandle, write2BulkCommand.Entries, value => variantConverter.ConvertToComValue(value))); } private MxCommandReply ExecuteWriteSecuredBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.WriteSecuredBulk) { return CreateInvalidRequestReply(command, "WriteSecuredBulk command payload is required."); } WriteSecuredBulkCommand writeSecuredBulkCommand = command.Command.WriteSecuredBulk; foreach (WriteSecuredBulkEntry entry in writeSecuredBulkCommand.Entries) { if (entry.Value is null) { return CreateInvalidRequestReply( command, $"WriteSecuredBulk entry for item handle {entry.ItemHandle} is missing its value."); } } return CreateBulkWriteReply( command, session.WriteSecuredBulk( writeSecuredBulkCommand.ServerHandle, writeSecuredBulkCommand.Entries, value => variantConverter.ConvertToComValue(value))); } private MxCommandReply ExecuteWriteSecured2Bulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.WriteSecured2Bulk) { return CreateInvalidRequestReply(command, "WriteSecured2Bulk command payload is required."); } WriteSecured2BulkCommand writeSecured2BulkCommand = command.Command.WriteSecured2Bulk; foreach (WriteSecured2BulkEntry entry in writeSecured2BulkCommand.Entries) { if (entry.Value is null) { return CreateInvalidRequestReply( command, $"WriteSecured2Bulk entry for item handle {entry.ItemHandle} is missing its value."); } if (entry.TimestampValue is null) { return CreateInvalidRequestReply( command, $"WriteSecured2Bulk entry for item handle {entry.ItemHandle} is missing its timestamp value."); } } return CreateBulkWriteReply( command, session.WriteSecured2Bulk( writeSecured2BulkCommand.ServerHandle, writeSecured2BulkCommand.Entries, value => variantConverter.ConvertToComValue(value))); } private MxCommandReply ExecuteReadBulk(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.ReadBulk) { return CreateInvalidRequestReply(command, "ReadBulk command payload is required."); } ReadBulkCommand readBulkCommand = command.Command.ReadBulk; TimeSpan timeout = readBulkCommand.TimeoutMs == 0 ? DefaultReadBulkTimeout : TimeSpan.FromMilliseconds(readBulkCommand.TimeoutMs); IReadOnlyList results = session.ReadBulk( readBulkCommand.ServerHandle, readBulkCommand.TagAddresses, timeout, pumpStep); MxCommandReply reply = CreateOkReply(command); BulkReadReply bulkReply = new(); bulkReply.Results.Add(results); reply.ReadBulk = bulkReply; return reply; } private MxCommandReply ExecuteSubscribeAlarms(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.SubscribeAlarms) { return CreateInvalidRequestReply(command, "SubscribeAlarms command payload is required."); } if (alarmCommandHandler is null) { return CreateInvalidRequestReply( command, "SubscribeAlarms requires an alarm command handler; the worker was constructed without one."); } string subscription = command.Command.SubscribeAlarms.SubscriptionExpression ?? string.Empty; if (string.IsNullOrWhiteSpace(subscription)) { return CreateInvalidRequestReply(command, "SubscribeAlarms.subscription_expression is required."); } try { alarmCommandHandler.Subscribe(subscription, command.SessionId); return CreateOkReply(command); } catch (Exception ex) { return CreateAlarmFailureReply(command, ex); } } private MxCommandReply ExecuteUnsubscribeAlarms(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.UnsubscribeAlarms) { return CreateInvalidRequestReply(command, "UnsubscribeAlarms command payload is required."); } if (alarmCommandHandler is null) { // No handler configured — Unsubscribe is a no-op in that case; // it can't be in a subscribed state to begin with. return CreateOkReply(command); } try { alarmCommandHandler.Unsubscribe(); return CreateOkReply(command); } catch (Exception ex) { return CreateAlarmFailureReply(command, ex); } } private MxCommandReply ExecuteAcknowledgeAlarm(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AcknowledgeAlarmCommand) { return CreateInvalidRequestReply(command, "AcknowledgeAlarm command payload is required."); } if (alarmCommandHandler is null) { return CreateInvalidRequestReply( command, "AcknowledgeAlarm requires an alarm command handler; the worker was constructed without one."); } AcknowledgeAlarmCommand payload = command.Command.AcknowledgeAlarmCommand; if (!Guid.TryParse(payload.AlarmGuid, out Guid alarmGuid)) { return CreateInvalidRequestReply( command, $"AcknowledgeAlarm.alarm_guid is not a valid canonical GUID: '{payload.AlarmGuid}'."); } try { int rc = alarmCommandHandler.Acknowledge( alarmGuid, payload.Comment, payload.OperatorUser, payload.OperatorNode, payload.OperatorDomain, payload.OperatorFullName); MxCommandReply reply = CreateOkReply(command); reply.Hresult = rc; reply.AcknowledgeAlarm = new AcknowledgeAlarmReplyPayload { NativeStatus = rc, }; if (rc != 0) { reply.DiagnosticMessage = $"AVEVA AlarmAckByGUID returned non-zero status {rc}."; } return reply; } catch (Exception ex) { return CreateAlarmFailureReply(command, ex); } } private MxCommandReply ExecuteAcknowledgeAlarmByName(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AcknowledgeAlarmByNameCommand) { return CreateInvalidRequestReply(command, "AcknowledgeAlarmByName command payload is required."); } if (alarmCommandHandler is null) { return CreateInvalidRequestReply( command, "AcknowledgeAlarmByName requires an alarm command handler; the worker was constructed without one."); } AcknowledgeAlarmByNameCommand payload = command.Command.AcknowledgeAlarmByNameCommand; if (string.IsNullOrWhiteSpace(payload.AlarmName)) { return CreateInvalidRequestReply(command, "AcknowledgeAlarmByName.alarm_name is required."); } try { int rc = alarmCommandHandler.AcknowledgeByName( payload.AlarmName, payload.ProviderName, payload.GroupName, payload.Comment, payload.OperatorUser, payload.OperatorNode, payload.OperatorDomain, payload.OperatorFullName); MxCommandReply reply = CreateOkReply(command); reply.Hresult = rc; reply.AcknowledgeAlarm = new AcknowledgeAlarmReplyPayload { NativeStatus = rc, }; if (rc != 0) { reply.DiagnosticMessage = $"AVEVA AlarmAckByName returned non-zero status {rc}."; } return reply; } catch (Exception ex) { return CreateAlarmFailureReply(command, ex); } } private MxCommandReply ExecuteQueryActiveAlarms(StaCommand command) { if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.QueryActiveAlarmsCommand) { return CreateInvalidRequestReply(command, "QueryActiveAlarms command payload is required."); } if (alarmCommandHandler is null) { return CreateInvalidRequestReply( command, "QueryActiveAlarms requires an alarm command handler; the worker was constructed without one."); } try { IReadOnlyList snapshots = alarmCommandHandler.QueryActive( command.Command.QueryActiveAlarmsCommand.AlarmFilterPrefix); QueryActiveAlarmsReplyPayload payload = new QueryActiveAlarmsReplyPayload(); payload.Snapshots.AddRange(snapshots); MxCommandReply reply = CreateOkReply(command); reply.QueryActiveAlarms = payload; return reply; } catch (Exception ex) { return CreateAlarmFailureReply(command, ex); } } private static MxCommandReply CreateAlarmFailureReply(StaCommand command, Exception exception) { return new MxCommandReply { SessionId = command.SessionId, CorrelationId = command.CorrelationId, Kind = command.Kind, ProtocolStatus = new ProtocolStatus { Code = ProtocolStatusCode.MxaccessFailure, Message = exception.Message, }, DiagnosticMessage = $"{exception.GetType().FullName}: {exception.Message}", }; } 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 CreateBulkWriteReply( StaCommand command, IEnumerable results) { MxCommandReply reply = CreateOkReply(command); BulkWriteReply bulkReply = new(); bulkReply.Results.Add(results); switch (command.Kind) { case MxCommandKind.WriteBulk: reply.WriteBulk = bulkReply; break; case MxCommandKind.Write2Bulk: reply.Write2Bulk = bulkReply; break; case MxCommandKind.WriteSecuredBulk: reply.WriteSecuredBulk = bulkReply; break; case MxCommandKind.WriteSecured2Bulk: reply.WriteSecured2Bulk = bulkReply; break; default: throw new InvalidOperationException($"Unsupported bulk write 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, }; } }