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:
Joseph Doherty
2026-05-19 14:45:35 -04:00
parent e355a7674b
commit 06030dd1ef
9 changed files with 823 additions and 4 deletions
@@ -227,6 +227,78 @@ public sealed class MxAccessSession : IDisposable
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>
/// <param name="serverHandle">Handle returned by the worker.</param>
/// <param name="tagAddresses">Enumerable of item definitions to add.</param>