using Google.Protobuf.WellKnownTypes; using ZB.MOM.WW.MxGateway.Contracts.Proto; namespace ZB.MOM.WW.MxGateway.Server.Grpc; /// /// Maps between worker IPC types and gRPC contract types. /// public sealed class MxAccessGrpcMapper { private readonly TimeProvider _timeProvider; /// /// Initializes the mapper with an optional time provider. /// /// Time provider for timestamps; defaults to system time if null. public MxAccessGrpcMapper(TimeProvider? timeProvider = null) { _timeProvider = timeProvider ?? TimeProvider.System; } /// /// Maps a gRPC MX command request to a worker command. /// /// Request payload. /// The mapped ready for worker dispatch. public WorkerCommand MapCommand(MxCommandRequest request) { ArgumentNullException.ThrowIfNull(request); ArgumentNullException.ThrowIfNull(request.Command); return new WorkerCommand { Command = request.Command.Clone(), EnqueueTimestamp = Timestamp.FromDateTimeOffset(_timeProvider.GetUtcNow()), }; } /// /// Maps a worker command reply to a gRPC MX command reply. /// /// Worker command reply. /// The mapped , or a protocol-violation reply if the worker reply carried no public payload. public MxCommandReply MapCommandReply(WorkerCommandReply reply) { ArgumentNullException.ThrowIfNull(reply); if (reply.Reply is null) { return new MxCommandReply { ProtocolStatus = ProtocolViolation("Worker command reply did not contain a public reply payload."), }; } return reply.Reply.Clone(); } /// /// Maps a worker event to a gRPC MX event. /// /// Worker event to map. /// The mapped , or an unspecified-family event if the worker event carried no public payload. public MxEvent MapEvent(WorkerEvent workerEvent) { ArgumentNullException.ThrowIfNull(workerEvent); // GWC-07 / IPC-05: ownership transfer, not a deep clone. The enclosing WorkerEvent is // parsed fresh from a single pipe frame in WorkerClient's read loop and is discarded // immediately after this mapping — the SessionEventDistributor pump is its single // consumer (GWC-01 claims the worker event channel as single-reader), so nothing else // aliases or mutates workerEvent.Event. We therefore move the inner MxEvent into the // outbound graph instead of cloning it. Downstream the pump fans this one MxEvent to // every subscriber and retains it in the replay ring, but that sharing is READ-ONLY // (subscribers only yield/filter it), so a single shared instance is safe. If a second // consumer of WorkerEvent is ever added, restore a .Clone() here to re-isolate. return workerEvent.Event ?? new MxEvent { Family = MxEventFamily.Unspecified, RawStatus = "Worker event did not contain a public event payload.", }; } /// /// Creates an OK protocol status. /// /// Status message. /// A with code . public static ProtocolStatus Ok(string message = "OK") { return new ProtocolStatus { Code = ProtocolStatusCode.Ok, Message = message, }; } /// /// Creates an InvalidRequest protocol status. /// /// Status message. /// A with code . public static ProtocolStatus InvalidRequest(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.InvalidRequest, Message = message, }; } /// /// Creates a SessionNotFound protocol status. /// /// Status message. /// A with code . public static ProtocolStatus SessionNotFound(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.SessionNotFound, Message = message, }; } /// /// Creates a SessionNotReady protocol status. /// /// Status message. /// A with code . public static ProtocolStatus SessionNotReady(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.SessionNotReady, Message = message, }; } /// /// Creates a WorkerUnavailable protocol status. /// /// Status message. /// A with code . public static ProtocolStatus WorkerUnavailable(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.WorkerUnavailable, Message = message, }; } /// /// Creates a Timeout protocol status. /// /// Status message. /// A with code . public static ProtocolStatus Timeout(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.Timeout, Message = message, }; } /// /// Creates a Canceled protocol status. /// /// Status message. /// A with code . public static ProtocolStatus Canceled(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.Canceled, Message = message, }; } /// /// Creates a ProtocolViolation protocol status. /// /// Status message. /// A with code . public static ProtocolStatus ProtocolViolation(string message) { return new ProtocolStatus { Code = ProtocolStatusCode.ProtocolViolation, Message = message, }; } }