feat(worker): correlate OnWriteComplete onto plain Write/Write2 replies (06/S-1 follow-up)

OtOpcUa's dominant FreeAccess write path goes out as MX_COMMAND_KIND_WRITE,
not WriteSecured — the original 06/S-1 brief mis-scoped the correlation, so
a refused plain write was invisible on the unary reply (verified live on
windev 2026-08-09). ExecuteWrite/ExecuteWrite2 now use the same pre-call
version baseline + bounded pump-wait as the secured kinds. Bulk writes stay
fire-and-forget.
This commit is contained in:
Joseph Doherty
2026-08-09 19:47:00 -04:00
parent b948e6975e
commit b0e65d4f31
4 changed files with 170 additions and 35 deletions
@@ -427,13 +427,28 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
return CreateInvalidRequestReply(command, "Write command value is required.");
}
// Same pre-call baseline rule as ExecuteWriteSecured: plain Write is
// also fire-and-forget in MXAccess, and its commit outcome only exists
// in the later OnWriteComplete callback.
MxAccessWriteCompletionCache completionCache = session.WriteCompletionCache;
ulong completionBaseline = completionCache.CurrentVersion(
writeCommand.ServerHandle,
writeCommand.ItemHandle);
session.Write(
writeCommand.ServerHandle,
writeCommand.ItemHandle,
variantConverter.ConvertToComValue(writeCommand.Value),
writeCommand.UserId);
return CreateOkReply(command);
MxCommandReply reply = CreateOkReply(command);
AwaitWriteCompletion(
reply,
completionCache,
writeCommand.ServerHandle,
writeCommand.ItemHandle,
completionBaseline);
return reply;
}
private MxCommandReply ExecuteWrite2(StaCommand command)
@@ -454,6 +469,12 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
return CreateInvalidRequestReply(command, "Write2 command timestamp value is required.");
}
// Same pre-call baseline rule as ExecuteWriteSecured.
MxAccessWriteCompletionCache completionCache = session.WriteCompletionCache;
ulong completionBaseline = completionCache.CurrentVersion(
write2Command.ServerHandle,
write2Command.ItemHandle);
session.Write2(
write2Command.ServerHandle,
write2Command.ItemHandle,
@@ -461,7 +482,14 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
variantConverter.ConvertToComValue(write2Command.TimestampValue),
write2Command.UserId);
return CreateOkReply(command);
MxCommandReply reply = CreateOkReply(command);
AwaitWriteCompletion(
reply,
completionCache,
write2Command.ServerHandle,
write2Command.ItemHandle,
completionBaseline);
return reply;
}
private MxCommandReply ExecuteWriteSecured(StaCommand command)