Implement MXAccess write commands in the worker
The .proto contract and MxCommandKind already defined Write, Write2,
WriteSecured, and WriteSecured2, but the worker's MxAccessCommandExecutor
had no case for any of them — every write kind fell through to
CreateInvalidRequestReply ("Unsupported MXAccess command kind Write").
Implement all four:
- VariantConverter.ConvertToComValue projects an MxValue into a
COM-marshalable object (scalars, arrays, null) — the inverse of the
existing COM-to-MxValue projection.
- IMxAccessServer / MxAccessComServer gain Write/Write2/WriteSecured/
WriteSecured2, routed to ILMXProxyServer / ILMXProxyServer4.
- MxAccessSession and MxAccessCommandExecutor add the four write paths,
following the existing ExecuteAdvise pattern; the reply is a plain OK
reply and the outcome surfaces later as an OnWriteComplete event.
Verified live: a Write now returns PROTOCOL_STATUS_CODE_OK and produces
an OnWriteComplete event where it previously returned InvalidRequest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,78 @@ public sealed class VariantConverterTests
|
|||||||
Assert.Equal("VT_DATE", converted.VariantType);
|
Assert.Equal("VT_DATE", converted.VariantType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that scalar MxValue kinds convert to the matching boxed CLR type for a COM write.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithInt32_ReturnsBoxedInt()
|
||||||
|
{
|
||||||
|
object? result = _converter.ConvertToComValue(new MxValue { Int32Value = 123 });
|
||||||
|
|
||||||
|
Assert.Equal(123, Assert.IsType<int>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that a boolean MxValue converts to a boxed bool for a COM write.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithBool_ReturnsBoxedBool()
|
||||||
|
{
|
||||||
|
object? result = _converter.ConvertToComValue(new MxValue { BoolValue = true });
|
||||||
|
|
||||||
|
Assert.True(Assert.IsType<bool>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that a string MxValue converts to a string for a COM write.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithString_ReturnsString()
|
||||||
|
{
|
||||||
|
object? result = _converter.ConvertToComValue(new MxValue { StringValue = "abc" });
|
||||||
|
|
||||||
|
Assert.Equal("abc", Assert.IsType<string>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that a timestamp MxValue converts to a UTC DateTime the COM marshaler renders as VT_DATE.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithTimestamp_ReturnsUtcDateTime()
|
||||||
|
{
|
||||||
|
DateTime dateTime = new(2026, 5, 19, 12, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
object? result = _converter.ConvertToComValue(
|
||||||
|
new MxValue { TimestampValue = ProtobufTimestamp.FromDateTime(dateTime) });
|
||||||
|
|
||||||
|
Assert.Equal(dateTime, Assert.IsType<DateTime>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that an MXAccess-null MxValue converts to a CLR null.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithNull_ReturnsNull()
|
||||||
|
{
|
||||||
|
object? result = _converter.ConvertToComValue(new MxValue { IsNull = true });
|
||||||
|
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that an integer-array MxValue converts to an int array the COM marshaler renders as a SAFEARRAY.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithInt32Array_ReturnsInt32Array()
|
||||||
|
{
|
||||||
|
MxValue value = new()
|
||||||
|
{
|
||||||
|
ArrayValue = new MxArray
|
||||||
|
{
|
||||||
|
Int32Values = new Int32Array { Values = { 1, 2, 3 } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
object? result = _converter.ConvertToComValue(value);
|
||||||
|
|
||||||
|
Assert.Equal(new[] { 1, 2, 3 }, Assert.IsType<int[]>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that an MxValue with no value kind set cannot be converted for a COM write.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void ConvertToComValue_WithNoKind_Throws()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => _converter.ConvertToComValue(new MxValue()));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Verifies that file time values with expected time data type are converted to protobuf timestamps.</summary>
|
/// <summary>Verifies that file time values with expected time data type are converted to protobuf timestamps.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Convert_WithFileTimeAndExpectedTime_ProjectsTimestamp()
|
public void Convert_WithFileTimeAndExpectedTime_ProjectsTimestamp()
|
||||||
|
|||||||
@@ -379,10 +379,10 @@ public sealed class AlarmCommandExecutorTests
|
|||||||
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds) { }
|
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds) { }
|
||||||
public void Suspend(int serverHandle, int itemHandle) { }
|
public void Suspend(int serverHandle, int itemHandle) { }
|
||||||
public void Activate(int serverHandle, int itemHandle) { }
|
public void Activate(int serverHandle, int itemHandle) { }
|
||||||
public void Write(int serverHandle, int itemHandle, object value, int userId) { }
|
public void Write(int serverHandle, int itemHandle, object? value, int userId) { }
|
||||||
public void Write2(int serverHandle, int itemHandle, object value, object timestampValue, int userId) { }
|
public void Write2(int serverHandle, int itemHandle, object? value, object? timestampValue, int userId) { }
|
||||||
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object value) { }
|
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value) { }
|
||||||
public void WriteSecured2(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object value, object timestampValue) { }
|
public void WriteSecured2(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestampValue) { }
|
||||||
public int AuthenticateUser(string userName, string password) => 0;
|
public int AuthenticateUser(string userName, string password) => 0;
|
||||||
public int ArchestrAUserToId(string userName) => 0;
|
public int ArchestrAUserToId(string userName) => 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,5 +134,26 @@ public sealed class MxAccessComServerTests
|
|||||||
{
|
{
|
||||||
calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}");
|
calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Write(int serverHandle, int itemHandle, object? value, int userId)
|
||||||
|
{
|
||||||
|
calls.Add($"Write:{serverHandle}:{itemHandle}:{value}:{userId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId)
|
||||||
|
{
|
||||||
|
calls.Add($"Write2:{serverHandle}:{itemHandle}:{value}:{timestamp}:{userId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value)
|
||||||
|
{
|
||||||
|
calls.Add($"WriteSecured:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteSecured2(
|
||||||
|
int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp)
|
||||||
|
{
|
||||||
|
calls.Add($"WriteSecured2:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}:{timestamp}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using MxGateway.Contracts.Proto;
|
using MxGateway.Contracts.Proto;
|
||||||
using MxGateway.Worker.MxAccess;
|
using MxGateway.Worker.MxAccess;
|
||||||
using MxGateway.Worker.Sta;
|
using MxGateway.Worker.Sta;
|
||||||
@@ -617,6 +618,143 @@ public sealed class MxAccessCommandExecutorTests
|
|||||||
Assert.Null(factory.FakeComObject.AdviseServerHandle);
|
Assert.Null(factory.FakeComObject.AdviseServerHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that Write dispatches the converted value to MXAccess on the STA thread.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_Write_CallsMxAccessOnStaWithConvertedValue()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 70);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(CreateWriteCommand(
|
||||||
|
"write", serverHandle: 70, itemHandle: 700, value: 123, userId: 5));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Equal(MxCommandKind.Write, reply.Kind);
|
||||||
|
Assert.Equal(70, fakeComObject.WriteServerHandle);
|
||||||
|
Assert.Equal(700, fakeComObject.WriteItemHandle);
|
||||||
|
Assert.Equal(123, fakeComObject.WriteValue);
|
||||||
|
Assert.Equal(5, fakeComObject.WriteUserId);
|
||||||
|
Assert.Equal(runtime.StaThreadId, fakeComObject.WriteThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that Write2 forwards the converted value and timestamp to MXAccess.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_Write2_ForwardsValueAndTimestamp()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 71);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
DateTime timestamp = new(2026, 5, 19, 12, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(CreateWrite2Command(
|
||||||
|
"write2", serverHandle: 71, itemHandle: 710, value: 456, timestamp: timestamp, userId: 6));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Equal(MxCommandKind.Write2, reply.Kind);
|
||||||
|
Assert.Equal(710, fakeComObject.WriteItemHandle);
|
||||||
|
Assert.Equal(456, fakeComObject.WriteValue);
|
||||||
|
Assert.Equal(timestamp, fakeComObject.WriteTimestamp);
|
||||||
|
Assert.Equal(6, fakeComObject.WriteUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that WriteSecured forwards the operator and verifier user ids to MXAccess.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_WriteSecured_ForwardsUserIds()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 72);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(CreateWriteSecuredCommand(
|
||||||
|
"write-secured", serverHandle: 72, itemHandle: 720, value: 789, currentUserId: 11, verifierUserId: 22));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Equal(MxCommandKind.WriteSecured, reply.Kind);
|
||||||
|
Assert.Equal(720, fakeComObject.WriteItemHandle);
|
||||||
|
Assert.Equal(789, fakeComObject.WriteValue);
|
||||||
|
Assert.Equal(11, fakeComObject.WriteCurrentUserId);
|
||||||
|
Assert.Equal(22, fakeComObject.WriteVerifierUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that WriteSecured2 forwards user ids, value, and timestamp to MXAccess.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_WriteSecured2_ForwardsUserIdsValueAndTimestamp()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 73);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
DateTime timestamp = new(2026, 5, 19, 13, 30, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(CreateWriteSecured2Command(
|
||||||
|
"write-secured2", serverHandle: 73, itemHandle: 730, value: 1011,
|
||||||
|
timestamp: timestamp, currentUserId: 33, verifierUserId: 44));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Equal(MxCommandKind.WriteSecured2, reply.Kind);
|
||||||
|
Assert.Equal(1011, fakeComObject.WriteValue);
|
||||||
|
Assert.Equal(timestamp, fakeComObject.WriteTimestamp);
|
||||||
|
Assert.Equal(33, fakeComObject.WriteCurrentUserId);
|
||||||
|
Assert.Equal(44, fakeComObject.WriteVerifierUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that Write without a payload returns an invalid request error.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_WriteWithoutPayload_ReturnsInvalidRequest()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 74);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
"missing-write-payload",
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Null(fakeComObject.WriteServerHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that Write without a value returns an invalid request error.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task DispatchAsync_WriteWithoutValue_ReturnsInvalidRequest()
|
||||||
|
{
|
||||||
|
FakeMxAccessComObject fakeComObject = new(registerHandle: 75);
|
||||||
|
FakeMxAccessComObjectFactory factory = new(fakeComObject);
|
||||||
|
using StaRuntime runtime = CreateRuntime();
|
||||||
|
using MxAccessStaSession session = new(runtime, factory, new NoopEventSink());
|
||||||
|
await session.StartAsync(workerProcessId: 1234);
|
||||||
|
|
||||||
|
MxCommandReply reply = await session.DispatchAsync(new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
"missing-write-value",
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write,
|
||||||
|
Write = new WriteCommand
|
||||||
|
{
|
||||||
|
ServerHandle = 75,
|
||||||
|
ItemHandle = 750,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||||
|
Assert.Null(fakeComObject.WriteServerHandle);
|
||||||
|
}
|
||||||
|
|
||||||
private static StaCommand CreateRegisterCommand(
|
private static StaCommand CreateRegisterCommand(
|
||||||
string correlationId,
|
string correlationId,
|
||||||
string clientName)
|
string clientName)
|
||||||
@@ -729,6 +867,126 @@ public sealed class MxAccessCommandExecutorTests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MxValue CreateIntegerValue(int value)
|
||||||
|
{
|
||||||
|
return new MxValue
|
||||||
|
{
|
||||||
|
DataType = MxDataType.Integer,
|
||||||
|
VariantType = "VT_I4",
|
||||||
|
Int32Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MxValue CreateTimestampValue(DateTime timestamp)
|
||||||
|
{
|
||||||
|
return new MxValue
|
||||||
|
{
|
||||||
|
DataType = MxDataType.Time,
|
||||||
|
VariantType = "VT_DATE",
|
||||||
|
TimestampValue = Timestamp.FromDateTime(timestamp),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StaCommand CreateWriteCommand(
|
||||||
|
string correlationId,
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int value,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
return new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
correlationId,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write,
|
||||||
|
Write = new WriteCommand
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
ItemHandle = itemHandle,
|
||||||
|
Value = CreateIntegerValue(value),
|
||||||
|
UserId = userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StaCommand CreateWrite2Command(
|
||||||
|
string correlationId,
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int value,
|
||||||
|
DateTime timestamp,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
return new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
correlationId,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.Write2,
|
||||||
|
Write2 = new Write2Command
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
ItemHandle = itemHandle,
|
||||||
|
Value = CreateIntegerValue(value),
|
||||||
|
TimestampValue = CreateTimestampValue(timestamp),
|
||||||
|
UserId = userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StaCommand CreateWriteSecuredCommand(
|
||||||
|
string correlationId,
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int value,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId)
|
||||||
|
{
|
||||||
|
return new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
correlationId,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecured,
|
||||||
|
WriteSecured = new WriteSecuredCommand
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
ItemHandle = itemHandle,
|
||||||
|
CurrentUserId = currentUserId,
|
||||||
|
VerifierUserId = verifierUserId,
|
||||||
|
Value = CreateIntegerValue(value),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StaCommand CreateWriteSecured2Command(
|
||||||
|
string correlationId,
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int value,
|
||||||
|
DateTime timestamp,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId)
|
||||||
|
{
|
||||||
|
return new StaCommand(
|
||||||
|
"session-1",
|
||||||
|
correlationId,
|
||||||
|
new MxCommand
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.WriteSecured2,
|
||||||
|
WriteSecured2 = new WriteSecured2Command
|
||||||
|
{
|
||||||
|
ServerHandle = serverHandle,
|
||||||
|
ItemHandle = itemHandle,
|
||||||
|
CurrentUserId = currentUserId,
|
||||||
|
VerifierUserId = verifierUserId,
|
||||||
|
Value = CreateIntegerValue(value),
|
||||||
|
TimestampValue = CreateTimestampValue(timestamp),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static StaCommand CreateUnAdviseCommand(
|
private static StaCommand CreateUnAdviseCommand(
|
||||||
string correlationId,
|
string correlationId,
|
||||||
int serverHandle,
|
int serverHandle,
|
||||||
@@ -1080,6 +1338,118 @@ public sealed class MxAccessCommandExecutorTests
|
|||||||
throw adviseSupervisoryException;
|
throw adviseSupervisoryException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the server handle passed to the most recent write, if called.</summary>
|
||||||
|
public int? WriteServerHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the item handle passed to the most recent write, if called.</summary>
|
||||||
|
public int? WriteItemHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the value passed to the most recent write, if called.</summary>
|
||||||
|
public object? WriteValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the timestamp passed to the most recent timestamped write, if called.</summary>
|
||||||
|
public object? WriteTimestamp { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the user id passed to the most recent Write/Write2, if called.</summary>
|
||||||
|
public int? WriteUserId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the current user id passed to the most recent secured write, if called.</summary>
|
||||||
|
public int? WriteCurrentUserId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the verifier user id passed to the most recent secured write, if called.</summary>
|
||||||
|
public int? WriteVerifierUserId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the thread ID on which the most recent write was called.</summary>
|
||||||
|
public int? WriteThreadId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Writes a value to an item and tracks the operation.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle for the write.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="value">Value to write.</param>
|
||||||
|
/// <param name="userId">MXAccess user id for the write.</param>
|
||||||
|
public void Write(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
operationNames.Add($"Write:{serverHandle}:{itemHandle}");
|
||||||
|
WriteServerHandle = serverHandle;
|
||||||
|
WriteItemHandle = itemHandle;
|
||||||
|
WriteValue = value;
|
||||||
|
WriteUserId = userId;
|
||||||
|
WriteThreadId = Environment.CurrentManagedThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a timestamped value to an item and tracks the operation.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle for the write.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="value">Value to write.</param>
|
||||||
|
/// <param name="timestamp">Source timestamp for the write.</param>
|
||||||
|
/// <param name="userId">MXAccess user id for the write.</param>
|
||||||
|
public void Write2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
object? timestamp,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
operationNames.Add($"Write2:{serverHandle}:{itemHandle}");
|
||||||
|
WriteServerHandle = serverHandle;
|
||||||
|
WriteItemHandle = itemHandle;
|
||||||
|
WriteValue = value;
|
||||||
|
WriteTimestamp = timestamp;
|
||||||
|
WriteUserId = userId;
|
||||||
|
WriteThreadId = Environment.CurrentManagedThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs a secured write to an item and tracks the operation.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle for the write.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="currentUserId">Operator user id.</param>
|
||||||
|
/// <param name="verifierUserId">Verifier user id.</param>
|
||||||
|
/// <param name="value">Value to write.</param>
|
||||||
|
public void WriteSecured(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value)
|
||||||
|
{
|
||||||
|
operationNames.Add($"WriteSecured:{serverHandle}:{itemHandle}");
|
||||||
|
WriteServerHandle = serverHandle;
|
||||||
|
WriteItemHandle = itemHandle;
|
||||||
|
WriteCurrentUserId = currentUserId;
|
||||||
|
WriteVerifierUserId = verifierUserId;
|
||||||
|
WriteValue = value;
|
||||||
|
WriteThreadId = Environment.CurrentManagedThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs a secured timestamped write to an item and tracks the operation.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle for the write.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="currentUserId">Operator user id.</param>
|
||||||
|
/// <param name="verifierUserId">Verifier user id.</param>
|
||||||
|
/// <param name="value">Value to write.</param>
|
||||||
|
/// <param name="timestamp">Source timestamp for the write.</param>
|
||||||
|
public void WriteSecured2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value,
|
||||||
|
object? timestamp)
|
||||||
|
{
|
||||||
|
operationNames.Add($"WriteSecured2:{serverHandle}:{itemHandle}");
|
||||||
|
WriteServerHandle = serverHandle;
|
||||||
|
WriteItemHandle = itemHandle;
|
||||||
|
WriteCurrentUserId = currentUserId;
|
||||||
|
WriteVerifierUserId = verifierUserId;
|
||||||
|
WriteValue = value;
|
||||||
|
WriteTimestamp = timestamp;
|
||||||
|
WriteThreadId = Environment.CurrentManagedThreadId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Factory for creating fake MXAccess COM objects in tests.</summary>
|
/// <summary>Factory for creating fake MXAccess COM objects in tests.</summary>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using MxGateway.Contracts.Proto;
|
using MxGateway.Contracts.Proto;
|
||||||
@@ -118,6 +119,63 @@ public sealed class VariantConverter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an <see cref="MxValue"/> into a CLR object suitable for an
|
||||||
|
/// MXAccess COM write. The COM marshaler boxes the returned value into the
|
||||||
|
/// matching VARIANT, so this is the inverse of <see cref="Convert(object?)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Protobuf value to convert.</param>
|
||||||
|
/// <returns>A COM-marshalable value, or <see langword="null"/> for an MXAccess null.</returns>
|
||||||
|
public object? ConvertToComValue(MxValue value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.IsNull)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.KindCase switch
|
||||||
|
{
|
||||||
|
MxValue.KindOneofCase.BoolValue => value.BoolValue,
|
||||||
|
MxValue.KindOneofCase.Int32Value => value.Int32Value,
|
||||||
|
MxValue.KindOneofCase.Int64Value => value.Int64Value,
|
||||||
|
MxValue.KindOneofCase.FloatValue => value.FloatValue,
|
||||||
|
MxValue.KindOneofCase.DoubleValue => value.DoubleValue,
|
||||||
|
MxValue.KindOneofCase.StringValue => value.StringValue,
|
||||||
|
// The COM marshaler renders a DateTime as VT_DATE; MXAccess accepts
|
||||||
|
// it as the timestamped-write time argument.
|
||||||
|
MxValue.KindOneofCase.TimestampValue => value.TimestampValue.ToDateTime(),
|
||||||
|
MxValue.KindOneofCase.ArrayValue => ConvertToComArray(value.ArrayValue),
|
||||||
|
MxValue.KindOneofCase.RawValue => throw new ArgumentException(
|
||||||
|
"MxValue raw payloads cannot be written to MXAccess.", nameof(value)),
|
||||||
|
_ => throw new ArgumentException(
|
||||||
|
"MxValue has no value kind set; nothing to write.", nameof(value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Array ConvertToComArray(MxArray array)
|
||||||
|
{
|
||||||
|
return array.ValuesCase switch
|
||||||
|
{
|
||||||
|
MxArray.ValuesOneofCase.BoolValues => array.BoolValues.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.Int32Values => array.Int32Values.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.Int64Values => array.Int64Values.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.FloatValues => array.FloatValues.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.DoubleValues => array.DoubleValues.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.StringValues => array.StringValues.Values.ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.TimestampValues =>
|
||||||
|
array.TimestampValues.Values.Select(timestamp => timestamp.ToDateTime()).ToArray(),
|
||||||
|
MxArray.ValuesOneofCase.RawValues => throw new ArgumentException(
|
||||||
|
"MxArray raw payloads cannot be written to MXAccess.", nameof(array)),
|
||||||
|
_ => throw new ArgumentException(
|
||||||
|
"MxArray has no element values set; nothing to write.", nameof(array)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static MxValue ConvertScalar(
|
private static MxValue ConvertScalar(
|
||||||
object value,
|
object value,
|
||||||
MxDataType expectedDataType)
|
MxDataType expectedDataType)
|
||||||
|
|||||||
@@ -56,4 +56,56 @@ public interface IMxAccessServer
|
|||||||
void AdviseSupervisory(
|
void AdviseSupervisory(
|
||||||
int serverHandle,
|
int serverHandle,
|
||||||
int itemHandle);
|
int itemHandle);
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <param name="value">COM-marshalable value to write; <see langword="null"/> writes an MXAccess null.</param>
|
||||||
|
/// <param name="userId">MXAccess user id (security classification) for the write.</param>
|
||||||
|
void Write(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
int userId);
|
||||||
|
|
||||||
|
/// <summary>Writes a value with an explicit source timestamp to an item.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write; <see langword="null"/> writes an MXAccess null.</param>
|
||||||
|
/// <param name="timestamp">COM-marshalable source timestamp for the write.</param>
|
||||||
|
/// <param name="userId">MXAccess user id (security classification) for the write.</param>
|
||||||
|
void Write2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
object? timestamp,
|
||||||
|
int userId);
|
||||||
|
|
||||||
|
/// <summary>Performs a secured/verified write to an item.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="currentUserId">MXAccess user id of the operator performing the write.</param>
|
||||||
|
/// <param name="verifierUserId">MXAccess user id of the verifier authorizing the write.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write; <see langword="null"/> writes an MXAccess null.</param>
|
||||||
|
void WriteSecured(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value);
|
||||||
|
|
||||||
|
/// <summary>Performs a secured/verified write with an explicit source timestamp.</summary>
|
||||||
|
/// <param name="serverHandle">Server handle identifying the registration.</param>
|
||||||
|
/// <param name="itemHandle">Item handle to write to.</param>
|
||||||
|
/// <param name="currentUserId">MXAccess user id of the operator performing the write.</param>
|
||||||
|
/// <param name="verifierUserId">MXAccess user id of the verifier authorizing the write.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write; <see langword="null"/> writes an MXAccess null.</param>
|
||||||
|
/// <param name="timestamp">COM-marshalable source timestamp for the write.</param>
|
||||||
|
void WriteSecured2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value,
|
||||||
|
object? timestamp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,74 @@ public sealed class MxAccessComServer : IMxAccessServer
|
|||||||
AsProxyServer4().AdviseSupervisory(serverHandle, itemHandle);
|
AsProxyServer4().AdviseSupervisory(serverHandle, itemHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Write(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||||
|
{
|
||||||
|
typedFake.Write(serverHandle, itemHandle, value, userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsProxyServer().Write(serverHandle, itemHandle, value!, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Write2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
object? timestamp,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||||
|
{
|
||||||
|
typedFake.Write2(serverHandle, itemHandle, value, timestamp, userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsProxyServer4().Write2(serverHandle, itemHandle, value!, timestamp!, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void WriteSecured(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value)
|
||||||
|
{
|
||||||
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||||
|
{
|
||||||
|
typedFake.WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsProxyServer().WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void WriteSecured2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value,
|
||||||
|
object? timestamp)
|
||||||
|
{
|
||||||
|
if (mxAccessComObject is IMxAccessServer typedFake)
|
||||||
|
{
|
||||||
|
typedFake.WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value, timestamp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsProxyServer4().WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value!, timestamp!);
|
||||||
|
}
|
||||||
|
|
||||||
private ILMXProxyServer AsProxyServer()
|
private ILMXProxyServer AsProxyServer()
|
||||||
{
|
{
|
||||||
return mxAccessComObject as ILMXProxyServer
|
return mxAccessComObject as ILMXProxyServer
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
|||||||
MxCommandKind.Advise => ExecuteAdvise(command),
|
MxCommandKind.Advise => ExecuteAdvise(command),
|
||||||
MxCommandKind.UnAdvise => ExecuteUnAdvise(command),
|
MxCommandKind.UnAdvise => ExecuteUnAdvise(command),
|
||||||
MxCommandKind.AdviseSupervisory => ExecuteAdviseSupervisory(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.AddItemBulk => ExecuteAddItemBulk(command),
|
||||||
MxCommandKind.AdviseItemBulk => ExecuteAdviseItemBulk(command),
|
MxCommandKind.AdviseItemBulk => ExecuteAdviseItemBulk(command),
|
||||||
MxCommandKind.RemoveItemBulk => ExecuteRemoveItemBulk(command),
|
MxCommandKind.RemoveItemBulk => ExecuteRemoveItemBulk(command),
|
||||||
@@ -223,6 +227,108 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
|
|||||||
return CreateOkReply(command);
|
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)
|
private MxCommandReply ExecuteAddItemBulk(StaCommand command)
|
||||||
{
|
{
|
||||||
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddItemBulk)
|
if (command.Command.PayloadCase != MxCommand.PayloadOneofCase.AddItemBulk)
|
||||||
|
|||||||
@@ -227,6 +227,78 @@ public sealed class MxAccessSession : IDisposable
|
|||||||
MxAccessAdviceKind.Supervisory);
|
MxAccessAdviceKind.Supervisory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// <param name="value">COM-marshalable value to write.</param>
|
||||||
|
/// <param name="userId">MXAccess user id (security classification) for the write.</param>
|
||||||
|
public void Write(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
mxAccessServer.Write(serverHandle, itemHandle, value, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a value with an explicit source timestamp to an item.</summary>
|
||||||
|
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write.</param>
|
||||||
|
/// <param name="timestamp">COM-marshalable source timestamp for the write.</param>
|
||||||
|
/// <param name="userId">MXAccess user id (security classification) for the write.</param>
|
||||||
|
public void Write2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
object? value,
|
||||||
|
object? timestamp,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
mxAccessServer.Write2(serverHandle, itemHandle, value, timestamp, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs a secured/verified write to an item.</summary>
|
||||||
|
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="currentUserId">MXAccess user id of the operator performing the write.</param>
|
||||||
|
/// <param name="verifierUserId">MXAccess user id of the verifier authorizing the write.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write.</param>
|
||||||
|
public void WriteSecured(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
mxAccessServer.WriteSecured(serverHandle, itemHandle, currentUserId, verifierUserId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs a secured/verified write with an explicit source timestamp.</summary>
|
||||||
|
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="itemHandle">Handle returned by the worker.</param>
|
||||||
|
/// <param name="currentUserId">MXAccess user id of the operator performing the write.</param>
|
||||||
|
/// <param name="verifierUserId">MXAccess user id of the verifier authorizing the write.</param>
|
||||||
|
/// <param name="value">COM-marshalable value to write.</param>
|
||||||
|
/// <param name="timestamp">COM-marshalable source timestamp for the write.</param>
|
||||||
|
public void WriteSecured2(
|
||||||
|
int serverHandle,
|
||||||
|
int itemHandle,
|
||||||
|
int currentUserId,
|
||||||
|
int verifierUserId,
|
||||||
|
object? value,
|
||||||
|
object? timestamp)
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
mxAccessServer.WriteSecured2(serverHandle, itemHandle, currentUserId, verifierUserId, value, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Adds multiple items in bulk, returning success/failure results.</summary>
|
/// <summary>Adds multiple items in bulk, returning success/failure results.</summary>
|
||||||
/// <param name="serverHandle">Handle returned by the worker.</param>
|
/// <param name="serverHandle">Handle returned by the worker.</param>
|
||||||
/// <param name="tagAddresses">Enumerable of item definitions to add.</param>
|
/// <param name="tagAddresses">Enumerable of item definitions to add.</param>
|
||||||
|
|||||||
Reference in New Issue
Block a user