feat(worker): implement 6 MXAccess COM commands in executor
Wire up the previously-unimplemented Suspend, Activate, AuthenticateUser, ArchestrAUserToId, AddBufferedItem, and SetBufferedUpdateInterval command kinds in MxAccessCommandExecutor. These are real COM calls and run on the STA via the executor. - IMxAccessServer gains the 6 methods; MxAccessComServer routes them to the right interface version (Suspend/Activate -> ILMXProxyServer4 out MxStatus, AuthenticateUser -> base ILMXProxyServer, ArchestrAUserToId -> ILMXProxyServer2, AddBufferedItem/SetBufferedUpdateInterval -> ILMXProxyServer5). - Suspend/Activate surface the native MxStatus, converted to MxStatusProxy via the existing MxStatusProxyConverter. - AuthenticateUser hands the credential straight to MXAccess and never logs it; native HResult failures propagate via the dispatcher. - MxAccessSession gains matching pass-throughs; AddBufferedItem registers the item handle in the handle registry. - Unit tests (fake IMxAccessServer / fake COM object) cover each arm plus a password-non-leak assertion; existing IMxAccessServer fakes updated. No proto changes (all request/reply messages already exist).
This commit is contained in:
@@ -259,6 +259,21 @@ public sealed class LmxSubtagAlarmSourceTests
|
||||
{
|
||||
}
|
||||
|
||||
public object Suspend(int serverHandle, int itemHandle) => new object();
|
||||
|
||||
public object Activate(int serverHandle, int itemHandle) => new object();
|
||||
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword) => 0;
|
||||
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid) => 0;
|
||||
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
=> AddItem(serverHandle, itemDefinition);
|
||||
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
}
|
||||
|
||||
internal sealed class WriteRecord
|
||||
{
|
||||
public WriteRecord(int serverHandle, int itemHandle, object? value, int userId)
|
||||
|
||||
@@ -33,6 +33,39 @@ public sealed class MxAccessComServerTests
|
||||
Assert.Equal(new[] { "Register:client-a", "Advise:77:9", "Unregister:77" }, typed.Calls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The MXAccess command methods added in the worker COM commands bundle
|
||||
/// (Suspend/Activate/AuthenticateUser/ArchestrAUserToId/AddBufferedItem/
|
||||
/// SetBufferedUpdateInterval) route through the typed interface with their
|
||||
/// arguments preserved, and the credential is never echoed back.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommandMethods_WithTypedServer_RouteThroughTypedInterface()
|
||||
{
|
||||
RecordingMxAccessServer typed = new(registerHandle: 5);
|
||||
MxAccessComServer adapter = new(typed);
|
||||
|
||||
adapter.Suspend(serverHandle: 5, itemHandle: 11);
|
||||
adapter.Activate(serverHandle: 5, itemHandle: 12);
|
||||
adapter.AuthenticateUser(serverHandle: 5, verifyUser: "Administrator", verifyUserPassword: "s3cret");
|
||||
adapter.ArchestrAUserToId(serverHandle: 5, userIdGuid: "guid-1");
|
||||
adapter.AddBufferedItem(serverHandle: 5, itemDefinition: "TestInt", itemContext: "TestChildObject");
|
||||
adapter.SetBufferedUpdateInterval(serverHandle: 5, updateIntervalMilliseconds: 250);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"Suspend:5:11",
|
||||
"Activate:5:12",
|
||||
"AuthenticateUser:5:Administrator",
|
||||
"ArchestrAUserToId:5:guid-1",
|
||||
"AddBufferedItem:5:TestInt:TestChildObject",
|
||||
"SetBufferedUpdateInterval:5:250",
|
||||
},
|
||||
typed.Calls);
|
||||
Assert.DoesNotContain(typed.Calls, call => call.Contains("s3cret", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A COM object that implements neither the typed COM interface family
|
||||
/// nor <see cref="IMxAccessServer"/> fails fast with a clear
|
||||
@@ -207,5 +240,60 @@ public sealed class MxAccessComServerTests
|
||||
{
|
||||
calls.Add($"WriteSecured2:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}:{timestamp}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Suspend call and returns a canned status.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public object Suspend(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Suspend:{serverHandle}:{itemHandle}");
|
||||
return new object();
|
||||
}
|
||||
|
||||
/// <summary>Records an Activate call and returns a canned status.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public object Activate(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Activate:{serverHandle}:{itemHandle}");
|
||||
return new object();
|
||||
}
|
||||
|
||||
/// <summary>Records an AuthenticateUser call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="verifyUser">The user name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">The credential; recorded only as a fixed marker, never echoed.</param>
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword)
|
||||
{
|
||||
calls.Add($"AuthenticateUser:{serverHandle}:{verifyUser}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an ArchestrAUserToId call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="userIdGuid">The ArchestrA user GUID to resolve.</param>
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid)
|
||||
{
|
||||
calls.Add($"ArchestrAUserToId:{serverHandle}:{userIdGuid}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an AddBufferedItem call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
/// <param name="itemContext">The item context string to record.</param>
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
calls.Add($"AddBufferedItem:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records a SetBufferedUpdateInterval call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="updateIntervalMilliseconds">The buffered update interval in milliseconds.</param>
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
calls.Add($"SetBufferedUpdateInterval:{serverHandle}:{updateIntervalMilliseconds}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,6 +952,295 @@ public sealed class MxAccessCommandExecutorTests
|
||||
Assert.Null(fakeComObject.WriteServerHandle);
|
||||
}
|
||||
|
||||
/// <summary>Verifies Suspend calls MXAccess on the STA and maps the native status to MxStatusProxy.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Suspend_CallsMxAccessOnStaAndMapsStatus()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 200);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-suspend", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(CreateSuspendCommand("suspend-1", 200, 21));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.Equal(0, reply.Hresult);
|
||||
Assert.NotNull(reply.Suspend);
|
||||
Assert.NotNull(reply.Suspend.Status);
|
||||
Assert.Equal(1, reply.Suspend.Status.Success);
|
||||
Assert.Equal(MxStatusCategory.Ok, reply.Suspend.Status.Category);
|
||||
Assert.Equal(200, fakeComObject.SuspendServerHandle);
|
||||
Assert.Equal(21, fakeComObject.SuspendItemHandle);
|
||||
Assert.Equal(runtime.StaThreadId, fakeComObject.SuspendThreadId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies Activate calls MXAccess on the STA and maps the native status to MxStatusProxy.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_Activate_CallsMxAccessOnStaAndMapsStatus()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 201);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-activate", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(CreateActivateCommand("activate-1", 201, 22));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.NotNull(reply.Activate);
|
||||
Assert.NotNull(reply.Activate.Status);
|
||||
Assert.Equal(1, reply.Activate.Status.Success);
|
||||
Assert.Equal(MxStatusCategory.Ok, reply.Activate.Status.Category);
|
||||
Assert.Equal(201, fakeComObject.ActivateServerHandle);
|
||||
Assert.Equal(22, fakeComObject.ActivateItemHandle);
|
||||
Assert.Equal(runtime.StaThreadId, fakeComObject.ActivateThreadId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies AuthenticateUser passes credentials to MXAccess on the STA and returns the user id.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AuthenticateUser_CallsMxAccessOnStaAndReturnsUserId()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 202);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-auth", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(
|
||||
CreateAuthenticateUserCommand("auth-1", 202, "Administrator", string.Empty));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.NotNull(reply.AuthenticateUser);
|
||||
Assert.Equal(1, reply.AuthenticateUser.UserId);
|
||||
Assert.Equal(202, fakeComObject.AuthenticateServerHandle);
|
||||
Assert.Equal("Administrator", fakeComObject.AuthenticateUserName);
|
||||
Assert.Equal(runtime.StaThreadId, fakeComObject.AuthenticateThreadId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the AuthenticateUser path never surfaces the credential into the
|
||||
/// command reply or any recorded diagnostic — the password is only ever
|
||||
/// handed straight to the MXAccess wrapper.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AuthenticateUser_DoesNotLeakPassword()
|
||||
{
|
||||
const string secret = "sup3r-secret-pw";
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 203);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-auth-leak", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(
|
||||
CreateAuthenticateUserCommand("auth-leak", 203, "Administrator", secret));
|
||||
|
||||
// The wrapper still receives the credential verbatim...
|
||||
Assert.Equal(secret, fakeComObject.AuthenticatePassword);
|
||||
|
||||
// ...but the reply (diagnostics, status text) and the fake's operation
|
||||
// log must never contain it.
|
||||
Assert.DoesNotContain(secret, reply.DiagnosticMessage ?? string.Empty, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(secret, reply.ProtocolStatus.Message ?? string.Empty, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(fakeComObject.OperationNames, name => name.Contains(secret, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies ArchestrAUserToId calls MXAccess on the STA and returns the resolved user id.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_ArchestrAUserToId_CallsMxAccessOnStaAndReturnsUserId()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 204);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-user-to-id", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(
|
||||
CreateArchestrAUserToIdCommand("user-to-id-1", 204, "11112222-3333-4444-5555-666677778888"));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.NotNull(reply.ArchestraUserToId);
|
||||
Assert.Equal(7, reply.ArchestraUserToId.UserId);
|
||||
Assert.Equal(204, fakeComObject.ArchestrAUserToIdServerHandle);
|
||||
Assert.Equal("11112222-3333-4444-5555-666677778888", fakeComObject.ArchestrAUserToIdGuid);
|
||||
}
|
||||
|
||||
/// <summary>Verifies AddBufferedItem calls MXAccess on the STA and tracks the buffered item handle.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_AddBufferedItem_CallsMxAccessOnStaAndTracksItemHandle()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 205);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-buffered", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(
|
||||
CreateAddBufferedItemCommand("buffered-1", 205, "TestInt", "TestChildObject"));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.NotNull(reply.AddBufferedItem);
|
||||
Assert.Equal(1, reply.AddBufferedItem.ItemHandle);
|
||||
Assert.Equal(MxDataType.Integer, reply.ReturnValue.DataType);
|
||||
Assert.Equal(1, reply.ReturnValue.Int32Value);
|
||||
Assert.Equal(205, fakeComObject.AddBufferedItemServerHandle);
|
||||
Assert.Equal("TestInt", fakeComObject.AddBufferedItemDefinition);
|
||||
Assert.Equal("TestChildObject", fakeComObject.AddBufferedItemContext);
|
||||
|
||||
RegisteredItemHandle registeredItemHandle = Assert.Single(
|
||||
await session.GetRegisteredItemHandlesAsync());
|
||||
Assert.Equal(205, registeredItemHandle.ServerHandle);
|
||||
Assert.Equal(1, registeredItemHandle.ItemHandle);
|
||||
Assert.Equal("TestInt", registeredItemHandle.ItemDefinition);
|
||||
Assert.Equal("TestChildObject", registeredItemHandle.ItemContext);
|
||||
Assert.True(registeredItemHandle.HasItemContext);
|
||||
}
|
||||
|
||||
/// <summary>Verifies SetBufferedUpdateInterval calls MXAccess on the STA and returns a base OK reply.</summary>
|
||||
[Fact]
|
||||
public async Task DispatchAsync_SetBufferedUpdateInterval_CallsMxAccessOnStaAndReturnsOk()
|
||||
{
|
||||
FakeMxAccessComObject fakeComObject = new(registerHandle: 206);
|
||||
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||
using StaRuntime runtime = CreateRuntime();
|
||||
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||
await session.StartAsync(workerProcessId: 1234);
|
||||
await session.DispatchAsync(CreateRegisterCommand("register-before-interval", "client-a"));
|
||||
|
||||
MxCommandReply reply = await session.DispatchAsync(
|
||||
CreateSetBufferedUpdateIntervalCommand("interval-1", 206, 500));
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
Assert.Equal(0, reply.Hresult);
|
||||
Assert.Equal(206, fakeComObject.SetBufferedUpdateIntervalServerHandle);
|
||||
Assert.Equal(500, fakeComObject.SetBufferedUpdateIntervalValue);
|
||||
}
|
||||
|
||||
private static StaCommand CreateSuspendCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.Suspend,
|
||||
Suspend = new SuspendCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
ItemHandle = itemHandle,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateActivateCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.Activate,
|
||||
Activate = new ActivateCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
ItemHandle = itemHandle,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateAuthenticateUserCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
string verifyUser,
|
||||
string verifyUserPassword)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.AuthenticateUser,
|
||||
AuthenticateUser = new AuthenticateUserCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
VerifyUser = verifyUser,
|
||||
VerifyUserPassword = verifyUserPassword,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateArchestrAUserToIdCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
string userIdGuid)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.ArchestraUserToId,
|
||||
ArchestraUserToId = new ArchestrAUserToIdCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
UserIdGuid = userIdGuid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateAddBufferedItemCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
string itemContext)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.AddBufferedItem,
|
||||
AddBufferedItem = new AddBufferedItemCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
ItemDefinition = itemDefinition,
|
||||
ItemContext = itemContext,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateSetBufferedUpdateIntervalCommand(
|
||||
string correlationId,
|
||||
int serverHandle,
|
||||
int updateIntervalMilliseconds)
|
||||
{
|
||||
return new StaCommand(
|
||||
"session-1",
|
||||
correlationId,
|
||||
new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.SetBufferedUpdateInterval,
|
||||
SetBufferedUpdateInterval = new SetBufferedUpdateIntervalCommand
|
||||
{
|
||||
ServerHandle = serverHandle,
|
||||
UpdateIntervalMilliseconds = updateIntervalMilliseconds,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static StaCommand CreateRegisterCommand(
|
||||
string correlationId,
|
||||
string clientName)
|
||||
@@ -1810,6 +2099,151 @@ public sealed class MxAccessCommandExecutorTests
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the server handle passed to Suspend, if called.</summary>
|
||||
public int? SuspendServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the item handle passed to Suspend, if called.</summary>
|
||||
public int? SuspendItemHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the thread ID on which Suspend was called.</summary>
|
||||
public int? SuspendThreadId { get; private set; }
|
||||
|
||||
/// <summary>Gets the server handle passed to Activate, if called.</summary>
|
||||
public int? ActivateServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the item handle passed to Activate, if called.</summary>
|
||||
public int? ActivateItemHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the thread ID on which Activate was called.</summary>
|
||||
public int? ActivateThreadId { get; private set; }
|
||||
|
||||
/// <summary>Gets the server handle passed to AuthenticateUser, if called.</summary>
|
||||
public int? AuthenticateServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the user name passed to AuthenticateUser, if called.</summary>
|
||||
public string? AuthenticateUserName { get; private set; }
|
||||
|
||||
/// <summary>Gets the credential passed to AuthenticateUser, if called. Used only to prove non-logging.</summary>
|
||||
public string? AuthenticatePassword { get; private set; }
|
||||
|
||||
/// <summary>Gets the thread ID on which AuthenticateUser was called.</summary>
|
||||
public int? AuthenticateThreadId { get; private set; }
|
||||
|
||||
/// <summary>Gets the server handle passed to ArchestrAUserToId, if called.</summary>
|
||||
public int? ArchestrAUserToIdServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the GUID passed to ArchestrAUserToId, if called.</summary>
|
||||
public string? ArchestrAUserToIdGuid { get; private set; }
|
||||
|
||||
/// <summary>Gets the server handle passed to AddBufferedItem, if called.</summary>
|
||||
public int? AddBufferedItemServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the item definition passed to AddBufferedItem, if called.</summary>
|
||||
public string? AddBufferedItemDefinition { get; private set; }
|
||||
|
||||
/// <summary>Gets the item context passed to AddBufferedItem, if called.</summary>
|
||||
public string? AddBufferedItemContext { get; private set; }
|
||||
|
||||
/// <summary>Gets the server handle passed to SetBufferedUpdateInterval, if called.</summary>
|
||||
public int? SetBufferedUpdateIntervalServerHandle { get; private set; }
|
||||
|
||||
/// <summary>Gets the interval passed to SetBufferedUpdateInterval, if called.</summary>
|
||||
public int? SetBufferedUpdateIntervalValue { get; private set; }
|
||||
|
||||
/// <summary>Suspends an item and returns a canned status whose fields drive MxStatusProxy conversion.</summary>
|
||||
/// <param name="serverHandle">Server handle for the suspend.</param>
|
||||
/// <param name="itemHandle">Item handle to suspend.</param>
|
||||
/// <returns>A status stand-in with all-OK fields.</returns>
|
||||
public object Suspend(int serverHandle, int itemHandle)
|
||||
{
|
||||
operationNames.Add($"Suspend:{serverHandle}:{itemHandle}");
|
||||
SuspendServerHandle = serverHandle;
|
||||
SuspendItemHandle = itemHandle;
|
||||
SuspendThreadId = Environment.CurrentManagedThreadId;
|
||||
return new FakeMxStatus { success = 1, category = 0, detectedBy = 0, detail = 0 };
|
||||
}
|
||||
|
||||
/// <summary>Activates an item and returns a canned status whose fields drive MxStatusProxy conversion.</summary>
|
||||
/// <param name="serverHandle">Server handle for the activate.</param>
|
||||
/// <param name="itemHandle">Item handle to activate.</param>
|
||||
/// <returns>A status stand-in with all-OK fields.</returns>
|
||||
public object Activate(int serverHandle, int itemHandle)
|
||||
{
|
||||
operationNames.Add($"Activate:{serverHandle}:{itemHandle}");
|
||||
ActivateServerHandle = serverHandle;
|
||||
ActivateItemHandle = itemHandle;
|
||||
ActivateThreadId = Environment.CurrentManagedThreadId;
|
||||
return new FakeMxStatus { success = 1, category = 0, detectedBy = 0, detail = 0 };
|
||||
}
|
||||
|
||||
/// <summary>Authenticates a user and returns a canned user id.</summary>
|
||||
/// <param name="serverHandle">Server handle for the authentication.</param>
|
||||
/// <param name="verifyUser">User name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">Credential; recorded only to assert it is never logged.</param>
|
||||
/// <returns>The canned MXAccess user id (1).</returns>
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword)
|
||||
{
|
||||
// Deliberately does NOT include the password in the operation log.
|
||||
operationNames.Add($"AuthenticateUser:{serverHandle}:{verifyUser}");
|
||||
AuthenticateServerHandle = serverHandle;
|
||||
AuthenticateUserName = verifyUser;
|
||||
AuthenticatePassword = verifyUserPassword;
|
||||
AuthenticateThreadId = Environment.CurrentManagedThreadId;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>Resolves an ArchestrA user GUID and returns a canned user id.</summary>
|
||||
/// <param name="serverHandle">Server handle for the resolution.</param>
|
||||
/// <param name="userIdGuid">ArchestrA user GUID to resolve.</param>
|
||||
/// <returns>The canned MXAccess user id (7).</returns>
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid)
|
||||
{
|
||||
operationNames.Add($"ArchestrAUserToId:{serverHandle}:{userIdGuid}");
|
||||
ArchestrAUserToIdServerHandle = serverHandle;
|
||||
ArchestrAUserToIdGuid = userIdGuid;
|
||||
return 7;
|
||||
}
|
||||
|
||||
/// <summary>Adds a buffered item and returns a canned item handle.</summary>
|
||||
/// <param name="serverHandle">Server handle to add the item to.</param>
|
||||
/// <param name="itemDefinition">Item definition string.</param>
|
||||
/// <param name="itemContext">Item context string.</param>
|
||||
/// <returns>The canned buffered item handle (1).</returns>
|
||||
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
operationNames.Add($"AddBufferedItem:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
AddBufferedItemServerHandle = serverHandle;
|
||||
AddBufferedItemDefinition = itemDefinition;
|
||||
AddBufferedItemContext = itemContext;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>Sets the buffered update interval and tracks the operation.</summary>
|
||||
/// <param name="serverHandle">Server handle for the interval change.</param>
|
||||
/// <param name="updateIntervalMilliseconds">Buffered update interval in milliseconds.</param>
|
||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
|
||||
{
|
||||
operationNames.Add($"SetBufferedUpdateInterval:{serverHandle}:{updateIntervalMilliseconds}");
|
||||
SetBufferedUpdateIntervalServerHandle = serverHandle;
|
||||
SetBufferedUpdateIntervalValue = updateIntervalMilliseconds;
|
||||
}
|
||||
|
||||
/// <summary>Status stand-in reflected over by the worker's MxStatusProxy converter.</summary>
|
||||
internal sealed class FakeMxStatus
|
||||
{
|
||||
/// <summary>Success indicator read by the status converter.</summary>
|
||||
public int success;
|
||||
|
||||
/// <summary>Status category read by the status converter.</summary>
|
||||
public int category;
|
||||
|
||||
/// <summary>Status detected-by read by the status converter.</summary>
|
||||
public int detectedBy;
|
||||
|
||||
/// <summary>Status detail read by the status converter.</summary>
|
||||
public int detail;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Factory for creating fake MXAccess COM objects in tests.</summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using ZB.MOM.WW.MxGateway.Worker.Conversion;
|
||||
using ZB.MOM.WW.MxGateway.Worker.MxAccess;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Worker.Tests.TestSupport;
|
||||
@@ -55,14 +56,10 @@ internal sealed class NoopMxAccessServer : IMxAccessServer
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Suspend(int serverHandle, int itemHandle)
|
||||
{
|
||||
}
|
||||
public object Suspend(int serverHandle, int itemHandle) => new FakeMxStatus();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Activate(int serverHandle, int itemHandle)
|
||||
{
|
||||
}
|
||||
public object Activate(int serverHandle, int itemHandle) => new FakeMxStatus();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Write(int serverHandle, int itemHandle, object? value, int userId)
|
||||
@@ -85,8 +82,29 @@ internal sealed class NoopMxAccessServer : IMxAccessServer
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AuthenticateUser(string userName, string password) => 0;
|
||||
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword) => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ArchestrAUserToId(string userName) => 0;
|
||||
public int ArchestrAUserToId(int serverHandle, string userIdGuid) => 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimal stand-in for the native <c>ArchestrA.MxAccess.MxStatus</c> struct.
|
||||
/// <see cref="MxStatusProxyConverter"/> reflects over the public
|
||||
/// <c>success</c>, <c>category</c>, <c>detectedBy</c>, and <c>detail</c>
|
||||
/// fields, so this fake exposes the same field shape with all-OK values.
|
||||
/// </summary>
|
||||
internal sealed class FakeMxStatus
|
||||
{
|
||||
/// <summary>Success indicator field read by the status converter.</summary>
|
||||
public int success;
|
||||
|
||||
/// <summary>Status category field read by the status converter.</summary>
|
||||
public int category;
|
||||
|
||||
/// <summary>Status detected-by field read by the status converter.</summary>
|
||||
public int detectedBy;
|
||||
|
||||
/// <summary>Status detail field read by the status converter.</summary>
|
||||
public int detail;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,68 @@ public interface IMxAccessServer
|
||||
int serverHandle,
|
||||
int itemHandle);
|
||||
|
||||
/// <summary>Suspends data acquisition for an advised item (ILMXProxyServer4).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="itemHandle">Item handle to suspend.</param>
|
||||
/// <returns>
|
||||
/// The native MXAccess <c>MxStatus</c> value (boxed) produced by the call.
|
||||
/// Callers convert it to a protobuf <c>MxStatusProxy</c> via the worker's
|
||||
/// status converter; the underlying type is reflected over, not cast.
|
||||
/// </returns>
|
||||
object Suspend(
|
||||
int serverHandle,
|
||||
int itemHandle);
|
||||
|
||||
/// <summary>Reactivates data acquisition for a suspended item (ILMXProxyServer4).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="itemHandle">Item handle to activate.</param>
|
||||
/// <returns>
|
||||
/// The native MXAccess <c>MxStatus</c> value (boxed) produced by the call.
|
||||
/// Callers convert it to a protobuf <c>MxStatusProxy</c> via the worker's
|
||||
/// status converter; the underlying type is reflected over, not cast.
|
||||
/// </returns>
|
||||
object Activate(
|
||||
int serverHandle,
|
||||
int itemHandle);
|
||||
|
||||
/// <summary>Authenticates an MXAccess user and returns its user id (base ILMXProxyServer).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="verifyUser">MXAccess user name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">
|
||||
/// Raw MXAccess credential. Implementations must keep this value out of
|
||||
/// logs, metrics, command lines, and diagnostics.
|
||||
/// </param>
|
||||
/// <returns>The MXAccess user id for the authenticated user.</returns>
|
||||
int AuthenticateUser(
|
||||
int serverHandle,
|
||||
string verifyUser,
|
||||
string verifyUserPassword);
|
||||
|
||||
/// <summary>Resolves an ArchestrA user GUID to an MXAccess user id (ILMXProxyServer2).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="userIdGuid">ArchestrA user GUID to resolve.</param>
|
||||
/// <returns>The MXAccess user id for the resolved user.</returns>
|
||||
int ArchestrAUserToId(
|
||||
int serverHandle,
|
||||
string userIdGuid);
|
||||
|
||||
/// <summary>Adds a buffered item to a server and returns an item handle (ILMXProxyServer5).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="itemDefinition">Item definition string.</param>
|
||||
/// <param name="itemContext">Item context string.</param>
|
||||
/// <returns>Item handle for the added buffered item.</returns>
|
||||
int AddBufferedItem(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
string itemContext);
|
||||
|
||||
/// <summary>Sets the buffered-update interval for a server (ILMXProxyServer5).</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="updateIntervalMilliseconds">Buffered update interval in milliseconds.</param>
|
||||
void SetBufferedUpdateInterval(
|
||||
int serverHandle,
|
||||
int updateIntervalMilliseconds);
|
||||
|
||||
/// <summary>Writes a value to an item.</summary>
|
||||
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||
/// <param name="itemHandle">Item handle to write to.</param>
|
||||
|
||||
@@ -140,6 +140,89 @@ public sealed class MxAccessComServer : IMxAccessServer
|
||||
AsProxyServer4().AdviseSupervisory(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Suspend(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
return typedFake.Suspend(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
AsProxyServer4().Suspend(serverHandle, itemHandle, out MxStatus status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Activate(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
return typedFake.Activate(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
AsProxyServer4().Activate(serverHandle, itemHandle, out MxStatus status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AuthenticateUser(
|
||||
int serverHandle,
|
||||
string verifyUser,
|
||||
string verifyUserPassword)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
return typedFake.AuthenticateUser(serverHandle, verifyUser, verifyUserPassword);
|
||||
}
|
||||
|
||||
return AsProxyServer().AuthenticateUser(serverHandle, verifyUser, verifyUserPassword);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ArchestrAUserToId(
|
||||
int serverHandle,
|
||||
string userIdGuid)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
return typedFake.ArchestrAUserToId(serverHandle, userIdGuid);
|
||||
}
|
||||
|
||||
return AsProxyServer2().ArchestrAUserToId(serverHandle, userIdGuid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AddBufferedItem(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
string itemContext)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
return typedFake.AddBufferedItem(serverHandle, itemDefinition, itemContext);
|
||||
}
|
||||
|
||||
return AsProxyServer5().AddBufferedItem(serverHandle, itemDefinition, itemContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetBufferedUpdateInterval(
|
||||
int serverHandle,
|
||||
int updateIntervalMilliseconds)
|
||||
{
|
||||
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||
{
|
||||
typedFake.SetBufferedUpdateInterval(serverHandle, updateIntervalMilliseconds);
|
||||
return;
|
||||
}
|
||||
|
||||
AsProxyServer5().SetBufferedUpdateInterval(serverHandle, updateIntervalMilliseconds);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Write(
|
||||
int serverHandle,
|
||||
@@ -216,6 +299,14 @@ public sealed class MxAccessComServer : IMxAccessServer
|
||||
+ $"{nameof(ILMXProxyServer)} or {nameof(IMxAccessServer)}.");
|
||||
}
|
||||
|
||||
private ILMXProxyServer2 AsProxyServer2()
|
||||
{
|
||||
return mxAccessComObject as ILMXProxyServer2
|
||||
?? throw new InvalidOperationException(
|
||||
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
||||
+ $"{nameof(ILMXProxyServer2)} or {nameof(IMxAccessServer)}.");
|
||||
}
|
||||
|
||||
private ILMXProxyServer3 AsProxyServer3()
|
||||
{
|
||||
return mxAccessComObject as ILMXProxyServer3
|
||||
@@ -231,4 +322,12 @@ public sealed class MxAccessComServer : IMxAccessServer
|
||||
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
||||
+ $"{nameof(ILMXProxyServer4)} or {nameof(IMxAccessServer)}.");
|
||||
}
|
||||
|
||||
private ILMXProxyServer5 AsProxyServer5()
|
||||
{
|
||||
return mxAccessComObject as ILMXProxyServer5
|
||||
?? throw new InvalidOperationException(
|
||||
$"MXAccess COM object of type '{mxAccessComObject.GetType().FullName}' does not implement "
|
||||
+ $"{nameof(ILMXProxyServer5)} or {nameof(IMxAccessServer)}.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
||||
|
||||
private readonly MxAccessSession session;
|
||||
private readonly VariantConverter variantConverter;
|
||||
private readonly MxStatusProxyConverter statusProxyConverter;
|
||||
private readonly IAlarmCommandHandler? alarmCommandHandler;
|
||||
private readonly Action pumpStep;
|
||||
|
||||
@@ -78,6 +79,7 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
||||
{
|
||||
this.session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
this.variantConverter = variantConverter ?? throw new ArgumentNullException(nameof(variantConverter));
|
||||
this.statusProxyConverter = new MxStatusProxyConverter();
|
||||
this.alarmCommandHandler = alarmCommandHandler;
|
||||
this.pumpStep = pumpStep ?? (static () => { });
|
||||
}
|
||||
@@ -104,6 +106,12 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
||||
MxCommandKind.Advise => ExecuteAdvise(command),
|
||||
MxCommandKind.UnAdvise => ExecuteUnAdvise(command),
|
||||
MxCommandKind.AdviseSupervisory => ExecuteAdviseSupervisory(command),
|
||||
MxCommandKind.Suspend => ExecuteSuspend(command),
|
||||
MxCommandKind.Activate => ExecuteActivate(command),
|
||||
MxCommandKind.AuthenticateUser => ExecuteAuthenticateUser(command),
|
||||
MxCommandKind.ArchestraUserToId => ExecuteArchestrAUserToId(command),
|
||||
MxCommandKind.AddBufferedItem => ExecuteAddBufferedItem(command),
|
||||
MxCommandKind.SetBufferedUpdateInterval => ExecuteSetBufferedUpdateInterval(command),
|
||||
MxCommandKind.Write => ExecuteWrite(command),
|
||||
MxCommandKind.Write2 => ExecuteWrite2(command),
|
||||
MxCommandKind.WriteSecured => ExecuteWriteSecured(command),
|
||||
@@ -262,6 +270,134 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
||||
return CreateOkReply(command);
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteSuspend(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Suspend)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "Suspend command payload is required.");
|
||||
}
|
||||
|
||||
SuspendCommand suspendCommand = command.Command.Suspend;
|
||||
object nativeStatus = session.Suspend(
|
||||
suspendCommand.ServerHandle,
|
||||
suspendCommand.ItemHandle);
|
||||
|
||||
MxCommandReply reply = CreateOkReply(command);
|
||||
reply.Suspend = new SuspendReply
|
||||
{
|
||||
Status = statusProxyConverter.Convert(nativeStatus),
|
||||
};
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteActivate(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Activate)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "Activate command payload is required.");
|
||||
}
|
||||
|
||||
ActivateCommand activateCommand = command.Command.Activate;
|
||||
object nativeStatus = session.Activate(
|
||||
activateCommand.ServerHandle,
|
||||
activateCommand.ItemHandle);
|
||||
|
||||
MxCommandReply reply = CreateOkReply(command);
|
||||
reply.Activate = new ActivateReply
|
||||
{
|
||||
Status = statusProxyConverter.Convert(nativeStatus),
|
||||
};
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteAuthenticateUser(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AuthenticateUser)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "AuthenticateUser command payload is required.");
|
||||
}
|
||||
|
||||
AuthenticateUserCommand authenticateUserCommand = command.Command.AuthenticateUser;
|
||||
|
||||
// The credential (verify_user_password) is passed straight to MXAccess
|
||||
// and is never written to logs, diagnostics, or the reply. MXAccess is
|
||||
// allowed to fail authentication; the native HResult is surfaced by the
|
||||
// dispatcher's exception path.
|
||||
int userId = session.AuthenticateUser(
|
||||
authenticateUserCommand.ServerHandle,
|
||||
authenticateUserCommand.VerifyUser,
|
||||
authenticateUserCommand.VerifyUserPassword);
|
||||
|
||||
MxCommandReply reply = CreateOkReply(command);
|
||||
reply.AuthenticateUser = new AuthenticateUserReply
|
||||
{
|
||||
UserId = userId,
|
||||
};
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteArchestrAUserToId(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.ArchestraUserToId)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "ArchestrAUserToId command payload is required.");
|
||||
}
|
||||
|
||||
ArchestrAUserToIdCommand archestrAUserToIdCommand = command.Command.ArchestraUserToId;
|
||||
int userId = session.ArchestrAUserToId(
|
||||
archestrAUserToIdCommand.ServerHandle,
|
||||
archestrAUserToIdCommand.UserIdGuid);
|
||||
|
||||
MxCommandReply reply = CreateOkReply(command);
|
||||
reply.ArchestraUserToId = new ArchestrAUserToIdReply
|
||||
{
|
||||
UserId = userId,
|
||||
};
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteAddBufferedItem(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddBufferedItem)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "AddBufferedItem command payload is required.");
|
||||
}
|
||||
|
||||
AddBufferedItemCommand addBufferedItemCommand = command.Command.AddBufferedItem;
|
||||
int itemHandle = session.AddBufferedItem(
|
||||
addBufferedItemCommand.ServerHandle,
|
||||
addBufferedItemCommand.ItemDefinition,
|
||||
addBufferedItemCommand.ItemContext);
|
||||
|
||||
MxCommandReply reply = CreateOkReply(command);
|
||||
reply.ReturnValue = variantConverter.Convert(itemHandle);
|
||||
reply.AddBufferedItem = new AddBufferedItemReply
|
||||
{
|
||||
ItemHandle = itemHandle,
|
||||
};
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteSetBufferedUpdateInterval(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.SetBufferedUpdateInterval)
|
||||
{
|
||||
return CreateInvalidRequestReply(command, "SetBufferedUpdateInterval command payload is required.");
|
||||
}
|
||||
|
||||
SetBufferedUpdateIntervalCommand setBufferedUpdateIntervalCommand = command.Command.SetBufferedUpdateInterval;
|
||||
session.SetBufferedUpdateInterval(
|
||||
setBufferedUpdateIntervalCommand.ServerHandle,
|
||||
setBufferedUpdateIntervalCommand.UpdateIntervalMilliseconds);
|
||||
|
||||
return CreateOkReply(command);
|
||||
}
|
||||
|
||||
private MxCommandReply ExecuteWrite(StaCommand command)
|
||||
{
|
||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.Write)
|
||||
|
||||
@@ -300,6 +300,94 @@ public sealed class MxAccessSession : IDisposable
|
||||
MxAccessAdviceKind.Supervisory);
|
||||
}
|
||||
|
||||
/// <summary>Suspends data acquisition for an advised item and returns the native MXAccess status.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||
/// <returns>The boxed native MXAccess status produced by the call.</returns>
|
||||
public object Suspend(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return mxAccessServer.Suspend(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
/// <summary>Reactivates data acquisition for a suspended item and returns the native MXAccess status.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||
/// <returns>The boxed native MXAccess status produced by the call.</returns>
|
||||
public object Activate(
|
||||
int serverHandle,
|
||||
int itemHandle)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return mxAccessServer.Activate(serverHandle, itemHandle);
|
||||
}
|
||||
|
||||
/// <summary>Authenticates an MXAccess user and returns its user id.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="verifyUser">MXAccess user name to authenticate.</param>
|
||||
/// <param name="verifyUserPassword">Raw MXAccess credential; never logged.</param>
|
||||
/// <returns>The MXAccess user id for the authenticated user.</returns>
|
||||
public int AuthenticateUser(
|
||||
int serverHandle,
|
||||
string verifyUser,
|
||||
string verifyUserPassword)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return mxAccessServer.AuthenticateUser(serverHandle, verifyUser, verifyUserPassword);
|
||||
}
|
||||
|
||||
/// <summary>Resolves an ArchestrA user GUID to an MXAccess user id.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="userIdGuid">ArchestrA user GUID to resolve.</param>
|
||||
/// <returns>The MXAccess user id for the resolved user.</returns>
|
||||
public int ArchestrAUserToId(
|
||||
int serverHandle,
|
||||
string userIdGuid)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return mxAccessServer.ArchestrAUserToId(serverHandle, userIdGuid);
|
||||
}
|
||||
|
||||
/// <summary>Adds a buffered item to an MXAccess server and returns the item handle.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="itemDefinition">Definition or address of the item to add.</param>
|
||||
/// <param name="itemContext">Context string for the item.</param>
|
||||
public int AddBufferedItem(
|
||||
int serverHandle,
|
||||
string itemDefinition,
|
||||
string itemContext)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
int itemHandle = mxAccessServer.AddBufferedItem(serverHandle, itemDefinition, itemContext);
|
||||
handleRegistry.RegisterItemHandle(
|
||||
serverHandle,
|
||||
itemHandle,
|
||||
itemDefinition,
|
||||
itemContext,
|
||||
hasItemContext: true);
|
||||
|
||||
return itemHandle;
|
||||
}
|
||||
|
||||
/// <summary>Sets the buffered-update interval for an MXAccess server.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="updateIntervalMilliseconds">Buffered update interval in milliseconds.</param>
|
||||
public void SetBufferedUpdateInterval(
|
||||
int serverHandle,
|
||||
int updateIntervalMilliseconds)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
mxAccessServer.SetBufferedUpdateInterval(serverHandle, updateIntervalMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>Writes a value to an item.</summary>
|
||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||
|
||||
Reference in New Issue
Block a user